commit c60f598661fe86270911a8e4cd84abbab6ca3a98 Author: milkwx3 Date: Sat Jun 13 22:02:49 2026 +0300 First commit (very unfinished) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..267bf5d --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +OUTPUT/* +OLD STUFF +build +makeheaders +libraries/* +.vscode +android +__UTILS__ +snapshots \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7317a02 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.23) + +project(RCRAFT) +add_subdirectory(src) \ No newline at end of file diff --git a/compile.sh b/compile.sh new file mode 100644 index 0000000..d702b9d --- /dev/null +++ b/compile.sh @@ -0,0 +1,21 @@ +#/usr/bin/bash + +./makeheaders/makeheaders -h src/helpers/vars.hpp src/helpers/chunks.cpp src/helpers/debug.cpp src/helpers/entities.cpp src/helpers/input.cpp src/helpers/menu.cpp src/helpers/mesher.cpp src/helpers/networking.cpp src/helpers/utils.cpp src/helpers/inlines.hpp src/helpers/inventory.cpp src/helpers/locale.cpp src/helpers/recipes.cpp src/helpers/particles.cpp src/helpers/player.cpp src/helpers/multisound.cpp src/helpers/svlib.cpp -v > src/helpers/common.hpp + +#rm -r build +echo Building Linux... +cmake -B build -DUNIX=ON -DUNIX=true +cd build +make -j12 +cd .. +cp build/src/RCRAFT* OUTPUT +#cp build/src/dedicated_server/RDS* OUTPUT + +#rm -r build +#echo Building Windows... +#cmake -DCMAKE_TOOLCHAIN_FILE=toolchains/mingw-w64.cmake -DWIN32=ON -DWIN32=true -B build +#cd build +#make -j12 +#cd .. +#cp build/src/RCRAFT.exe OUTPUT +#cp build/src/dedicated_server/RDS.exe OUTPUT diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..6563c4e --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.23) + +project(RCRAFT) +add_subdirectory(helpers) +#add_subdirectory(dedicated_server) +set(YAML_BUILD_SHARED_LIBS OFF) +set(YAML_MSVC_SHARED_RT OFF) +set(OPENGL_VERSION 4.3) +add_subdirectory(libraries/raylib) +add_subdirectory(libraries/enet) +add_subdirectory(libraries/yaml-cpp) +add_subdirectory(libraries/Lua) +if (WIN32) + add_compile_definitions(WIN32) + add_compile_options(-DWIN32) + link_libraries(ws2_32) +endif() + +execute_process(COMMAND date +v.%yw%W OUTPUT_VARIABLE BUILD_DATE OUTPUT_STRIP_TRAILING_WHITESPACE) +add_compile_definitions(GAME_VERSION="${BUILD_DATE}") + +add_executable(RCRAFT rcraft.cpp) +target_include_directories(RCRAFT PRIVATE + libraries/enet/include/. + libraries/yaml-cpp/include/. + libraries/raylib/src/. + libraries/Lua/lua-5.4.7/src/ +) +target_link_libraries(RCRAFT PRIVATE + raylib + enet + helpers + yaml-cpp + lua_static +) \ No newline at end of file diff --git a/src/PerlinNoise.hpp b/src/PerlinNoise.hpp new file mode 100644 index 0000000..9dc3106 --- /dev/null +++ b/src/PerlinNoise.hpp @@ -0,0 +1,659 @@ +//---------------------------------------------------------------------------------------- +// +// siv::PerlinNoise +// Perlin noise library for modern C++ +// +// Copyright (C) 2013-2021 Ryo Suzuki +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//---------------------------------------------------------------------------------------- + +# pragma once +# include +# include +# include +# include +# include +# include +# include + +# if __has_include() && defined(__cpp_concepts) +# include +# endif + + +// Library major version +# define SIVPERLIN_VERSION_MAJOR 3 + +// Library minor version +# define SIVPERLIN_VERSION_MINOR 0 + +// Library revision version +# define SIVPERLIN_VERSION_REVISION 0 + +// Library version +# define SIVPERLIN_VERSION ((SIVPERLIN_VERSION_MAJOR * 100 * 100) + (SIVPERLIN_VERSION_MINOR * 100) + (SIVPERLIN_VERSION_REVISION)) + + +// [[nodiscard]] for constructors +# if (201907L <= __has_cpp_attribute(nodiscard)) +# define SIVPERLIN_NODISCARD_CXX20 [[nodiscard]] +# else +# define SIVPERLIN_NODISCARD_CXX20 +# endif + + +// std::uniform_random_bit_generator concept +# if __cpp_lib_concepts +# define SIVPERLIN_CONCEPT_URBG template +# define SIVPERLIN_CONCEPT_URBG_ template +# else +# define SIVPERLIN_CONCEPT_URBG template , std::is_unsigned>>>* = nullptr> +# define SIVPERLIN_CONCEPT_URBG_ template , std::is_unsigned>>>*> +# endif + + +// arbitrary value for increasing entropy +# ifndef SIVPERLIN_DEFAULT_Y +# define SIVPERLIN_DEFAULT_Y (0.12345) +# endif + +// arbitrary value for increasing entropy +# ifndef SIVPERLIN_DEFAULT_Z +# define SIVPERLIN_DEFAULT_Z (0.34567) +# endif + + +namespace siv +{ + template + class BasicPerlinNoise + { + public: + + static_assert(std::is_floating_point_v); + + /////////////////////////////////////// + // + // Typedefs + // + + using state_type = std::array; + + using value_type = Float; + + using default_random_engine = std::mt19937; + + using seed_type = typename default_random_engine::result_type; + + /////////////////////////////////////// + // + // Constructors + // + + SIVPERLIN_NODISCARD_CXX20 + constexpr BasicPerlinNoise() noexcept; + + SIVPERLIN_NODISCARD_CXX20 + explicit BasicPerlinNoise(seed_type seed); + + SIVPERLIN_CONCEPT_URBG + SIVPERLIN_NODISCARD_CXX20 + explicit BasicPerlinNoise(URBG&& urbg); + + /////////////////////////////////////// + // + // Reseed + // + + void reseed(seed_type seed); + + SIVPERLIN_CONCEPT_URBG + void reseed(URBG&& urbg); + + /////////////////////////////////////// + // + // Serialization + // + + [[nodiscard]] + constexpr const state_type& serialize() const noexcept; + + constexpr void deserialize(const state_type& state) noexcept; + + /////////////////////////////////////// + // + // Noise (The result is in the range [-1, 1]) + // + + [[nodiscard]] + value_type noise1D(value_type x) const noexcept; + + [[nodiscard]] + value_type noise2D(value_type x, value_type y) const noexcept; + + [[nodiscard]] + value_type noise3D(value_type x, value_type y, value_type z) const noexcept; + + /////////////////////////////////////// + // + // Noise (The result is remapped to the range [0, 1]) + // + + [[nodiscard]] + value_type noise1D_01(value_type x) const noexcept; + + [[nodiscard]] + value_type noise2D_01(value_type x, value_type y) const noexcept; + + [[nodiscard]] + value_type noise3D_01(value_type x, value_type y, value_type z) const noexcept; + + /////////////////////////////////////// + // + // Octave noise (The result can be out of the range [-1, 1]) + // + + [[nodiscard]] + value_type octave1D(value_type x, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + + [[nodiscard]] + value_type octave2D(value_type x, value_type y, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + + [[nodiscard]] + value_type octave3D(value_type x, value_type y, value_type z, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + + /////////////////////////////////////// + // + // Octave noise (The result is clamped to the range [-1, 1]) + // + + [[nodiscard]] + value_type octave1D_11(value_type x, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + + [[nodiscard]] + value_type octave2D_11(value_type x, value_type y, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + + [[nodiscard]] + value_type octave3D_11(value_type x, value_type y, value_type z, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + + /////////////////////////////////////// + // + // Octave noise (The result is clamped and remapped to the range [0, 1]) + // + + [[nodiscard]] + value_type octave1D_01(value_type x, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + + [[nodiscard]] + value_type octave2D_01(value_type x, value_type y, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + + [[nodiscard]] + value_type octave3D_01(value_type x, value_type y, value_type z, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + + /////////////////////////////////////// + // + // Octave noise (The result is normalized to the range [-1, 1]) + // + + [[nodiscard]] + value_type normalizedOctave1D(value_type x, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + + [[nodiscard]] + value_type normalizedOctave2D(value_type x, value_type y, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + + [[nodiscard]] + value_type normalizedOctave3D(value_type x, value_type y, value_type z, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + + /////////////////////////////////////// + // + // Octave noise (The result is normalized and remapped to the range [0, 1]) + // + + [[nodiscard]] + value_type normalizedOctave1D_01(value_type x, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + + [[nodiscard]] + value_type normalizedOctave2D_01(value_type x, value_type y, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + + [[nodiscard]] + value_type normalizedOctave3D_01(value_type x, value_type y, value_type z, std::int32_t octaves, value_type persistence = value_type(0.5)) const noexcept; + + private: + + state_type m_permutation; + }; + + using PerlinNoise = BasicPerlinNoise; + + namespace perlin_detail + { + //////////////////////////////////////////////// + // + // These functions are provided for consistency. + // You may get different results from std::shuffle() with different standard library implementations. + // + SIVPERLIN_CONCEPT_URBG + [[nodiscard]] + inline std::uint64_t Random(const std::uint64_t max, URBG&& urbg) + { + return (urbg() % (max + 1)); + } + + template + inline void Shuffle(RandomIt first, RandomIt last, URBG&& urbg) + { + if (first == last) + { + return; + } + + using difference_type = typename std::iterator_traits::difference_type; + + for (RandomIt it = first + 1; it < last; ++it) + { + const std::uint64_t n = static_cast(it - first); + std::iter_swap(it, first + static_cast(Random(n, std::forward(urbg)))); + } + } + // + //////////////////////////////////////////////// + + template + [[nodiscard]] + inline constexpr Float Fade(const Float t) noexcept + { + return t * t * t * (t * (t * 6 - 15) + 10); + } + + template + [[nodiscard]] + inline constexpr Float Lerp(const Float a, const Float b, const Float t) noexcept + { + return (a + (b - a) * t); + } + + template + [[nodiscard]] + inline constexpr Float Grad(const std::uint8_t hash, const Float x, const Float y, const Float z) noexcept + { + const std::uint8_t h = hash & 15; + const Float u = h < 8 ? x : y; + const Float v = h < 4 ? y : h == 12 || h == 14 ? x : z; + return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); + } + + template + [[nodiscard]] + inline constexpr Float Remap_01(const Float x) noexcept + { + return (x * Float(0.5) + Float(0.5)); + } + + template + [[nodiscard]] + inline constexpr Float Clamp_11(const Float x) noexcept + { + return std::clamp(x, Float(-1.0), Float(1.0)); + } + + template + [[nodiscard]] + inline constexpr Float RemapClamp_01(const Float x) noexcept + { + if (x <= Float(-1.0)) + { + return Float(0.0); + } + else if (Float(1.0) <= x) + { + return Float(1.0); + } + + return (x * Float(0.5) + Float(0.5)); + } + + template + [[nodiscard]] + inline auto Octave1D(const Noise& noise, Float x, const std::int32_t octaves, const Float persistence) noexcept + { + using value_type = Float; + value_type result = 0; + value_type amplitude = 1; + + for (std::int32_t i = 0; i < octaves; ++i) + { + result += (noise.noise1D(x) * amplitude); + x *= 2; + amplitude *= persistence; + } + + return result; + } + + template + [[nodiscard]] + inline auto Octave2D(const Noise& noise, Float x, Float y, const std::int32_t octaves, const Float persistence) noexcept + { + using value_type = Float; + value_type result = 0; + value_type amplitude = 1; + + for (std::int32_t i = 0; i < octaves; ++i) + { + result += (noise.noise2D(x, y) * amplitude); + x *= 2; + y *= 2; + amplitude *= persistence; + } + + return result; + } + + template + [[nodiscard]] + inline auto Octave3D(const Noise& noise, Float x, Float y, Float z, const std::int32_t octaves, const Float persistence) noexcept + { + using value_type = Float; + value_type result = 0; + value_type amplitude = 1; + + for (std::int32_t i = 0; i < octaves; ++i) + { + result += (noise.noise3D(x, y, z) * amplitude); + x *= 2; + y *= 2; + z *= 2; + amplitude *= persistence; + } + + return result; + } + + template + [[nodiscard]] + inline constexpr Float MaxAmplitude(const std::int32_t octaves, const Float persistence) noexcept + { + using value_type = Float; + value_type result = 0; + value_type amplitude = 1; + + for (std::int32_t i = 0; i < octaves; ++i) + { + result += amplitude; + amplitude *= persistence; + } + + return result; + } + } + + /////////////////////////////////////// + + template + inline constexpr BasicPerlinNoise::BasicPerlinNoise() noexcept + : m_permutation{ 151,160,137,91,90,15, + 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, + 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, + 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, + 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, + 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, + 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, + 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, + 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, + 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, + 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, + 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, + 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 } {} + + template + inline BasicPerlinNoise::BasicPerlinNoise(const seed_type seed) + { + reseed(seed); + } + + template + SIVPERLIN_CONCEPT_URBG_ + inline BasicPerlinNoise::BasicPerlinNoise(URBG&& urbg) + { + reseed(std::forward(urbg)); + } + + /////////////////////////////////////// + + template + inline void BasicPerlinNoise::reseed(const seed_type seed) + { + reseed(default_random_engine{ seed }); + } + + template + SIVPERLIN_CONCEPT_URBG_ + inline void BasicPerlinNoise::reseed(URBG&& urbg) + { + std::iota(m_permutation.begin(), m_permutation.end(), uint8_t{ 0 }); + + perlin_detail::Shuffle(m_permutation.begin(), m_permutation.end(), std::forward(urbg)); + } + + /////////////////////////////////////// + + template + inline constexpr const typename BasicPerlinNoise::state_type& BasicPerlinNoise::serialize() const noexcept + { + return m_permutation; + } + + template + inline constexpr void BasicPerlinNoise::deserialize(const state_type& state) noexcept + { + m_permutation = state; + } + + /////////////////////////////////////// + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::noise1D(const value_type x) const noexcept + { + return noise3D(x, + static_cast(SIVPERLIN_DEFAULT_Y), + static_cast(SIVPERLIN_DEFAULT_Z)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::noise2D(const value_type x, const value_type y) const noexcept + { + return noise3D(x, + y, + static_cast(SIVPERLIN_DEFAULT_Z)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::noise3D(const value_type x, const value_type y, const value_type z) const noexcept + { + const value_type _x = std::floor(x); + const value_type _y = std::floor(y); + const value_type _z = std::floor(z); + + const std::int32_t ix = static_cast(_x) & 255; + const std::int32_t iy = static_cast(_y) & 255; + const std::int32_t iz = static_cast(_z) & 255; + + const value_type fx = (x - _x); + const value_type fy = (y - _y); + const value_type fz = (z - _z); + + const value_type u = perlin_detail::Fade(fx); + const value_type v = perlin_detail::Fade(fy); + const value_type w = perlin_detail::Fade(fz); + + const std::uint8_t A = (m_permutation[ix & 255] + iy) & 255; + const std::uint8_t B = (m_permutation[(ix + 1) & 255] + iy) & 255; + + const std::uint8_t AA = (m_permutation[A] + iz) & 255; + const std::uint8_t AB = (m_permutation[(A + 1) & 255] + iz) & 255; + + const std::uint8_t BA = (m_permutation[B] + iz) & 255; + const std::uint8_t BB = (m_permutation[(B + 1) & 255] + iz) & 255; + + const value_type p0 = perlin_detail::Grad(m_permutation[AA], fx, fy, fz); + const value_type p1 = perlin_detail::Grad(m_permutation[BA], fx - 1, fy, fz); + const value_type p2 = perlin_detail::Grad(m_permutation[AB], fx, fy - 1, fz); + const value_type p3 = perlin_detail::Grad(m_permutation[BB], fx - 1, fy - 1, fz); + const value_type p4 = perlin_detail::Grad(m_permutation[(AA + 1) & 255], fx, fy, fz - 1); + const value_type p5 = perlin_detail::Grad(m_permutation[(BA + 1) & 255], fx - 1, fy, fz - 1); + const value_type p6 = perlin_detail::Grad(m_permutation[(AB + 1) & 255], fx, fy - 1, fz - 1); + const value_type p7 = perlin_detail::Grad(m_permutation[(BB + 1) & 255], fx - 1, fy - 1, fz - 1); + + const value_type q0 = perlin_detail::Lerp(p0, p1, u); + const value_type q1 = perlin_detail::Lerp(p2, p3, u); + const value_type q2 = perlin_detail::Lerp(p4, p5, u); + const value_type q3 = perlin_detail::Lerp(p6, p7, u); + + const value_type r0 = perlin_detail::Lerp(q0, q1, v); + const value_type r1 = perlin_detail::Lerp(q2, q3, v); + + return perlin_detail::Lerp(r0, r1, w); + } + + /////////////////////////////////////// + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::noise1D_01(const value_type x) const noexcept + { + return perlin_detail::Remap_01(noise1D(x)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::noise2D_01(const value_type x, const value_type y) const noexcept + { + return perlin_detail::Remap_01(noise2D(x, y)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::noise3D_01(const value_type x, const value_type y, const value_type z) const noexcept + { + return perlin_detail::Remap_01(noise3D(x, y, z)); + } + + /////////////////////////////////////// + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave1D(const value_type x, const std::int32_t octaves, const value_type persistence) const noexcept + { + return perlin_detail::Octave1D(*this, x, octaves, persistence); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave2D(const value_type x, const value_type y, const std::int32_t octaves, const value_type persistence) const noexcept + { + return perlin_detail::Octave2D(*this, x, y, octaves, persistence); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave3D(const value_type x, const value_type y, const value_type z, const std::int32_t octaves, const value_type persistence) const noexcept + { + return perlin_detail::Octave3D(*this, x, y, z, octaves, persistence); + } + + /////////////////////////////////////// + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave1D_11(const value_type x, const std::int32_t octaves, const value_type persistence) const noexcept + { + return perlin_detail::Clamp_11(octave1D(x, octaves, persistence)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave2D_11(const value_type x, const value_type y, const std::int32_t octaves, const value_type persistence) const noexcept + { + return perlin_detail::Clamp_11(octave2D(x, y, octaves, persistence)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave3D_11(const value_type x, const value_type y, const value_type z, const std::int32_t octaves, const value_type persistence) const noexcept + { + return perlin_detail::Clamp_11(octave3D(x, y, z, octaves, persistence)); + } + + /////////////////////////////////////// + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave1D_01(const value_type x, const std::int32_t octaves, const value_type persistence) const noexcept + { + return perlin_detail::RemapClamp_01(octave1D(x, octaves, persistence)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave2D_01(const value_type x, const value_type y, const std::int32_t octaves, const value_type persistence) const noexcept + { + return perlin_detail::RemapClamp_01(octave2D(x, y, octaves, persistence)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::octave3D_01(const value_type x, const value_type y, const value_type z, const std::int32_t octaves, const value_type persistence) const noexcept + { + return perlin_detail::RemapClamp_01(octave3D(x, y, z, octaves, persistence)); + } + + /////////////////////////////////////// + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::normalizedOctave1D(const value_type x, const std::int32_t octaves, const value_type persistence) const noexcept + { + return (octave1D(x, octaves, persistence) / perlin_detail::MaxAmplitude(octaves, persistence)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::normalizedOctave2D(const value_type x, const value_type y, const std::int32_t octaves, const value_type persistence) const noexcept + { + return (octave2D(x, y, octaves, persistence) / perlin_detail::MaxAmplitude(octaves, persistence)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::normalizedOctave3D(const value_type x, const value_type y, const value_type z, const std::int32_t octaves, const value_type persistence) const noexcept + { + return (octave3D(x, y, z, octaves, persistence) / perlin_detail::MaxAmplitude(octaves, persistence)); + } + + /////////////////////////////////////// + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::normalizedOctave1D_01(const value_type x, const std::int32_t octaves, const value_type persistence) const noexcept + { + return perlin_detail::Remap_01(normalizedOctave1D(x, octaves, persistence)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::normalizedOctave2D_01(const value_type x, const value_type y, const std::int32_t octaves, const value_type persistence) const noexcept + { + return perlin_detail::Remap_01(normalizedOctave2D(x, y, octaves, persistence)); + } + + template + inline typename BasicPerlinNoise::value_type BasicPerlinNoise::normalizedOctave3D_01(const value_type x, const value_type y, const value_type z, const std::int32_t octaves, const value_type persistence) const noexcept + { + return perlin_detail::Remap_01(normalizedOctave3D(x, y, z, octaves, persistence)); + } +} + +# undef SIVPERLIN_NODISCARD_CXX20 +# undef SIVPERLIN_CONCEPT_URBG +# undef SIVPERLIN_CONCEPT_URBG_ diff --git a/src/dedicated_server/CMakeLists.txt b/src/dedicated_server/CMakeLists.txt new file mode 100644 index 0000000..d914cb2 --- /dev/null +++ b/src/dedicated_server/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.23) + +project(RDS) + +add_compile_definitions(SERVER) +if (WIN32) + add_compile_definitions(WIN32 SERVER) +endif() + +add_executable(RDS ds.cpp) +add_subdirectory(Lua) +target_include_directories(RDS PRIVATE + ../libraries/enet/include/. +) +target_link_libraries(RDS PRIVATE + raylib + enet + helpers + lua_static +) \ No newline at end of file diff --git a/src/dedicated_server/ds.cpp b/src/dedicated_server/ds.cpp new file mode 100644 index 0000000..fac21f4 --- /dev/null +++ b/src/dedicated_server/ds.cpp @@ -0,0 +1,382 @@ +#include "common.hpp" +#include +#include +#include +#include +#include +#include +#include +#include // C++17 for plugin folder iteration +#include // Lua header (adjust include if needed) + +namespace fs = std::filesystem; + +// Globals +std::list players; +Chunk world[MAX_WORLD_AREA]; +ENetHost* server; + +// Lua plugin system types +struct LuaPlugin { + std::string name; + lua_State* L; + // presence flags for callbacks + bool has_on_connect = false; + bool has_on_disconnect = false; + bool has_on_packet = false; +}; + +std::vector> plugins; + +// Helper: push peer id (int) and send/broadcast wrappers +static int l_server_send(lua_State* L) { + // args: peer_id (number), string data + int peer_id = (int)luaL_checkinteger(L, 1); + size_t len; + const char* data = luaL_checklstring(L, 2, &len); + + // find peer addr/port from players list + for (const NetPlayer &p : players) { + if (p.id == peer_id) { + SendPacketR(p.peer, (void*)data, (int)len); + break; + } + } + return 0; +} + +static int l_server_broadcast(lua_State* L) { + size_t len; + const char* data = luaL_checklstring(L, 1, &len); + if (len > 0) { + BroadcastPacketR(server, (void*)data, (int)len); + } + return 0; +} + +static int l_get_players(lua_State* L) { + lua_newtable(L); + int idx = 1; + for (const NetPlayer &p : players) { + lua_pushinteger(L, p.id); + lua_rawseti(L, -2, idx++); + } + return 1; +} + +// Register C functions available to plugins +void RegisterLuaAPI(lua_State* L) { + lua_newtable(L); + lua_pushcfunction(L, l_server_send); + lua_setfield(L, -2, "send"); + lua_pushcfunction(L, l_server_broadcast); + lua_setfield(L, -2, "broadcast"); + lua_pushcfunction(L, l_get_players); + lua_setfield(L, -2, "get_players"); + // assign this table to global "server" + lua_setglobal(L, "server"); +} + +// Load one plugin file +bool LoadPluginFile(const fs::path& path) { + auto plugin = std::make_unique(); + plugin->name = path.filename().string(); + plugin->L = luaL_newstate(); + if (!plugin->L) return false; + luaL_openlibs(plugin->L); + + // Provide limited API: global 'server' table + RegisterLuaAPI(plugin->L); + + // Load file + int rc = luaL_dofile(plugin->L, path.string().c_str()); + if (rc != LUA_OK) { + const char* err = lua_tostring(plugin->L, -1); + std::cerr << "Error loading plugin " << plugin->name << ": " << (err ? err : "unknown") << std::endl; + lua_close(plugin->L); + return false; + } + + // Check for callbacks: on_connect, on_disconnect, on_packet + lua_getglobal(plugin->L, "on_connect"); + if (lua_isfunction(plugin->L, -1)) plugin->has_on_connect = true; + lua_pop(plugin->L, 1); + + lua_getglobal(plugin->L, "on_disconnect"); + if (lua_isfunction(plugin->L, -1)) plugin->has_on_disconnect = true; + lua_pop(plugin->L, 1); + + lua_getglobal(plugin->L, "on_packet"); + if (lua_isfunction(plugin->L, -1)) plugin->has_on_packet = true; + lua_pop(plugin->L, 1); + + plugins.push_back(std::move(plugin)); + std::cout << "Loaded plugin: " << path.filename().string() << std::endl; + return true; +} + +void LoadPluginsFromFolder(const std::string& folder) { + try { + if (!fs::exists(folder)) { + std::cout << "Plugins folder does not exist; skipping: " << folder << std::endl; + return; + } + for (auto& p : fs::directory_iterator(folder)) { + if (!p.is_regular_file()) continue; + auto ext = p.path().extension().string(); + if (ext == ".lua") LoadPluginFile(p.path()); + } + } catch (const std::exception& e) { + std::cerr << "Error enumerating plugins: " << e.what() << std::endl; + } +} + +// Helper to call plugin callbacks +void CallPluginOnConnect(int peer_id) { + for (auto &pl : plugins) { + if (!pl->has_on_connect) continue; + lua_State* L = pl->L; + lua_getglobal(L, "on_connect"); + lua_pushinteger(L, peer_id); + if (lua_pcall(L, 1, 0, 0) != LUA_OK) { + std::cerr << "Plugin " << pl->name << " on_connect error: " << lua_tostring(L, -1) << std::endl; + lua_pop(L, 1); + } + } +} + +void CallPluginOnDisconnect(int peer_id) { + for (auto &pl : plugins) { + if (!pl->has_on_disconnect) continue; + lua_State* L = pl->L; + lua_getglobal(L, "on_disconnect"); + lua_pushinteger(L, peer_id); + if (lua_pcall(L, 1, 0, 0) != LUA_OK) { + std::cerr << "Plugin " << pl->name << " on_disconnect error: " << lua_tostring(L, -1) << std::endl; + lua_pop(L, 1); + } + } +} + +void CallPluginOnPacket(int peer_id, const unsigned char* data, int len) { + for (auto &pl : plugins) { + if (!pl->has_on_packet) continue; + lua_State* L = pl->L; + lua_getglobal(L, "on_packet"); + lua_pushinteger(L, peer_id); + lua_pushlstring(L, (const char*)data, len); + if (lua_pcall(L, 2, 0, 0) != LUA_OK) { + std::cerr << "Plugin " << pl->name << " on_packet error: " << lua_tostring(L, -1) << std::endl; + lua_pop(L, 1); + } + } +} + +int PeerToPlayerID(ENetPeer* peer) { + for (const NetPlayer &p : players) { + if (p.addr == peer->address.host && p.port == peer->address.port) return p.id; + } + return -1; +} + +// Modified ParseData to call Lua plugin packet handlers +void ParseData(ENetPacket* packet, ENetPeer* peer) { + int len = packet->dataLength; + unsigned char data[len]; + memcpy(&data, packet->data, len); + + // Call Lua packet handlers first (they can inspect/log; they cannot modify server internals directly) + int pid = PeerToPlayerID(peer); + if (pid >= 0) CallPluginOnPacket(pid, data, len); + + unsigned char response[16]; + switch (data[0]) + { + case NET_ARG_REQWORLD: + puts("Client requested world; sending"); + for (int i = 0; i < 256; i++) + { + unsigned char responseChunk[2+CHUNK_DATA_SIZE+CHUNK_SIZE*CHUNK_SIZE]; + responseChunk[0] = NET_ARG_CHUNKF; + responseChunk[1] = i; + + memcpy(responseChunk+2, world[i].blocks, CHUNK_DATA_SIZE); + memcpy(responseChunk+2+CHUNK_DATA_SIZE, world[i].highestBlock, CHUNK_SIZE*CHUNK_SIZE); + SendPacket(peer, responseChunk, sizeof(responseChunk)); + } + break; + case NET_ARG_PLAYERMOVE: + response[0] = NET_ARG_ECHOMOVE; + for (NetPlayer &plr : players) + { + if(plr.addr == peer->address.host && plr.port == peer->address.port) { + float x = DeserializeFloat(data[1], data[2]); + float y = DeserializeFloat(data[3], data[4]); + float z = DeserializeFloat(data[5], data[6]); + plr.x = x; + plr.y = y; + plr.z = z; + response[1] = plr.id; + } + } + response[2] = data[1]; response[3] = data[2]; + response[4] = data[3]; response[5] = data[4]; + response[6] = data[5]; response[7] = data[6]; + response[8] = data[7]; + BroadcastPacketR(server, response, 9); + break; + case NET_ARG_PLAYERYAW: + response[0] = NET_ARG_ECHOYAW; + for (NetPlayer &plr : players) + { + if(plr.addr == peer->address.host && plr.port == peer->address.port) { + response[1] = plr.id; + plr.yaw = data[1]; + response[2] = plr.yaw; + } + } + BroadcastPacketR(server, response, 3); + break; + case NET_ARG_REQPLAYERS: + puts("Client requested players; sending"); + + for (NetPlayer &plr : players) + { + unsigned char responseP[9]; + responseP[0] = NET_ARG_PLRREG; + if(plr.addr != peer->address.host || plr.port != peer->address.port) { + SerializeFloat2Data(plr.x, responseP, 2); + SerializeFloat2Data(plr.y, responseP, 4); + SerializeFloat2Data(plr.z, responseP, 6); + responseP[1] = plr.id; + responseP[8] = plr.yaw; + SendPacketR(peer, responseP, 9); + } + } + break; + case NET_ARG_BLOCKDELTA: + response[0] = NET_ARG_BLOCK; + for (NetPlayer &plr : players) + { + if(plr.addr == peer->address.host || plr.port == peer->address.port) { + unsigned char responseB[6]; + responseB[0] = NET_ARG_BLOCK; + responseB[1] = plr.id; + responseB[2] = data[1]; + responseB[3] = data[2]; + responseB[4] = data[3]; + responseB[5] = data[4]; + SetBlock(world, data[1], data[2], data[3], data[4]); + BroadcastPacketR(server, responseB, 6); + } + } + break; + default: + break; + } +} + +int FirstFreeID(const std::list& players) { + std::unordered_set used; + used.reserve(players.size()); + for (const auto &p : players) used.insert(p.id); + + int id = 0; + while (used.find(id) != used.end()) ++id; + return id; +} + +int main(int argc, char** argv) { + + int worldType = 0; + InitWorldgen(worldType); + + std::cout << "Building chunks..." << std::endl; + for (int x = 0; x < MAX_WORLD_SIZE; x++) + { + for (int y = 0; y < MAX_WORLD_SIZE; y++) + { + Chunk chunk = GenerateChunk(x, y); + world[x + y * MAX_WORLD_SIZE] = chunk; + } + } + + // Load Lua plugins + LoadPluginsFromFolder("plugins"); + + std::cout << "Starting server..." << std::endl; + server = StartServer(); + ENetEvent event; + std::cout << "Serving on port: " << PORT << std::endl; + + while(true) + { + ENetEvent event; + while (enet_host_service (server, & event, 1000) > 0) + { + switch (event.type) + { + int id; + char response[2]; + case ENET_EVENT_TYPE_CONNECT: + printf ("A new client connected from %x:%u.\n", + event.peer -> address.host, + event.peer -> address.port); + response[0] = NET_ARG_PLRID; + id = FirstFreeID(players); + response[1] = id; + SendPacketR(event.peer, response, sizeof(response)); + { + NetPlayer netPlayer; + netPlayer.id = id; + netPlayer.addr = event.peer->address.host; + netPlayer.port = event.peer->address.port; + netPlayer.peer = event.peer; + players.push_back(netPlayer); + + response[0] = NET_ARG_PLRCON; + response[1] = netPlayer.id; + BroadcastPacketR(server, response, sizeof(response)); + } + // notify plugins + CallPluginOnConnect(id); + break; + + case ENET_EVENT_TYPE_RECEIVE: + ParseData(event.packet, event.peer); + enet_packet_destroy (event.packet); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + printf ("%s disconnected.\n", event.peer -> data); + event.peer -> data = NULL; + for (auto it = players.begin(); it != players.end(); ++it) { + if (it->addr == event.peer->address.host && it->port == event.peer->address.port) { + char responseD[2]; + responseD[0] = NET_ARG_PLRDCN; + responseD[1] = it->id; + BroadcastPacketR(server, (unsigned char*)responseD, sizeof(responseD)); + + int disconnected_id = it->id; + it = players.erase(it); + + // notify plugins + CallPluginOnDisconnect(disconnected_id); + break; + } + } + + break; + } + } + } + + // cleanup plugins + for (auto &pl : plugins) { + lua_close(pl->L); + } + + enet_host_destroy(server); + + return 0; +} diff --git a/src/helpers/CMakeFiles/CMakeDirectoryInformation.cmake b/src/helpers/CMakeFiles/CMakeDirectoryInformation.cmake new file mode 100644 index 0000000..784f12e --- /dev/null +++ b/src/helpers/CMakeFiles/CMakeDirectoryInformation.cmake @@ -0,0 +1,16 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.31 + +# Relative path conversion top directories. +set(CMAKE_RELATIVE_PATH_TOP_SOURCE "/home/milkwx3/workspace/RCRAFT-MINEVOX") +set(CMAKE_RELATIVE_PATH_TOP_BINARY "/home/milkwx3/workspace/RCRAFT-MINEVOX") + +# Force unix paths in dependencies. +set(CMAKE_FORCE_UNIX_PATHS 1) + + +# The C and CXX include file regular expressions for this directory. +set(CMAKE_C_INCLUDE_REGEX_SCAN "^.*$") +set(CMAKE_C_INCLUDE_REGEX_COMPLAIN "^$") +set(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN}) +set(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN ${CMAKE_C_INCLUDE_REGEX_COMPLAIN}) diff --git a/src/helpers/CMakeFiles/helpers.dir/DependInfo.cmake b/src/helpers/CMakeFiles/helpers.dir/DependInfo.cmake new file mode 100644 index 0000000..7d5a212 --- /dev/null +++ b/src/helpers/CMakeFiles/helpers.dir/DependInfo.cmake @@ -0,0 +1,30 @@ + +# Consider dependencies only in project. +set(CMAKE_DEPENDS_IN_PROJECT_ONLY OFF) + +# The set of languages for which implicit dependencies are needed: +set(CMAKE_DEPENDS_LANGUAGES + ) + +# The set of dependency files which are needed: +set(CMAKE_DEPENDS_DEPENDENCY_FILES + "/home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/chunks.cpp" "helpers/CMakeFiles/helpers.dir/chunks.cpp.o" "gcc" "helpers/CMakeFiles/helpers.dir/chunks.cpp.o.d" + "/home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/debug.cpp" "helpers/CMakeFiles/helpers.dir/debug.cpp.o" "gcc" "helpers/CMakeFiles/helpers.dir/debug.cpp.o.d" + "/home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/entities.cpp" "helpers/CMakeFiles/helpers.dir/entities.cpp.o" "gcc" "helpers/CMakeFiles/helpers.dir/entities.cpp.o.d" + "/home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/input.cpp" "helpers/CMakeFiles/helpers.dir/input.cpp.o" "gcc" "helpers/CMakeFiles/helpers.dir/input.cpp.o.d" + "/home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/menu.cpp" "helpers/CMakeFiles/helpers.dir/menu.cpp.o" "gcc" "helpers/CMakeFiles/helpers.dir/menu.cpp.o.d" + "/home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/mesher.cpp" "helpers/CMakeFiles/helpers.dir/mesher.cpp.o" "gcc" "helpers/CMakeFiles/helpers.dir/mesher.cpp.o.d" + "/home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/networking.cpp" "helpers/CMakeFiles/helpers.dir/networking.cpp.o" "gcc" "helpers/CMakeFiles/helpers.dir/networking.cpp.o.d" + "/home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/utils.cpp" "helpers/CMakeFiles/helpers.dir/utils.cpp.o" "gcc" "helpers/CMakeFiles/helpers.dir/utils.cpp.o.d" + ) + +# Targets to which this target links which contain Fortran sources. +set(CMAKE_Fortran_TARGET_LINKED_INFO_FILES + ) + +# Targets to which this target links which contain Fortran sources. +set(CMAKE_Fortran_TARGET_FORWARD_LINKED_INFO_FILES + ) + +# Fortran module output directory. +set(CMAKE_Fortran_TARGET_MODULE_DIR "") diff --git a/src/helpers/CMakeFiles/helpers.dir/build.make b/src/helpers/CMakeFiles/helpers.dir/build.make new file mode 100644 index 0000000..0023d63 --- /dev/null +++ b/src/helpers/CMakeFiles/helpers.dir/build.make @@ -0,0 +1,226 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.31 + +# Delete rule output on recipe failure. +.DELETE_ON_ERROR: + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + +# Disable VCS-based implicit rules. +% : %,v + +# Disable VCS-based implicit rules. +% : RCS/% + +# Disable VCS-based implicit rules. +% : RCS/%,v + +# Disable VCS-based implicit rules. +% : SCCS/s.% + +# Disable VCS-based implicit rules. +% : s.% + +.SUFFIXES: .hpux_make_needs_suffix_list + +# Command-line flag to silence nested $(MAKE). +$(VERBOSE)MAKESILENT = -s + +#Suppress display of executed commands. +$(VERBOSE).SILENT: + +# A target that is always out of date. +cmake_force: +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E rm -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /home/milkwx3/workspace/RCRAFT-MINEVOX + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /home/milkwx3/workspace/RCRAFT-MINEVOX + +# Include any dependencies generated for this target. +include helpers/CMakeFiles/helpers.dir/depend.make +# Include any dependencies generated by the compiler for this target. +include helpers/CMakeFiles/helpers.dir/compiler_depend.make + +# Include the progress variables for this target. +include helpers/CMakeFiles/helpers.dir/progress.make + +# Include the compile flags for this target's objects. +include helpers/CMakeFiles/helpers.dir/flags.make + +helpers/CMakeFiles/helpers.dir/codegen: +.PHONY : helpers/CMakeFiles/helpers.dir/codegen + +helpers/CMakeFiles/helpers.dir/chunks.cpp.o: helpers/CMakeFiles/helpers.dir/flags.make +helpers/CMakeFiles/helpers.dir/chunks.cpp.o: helpers/chunks.cpp +helpers/CMakeFiles/helpers.dir/chunks.cpp.o: helpers/CMakeFiles/helpers.dir/compiler_depend.ts + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --progress-dir=/home/milkwx3/workspace/RCRAFT-MINEVOX/CMakeFiles --progress-num=$(CMAKE_PROGRESS_1) "Building CXX object helpers/CMakeFiles/helpers.dir/chunks.cpp.o" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -MD -MT helpers/CMakeFiles/helpers.dir/chunks.cpp.o -MF CMakeFiles/helpers.dir/chunks.cpp.o.d -o CMakeFiles/helpers.dir/chunks.cpp.o -c /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/chunks.cpp + +helpers/CMakeFiles/helpers.dir/chunks.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Preprocessing CXX source to CMakeFiles/helpers.dir/chunks.cpp.i" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/chunks.cpp > CMakeFiles/helpers.dir/chunks.cpp.i + +helpers/CMakeFiles/helpers.dir/chunks.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Compiling CXX source to assembly CMakeFiles/helpers.dir/chunks.cpp.s" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/chunks.cpp -o CMakeFiles/helpers.dir/chunks.cpp.s + +helpers/CMakeFiles/helpers.dir/debug.cpp.o: helpers/CMakeFiles/helpers.dir/flags.make +helpers/CMakeFiles/helpers.dir/debug.cpp.o: helpers/debug.cpp +helpers/CMakeFiles/helpers.dir/debug.cpp.o: helpers/CMakeFiles/helpers.dir/compiler_depend.ts + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --progress-dir=/home/milkwx3/workspace/RCRAFT-MINEVOX/CMakeFiles --progress-num=$(CMAKE_PROGRESS_2) "Building CXX object helpers/CMakeFiles/helpers.dir/debug.cpp.o" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -MD -MT helpers/CMakeFiles/helpers.dir/debug.cpp.o -MF CMakeFiles/helpers.dir/debug.cpp.o.d -o CMakeFiles/helpers.dir/debug.cpp.o -c /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/debug.cpp + +helpers/CMakeFiles/helpers.dir/debug.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Preprocessing CXX source to CMakeFiles/helpers.dir/debug.cpp.i" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/debug.cpp > CMakeFiles/helpers.dir/debug.cpp.i + +helpers/CMakeFiles/helpers.dir/debug.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Compiling CXX source to assembly CMakeFiles/helpers.dir/debug.cpp.s" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/debug.cpp -o CMakeFiles/helpers.dir/debug.cpp.s + +helpers/CMakeFiles/helpers.dir/entities.cpp.o: helpers/CMakeFiles/helpers.dir/flags.make +helpers/CMakeFiles/helpers.dir/entities.cpp.o: helpers/entities.cpp +helpers/CMakeFiles/helpers.dir/entities.cpp.o: helpers/CMakeFiles/helpers.dir/compiler_depend.ts + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --progress-dir=/home/milkwx3/workspace/RCRAFT-MINEVOX/CMakeFiles --progress-num=$(CMAKE_PROGRESS_3) "Building CXX object helpers/CMakeFiles/helpers.dir/entities.cpp.o" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -MD -MT helpers/CMakeFiles/helpers.dir/entities.cpp.o -MF CMakeFiles/helpers.dir/entities.cpp.o.d -o CMakeFiles/helpers.dir/entities.cpp.o -c /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/entities.cpp + +helpers/CMakeFiles/helpers.dir/entities.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Preprocessing CXX source to CMakeFiles/helpers.dir/entities.cpp.i" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/entities.cpp > CMakeFiles/helpers.dir/entities.cpp.i + +helpers/CMakeFiles/helpers.dir/entities.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Compiling CXX source to assembly CMakeFiles/helpers.dir/entities.cpp.s" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/entities.cpp -o CMakeFiles/helpers.dir/entities.cpp.s + +helpers/CMakeFiles/helpers.dir/input.cpp.o: helpers/CMakeFiles/helpers.dir/flags.make +helpers/CMakeFiles/helpers.dir/input.cpp.o: helpers/input.cpp +helpers/CMakeFiles/helpers.dir/input.cpp.o: helpers/CMakeFiles/helpers.dir/compiler_depend.ts + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --progress-dir=/home/milkwx3/workspace/RCRAFT-MINEVOX/CMakeFiles --progress-num=$(CMAKE_PROGRESS_4) "Building CXX object helpers/CMakeFiles/helpers.dir/input.cpp.o" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -MD -MT helpers/CMakeFiles/helpers.dir/input.cpp.o -MF CMakeFiles/helpers.dir/input.cpp.o.d -o CMakeFiles/helpers.dir/input.cpp.o -c /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/input.cpp + +helpers/CMakeFiles/helpers.dir/input.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Preprocessing CXX source to CMakeFiles/helpers.dir/input.cpp.i" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/input.cpp > CMakeFiles/helpers.dir/input.cpp.i + +helpers/CMakeFiles/helpers.dir/input.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Compiling CXX source to assembly CMakeFiles/helpers.dir/input.cpp.s" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/input.cpp -o CMakeFiles/helpers.dir/input.cpp.s + +helpers/CMakeFiles/helpers.dir/menu.cpp.o: helpers/CMakeFiles/helpers.dir/flags.make +helpers/CMakeFiles/helpers.dir/menu.cpp.o: helpers/menu.cpp +helpers/CMakeFiles/helpers.dir/menu.cpp.o: helpers/CMakeFiles/helpers.dir/compiler_depend.ts + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --progress-dir=/home/milkwx3/workspace/RCRAFT-MINEVOX/CMakeFiles --progress-num=$(CMAKE_PROGRESS_5) "Building CXX object helpers/CMakeFiles/helpers.dir/menu.cpp.o" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -MD -MT helpers/CMakeFiles/helpers.dir/menu.cpp.o -MF CMakeFiles/helpers.dir/menu.cpp.o.d -o CMakeFiles/helpers.dir/menu.cpp.o -c /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/menu.cpp + +helpers/CMakeFiles/helpers.dir/menu.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Preprocessing CXX source to CMakeFiles/helpers.dir/menu.cpp.i" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/menu.cpp > CMakeFiles/helpers.dir/menu.cpp.i + +helpers/CMakeFiles/helpers.dir/menu.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Compiling CXX source to assembly CMakeFiles/helpers.dir/menu.cpp.s" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/menu.cpp -o CMakeFiles/helpers.dir/menu.cpp.s + +helpers/CMakeFiles/helpers.dir/mesher.cpp.o: helpers/CMakeFiles/helpers.dir/flags.make +helpers/CMakeFiles/helpers.dir/mesher.cpp.o: helpers/mesher.cpp +helpers/CMakeFiles/helpers.dir/mesher.cpp.o: helpers/CMakeFiles/helpers.dir/compiler_depend.ts + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --progress-dir=/home/milkwx3/workspace/RCRAFT-MINEVOX/CMakeFiles --progress-num=$(CMAKE_PROGRESS_6) "Building CXX object helpers/CMakeFiles/helpers.dir/mesher.cpp.o" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -MD -MT helpers/CMakeFiles/helpers.dir/mesher.cpp.o -MF CMakeFiles/helpers.dir/mesher.cpp.o.d -o CMakeFiles/helpers.dir/mesher.cpp.o -c /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/mesher.cpp + +helpers/CMakeFiles/helpers.dir/mesher.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Preprocessing CXX source to CMakeFiles/helpers.dir/mesher.cpp.i" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/mesher.cpp > CMakeFiles/helpers.dir/mesher.cpp.i + +helpers/CMakeFiles/helpers.dir/mesher.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Compiling CXX source to assembly CMakeFiles/helpers.dir/mesher.cpp.s" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/mesher.cpp -o CMakeFiles/helpers.dir/mesher.cpp.s + +helpers/CMakeFiles/helpers.dir/networking.cpp.o: helpers/CMakeFiles/helpers.dir/flags.make +helpers/CMakeFiles/helpers.dir/networking.cpp.o: helpers/networking.cpp +helpers/CMakeFiles/helpers.dir/networking.cpp.o: helpers/CMakeFiles/helpers.dir/compiler_depend.ts + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --progress-dir=/home/milkwx3/workspace/RCRAFT-MINEVOX/CMakeFiles --progress-num=$(CMAKE_PROGRESS_7) "Building CXX object helpers/CMakeFiles/helpers.dir/networking.cpp.o" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -MD -MT helpers/CMakeFiles/helpers.dir/networking.cpp.o -MF CMakeFiles/helpers.dir/networking.cpp.o.d -o CMakeFiles/helpers.dir/networking.cpp.o -c /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/networking.cpp + +helpers/CMakeFiles/helpers.dir/networking.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Preprocessing CXX source to CMakeFiles/helpers.dir/networking.cpp.i" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/networking.cpp > CMakeFiles/helpers.dir/networking.cpp.i + +helpers/CMakeFiles/helpers.dir/networking.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Compiling CXX source to assembly CMakeFiles/helpers.dir/networking.cpp.s" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/networking.cpp -o CMakeFiles/helpers.dir/networking.cpp.s + +helpers/CMakeFiles/helpers.dir/utils.cpp.o: helpers/CMakeFiles/helpers.dir/flags.make +helpers/CMakeFiles/helpers.dir/utils.cpp.o: helpers/utils.cpp +helpers/CMakeFiles/helpers.dir/utils.cpp.o: helpers/CMakeFiles/helpers.dir/compiler_depend.ts + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --progress-dir=/home/milkwx3/workspace/RCRAFT-MINEVOX/CMakeFiles --progress-num=$(CMAKE_PROGRESS_8) "Building CXX object helpers/CMakeFiles/helpers.dir/utils.cpp.o" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -MD -MT helpers/CMakeFiles/helpers.dir/utils.cpp.o -MF CMakeFiles/helpers.dir/utils.cpp.o.d -o CMakeFiles/helpers.dir/utils.cpp.o -c /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/utils.cpp + +helpers/CMakeFiles/helpers.dir/utils.cpp.i: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Preprocessing CXX source to CMakeFiles/helpers.dir/utils.cpp.i" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/utils.cpp > CMakeFiles/helpers.dir/utils.cpp.i + +helpers/CMakeFiles/helpers.dir/utils.cpp.s: cmake_force + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green "Compiling CXX source to assembly CMakeFiles/helpers.dir/utils.cpp.s" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && /usr/bin/c++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/utils.cpp -o CMakeFiles/helpers.dir/utils.cpp.s + +# Object files for target helpers +helpers_OBJECTS = \ +"CMakeFiles/helpers.dir/chunks.cpp.o" \ +"CMakeFiles/helpers.dir/debug.cpp.o" \ +"CMakeFiles/helpers.dir/entities.cpp.o" \ +"CMakeFiles/helpers.dir/input.cpp.o" \ +"CMakeFiles/helpers.dir/menu.cpp.o" \ +"CMakeFiles/helpers.dir/mesher.cpp.o" \ +"CMakeFiles/helpers.dir/networking.cpp.o" \ +"CMakeFiles/helpers.dir/utils.cpp.o" + +# External object files for target helpers +helpers_EXTERNAL_OBJECTS = + +helpers/libhelpers.a: helpers/CMakeFiles/helpers.dir/chunks.cpp.o +helpers/libhelpers.a: helpers/CMakeFiles/helpers.dir/debug.cpp.o +helpers/libhelpers.a: helpers/CMakeFiles/helpers.dir/entities.cpp.o +helpers/libhelpers.a: helpers/CMakeFiles/helpers.dir/input.cpp.o +helpers/libhelpers.a: helpers/CMakeFiles/helpers.dir/menu.cpp.o +helpers/libhelpers.a: helpers/CMakeFiles/helpers.dir/mesher.cpp.o +helpers/libhelpers.a: helpers/CMakeFiles/helpers.dir/networking.cpp.o +helpers/libhelpers.a: helpers/CMakeFiles/helpers.dir/utils.cpp.o +helpers/libhelpers.a: helpers/CMakeFiles/helpers.dir/build.make +helpers/libhelpers.a: helpers/CMakeFiles/helpers.dir/link.txt + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --green --bold --progress-dir=/home/milkwx3/workspace/RCRAFT-MINEVOX/CMakeFiles --progress-num=$(CMAKE_PROGRESS_9) "Linking CXX static library libhelpers.a" + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && $(CMAKE_COMMAND) -P CMakeFiles/helpers.dir/cmake_clean_target.cmake + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && $(CMAKE_COMMAND) -E cmake_link_script CMakeFiles/helpers.dir/link.txt --verbose=$(VERBOSE) + +# Rule to build all files generated by this target. +helpers/CMakeFiles/helpers.dir/build: helpers/libhelpers.a +.PHONY : helpers/CMakeFiles/helpers.dir/build + +helpers/CMakeFiles/helpers.dir/clean: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers && $(CMAKE_COMMAND) -P CMakeFiles/helpers.dir/cmake_clean.cmake +.PHONY : helpers/CMakeFiles/helpers.dir/clean + +helpers/CMakeFiles/helpers.dir/depend: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(CMAKE_COMMAND) -E cmake_depends "Unix Makefiles" /home/milkwx3/workspace/RCRAFT-MINEVOX /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers /home/milkwx3/workspace/RCRAFT-MINEVOX /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/CMakeFiles/helpers.dir/DependInfo.cmake "--color=$(COLOR)" +.PHONY : helpers/CMakeFiles/helpers.dir/depend + diff --git a/src/helpers/CMakeFiles/helpers.dir/cmake_clean.cmake b/src/helpers/CMakeFiles/helpers.dir/cmake_clean.cmake new file mode 100644 index 0000000..4f576ce --- /dev/null +++ b/src/helpers/CMakeFiles/helpers.dir/cmake_clean.cmake @@ -0,0 +1,25 @@ +file(REMOVE_RECURSE + "CMakeFiles/helpers.dir/chunks.cpp.o" + "CMakeFiles/helpers.dir/chunks.cpp.o.d" + "CMakeFiles/helpers.dir/debug.cpp.o" + "CMakeFiles/helpers.dir/debug.cpp.o.d" + "CMakeFiles/helpers.dir/entities.cpp.o" + "CMakeFiles/helpers.dir/entities.cpp.o.d" + "CMakeFiles/helpers.dir/input.cpp.o" + "CMakeFiles/helpers.dir/input.cpp.o.d" + "CMakeFiles/helpers.dir/menu.cpp.o" + "CMakeFiles/helpers.dir/menu.cpp.o.d" + "CMakeFiles/helpers.dir/mesher.cpp.o" + "CMakeFiles/helpers.dir/mesher.cpp.o.d" + "CMakeFiles/helpers.dir/networking.cpp.o" + "CMakeFiles/helpers.dir/networking.cpp.o.d" + "CMakeFiles/helpers.dir/utils.cpp.o" + "CMakeFiles/helpers.dir/utils.cpp.o.d" + "libhelpers.a" + "libhelpers.pdb" +) + +# Per-language clean rules from dependency scanning. +foreach(lang CXX) + include(CMakeFiles/helpers.dir/cmake_clean_${lang}.cmake OPTIONAL) +endforeach() diff --git a/src/helpers/CMakeFiles/helpers.dir/cmake_clean_target.cmake b/src/helpers/CMakeFiles/helpers.dir/cmake_clean_target.cmake new file mode 100644 index 0000000..9a35a23 --- /dev/null +++ b/src/helpers/CMakeFiles/helpers.dir/cmake_clean_target.cmake @@ -0,0 +1,3 @@ +file(REMOVE_RECURSE + "libhelpers.a" +) diff --git a/src/helpers/CMakeFiles/helpers.dir/compiler_depend.make b/src/helpers/CMakeFiles/helpers.dir/compiler_depend.make new file mode 100644 index 0000000..69f44ef --- /dev/null +++ b/src/helpers/CMakeFiles/helpers.dir/compiler_depend.make @@ -0,0 +1,2 @@ +# Empty compiler generated dependencies file for helpers. +# This may be replaced when dependencies are built. diff --git a/src/helpers/CMakeFiles/helpers.dir/compiler_depend.ts b/src/helpers/CMakeFiles/helpers.dir/compiler_depend.ts new file mode 100644 index 0000000..30daa36 --- /dev/null +++ b/src/helpers/CMakeFiles/helpers.dir/compiler_depend.ts @@ -0,0 +1,2 @@ +# CMAKE generated file: DO NOT EDIT! +# Timestamp file for compiler generated dependencies management for helpers. diff --git a/src/helpers/CMakeFiles/helpers.dir/depend.make b/src/helpers/CMakeFiles/helpers.dir/depend.make new file mode 100644 index 0000000..f990b00 --- /dev/null +++ b/src/helpers/CMakeFiles/helpers.dir/depend.make @@ -0,0 +1,2 @@ +# Empty dependencies file for helpers. +# This may be replaced when dependencies are built. diff --git a/src/helpers/CMakeFiles/helpers.dir/flags.make b/src/helpers/CMakeFiles/helpers.dir/flags.make new file mode 100644 index 0000000..a8a6939 --- /dev/null +++ b/src/helpers/CMakeFiles/helpers.dir/flags.make @@ -0,0 +1,10 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.31 + +# compile CXX with /usr/bin/c++ +CXX_DEFINES = -DGRAPHICS_API_OPENGL_33 -DPLATFORM_DESKTOP + +CXX_INCLUDES = -I/home/milkwx3/workspace/RCRAFT-MINEVOX/helpers -I/home/milkwx3/workspace/RCRAFT-MINEVOX/libraries/raylib/src + +CXX_FLAGS = + diff --git a/src/helpers/CMakeFiles/helpers.dir/link.txt b/src/helpers/CMakeFiles/helpers.dir/link.txt new file mode 100644 index 0000000..3598123 --- /dev/null +++ b/src/helpers/CMakeFiles/helpers.dir/link.txt @@ -0,0 +1,2 @@ +/usr/bin/ar qc libhelpers.a CMakeFiles/helpers.dir/chunks.cpp.o CMakeFiles/helpers.dir/debug.cpp.o CMakeFiles/helpers.dir/entities.cpp.o CMakeFiles/helpers.dir/input.cpp.o CMakeFiles/helpers.dir/menu.cpp.o CMakeFiles/helpers.dir/mesher.cpp.o CMakeFiles/helpers.dir/networking.cpp.o CMakeFiles/helpers.dir/utils.cpp.o +/usr/bin/ranlib libhelpers.a diff --git a/src/helpers/CMakeFiles/helpers.dir/progress.make b/src/helpers/CMakeFiles/helpers.dir/progress.make new file mode 100644 index 0000000..ab06e77 --- /dev/null +++ b/src/helpers/CMakeFiles/helpers.dir/progress.make @@ -0,0 +1,10 @@ +CMAKE_PROGRESS_1 = 36 +CMAKE_PROGRESS_2 = 37 +CMAKE_PROGRESS_3 = 38 +CMAKE_PROGRESS_4 = 39 +CMAKE_PROGRESS_5 = 40 +CMAKE_PROGRESS_6 = 41 +CMAKE_PROGRESS_7 = 42 +CMAKE_PROGRESS_8 = 43 +CMAKE_PROGRESS_9 = 44 + diff --git a/src/helpers/CMakeFiles/progress.marks b/src/helpers/CMakeFiles/progress.marks new file mode 100644 index 0000000..a272009 --- /dev/null +++ b/src/helpers/CMakeFiles/progress.marks @@ -0,0 +1 @@ +39 diff --git a/src/helpers/CMakeLists.txt b/src/helpers/CMakeLists.txt new file mode 100644 index 0000000..89fa467 --- /dev/null +++ b/src/helpers/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.14) + +add_library(helpers STATIC + chunks.cpp + debug.cpp + entities.cpp + input.cpp + menu.cpp + mesher.cpp + networking.cpp + utils.cpp + locale.cpp + inventory.cpp + recipes.cpp + particles.cpp + player.cpp + multisound.cpp + svlib.cpp +) + +execute_process(COMMAND date +v.%yw%W OUTPUT_VARIABLE BUILD_DATE OUTPUT_STRIP_TRAILING_WHITESPACE) +add_compile_definitions(GAME_VERSION="${BUILD_DATE}") + +target_include_directories(helpers + PUBLIC + $ + $ +) +target_include_directories(helpers PRIVATE + ../libraries/enet/include/. + ../libraries/yaml-cpp/include/. + ../libraries/Lua/lua-5.4.7/src/ +) +target_sources(helpers + PUBLIC + FILE_SET headers TYPE HEADERS BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} FILES common.hpp +) + +target_link_libraries(helpers PRIVATE raylib yaml-cpp lua_static) \ No newline at end of file diff --git a/src/helpers/Makefile b/src/helpers/Makefile new file mode 100644 index 0000000..db23be4 --- /dev/null +++ b/src/helpers/Makefile @@ -0,0 +1,442 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 3.31 + +# Default target executed when no arguments are given to make. +default_target: all +.PHONY : default_target + +# Allow only one "make -f Makefile2" at a time, but pass parallelism. +.NOTPARALLEL: + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canonical targets will work. +.SUFFIXES: + +# Disable VCS-based implicit rules. +% : %,v + +# Disable VCS-based implicit rules. +% : RCS/% + +# Disable VCS-based implicit rules. +% : RCS/%,v + +# Disable VCS-based implicit rules. +% : SCCS/s.% + +# Disable VCS-based implicit rules. +% : s.% + +.SUFFIXES: .hpux_make_needs_suffix_list + +# Command-line flag to silence nested $(MAKE). +$(VERBOSE)MAKESILENT = -s + +#Suppress display of executed commands. +$(VERBOSE).SILENT: + +# A target that is always out of date. +cmake_force: +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E rm -f + +# Escaping for special characters. +EQUALS = = + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /home/milkwx3/workspace/RCRAFT-MINEVOX + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /home/milkwx3/workspace/RCRAFT-MINEVOX + +#============================================================================= +# Targets provided globally by CMake. + +# Special rule for the target package +package: preinstall + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Run CPack packaging tool..." + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && /usr/bin/cpack --config ./CPackConfig.cmake +.PHONY : package + +# Special rule for the target package +package/fast: package +.PHONY : package/fast + +# Special rule for the target package_source +package_source: + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Run CPack packaging tool for source..." + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && /usr/bin/cpack --config ./CPackSourceConfig.cmake /home/milkwx3/workspace/RCRAFT-MINEVOX/CPackSourceConfig.cmake +.PHONY : package_source + +# Special rule for the target package_source +package_source/fast: package_source +.PHONY : package_source/fast + +# Special rule for the target edit_cache +edit_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "No interactive CMake dialog available..." + /usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available. +.PHONY : edit_cache + +# Special rule for the target edit_cache +edit_cache/fast: edit_cache +.PHONY : edit_cache/fast + +# Special rule for the target rebuild_cache +rebuild_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Running CMake to regenerate build system..." + /usr/bin/cmake --regenerate-during-build -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : rebuild_cache + +# Special rule for the target rebuild_cache +rebuild_cache/fast: rebuild_cache +.PHONY : rebuild_cache/fast + +# Special rule for the target list_install_components +list_install_components: + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Available install components are: \"Unspecified\"" +.PHONY : list_install_components + +# Special rule for the target list_install_components +list_install_components/fast: list_install_components +.PHONY : list_install_components/fast + +# Special rule for the target install +install: preinstall + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Install the project..." + /usr/bin/cmake -P cmake_install.cmake +.PHONY : install + +# Special rule for the target install +install/fast: preinstall/fast + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Install the project..." + /usr/bin/cmake -P cmake_install.cmake +.PHONY : install/fast + +# Special rule for the target install/local +install/local: preinstall + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Installing only the local directory..." + /usr/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake +.PHONY : install/local + +# Special rule for the target install/local +install/local/fast: preinstall/fast + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Installing only the local directory..." + /usr/bin/cmake -DCMAKE_INSTALL_LOCAL_ONLY=1 -P cmake_install.cmake +.PHONY : install/local/fast + +# Special rule for the target install/strip +install/strip: preinstall + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Installing the project stripped..." + /usr/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake +.PHONY : install/strip + +# Special rule for the target install/strip +install/strip/fast: preinstall/fast + @$(CMAKE_COMMAND) -E cmake_echo_color "--switch=$(COLOR)" --cyan "Installing the project stripped..." + /usr/bin/cmake -DCMAKE_INSTALL_DO_STRIP=1 -P cmake_install.cmake +.PHONY : install/strip/fast + +# The main all target +all: cmake_check_build_system + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(CMAKE_COMMAND) -E cmake_progress_start /home/milkwx3/workspace/RCRAFT-MINEVOX/CMakeFiles /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers//CMakeFiles/progress.marks + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 helpers/all + $(CMAKE_COMMAND) -E cmake_progress_start /home/milkwx3/workspace/RCRAFT-MINEVOX/CMakeFiles 0 +.PHONY : all + +# The main clean target +clean: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 helpers/clean +.PHONY : clean + +# The main clean target +clean/fast: clean +.PHONY : clean/fast + +# Prepare targets for installation. +preinstall: all + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 helpers/preinstall +.PHONY : preinstall + +# Prepare targets for installation. +preinstall/fast: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 helpers/preinstall +.PHONY : preinstall/fast + +# clear depends +depend: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 +.PHONY : depend + +# Convenience name for target. +helpers/CMakeFiles/helpers.dir/rule: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f CMakeFiles/Makefile2 helpers/CMakeFiles/helpers.dir/rule +.PHONY : helpers/CMakeFiles/helpers.dir/rule + +# Convenience name for target. +helpers: helpers/CMakeFiles/helpers.dir/rule +.PHONY : helpers + +# fast build rule for target. +helpers/fast: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/build +.PHONY : helpers/fast + +chunks.o: chunks.cpp.o +.PHONY : chunks.o + +# target to build an object file +chunks.cpp.o: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/chunks.cpp.o +.PHONY : chunks.cpp.o + +chunks.i: chunks.cpp.i +.PHONY : chunks.i + +# target to preprocess a source file +chunks.cpp.i: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/chunks.cpp.i +.PHONY : chunks.cpp.i + +chunks.s: chunks.cpp.s +.PHONY : chunks.s + +# target to generate assembly for a file +chunks.cpp.s: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/chunks.cpp.s +.PHONY : chunks.cpp.s + +debug.o: debug.cpp.o +.PHONY : debug.o + +# target to build an object file +debug.cpp.o: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/debug.cpp.o +.PHONY : debug.cpp.o + +debug.i: debug.cpp.i +.PHONY : debug.i + +# target to preprocess a source file +debug.cpp.i: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/debug.cpp.i +.PHONY : debug.cpp.i + +debug.s: debug.cpp.s +.PHONY : debug.s + +# target to generate assembly for a file +debug.cpp.s: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/debug.cpp.s +.PHONY : debug.cpp.s + +entities.o: entities.cpp.o +.PHONY : entities.o + +# target to build an object file +entities.cpp.o: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/entities.cpp.o +.PHONY : entities.cpp.o + +entities.i: entities.cpp.i +.PHONY : entities.i + +# target to preprocess a source file +entities.cpp.i: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/entities.cpp.i +.PHONY : entities.cpp.i + +entities.s: entities.cpp.s +.PHONY : entities.s + +# target to generate assembly for a file +entities.cpp.s: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/entities.cpp.s +.PHONY : entities.cpp.s + +input.o: input.cpp.o +.PHONY : input.o + +# target to build an object file +input.cpp.o: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/input.cpp.o +.PHONY : input.cpp.o + +input.i: input.cpp.i +.PHONY : input.i + +# target to preprocess a source file +input.cpp.i: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/input.cpp.i +.PHONY : input.cpp.i + +input.s: input.cpp.s +.PHONY : input.s + +# target to generate assembly for a file +input.cpp.s: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/input.cpp.s +.PHONY : input.cpp.s + +menu.o: menu.cpp.o +.PHONY : menu.o + +# target to build an object file +menu.cpp.o: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/menu.cpp.o +.PHONY : menu.cpp.o + +menu.i: menu.cpp.i +.PHONY : menu.i + +# target to preprocess a source file +menu.cpp.i: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/menu.cpp.i +.PHONY : menu.cpp.i + +menu.s: menu.cpp.s +.PHONY : menu.s + +# target to generate assembly for a file +menu.cpp.s: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/menu.cpp.s +.PHONY : menu.cpp.s + +mesher.o: mesher.cpp.o +.PHONY : mesher.o + +# target to build an object file +mesher.cpp.o: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/mesher.cpp.o +.PHONY : mesher.cpp.o + +mesher.i: mesher.cpp.i +.PHONY : mesher.i + +# target to preprocess a source file +mesher.cpp.i: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/mesher.cpp.i +.PHONY : mesher.cpp.i + +mesher.s: mesher.cpp.s +.PHONY : mesher.s + +# target to generate assembly for a file +mesher.cpp.s: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/mesher.cpp.s +.PHONY : mesher.cpp.s + +networking.o: networking.cpp.o +.PHONY : networking.o + +# target to build an object file +networking.cpp.o: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/networking.cpp.o +.PHONY : networking.cpp.o + +networking.i: networking.cpp.i +.PHONY : networking.i + +# target to preprocess a source file +networking.cpp.i: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/networking.cpp.i +.PHONY : networking.cpp.i + +networking.s: networking.cpp.s +.PHONY : networking.s + +# target to generate assembly for a file +networking.cpp.s: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/networking.cpp.s +.PHONY : networking.cpp.s + +utils.o: utils.cpp.o +.PHONY : utils.o + +# target to build an object file +utils.cpp.o: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/utils.cpp.o +.PHONY : utils.cpp.o + +utils.i: utils.cpp.i +.PHONY : utils.i + +# target to preprocess a source file +utils.cpp.i: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/utils.cpp.i +.PHONY : utils.cpp.i + +utils.s: utils.cpp.s +.PHONY : utils.s + +# target to generate assembly for a file +utils.cpp.s: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(MAKE) $(MAKESILENT) -f helpers/CMakeFiles/helpers.dir/build.make helpers/CMakeFiles/helpers.dir/utils.cpp.s +.PHONY : utils.cpp.s + +# Help Target +help: + @echo "The following are some of the valid targets for this Makefile:" + @echo "... all (the default if no target is provided)" + @echo "... clean" + @echo "... depend" + @echo "... edit_cache" + @echo "... install" + @echo "... install/local" + @echo "... install/strip" + @echo "... list_install_components" + @echo "... package" + @echo "... package_source" + @echo "... rebuild_cache" + @echo "... helpers" + @echo "... chunks.o" + @echo "... chunks.i" + @echo "... chunks.s" + @echo "... debug.o" + @echo "... debug.i" + @echo "... debug.s" + @echo "... entities.o" + @echo "... entities.i" + @echo "... entities.s" + @echo "... input.o" + @echo "... input.i" + @echo "... input.s" + @echo "... menu.o" + @echo "... menu.i" + @echo "... menu.s" + @echo "... mesher.o" + @echo "... mesher.i" + @echo "... mesher.s" + @echo "... networking.o" + @echo "... networking.i" + @echo "... networking.s" + @echo "... utils.o" + @echo "... utils.i" + @echo "... utils.s" +.PHONY : help + + + +#============================================================================= +# Special targets to cleanup operation of make. + +# Special rule to run CMake to check the build system integrity. +# No rule that depends on this can have commands that come from listfiles +# because they might be regenerated. +cmake_check_build_system: + cd /home/milkwx3/workspace/RCRAFT-MINEVOX && $(CMAKE_COMMAND) -S$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 +.PHONY : cmake_check_build_system + diff --git a/src/helpers/chunks.cpp b/src/helpers/chunks.cpp new file mode 100644 index 0000000..47cc0f8 --- /dev/null +++ b/src/helpers/chunks.cpp @@ -0,0 +1,687 @@ +#include +#include +#include "common.hpp" +#include +#include +#include +#include +#include + +#define HUMIDITY_OFFSET 12.3f +#define CAVE_OFFSET 45.6f +#define SCALE 0.01f +#define CAVE_COEFF 0.06 +#define EDGES_FADE 64 +#define CLOUD_SCALE 0.25f + +#define BIOME_FOREST 0 +#define BIOME_TAIGA 1 + +static const siv::PerlinNoise perlin; + +static int world_type = 0; + +const BlockDef BLOCKS[] = { + BlockDef{ "air", R_Translucent, B_Stone, 0, 0, 0}, + BlockDef{ "stone", R_Normal, B_Stone, 7, 0, 3}, + BlockDef{ "grass", R_TriSide, B_Grass, 1, 0, 0, 1, 2}, + BlockDef{ "dirt", R_Normal, B_Dirt, 2, 0, 2}, + BlockDef{ "sand", R_Normal, B_Dirt, 2, 0, 4}, + BlockDef{ "water", R_Translucent, B_Stone, 0, 0, 14}, + BlockDef{ "planks", R_Normal, B_Wood, 3, 0, 11}, + BlockDef{ "gray_bricks", R_Normal, B_Stone, 5, 1, 16}, + BlockDef{ "red_bricks", R_Normal, B_Stone, 5, 1, 17}, + BlockDef{ "blue_bricks", R_Normal, B_Stone, 5, 1, 18}, + BlockDef{ "green_bricks", R_Normal, B_Stone, 5, 1, 19}, + BlockDef{ "glass", R_Translucent, B_Stone, 3, 1, 12}, + BlockDef{ "log", R_TriSide, B_Wood, 5, 0, 10, 9, 10}, + BlockDef{ "spruce_planks", R_Normal, B_Wood, 3, 0, 13}, + BlockDef{ "spruce_log", R_TriSide, B_Wood, 5, 0, 21, 20, 21}, + BlockDef{ "workbench", R_TranslucentAllSides, B_Stone, 3, 0, 23, 22, 23}, + BlockDef{ "blue_block", R_Normal, B_Stone, 5, 0, 22}, + BlockDef{ "yellow_block", R_Normal, B_Stone, 5, 0, 23}, + BlockDef{ "cyan_block", R_Normal, B_Stone, 5, 0, 24}, + BlockDef{ "magenta_block", R_Normal, B_Stone, 5, 0, 25}, + BlockDef{ "white_block", R_Normal, B_Stone, 5, 0, 26}, + BlockDef{ "black_block", R_Normal, B_Stone, 5, 0, 27}, + BlockDef{ "leaves", R_TranslucentAllSides, B_Grass, 1, 0, 28}, + BlockDef{ "copper_ore", R_Normal, B_Stone, 7, 1, 5}, + BlockDef{ "silver_ore", R_Normal, B_Stone, 7, 2, 6}, + BlockDef{ "gold_ore", R_Normal, B_Stone, 7, 3, 7}, + BlockDef{ "coal_ore", R_Normal, B_Stone, 7, 0, 8}, + BlockDef{ "unobtainium", R_Normal, B_Stone, 7, 999, 3}, // Bedrock? +}; +int GetNumericIDFromString(std::string s) { + int count = sizeof(BLOCKS) / sizeof(BLOCKS[0]); + for (int i = 0; i < count; ++i) { + if (BLOCKS[i].name == s) return i; + } + return -1; +} +static std::bitset<256> marked; + +static int GetBlockRegularWorld(int x, int y, int z) { + float fade_x = (fmax(EDGES_FADE - x, 0) + fmax(EDGES_FADE - WORLD_SIZE_BLOCKS + x, 0)) / (float)EDGES_FADE; + float fade_z = (fmax(EDGES_FADE - z, 0) + fmax(EDGES_FADE - WORLD_SIZE_BLOCKS + z, 0)) / (float)EDGES_FADE; + float fade = 1 - (fade_x + fade_z); + + const int SEA_HEIGHT = 32; // sea surface y + const int BEACH_DEPTH = 3; // how many blocks below surface become sand + + float height0 = (perlin.noise2D((double)x * 1.5f * SCALE, (double)z * 1.5f * SCALE) + 1.0f ) / 2.0f * 16.0f; + float mountains = pow((perlin.noise2D((double)x * 0.5f, (double)z * 0.5f * SCALE) + 1.0f ) / 2.0f, 16); + float height1 = (perlin.noise2D((double)x * 4.5f * SCALE, (double)z * 4.5f * SCALE) + 1.0f ) / 2.0f * 24.0f * mountains; + + int height = floor((height0 + height1) * fade) + 26; + + float caveNoise = fabs(perlin.noise3D((double)x * 1.0f * SCALE + CAVE_OFFSET, (double)y * 4.0f * SCALE + CAVE_OFFSET, (double)z * 1.0f * SCALE + CAVE_OFFSET)); + + if (y <= height) { + if(caveNoise < 0.02f) return BLOCK_AIR; + if (y == height) { + if (height <= SEA_HEIGHT) { + return BLOCK_SAND; + } + return BLOCK_GRASS; + } + + + if (y > height - BEACH_DEPTH && height <= SEA_HEIGHT + 2) { + return BLOCK_SAND; + } + if(caveNoise < 0.2f || y < 4) return BLOCK_STONE; + return BLOCK_DIRT; + } + + if (y <= SEA_HEIGHT && height < SEA_HEIGHT) { + return BLOCK_WATERS; + } + + return BLOCK_AIR; +} +bool CanTick(int blockType) { // TODO: make smarter in case other blocks can tick too! + return blockType == BLOCK_SAND; +} +bool GetCloud(int x, int z) { + return (perlin.noise2D((x / 8) * 1.5f * CLOUD_SCALE, (z / 8) * 1.5f * CLOUD_SCALE) + 1.0f) / 2.0f > 0.5f; +} +static int GetBlockCaveWorld(int x, int y, int z) { + float caveNoise = (perlin.noise3D((float)x * 3.0f * SCALE + CAVE_OFFSET, (float)y * 3.0f * SCALE + CAVE_OFFSET, (float)z * 3.0f * SCALE + CAVE_OFFSET) + 1.0f) / 2.0f; + if(caveNoise > 0.5f) { + return BLOCK_STONE; + } + return BLOCK_AIR; +} +static int has_block(int x, int y, int z) +{ + switch (world_type) + { + case 0: + return GetBlockRegularWorld(x, y, z); + break; + case 1: + return GetBlockCaveWorld(x, y, z); + break; + case 254: + if(y == 0 + x / 16) { + return BLOCK_STONE; + } + break; + case 255: + if((y >= 63 || y == 61) && (x < 4 && z < 4)) { + return BLOCK_STONE; + } + break; + default: + const int baseHeight = 32; + if(y <= baseHeight / 3) { + return BLOCK_STONE; + } + if (y > baseHeight / 3 && y < baseHeight) + { + return BLOCK_DIRT; + } + if (y == baseHeight) + { + return BLOCK_GRASS; + } + break; + } + return BLOCK_AIR; +} +bool IsTranslucent(int blockType) { + return marked.test(blockType); +} + +void SetBlockChunk(Chunk *c, int x, int y, int z, int t) { + c->blocks[GetIndexChunk(x,y,z)] = t; + if(y >= c->highestBlock[GetIndexChunk2D(x, z)]) { + if(t != 0) { + c->highestBlock[GetIndexChunk2D(x, z)] = y; + } + else { + for (int _y = y-1; _y >= 0; _y--) + { + /*if(!IsTranslucent(c->blocks[GetIndexChunk(x,_y,z)])) { + c->highestBlock[GetIndexChunk2D(x, z)] = _y; + break; + }*/ + if(c->blocks[GetIndexChunk(x,_y,z)] != 0) { + c->highestBlock[GetIndexChunk2D(x, z)] = _y; + break; + } + } + } + } +} +void RebuildHeightmap(Chunk *c) { + for (int x = 0; x < CHUNK_SIZE; x++) + { + for (int z = 0; z < CHUNK_SIZE; z++) + { + for (int y = WORLD_HEIGHT-1; y >= 0; y--) + { + if(c->blocks[GetIndexChunk(x,y,z)] != 0) { + c->highestBlock[GetIndexChunk2D(x, z)] = y; + break; + } + } + } + } +} +int GetBiome(int sx, int sy) { + return BIOME_TAIGA; +} +void SetBlockChunkFast(Chunk *c, int i, int t) { + c->blocks[i] = t; +} +void SetBlock(Chunk world[], int x, int y, int z, int t) { + if(CheckOOBWorld(x, y, z)) { return; } + int locX = x % CHUNK_SIZE; + int locZ = z % CHUNK_SIZE; + int cx = x / CHUNK_SIZE; + int cz = z / CHUNK_SIZE; + SetBlockChunk(&world[GetIndexWorld(cx, cz)], locX, y, locZ, t); +} +void InitWorldgen(int worldType) { + const siv::PerlinNoise::seed_type seed = 123456u; + const siv::PerlinNoise perlin{ seed }; + constexpr size_t MAX = 128; // allowed range: 0..MAX-1 + for (int i = 0; i < sizeof(BLOCKS) / sizeof(BlockDef); i++) + { + BlockDef def = BLOCKS[i]; + if(def.render == R_Translucent || def.render == R_TranslucentAllSides || def.render == R_TranslucentTriSide) { + marked.set(i); + } + } + world_type = worldType; +} +Vector3 WorldPosition(int cx, int cz, int localX, int y, int localZ) { + return { (float)cx * CHUNK_SIZE + localX, (float)y, (float)cz * CHUNK_SIZE + localZ }; +} + +Chunk GenerateChunk(int _x, int _y) { + Chunk new_chunk = { 0 }; + new_chunk.x = _x; + new_chunk.z = _y; + + for (int x = 0; x < CHUNK_SIZE; x++) + { + for (int y = 0; y < WORLD_HEIGHT; y++) + { + for (int z = 0; z < CHUNK_SIZE; z++) + { + int id = has_block(_x * CHUNK_SIZE + x,y,_y * CHUNK_SIZE + z); + SetBlockChunk(&new_chunk, x, y, z, id); + } + } + } + return new_chunk; +} +void GenerateWorldAdditional(Chunk world[]) { + int biome = GetBiome(0, 0); + SetRandomSeed(0); + switch (world_type) + { + case 0: + for (int x = 0; x < WORLD_SIZE_BLOCKS; x++) + { + for (int z = 0; z < WORLD_SIZE_BLOCKS; z++) { + Chunk c = world[GetIndexWorld(x / CHUNK_SIZE, z / CHUNK_SIZE)]; + int highestY = c.highestBlock[GetIndexChunk2D(x % CHUNK_SIZE, z % CHUNK_SIZE)]; + int b = fetch_block(c, x % CHUNK_SIZE, highestY, z % CHUNK_SIZE); + for (int _ = 0; _ < GetRandomValue(0, 6); _++) + { + if(GetRandomValue(0, 800) == 0) { + int max = GetRandomValue(10, 120); + Vector3 pos = {x, GetRandomValue(12, WORLD_HEIGHT-32), z}; + Vector3 direction = {GetRandomValue(-100, 100) / 100.0f, GetRandomValue(-100, -5) / 100.0f, GetRandomValue(-100, 100) / 100.0f}; + int radius = 2; + for (int _ = 0; _ < max; _++) + { + for (int x = -radius; x < radius; x++) + { + for (int y = -radius; y < radius; y++) + { + for (int z = -radius; z < radius; z++) + { + if(Vector3Distance({pos.x + x, pos.y + y, pos.z + z}, pos) <= radius) { + if(!CheckOOBWorld(pos.x + x, pos.y + y, pos.z + z)) { + SetBlock(world, pos.x + x, pos.y + y, pos.z + z, 0); + } + } + } + } + } + direction += {GetRandomValue(-100, 100) / 100.0f, GetRandomValue(-100, -5) / 100.0f, GetRandomValue(-100, 100) / 100.0f}; + pos += direction; + } + } + } + for (int _ = 0; _ < GetRandomValue(8, 24); _++) + { + if(GetRandomValue(0, 100) == 0) { + Vector3 pos = {x, GetRandomValue(2, WORLD_HEIGHT-24), z}; + int type = 23; + if(pos.y < 24) + type = 24; + if(pos.y < 12) + type = 25; + int radius = 2; + for (int x = -radius; x < radius; x++) + { + for (int y = -radius; y < radius; y++) + { + for (int z = -radius; z < radius; z++) + { + if(Vector3Distance({pos.x + x, pos.y + y, pos.z + z}, pos) <= radius) { + if(!CheckOOBWorld(pos.x + x, pos.y + y, pos.z + z) && GetBlock(world, pos.x + x, pos.y + y, pos.z + z) == BLOCK_STONE) { + SetBlock(world, pos.x + x, pos.y + y, pos.z + z, type); + } + } + } + } + } + } + } + } + } + break; + } + switch (world_type) + { + case 0: + for (int x = 0; x < WORLD_SIZE_BLOCKS; x++) + { + for (int z = 0; z < WORLD_SIZE_BLOCKS; z++) { + Chunk c = world[GetIndexWorld(x / CHUNK_SIZE, z / CHUNK_SIZE)]; + int highestY = c.highestBlock[GetIndexChunk2D(x % CHUNK_SIZE, z % CHUNK_SIZE)]; + int b = fetch_block(c, x % CHUNK_SIZE, highestY, z % CHUNK_SIZE); + if(b == BLOCK_GRASS && GetRandomValue(0, 100) < 4 && biome == BIOME_FOREST) { // Regular trees + SetBlock(world, x, highestY, z, BLOCK_DIRT); + int maxY = GetRandomValue(5, 7); + for (int y = 1; y < maxY; y++) + { + int wy = highestY + y; + + if(y > 2) { //leaves + SetBlock(world, x-1, wy, z, 22); + SetBlock(world, x+1, wy, z, 22); + SetBlock(world, x, wy, z-1, 22); + SetBlock(world, x, wy, z+1, 22); + SetBlock(world, x, wy+1, z, 22); + } + SetBlock(world, x, wy, z, 12); // log + } + } + if(b == BLOCK_GRASS && GetRandomValue(0, 100) < 2 && biome == BIOME_TAIGA) { // Spruce + SetBlock(world, x, highestY, z, BLOCK_DIRT); + int maxY = 8; + for (int y = 1; y < maxY; y++) + { + int wy = highestY + y; + const int lower_size = 3; + const int upper_size = 2; + if(y > 2) { //leaves + if(y == 3) { + for (int _x = -lower_size; _x <= lower_size; _x++) + { + for (int _z = -lower_size; _z <= lower_size; _z++) + { + if( + !(fabs(_z) == lower_size && fabs(_x) == lower_size) + ) SetBlock(world, x+_x, wy, z+_z, 22); + } + } + } + else { + if(y == maxY-1) { + SetBlock(world, x-1, wy+1, z, 22); + SetBlock(world, x+1, wy+1, z, 22); + SetBlock(world, x, wy+1, z-1, 22); + SetBlock(world, x, wy+1, z+1, 22); + SetBlock(world, x, wy+1, z, 22); + } + if(y == 4) { + for (int _x = -upper_size; _x <= upper_size; _x++) + { + for (int _z = -upper_size; _z <= upper_size; _z++) + { + if( + !(fabs(_z) == upper_size && fabs(_x) == upper_size) + ) SetBlock(world, x+_x, wy, z+_z, 22); + } + } + } + if(y > 4 && y%2 == 0) { + SetBlock(world, x-1, wy, z, 22); + SetBlock(world, x+1, wy, z, 22); + SetBlock(world, x, wy, z-1, 22); + SetBlock(world, x, wy, z+1, 22); + SetBlock(world, x, wy, z, 22); + } + } + + } + SetBlock(world, x, wy, z, 14); // log + } + } + if(!marked.test(b) && GetRandomValue(0, 800) == 0) { + int maxY = GetRandomValue(5, 7); + for (int _x = -1; _x <= 1; _x++) + for (int _z = -1; _z <= 1; _z++) { + int t = BLOCK_STONE; + if(GetRandomValue(0, 50) == 0) t = 23; + SetBlock(world, x+_x-1, highestY+1, z+_z-1, t); + } + + for (int _x = -1; _x <= 1; _x++) + for (int _z = -1; _z <= 1; _z++) + if(_x != _z) { + int t = BLOCK_STONE; + if(GetRandomValue(0, 80) == 0) t = 23; + SetBlock(world, x+_x-1, highestY+2, z+_z-1, t); + } + SetBlock(world, x-1, highestY+2, z-1, BLOCK_STONE); + SetBlock(world, x-1, highestY+3, z-1, BLOCK_STONE); + } + } + } + break; + } + switch (world_type) + { + case 0: + for (int x = 0; x < WORLD_SIZE_BLOCKS; x++) + { + for (int z = 0; z < WORLD_SIZE_BLOCKS; z++) { + SetBlock(world, x, 1, z, 27); + } + } + break; + } +} +void TickBlock(Chunk world[], int wx, int wy, int wz) { + if(CheckOOBWorld(wx, wy, wz)) return; + int tile = GetBlock(world, wx, wy, wz); + if(tile == BLOCK_SAND) { + if(GetBlock(world, wx, wy-1, wz) == 0) { + SetBlockNetwork(world, wx, wy, wz, 0); + Entity temp = NewEntity(); + temp.type = 4; + temp.position = (Vector3){wx, wy, wz}; + temp.velocity = {0, 0, 0}; + temp.data[1] = BLOCK_SAND; + AddEntity(temp); + } + } +} +void TickChunk(Chunk *chunk, Chunk *world, int wx, int wz) { + for (int y = WORLD_HEIGHT-1; y > 1; y--) { + for (int x = 0; x < CHUNK_SIZE; x++) + { + for (int z = 0; z < CHUNK_SIZE; z++) + { + int tile = fetch_block_ptr(chunk, x, y, z); + if(tile == BLOCK_SAND) { + if(fetch_block_ptr(chunk, x, y-1, z) == 0) { + SetBlockChunk(chunk, x, y, z, 0); + Entity temp = NewEntity(); + temp.type = 4; + temp.position = (Vector3){((int)wx * CHUNK_SIZE + x), y, ((int)wz * CHUNK_SIZE + z)}; + temp.velocity = {0, 0, 0}; + temp.data[1] = BLOCK_SAND; + AddEntity(temp); + chunk->isDirty = true; + } + } + if(false) { // tile == BLOCK_WATERS + int rx = GetRandomValue(-1, 1); + int rz = GetRandomValue(-1, 1); + for (int _y = 0; _y >= -1; _y--) + { + if(GetBlock(world, wx+x+rx, y+_y, wz+z+rz) == 0) { + SetBlockChunk(chunk, x, y+_y, z, 0); + SetBlock(world, wx+x+rx, y+_y, wz+z+rz, BLOCK_WATERS); + chunk->isDirty = true; + continue; + } + } + } + } + } + } +} +void RebuildOpaqueMask(Chunk *chunk) { + const int GROUP = 16; + const int GROUPS = CHUNK_DATA_SIZE / GROUP; + + uint16_t oMask[GROUPS] = { 0 }; + uint16_t aMask[GROUPS] = { 0 }; + uint8_t blocks[CHUNK_DATA_SIZE]; + memcpy(blocks, chunk->blocks, sizeof(blocks)); + + for (int i = 0; i < CHUNK_DATA_SIZE; ++i) { + int g = i / GROUP; + int bit = i % GROUP; + if (chunk->blocks[i] == 0) { + aMask[g] |= 1 << bit; + } + if (!IsTranslucent(chunk->blocks[i])) { + oMask[g] |= 1 << bit; + } + } + + memcpy(chunk->opaqueMask, oMask, sizeof(oMask)); + memcpy(chunk->airMask, aMask, sizeof(aMask)); +} + +#ifndef SERVER +//Generates an opaque CRD (ChunkRenderData). Outputs the CRD. +ChunkRenderData GenerateCRDOpaque(const Chunk &chunk, Chunk world[]) { + struct ChunkRenderData new_chunk = { 0 }; + uint16_t mask[CHUNK_DATA_SIZE/16]; + memcpy(mask, chunk.opaqueMask, sizeof(chunk.opaqueMask)); + + const int cxw = chunk.x * CHUNK_SIZE; + const int cxz = chunk.z * CHUNK_SIZE; + const int chunkMaskSize = CHUNK_DATA_SIZE/16; + for (int step = 0; step < chunkMaskSize; ++step) { + uint16_t m = mask[step]; + if(m == 0) continue; + + while (m) { + int bit = __builtin_ctz(m); + m &= m - 1; + int i2 = (step<<4) + bit; + int _x = bit; + int y = step >> 4; + int _z = step & 0xF; + //int _x, y, _z; + //GetXYZFromIndex(i2, &_x, &y, &_z); + if (!TestMask(mask, i2)) continue; // Skip if this is translucent + + int x = _x + cxw; + int z = _z + cxz; + + bool l = (_x > 0) ? TestMask(mask, i2-1) : TestOpaqueMaskWorld(world, x - 1, y, z); + bool r = (_x < CHUNK_SIZE-1) ? TestMask(mask, i2+1) : TestOpaqueMaskWorld(world, x + 1, y, z); + bool u = (y==WORLD_HEIGHT-1) ? false : TestMask(mask, i2+CHUNK_SIZE_SQR); + bool d = (y==0) ? true : TestMask(mask, i2-CHUNK_SIZE_SQR); + bool b = (_z==0) ? TestOpaqueMaskWorld(world, x, y, z - 1) : TestMask(mask, i2-CHUNK_SIZE); + bool f = (_z==CHUNK_SIZE-1) ? TestOpaqueMaskWorld(world, x, y, z + 1) : TestMask(mask, i2+CHUNK_SIZE); + new_chunk.sides[i2] = pack6( + !l, + !r, + !u, + !d, + !b, + !f + ); + } + } + return new_chunk; +} + +//Generates a translucent CRD (ChunkRenderData). Outputs the CRD. +ChunkRenderData GenerateCRDTranslucent(Chunk chunk, Chunk world[]) { + struct ChunkRenderData new_chunk = { 0 }; + uint16_t *mask0= chunk.opaqueMask; + uint16_t *mask = chunk.airMask; + for (int step = 0; step < CHUNK_DATA_SIZE/16; ++step) + { + uint16_t m = ~(mask[step]); + if(m == 0) continue; + + while (m) { + int bit = __builtin_ctz(m); + m &= m - 1; + int i2 = (step<<4) + bit; + int _x = bit; + int y = step >> 4; + int _z = step & 0xF; + + if (TestMask(mask0, i2)) { + new_chunk.sides[i2] = 0; + continue; + }; + if(BLOCKS[fetch_block(chunk, _x, y, _z)].render == R_TranslucentAllSides) { + new_chunk.sides[i2] = 0xFF; + continue; + } + int x = _x + chunk.x * CHUNK_SIZE; + int z = _z + chunk.z * CHUNK_SIZE; + bool l = (_x > 0) ? !TestMask(mask, i2-1) : !TestAirMaskWorld(world, x - 1, y, z); + bool r = (_x < CHUNK_SIZE-1) ? !TestMask(mask, i2+1) : !TestAirMaskWorld(world, x + 1, y, z); + bool u = (y==WORLD_HEIGHT-1) ? false : !TestMask(mask, i2+CHUNK_SIZE_SQR); + bool d = (y==0) ? true : !TestMask(mask, i2-CHUNK_SIZE_SQR); + bool b = (_z==0) ? !TestAirMaskWorld(world, x, y, z - 1) : !TestMask(mask, i2-CHUNK_SIZE); + bool f = (_z==CHUNK_SIZE-1) ? !TestAirMaskWorld(world, x, y, z + 1) : !TestMask(mask, i2+CHUNK_SIZE); + new_chunk.sides[i2] = pack6( + !l, + !r, + !u, + !d, + !b, + !f + ); + } + } + return new_chunk; +} +#endif +#define SAVE_FORMAT 0 + +void SaveWorld(Chunk world[], const char* path) { + std::ofstream data(path, std::ios::binary); + if (!data) return; + + data.write("WFF", 3); + char fmt = static_cast(SAVE_FORMAT); + data.write(&fmt, 1); + + for (int i = 0; i < MAX_WORLD_AREA; ++i) { + const Chunk &c = world[i]; + + std::vector temp; + int last_type = -1; + uint8_t count = 0; + + for (int j = 0; j < CHUNK_DATA_SIZE; ++j) { + uint8_t b = c.blocks[j]; + + if (last_type == -1) { + last_type = b; + count = 1; + } else if (b == last_type && count < 255) { + ++count; + } else { + temp.push_back(count); + temp.push_back(static_cast(last_type)); + last_type = b; + count = 1; + } + } + + if (last_type != -1) { + temp.push_back(count); + temp.push_back(static_cast(last_type)); + } + + uint16_t size = static_cast(temp.size()); + data.write(reinterpret_cast(&size), sizeof(size)); + if (!temp.empty()) { + data.write(reinterpret_cast(temp.data()), temp.size()); + } + } + + data.close(); +} + +void Debug_Write(const std::string &stuff); +bool LoadWorld(Chunk world[], const char* path) { + std::ifstream data(path, std::ios::binary); + if (!data) return false; + + char header[3]; + if (!data.read(header, 3)) return false; + if (std::memcmp(header, "WFF", 3) != 0) return false; + + char fmt; + if (!data.read(&fmt, 1)) return false; + + uint8_t* temp = (uint8_t*)malloc(65535); + for (int i = 0; i < MAX_WORLD_AREA; ++i) { + printf("[PRINTF] Chunk %d\n", i); + + uint16_t size = 0; + Debug_Write("Trying to read size"); + if (!data.read(reinterpret_cast(&size), sizeof(size))) { + free(temp); + return false; + }; + printf("[PRINTF] Size %d\n", size); + + if (size > 0) { + if (!data.read(reinterpret_cast(temp), size)) { + free(temp); + return false; + } + } + Chunk *c = &world[i]; + int outIndex = 0; + for (size_t p = 0; p + 1 < size; p += 2) { + uint8_t count = temp[p]; + uint8_t value = temp[p + 1]; + for (uint8_t k = 0; k < count; ++k) { + SetBlockChunkFast(c, outIndex++, value); + } + } + while (outIndex < CHUNK_DATA_SIZE) { + SetBlockChunkFast(c, outIndex++, 0); + } + RebuildHeightmap(c); + } + free(temp); + return true; +} diff --git a/src/helpers/cmake_install.cmake b/src/helpers/cmake_install.cmake new file mode 100644 index 0000000..708e077 --- /dev/null +++ b/src/helpers/cmake_install.cmake @@ -0,0 +1,50 @@ +# Install script for directory: /home/milkwx3/workspace/RCRAFT-MINEVOX/helpers + +# Set the install prefix +if(NOT DEFINED CMAKE_INSTALL_PREFIX) + set(CMAKE_INSTALL_PREFIX "/usr/local") +endif() +string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + +# Set the install configuration name. +if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) + if(BUILD_TYPE) + string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" + CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") + else() + set(CMAKE_INSTALL_CONFIG_NAME "") + endif() + message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") +endif() + +# Set the component getting installed. +if(NOT CMAKE_INSTALL_COMPONENT) + if(COMPONENT) + message(STATUS "Install component: \"${COMPONENT}\"") + set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") + else() + set(CMAKE_INSTALL_COMPONENT) + endif() +endif() + +# Install shared libraries without execute permission? +if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) + set(CMAKE_INSTALL_SO_NO_EXE "1") +endif() + +# Is this installation the result of a crosscompile? +if(NOT DEFINED CMAKE_CROSSCOMPILING) + set(CMAKE_CROSSCOMPILING "FALSE") +endif() + +# Set path to fallback-tool for dependency-resolution. +if(NOT DEFINED CMAKE_OBJDUMP) + set(CMAKE_OBJDUMP "/usr/bin/objdump") +endif() + +string(REPLACE ";" "\n" CMAKE_INSTALL_MANIFEST_CONTENT + "${CMAKE_INSTALL_MANIFEST_FILES}") +if(CMAKE_INSTALL_LOCAL_ONLY) + file(WRITE "/home/milkwx3/workspace/RCRAFT-MINEVOX/helpers/install_local_manifest.txt" + "${CMAKE_INSTALL_MANIFEST_CONTENT}") +endif() diff --git a/src/helpers/common.hpp b/src/helpers/common.hpp new file mode 100644 index 0000000..b759acc --- /dev/null +++ b/src/helpers/common.hpp @@ -0,0 +1,623 @@ +#if defined(WIN32) +#include "external/fix_win32_compatibility.h" +#endif +#include "glfw_keycodes_to_string.h" +#include "../PerlinNoise.hpp" +#include +#include "raylib.h" +#include "raymath.h" +#include +#include +#include +#include +#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 inputs; + std::list 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 InitSingleplayer(); +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(ATLAS_SIZE_W); + const float uv_mod_y = 1.0f / static_cast(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(w); + const float uv_mod_y = 1.0f / static_cast(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(ATLAS_SIZE_W); + const float uv_mod_y = 1.0f / static_cast(ATLAS_SIZE_H); + const float uv_mod1_x = sizeModX / static_cast(ATLAS_SIZE_W); + const float uv_mod1_y = sizeModY / static_cast(ATLAS_SIZE_H); + const float uv_off_x = offsetX / static_cast(ATLAS_SIZE_W); + const float uv_off_y = offsetY / static_cast(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(w); + const float uv_mod_y = 1.0f / static_cast(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 StartInternalServer(bool&started); +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 diff --git a/src/helpers/debug.cpp b/src/helpers/debug.cpp new file mode 100644 index 0000000..6af7300 --- /dev/null +++ b/src/helpers/debug.cpp @@ -0,0 +1,78 @@ +#include +#include +#include +#include "common.hpp" +#define __DEBUG__ +std::chrono::_V2::system_clock::time_point start; +std::chrono::_V2::system_clock::time_point end; +long nsTotal; + +void Debug_StartMeasure() { + start = std::chrono::high_resolution_clock::now(); +} + +void Debug_EndMeasure(const std::string &stuff) { + end = std::chrono::high_resolution_clock::now(); + int ns = std::chrono::duration_cast(end - start).count(); + #ifdef __DEBUG__ + std::cout << "[DEBUG] " << stuff << " took " << (float)(ns)/1000000.0f << " ms"<< std::endl; + #endif + nsTotal += ns; +} + +void Debug_EndMeasureLoops(const std::string &stuff, int loops) { + std::cout << "[DEBUG] " << stuff << " took " << (float)(nsTotal)/1000000000.0f << " seconds across " << loops << " loops (" << (float)(nsTotal)/1000000.0f / (float)loops << " ms per cycle)" << std::endl; + nsTotal = 0; +} + +void Debug_Write(const std::string &stuff) { + #ifdef __DEBUG__ + std::cout << "[DEBUG] " << stuff << std::endl; + #endif +} + +#if defined(WIN32) +#include +#include +bool Debug_GetProcessMemory(MemInfo &out) { + PROCESS_MEMORY_COUNTERS pmc{}; + if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc))) return false; + out.resident = pmc.WorkingSetSize; + out.virtualSize = pmc.PagefileUsage + pmc.WorkingSetSize; + out.privateBytes = 0; + return true; +} +#endif +#if !defined(WIN32) +#include +#include +#include +bool Debug_GetProcessMemory(MemInfo &out) { + out.resident = out.virtualSize = out.privateBytes = 0; + std::ifstream f("/proc/self/statm"); + if (f) { + long rss_pages=0, size_pages=0; + if (f >> size_pages >> rss_pages) { + long page = sysconf(_SC_PAGESIZE); + out.resident = (uint64_t)rss_pages * page; + out.virtualSize = (uint64_t)size_pages * page; + } + } else { + std::ifstream s("/proc/self/status"); + std::string line; + while (std::getline(s, line)) { + if (line.rfind("VmRSS:",0)==0) { sscanf(line.c_str(),"VmRSS: %llu kB",&out.resident); out.resident *= 1024; } + if (line.rfind("VmSize:",0)==0) { sscanf(line.c_str(),"VmSize: %llu kB",&out.virtualSize); out.virtualSize *= 1024; } + } + } + return true; +} +#endif +#if defined(PLAYSTATION2) +bool Debug_GetProcessMemory(MemInfo &out) { + out.resident = 0; + out.virtualSize = 32*1024*1024; + out.privateBytes = 0; + return true; +} +#endif \ No newline at end of file diff --git a/src/helpers/entities.cpp b/src/helpers/entities.cpp new file mode 100644 index 0000000..0292d00 --- /dev/null +++ b/src/helpers/entities.cpp @@ -0,0 +1,370 @@ +#include +#include +#include "common.hpp" +#include +#include +#include +#include +#include +#include +#include + +#define PIXUNIT 1.0f / 16.0f + +#ifndef SERVER +extern Camera camera; +extern Font fnt; +extern Texture2D terrain; + +Texture2D playerTex; +Texture2D bomb; +Sound nekit_idle; +void EntitiesInit() { + playerTex = LoadTexture(Pathify("player.png")); + bomb = LoadTexture(Pathify("bomb.png")); + nekit_idle = LoadSound(Pathify("sound/nekit/idle.ogg")); +} +extern bool online; +extern Vector3 player_position; +extern Vector3 player_velocity; +void TakeDamage(float amount); +void SetBlockNetwork(Chunk world[], int bx, int by, int bz, int t); +#else + bool online = true; +#endif + +std::list entities; + +Entity NewEntity() { + Entity ent; + ent.id = entities.size(); + ent.type = 0; + ent.yaw = 0; + ent.free = false; + return ent; +} + +void AddEntity(Entity ent) { + entities.push_back(ent); +} + +static void UpdateEntityPhysics(Chunk world[], Entity& ent, float dt) { + Vector3 velocityTgt = Vector3Add(ent.velocity, {0, -dt * 20.0f, 0}); + Vector3 tgt = Vector3Add(ent.position, Vector3Scale(velocityTgt, dt)); + Vector3 final = ent.position; + if(!GetBlock(world, (int)tgt.x, (int)final.y, (int)final.z)) { + final.x = tgt.x; + } + if(!GetBlock(world, (int)final.x, (int)tgt.y, (int)final.z)) { + final.y = tgt.y; + } + else { + velocityTgt.y = 0; + } + if(!GetBlock(world, (int)final.x, (int)final.y, (int)tgt.z)) { + final.z = tgt.z; + } + ent.velocity = velocityTgt; + ent.position = final; +} +void UpdateEntities(Chunk world[], float dt) { + if(entities.size() > 0) { + int idx = 0; + for(Entity& ent : entities) { + ent.smoothing = 0; + if(ent.type != 1) { + ent.lifetime += dt; + } + else { + ent.lifetime += dt * ent.data[1] / 255.0f; + if(ent.data[1]-4 >= 0) { + ent.data[1]-=4; + } + } + if(ent.damage_flash != 0) { + ent.damage_flash--; + } + if(ent.type == 2) { + Vector3 target = Vector3Zero(); + + #ifndef SERVER + target = player_position; + #endif + + Vector3 direction = Vector3Normalize(Vector3Subtract(target, ent.position)); + float degYaw = -atan2f(direction.z, direction.x) * RAD2DEG + 90; + if(GetRandomValue(0, 2000) == 0) { + MS_PlaySound(nekit_idle, GetRandomValue(75,125)/100.0f, 1.0f, ent.position); + } + ent.velocity = Vector3Add(ent.velocity, (Vector3){direction.x * 20.0f, 0, direction.z * 20.0f}*dt); + if(GetBlock(world, (int)ent.position.x, (int)ent.position.y-1, (int)ent.position.z) && GetRandomValue(0, 100) == 0) { + ent.velocity.y += 10.0f; + } + UpdateEntityPhysics(world, ent, dt); + if(Vector3Distance(target, ent.position) < 1.5f && ent.data[2] > 80) { + player_velocity += (Vector3){direction.x, 0, direction.z} * 30.0f; + player_velocity += {0, 2, 0}; + ent.data[2] = 0; + TakeDamage(10.0f); + } + if(ent.health < 0.1f) { + ent.free = true; + } + if(ent.data[2] < 90) + ent.data[2]++; + ent.yaw = (unsigned short)(degYaw/360.0f*65535); + } + /*if(ent.type == 3) { + Vector3 velocityTgt = Vector3Clamp(Vector3Add(ent.velocity, {0, -dt * 20.0f, 0}), {-30.0f, -30.0f, -30.0f}, {30.0f, 30.0f, 30.0f}); + + Vector3 tgt = Vector3Add(ent.position, Vector3Scale(velocityTgt, dt)); + Vector3 final = ent.position; + if(!GetBlock(world, (int)tgt.x, (int)final.y, (int)final.z)) { + final.x = tgt.x; + } + if(!GetBlock(world, (int)final.x, (int)tgt.y, (int)final.z)) { + final.y = tgt.y; + } + else { + velocityTgt.y = -velocityTgt.y / 2.0f; + velocityTgt = Vector3Scale(velocityTgt, 0.9f); + } + if(!GetBlock(world, (int)final.x, (int)final.y, (int)tgt.z)) { + final.z = tgt.z; + } + Entity* entp = &ent; + entp->yaw = 0; + entp->position = final; + entp->velocity = velocityTgt; + if(ent.lifetime > 5.0f) { + int radius = 3; + for (int x = -radius; x < radius; x++) + { + for (int y = -radius; y < radius; y++) + { + for (int z = -radius; z < radius; z++) + { + if(Vector3Distance({final.x + x, final.y + y, final.z + z}, final) <= radius) { + if(!CheckOOBWorld(final.x + x, final.y + y, final.z + z)) { + #ifndef SERVER + SetBlockNetwork(world, final.x + x, final.y + y, final.z + z, 0); + #endif + } + } + } + } + } + entp->free = true; + } + idx++; + }*/ + if(ent.type == 4) { + Vector3 velocityTgt = Vector3Clamp(Vector3Add(ent.velocity, {0, -dt * 20.0f, 0}), {-30.0f, -30.0f, -30.0f}, {30.0f, 30.0f, 30.0f}); + + Vector3 tgt = Vector3Add(ent.position, Vector3Scale(velocityTgt, dt)); + Vector3 final = ent.position; + bool hit = false; + if(!GetBlock(world, (int)tgt.x, (int)final.y, (int)final.z)) { + final.x = tgt.x; + } + else { + hit = true; + } + if(!GetBlock(world, (int)final.x, (int)tgt.y, (int)final.z)) { + final.y = tgt.y; + } + else { + hit = true; + } + if(!GetBlock(world, (int)final.x, (int)final.y, (int)tgt.z)) { + final.z = tgt.z; + } + else { + hit = true; + } + Entity* entp = &ent; + if(hit) { + SetBlockNetwork(world, (int)ent.position.x, (int)ent.position.y, (int)ent.position.z, ent.data[1]); + entp->free = true; + } + entp->yaw = 0; + entp->position = final; + entp->velocity = velocityTgt; + idx++; + } + } + auto match = [&](const Entity ent){ + return ent.free; + }; + auto it = std::find_if(entities.begin(), entities.end(), match); + if (it != entities.end()) { + entities.erase(it); + } + } +} +float globalCounter = 0; +#ifndef SERVER +void DrawEntities(float dt) { + globalCounter += dt; + for(Entity& ent : entities) { + ent.smoothing += dt; + if(ent.smoothing > TPS_DIV) { + ent.smoothing = 0; + } + Vector3 pos_lerped = Vector3Lerp(ent.position, Vector3Add(ent.position, ent.velocity*TPS_DIV), ent.smoothing * TPS); + if(ent.type == 0 || ent.type == 2) { + Color color = ColorLerp(WHITE, RED, ent.damage_flash / 50.0f); + Vector3 posLerp = Vector3Add(pos_lerped, {-0.5f, -0.1f, -0.5f}); + + float time = (ent.lifetime + ent.smoothing) * 4.0f; + float armWave0 = sinf(time * PI); + float armWave1 = -abs(cosf(time * PI)); + float headWave = cosf(time * PI); + posLerp = Vector3Add(posLerp, {0, abs(sinf(time * PI)) * 0.1f, 0}); + Vector3 headPivot = Vector3Add(posLerp, (Vector3){0, 16*PIXUNIT, 0}); + float yaw = ent.yaw / 65535.0f * 360.0f; + + rlColor4f(color.r, color.g, color.b, color.a); + rlPushMatrix(); + rlTranslatef(posLerp.x, posLerp.y, posLerp.z); // move to model origin + rlRotatef(yaw, 0, 1, 0); // rotate around Y + rlTranslatef(-posLerp.x, -posLerp.y, -posLerp.z); // move back (optional depending on your pivots) + + // + + rlPushMatrix(); + rlTranslatef(headPivot.x, headPivot.y, headPivot.z); + rlRotatef(25.0f * armWave0, 1, 0, 0); + rlRotatef(25.0f * headWave, 0, 0, 1); + // Head + DrawCubeTexture(playerTex, {0, 4*PIXUNIT, 0}, 8*PIXUNIT, 8*PIXUNIT, 8*PIXUNIT, WHITE, 8, 8, 8, 8); + rlPopMatrix(); + + Vector3 leftPivot = Vector3Add(posLerp, (Vector3){-6*PIXUNIT, 1.25f - 5*PIXUNIT, 0}); + Vector3 rightPivot = Vector3Add(posLerp, (Vector3){6*PIXUNIT, 1.25f - 5*PIXUNIT, 0}); + + rlPushMatrix(); + rlTranslatef(leftPivot.x, leftPivot.y, leftPivot.z); + rlRotatef(45.0f * armWave0, 1, 0, 0); + rlRotatef(45.0f * armWave1, 0, 0, 1); + DrawCubeTexture(playerTex, (Vector3){0, -6*PIXUNIT, 0}, 4*PIXUNIT, 12*PIXUNIT, 4*PIXUNIT, WHITE, 44,20,4,4); + rlPopMatrix(); + + rlPushMatrix(); + rlTranslatef(rightPivot.x, rightPivot.y, rightPivot.z); + rlRotatef(-45.0f * armWave0, 1, 0, 0); + rlRotatef(-45.0f * armWave1, 0, 0, 1); + DrawCubeTexture(playerTex, (Vector3){0, -6*PIXUNIT, 0}, 4*PIXUNIT, 12*PIXUNIT, 4*PIXUNIT, WHITE, 44,20,4,4); + rlPopMatrix(); + + Vector3 body = Vector3Add(posLerp, {0, 1.25f - 10*PIXUNIT, 0}); + rlPushMatrix(); + rlTranslatef(body.x, body.y, body.z); + DrawCubeTexture(playerTex, Vector3Zero(), 8*PIXUNIT, 12*PIXUNIT, 4*PIXUNIT, WHITE, 20, 20, 8, 4); + rlPopMatrix(); + + leftPivot = Vector3Add(posLerp, (Vector3){-2*PIXUNIT, 1.25f - 14*PIXUNIT, 0}); + rightPivot = Vector3Add(posLerp, (Vector3){2*PIXUNIT, 1.25f - 14*PIXUNIT, 0}); + + rlPushMatrix(); + rlTranslatef(leftPivot.x, leftPivot.y, leftPivot.z); + rlRotatef(-45.0f * armWave0, 1, 0, 0); + DrawCubeTexture(playerTex, {0*PIXUNIT, -8*PIXUNIT, 0}, 4*PIXUNIT, 12*PIXUNIT, 4*PIXUNIT, WHITE, 4, 20, 4, 4); + rlPopMatrix(); + + rlPushMatrix(); + rlTranslatef(rightPivot.x, rightPivot.y, rightPivot.z); + rlRotatef(45.0f * armWave0, 1, 0, 0); + DrawCubeTexture(playerTex, {0*PIXUNIT, -8*PIXUNIT, 0}, 4*PIXUNIT, 12*PIXUNIT, 4*PIXUNIT, WHITE, 4, 20, 4, 4); + rlPopMatrix(); + + rlPopMatrix(); + } + if(ent.type == 1) { + Color color = WHITE; + Vector3 posLerp = Vector3Add(ent.position, {-0.5f, -0.1f, -0.5f}); + + float time = ent.lifetime * 4.0f; + float armWave0 = sinf(time * PI); + float armWave1 = -abs(cosf(time * PI)); + float headWave = cosf(time * PI); + posLerp = Vector3Add(posLerp, {0, abs(sinf(time * PI)) * 0.1f, 0}); + Vector3 headPivot = Vector3Add(posLerp, (Vector3){0, 16*PIXUNIT, 0}); + float yaw = -ent.yaw / 65535.0f * 360.0f - 90; + + rlPushMatrix(); + rlTranslatef(posLerp.x, posLerp.y, posLerp.z); + rlRotatef(yaw, 0, 1, 0); + rlTranslatef(-posLerp.x, -posLerp.y, -posLerp.z); + + // + + rlPushMatrix(); + rlTranslatef(headPivot.x, headPivot.y, headPivot.z); + rlRotatef(25.0f * armWave0, 1, 0, 0); + rlRotatef(25.0f * headWave, 0, 0, 1); + // Head + DrawCubeTexture(playerTex, {0, 4*PIXUNIT, 0}, 8*PIXUNIT, 8*PIXUNIT, 8*PIXUNIT, WHITE, 8, 8, 8, 8); + rlPopMatrix(); + + Vector3 leftPivot = Vector3Add(posLerp, (Vector3){-6*PIXUNIT, 1.25f - 5*PIXUNIT, 0}); + Vector3 rightPivot = Vector3Add(posLerp, (Vector3){6*PIXUNIT, 1.25f - 5*PIXUNIT, 0}); + + rlPushMatrix(); + rlTranslatef(leftPivot.x, leftPivot.y, leftPivot.z); + rlRotatef(45.0f * armWave0, 1, 0, 0); + rlRotatef(45.0f * armWave1, 0, 0, 1); + DrawCubeTexture(playerTex, (Vector3){0, -6*PIXUNIT, 0}, 4*PIXUNIT, 12*PIXUNIT, 4*PIXUNIT, WHITE, 44,20,4,4); + rlPopMatrix(); + + rlPushMatrix(); + rlTranslatef(rightPivot.x, rightPivot.y, rightPivot.z); + rlRotatef(-45.0f * armWave0, 1, 0, 0); + rlRotatef(-45.0f * armWave1, 0, 0, 1); + DrawCubeTexture(playerTex, (Vector3){0, -6*PIXUNIT, 0}, 4*PIXUNIT, 12*PIXUNIT, 4*PIXUNIT, WHITE, 44,20,4,4); + rlPopMatrix(); + + Vector3 body = Vector3Add(posLerp, {0, 1.25f - 10*PIXUNIT, 0}); + + rlPushMatrix(); + rlTranslatef(body.x, body.y, body.z); + DrawCubeTexture(playerTex, Vector3Zero(), 8*PIXUNIT, 12*PIXUNIT, 4*PIXUNIT, WHITE, 20, 20, 8, 4); + rlPopMatrix(); + + leftPivot = Vector3Add(posLerp, (Vector3){-2*PIXUNIT, 1.25f - 14*PIXUNIT, 0}); + rightPivot = Vector3Add(posLerp, (Vector3){2*PIXUNIT, 1.25f - 14*PIXUNIT, 0}); + + rlPushMatrix(); + rlTranslatef(leftPivot.x, leftPivot.y, leftPivot.z); + rlRotatef(-45.0f * armWave0, 1, 0, 0); + DrawCubeTexture(playerTex, {0*PIXUNIT, -8*PIXUNIT, 0}, 4*PIXUNIT, 12*PIXUNIT, 4*PIXUNIT, WHITE, 4, 20, 4, 4); + rlPopMatrix(); + + rlPushMatrix(); + rlTranslatef(rightPivot.x, rightPivot.y, rightPivot.z); + rlRotatef(45.0f * armWave0, 1, 0, 0); + DrawCubeTexture(playerTex, {0*PIXUNIT, -8*PIXUNIT, 0}, 4*PIXUNIT, 12*PIXUNIT, 4*PIXUNIT, WHITE, 4, 20, 4, 4); + rlPopMatrix(); + + rlPopMatrix(); + + Vector3 dir = Vector3Subtract(camera.position, ent.position); + + rlPushMatrix(); + rlTranslatef(ent.position.x, ent.position.y + 1.5f, ent.position.z); + rlRotatef(atan2f(dir.x, dir.z) * RAD2DEG, 0.0f, 1.0f, 0.0f); + rlRotatef(90.0f, 1.0f, 0.0f, 0.0f); + char name[12]; + sprintf(name, "Player %d", (int)ent.data[0]); + DrawText3D(fnt, name, {-MeasureTextEx(fnt, name, PIXUNIT * 4, 0).x * 1.5f, 0, 0}, PIXUNIT * 4, PIXUNIT, 1, true, WHITE); + rlPopMatrix(); + } + if(ent.type == 3) { + DrawCube(pos_lerped, 1, 1, 1, RED); + } + if(ent.type == 4) { + DrawCubeBlock(terrain, pos_lerped, 1,1,1, BLOCKS[ent.data[1]]); + } + } +} +#endif \ No newline at end of file diff --git a/src/helpers/glfw_keycodes_to_string.h b/src/helpers/glfw_keycodes_to_string.h new file mode 100644 index 0000000..328f070 --- /dev/null +++ b/src/helpers/glfw_keycodes_to_string.h @@ -0,0 +1,424 @@ +// https://gist.github.com/0xD34DC0DE/910855d41786b962127ae401da2a3441 + +#include + +// Key code from the USB HID Usage Tables v1.12(p. 53-60) but re-arranged to map to 7-bit ASCII for printable keys +enum KeyCode : int32_t +{ + Unknown = -1, + Space = 32, + Apostrophe = 39, + Comma = 44, + Minus = 45, + Period = 46, + Slash = 47, + Num0 = 48, + Num1 = 49, + Num2 = 50, + Num3 = 51, + Num4 = 52, + Num5 = 53, + Num6 = 54, + Num7 = 55, + Num8 = 56, + Num9 = 57, + Semicolon = 59, + Equal = 61, + A = 65, + B = 66, + C = 67, + D = 68, + E = 69, + F = 70, + G = 71, + H = 72, + I = 73, + J = 74, + K = 75, + L = 76, + M = 77, + N = 78, + O = 79, + P = 80, + Q = 81, + R = 82, + S = 83, + T = 84, + U = 85, + V = 86, + W = 87, + X = 88, + Y = 89, + Z = 90, + LeftBracket = 91, + Backslash = 92, + RightBracket = 93, + GraveAccent = 96, + World1 = 161, + World2 = 162, + K_Escape = 256, + Enter = 257, + Tab = 258, + Backspace = 259, + Insert = 260, + Delete = 261, + Right = 262, + Left = 263, + Down = 264, + Up = 265, + PageUp = 266, + PageDown = 267, + Home = 268, + End = 269, + CapsLock = 280, + ScrollLock = 281, + NumLock = 282, + PrintScreen = 283, + Pause = 284, + F1 = 290, + F2 = 291, + F3 = 292, + F4 = 293, + F5 = 294, + F6 = 295, + F7 = 296, + F8 = 297, + F9 = 298, + F10 = 299, + F11 = 300, + F12 = 301, + F13 = 302, + F14 = 303, + F15 = 304, + F16 = 305, + F17 = 306, + F18 = 307, + F19 = 308, + F20 = 309, + F21 = 310, + F22 = 311, + F23 = 312, + F24 = 313, + F25 = 314, + Keypad0 = 320, + Keypad1 = 321, + Keypad2 = 322, + Keypad3 = 323, + Keypad4 = 324, + Keypad5 = 325, + Keypad6 = 326, + Keypad7 = 327, + Keypad8 = 328, + Keypad9 = 329, + KeypadDecimal = 330, + KeypadDivide = 331, + KeypadMultiply = 332, + KeypadSubtract = 333, + KeypadAdd = 334, + KeypadEnter = 335, + KeypadEqual = 336, + LeftShift = 340, + LeftControl = 341, + LeftAlt = 342, + LeftSuper = 343, + RightShift = 344, + RightControl = 345, + RightAlt = 346, + RightSuper = 347, + Menu = 348 +}; + +constexpr const char* const LUT44To96[] { "Comma", + "Minus", + "Period", + "Slash", + "Num0", + "Num1", + "Num2", + "Num3", + "Num4", + "Num5", + "Num6", + "Num7", + "Num8", + "Num9", + "Invalid", + "Semicolon", + "Invalid", + "Equal", + "Invalid", + "Invalid", + "Invalid", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + "LeftBracket", + "Backslash", + "RightBracket", + "Invalid", + "Invalid", + "GraveAccent" }; + +constexpr const char* const LUT256To348[] { "Escape", + "Enter", + "Tab", + "Backspace", + "Insert", + "Delete", + "Right", + "Left", + "Down", + "Up", + "PageUp", + "PageDown", + "Home", + "End", + "Invalid", + "Invalid", + "Invalid", + "Invalid", + "Invalid", + "Invalid", + "Invalid", + "Invalid", + "Invalid", + "Invalid", + "CapsLock", + "ScrollLock", + "NumLock", + "PrintScreen", + "Pause", + "Invalid", + "Invalid", + "Invalid", + "Invalid", + "Invalid", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "F11", + "F12", + "F13", + "F14", + "F15", + "F16", + "F17", + "F18", + "F19", + "F20", + "F21", + "F22", + "F23", + "F24", + "F25", + "Invalid", + "Invalid", + "Invalid", + "Invalid", + "Invalid", + "Keypad0", + "Keypad1", + "Keypad2", + "Keypad3", + "Keypad4", + "Keypad5", + "Keypad6", + "Keypad7", + "Keypad8", + "Keypad9", + "KeypadDecimal", + "KeypadDivide", + "KeypadMultiply", + "KeypadSubtract", + "KeypadAdd", + "KeypadEnter", + "KeypadEqual", + "Invalid", + "Invalid", + "Invalid", + "LeftShift", + "LeftControl", + "LeftAlt", + "LeftSuper", + "RightShift", + "RightControl", + "RightAlt", + "RightSuper", + "Menu" }; + +constexpr const char* KeyCodeToString(KeyCode keycode) noexcept +{ + if (keycode == 32) { return "Space"; } // Common key, don't treat as an unlikely scenario + + if (keycode >= 44 && keycode <= 96) [[likely]] { return LUT44To96[keycode - 44]; } + + if (keycode >= 256 && keycode <= 348) [[likely]] { return LUT256To348[keycode - 256]; } + + // Unlikely scenario where the keycode didn't fall inside one of the two lookup tables + switch (keycode) + { + case 39: return "Apostrophe"; + case 161: return "World1"; + case 162: return "Wordl2"; + default: return "Unknown"; + } +} + +constexpr const char* KeyCodeToStringSwitch(KeyCode keycode) noexcept +{ + switch (keycode) + { + case -1: return "Unknown"; + case 32: return "Space"; + case 39: return "Apostrophe"; + case 44: return "Comma"; + case 45: return "Minus"; + case 46: return "Period"; + case 47: return "Slash"; + case 48: return "Num0"; + case 49: return "Num1"; + case 50: return "Num2"; + case 51: return "Num3"; + case 52: return "Num4"; + case 53: return "Num5"; + case 54: return "Num6"; + case 55: return "Num7"; + case 56: return "Num8"; + case 57: return "Num9"; + case 59: return "Semicolon"; + case 61: return "Equal"; + case 65: return "A"; + case 66: return "B"; + case 67: return "C"; + case 68: return "D"; + case 69: return "E"; + case 70: return "F"; + case 71: return "G"; + case 72: return "H"; + case 73: return "I"; + case 74: return "J"; + case 75: return "K"; + case 76: return "L"; + case 77: return "M"; + case 78: return "N"; + case 79: return "O"; + case 80: return "P"; + case 81: return "Q"; + case 82: return "R"; + case 83: return "S"; + case 84: return "T"; + case 85: return "U"; + case 86: return "V"; + case 87: return "W"; + case 88: return "X"; + case 89: return "Y"; + case 90: return "Z"; + case 91: return "Left Bracket"; + case 92: return "Backslash"; + case 93: return "Right Bracket"; + case 96: return "Grave Accent"; + case 161: return "World1"; + case 162: return "World2"; + case 256: return "Escape"; + case 257: return "Enter"; + case 258: return "Tab"; + case 259: return "Backspace"; + case 260: return "Insert"; + case 261: return "Delete"; + case 262: return "Right"; + case 263: return "Left"; + case 264: return "Down"; + case 265: return "Up"; + case 266: return "Page Up"; + case 267: return "Page Down"; + case 268: return "Home"; + case 269: return "End"; + case 280: return "Caps Lock"; + case 281: return "Scroll Lock"; + case 282: return "Num Lock"; + case 283: return "Print Screen"; + case 284: return "Pause"; + case 290: return "F1"; + case 291: return "F2"; + case 292: return "F3"; + case 293: return "F4"; + case 294: return "F5"; + case 295: return "F6"; + case 296: return "F7"; + case 297: return "F8"; + case 298: return "F9"; + case 299: return "F10"; + case 300: return "F11"; + case 301: return "F12"; + case 302: return "F13"; + case 303: return "F14"; + case 304: return "F15"; + case 305: return "F16"; + case 306: return "F17"; + case 307: return "F18"; + case 308: return "F19"; + case 309: return "F20"; + case 310: return "F21"; + case 311: return "F22"; + case 312: return "F23"; + case 313: return "F24"; + case 314: return "F25"; + case 320: return "Keypad 0"; + case 321: return "Keypad 1"; + case 322: return "Keypad 2"; + case 323: return "Keypad 3"; + case 324: return "Keypad 4"; + case 325: return "Keypad 5"; + case 326: return "Keypad 6"; + case 327: return "Keypad 7"; + case 328: return "Keypad 8"; + case 329: return "Keypad 9"; + case 330: return "Keypad Decimal"; + case 331: return "Keypad Divide"; + case 332: return "Keypad Multiply"; + case 333: return "Keypad Subtract"; + case 334: return "Keypad Add"; + case 335: return "Keypad Enter"; + case 336: return "Keypad Equal"; + case 340: return "Left Shift"; + case 341: return "Left Control"; + case 342: return "Left Alt"; + case 343: return "Left Super"; + case 344: return "Right Shift"; + case 345: return "Right Control"; + case 346: return "Right Alt"; + case 347: return "Right Super"; + case 348: return "Menu"; + default: return "Unknown"; + }; +} \ No newline at end of file diff --git a/src/helpers/inlines.hpp b/src/helpers/inlines.hpp new file mode 100644 index 0000000..c309620 --- /dev/null +++ b/src/helpers/inlines.hpp @@ -0,0 +1,177 @@ +// For use within makeheaders! + +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(ATLAS_SIZE_W); + const float uv_mod_y = 1.0f / static_cast(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 texture, float offsetX, float offsetY, float sizeModX, float sizeModY) { + const float uv_mod_x = 1.0f / static_cast(ATLAS_SIZE_W); + const float uv_mod_y = 1.0f / static_cast(ATLAS_SIZE_H); + const float uv_mod1_x = sizeModX / static_cast(ATLAS_SIZE_W); + const float uv_mod1_y = sizeModY / static_cast(ATLAS_SIZE_H); + const float uv_off_x = offsetX / static_cast(ATLAS_SIZE_W); + const float uv_off_y = offsetY / static_cast(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 GetUVTex(int w, int h, int offsetX, int offsetY, int sizeX, int sizeY) { + const float uv_mod_x = 1.0f / static_cast(w); + const float uv_mod_y = 1.0f / static_cast(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 GetUVTexFlipX(int w, int h, int offsetX, int offsetY, int sizeX, int sizeY) { + const float uv_mod_x = 1.0f / static_cast(w); + const float uv_mod_y = 1.0f / static_cast(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; +} +#endif \ No newline at end of file diff --git a/src/helpers/input.cpp b/src/helpers/input.cpp new file mode 100644 index 0000000..edfc81f --- /dev/null +++ b/src/helpers/input.cpp @@ -0,0 +1,147 @@ +#ifndef SERVER +#include "common.hpp" +#include +#include +#include +#include +#include "yaml-cpp/yaml.h" +#define CONFIG_LOC "config.yaml" + +static bool IsMouseMoving() { + return GetMouseDelta().x > 0.01f || GetMouseDelta().y > 0.01f; +} + +// Config + +std::map controls = { + {"forward", KEY_W}, + {"left", KEY_A}, + {"back", KEY_S}, + {"right", KEY_D}, + {"jump", KEY_SPACE}, + {"shift", KEY_LEFT_SHIFT}, + {"speed_up", KEY_LEFT_CONTROL}, + {"fly", KEY_V}, + {"inventory", KEY_E}, + {"chat", KEY_APOSTROPHE} +}; + +bool swap_mouse = false; // H1N1 mode (wink) +bool InputGetKeyDown(const std::string name) { + return IsKeyDown(controls.at(name)); +} +bool InputGetKeyPressed(const std::string name) { + return IsKeyPressed(controls.at(name)); +} +bool InputGetLMB() { + if(swap_mouse) { + return IsMouseButtonPressed(MOUSE_RIGHT_BUTTON); + } + else { + return IsMouseButtonPressed(MOUSE_LEFT_BUTTON); + } +} +bool InputGetLMBDown() { + if(swap_mouse) { + return IsMouseButtonDown(MOUSE_RIGHT_BUTTON); + } + else { + return IsMouseButtonDown(MOUSE_LEFT_BUTTON); + } +} +bool InputGetRMB() { + if(swap_mouse) { + return IsMouseButtonPressed(MOUSE_LEFT_BUTTON); + } + else { + return IsMouseButtonPressed(MOUSE_RIGHT_BUTTON); + } +} +Vector2 InputGetWalkAxis() { + bool isJoystick = IsGamepadAvailable(0); + float x = 0; + float y = 0; + if(isJoystick) { + x = GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X); + y = GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y); + if(abs(x) < 0.5f) { + x = 0; + } + if(abs(y) < 0.5f) { + y = 0; + } + } + + if (InputGetKeyDown("forward")) { y = 1; } + if (InputGetKeyDown("back")) { y = -1; } + if (InputGetKeyDown("right")) { x = 1; } + if (InputGetKeyDown("left")) { x = -1; } + + return {x, y}; +} +Vector2 InputGetLookAxis() { + bool isJoystick = IsGamepadAvailable(0); + float x = 0; + float y = 0; + if(isJoystick) { + x = GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_X); + y = GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_Y); + if(abs(x) < 0.1f) { + x = 0; + } + if(abs(y) < 0.1f) { + y = 0; + } + return {x, y}; + } + else { + return GetMouseDelta(); + } +} +Vector2 InputGetDPAD() { + bool isJoystick = IsGamepadAvailable(0); + float x = 0.0f; + float y = 0.0f; + if(isJoystick) { + if (IsGamepadButtonPressed(0, GAMEPAD_BUTTON_LEFT_FACE_UP)) { y = 1; } + if (IsGamepadButtonPressed(0, GAMEPAD_BUTTON_LEFT_FACE_DOWN)) { y = -1; } + if (IsGamepadButtonPressed(0, GAMEPAD_BUTTON_LEFT_FACE_RIGHT)) { x = 1; } + if (IsGamepadButtonPressed(0, GAMEPAD_BUTTON_LEFT_FACE_LEFT)) { x = -1; } + } + return {x, y}; +} +void InputWriteControls() { + YAML::Emitter out; + out << YAML::BeginMap; + + out << YAML::Key << "Mapped controls"; + out << YAML::Value << controls; + out << YAML::Key << "Swap mouse buttons"; + out << YAML::Value << swap_mouse; + + out << YAML::EndMap; + FILE* file = fopen(CONFIG_LOC, "w" ); + fprintf( file, out.c_str()); + fclose( file ); +} +void InputInit() { + if(!FileExists(CONFIG_LOC)) { + InputWriteControls(); + } + else { + YAML::Node config = YAML::LoadFile(CONFIG_LOC); + const std::map _controls = config["Mapped controls"].as>(); + for (auto const& [key, val] : _controls) + { + controls[key] = val; + } + + + swap_mouse = config["Swap mouse buttons"].as(); + } +} +void InputUpdate() { + +} + +#endif \ No newline at end of file diff --git a/src/helpers/inventory.cpp b/src/helpers/inventory.cpp new file mode 100644 index 0000000..ec2ea33 --- /dev/null +++ b/src/helpers/inventory.cpp @@ -0,0 +1,554 @@ +#ifndef SERVER +#include +#include +#include "common.hpp" +#include +#include +#include +#include +#define PIXUNIT 1.0f / 16.0f +InventoryItem hotbar[ITEMS]; +InventoryItem inventory[INVENTORY_SIZE]; +static uint8_t selected = 0; +static Texture2D item_atlas; +extern Texture2D terrain; +extern Texture2D playerTex; +const std::string LocaleGet(std::string loc); +std::vector registry = { + Item{ "block", IType_Block, 0}, + Item{ "apple", IType_Edible, 0}, + Item{ "stick", IType_Generic, 1}, + Item{ "stone_pickaxe", IType_Mining, 2, 1}, + Item{ "copper_pickaxe", IType_Mining, 4, 2}, + Item{ "silver_pickaxe", IType_Mining, 3, 3}, + Item{ "gold_pickaxe", IType_Mining, 5, 4}, + Item{ "copper_ingot", IType_Generic, 6}, + Item{ "silver_ingot", IType_Generic, 7}, + Item{ "gold_ingot", IType_Generic, 8}, +}; +bool inventoryOpen = false; +InventoryItem* movingItem = nullptr; +static Image itemAtlasImg; +extern std::list recipes; +int GetNumericIDFromString(std::string s); + +void DrawItem(InventoryItem item, int x, int y) { + if(item.item != nullptr) { + int atl_size = ITEM_ATLAS_SIZE; + int tex = item.item->texture; + Texture2D atlas = item_atlas; + if(item.item->type == IType_Block) { + atlas = terrain; + atl_size = ATLAS_SIZE_W; + tex = BLOCKS[item.damage].textureTop; + } + DrawTexturePro(atlas, + { + (float)(tex % atl_size) * ITEM_SIZE, + (float)(tex / atl_size) * ITEM_SIZE, + ITEM_SIZE, + ITEM_SIZE + }, + { + (float)x+6, (float)y+6, 44, 44 + }, + {0, 0}, + 0, + WHITE); + char text[12] = "???\0"; + sprintf(text, "x%d", item.amount); + if(item.amount != 0) DrawText(text, x, y, DEFAULT_FONT_SIZE / 2, WHITE); + } +} +void InventoryRemoveItem(int slot) { + if(inventory[slot].amount-1 > 0) { + inventory[slot].amount--; + } + else { + inventory[slot].item = nullptr; + inventory[slot].damage = 0; + inventory[slot].amount = 0; + } +} +void HotbarRemoveItem(int slot) { + if(hotbar[slot].amount-1 > 0) { + hotbar[slot].amount--; + } + else { + hotbar[slot].item = nullptr; + hotbar[slot].damage = 0; + hotbar[slot].amount = 0; + } +} +void HotbarRemoveSelected() { + HotbarRemoveItem(selected); +} +void HotbarAddItem(Item* registry, int damage) { + for (int i = 0; i < ITEMS; i++) + { + InventoryItem item = hotbar[i]; + if((item.item == registry && item.damage == damage)) { + hotbar[i].item = registry; + hotbar[i].damage = damage; + hotbar[i].amount += 1; + return; + } + } + for (int i = 0; i < INVENTORY_SIZE; i++) + { + InventoryItem item = inventory[i]; + if((item.item == registry && item.damage == damage)) { + inventory[i].item = registry; + inventory[i].damage = damage; + inventory[i].amount += 1; + return; + } + } + for (int i = 0; i < ITEMS; i++) + { + InventoryItem item = hotbar[i]; + if(item.item == nullptr) { + hotbar[i].item = registry; + hotbar[i].damage = damage; + hotbar[i].amount += 1; + return; + } + } + for (int i = 0; i < INVENTORY_SIZE; i++) + { + InventoryItem item = inventory[i]; + if(item.item == nullptr) { + inventory[i].item = registry; + inventory[i].damage = damage; + inventory[i].amount += 1; + return; + } + } +} +void HotbarAddItemLots(Item* registry, int damage, int amount) { + for (int i = 0; i < ITEMS; i++) + { + InventoryItem item = hotbar[i]; + if((item.item == registry && item.damage == damage)) { + hotbar[i].item = registry; + hotbar[i].damage = damage; + hotbar[i].amount += amount; + return; + } + } + for (int i = 0; i < INVENTORY_SIZE; i++) + { + InventoryItem item = inventory[i]; + if((item.item == registry && item.damage == damage)) { + inventory[i].item = registry; + inventory[i].damage = damage; + inventory[i].amount += amount; + return; + } + } + for (int i = 0; i < ITEMS; i++) + { + InventoryItem item = hotbar[i]; + if(item.item == nullptr) { + hotbar[i].item = registry; + hotbar[i].damage = damage; + hotbar[i].amount += amount; + return; + } + } + for (int i = 0; i < INVENTORY_SIZE; i++) + { + InventoryItem item = inventory[i]; + if(item.item == nullptr) { + inventory[i].item = registry; + inventory[i].damage = damage; + inventory[i].amount += amount; + return; + } + } +} +Item* GetItemByID(std::string s) { + for (Item& i : registry) { + if (i.id == s) return &i; + } + return nullptr; +} +int GetItemNumID(std::string s) { + int o = 0; + for (Item& i : registry) { + if (i.id == s) return o; + o++; + } + return -1; +} +void InitHotbar() { + /*for (int i = 0; i < ITEMS; i++) + { + HotbarRemoveItem(i); + }*/ + //HotbarAddItem(®istry[0], GetNumericIDFromString("workbench")); + item_atlas = LoadTexture(Pathify("items.png")); + itemAtlasImg = LoadImageFromTexture(item_atlas); +} + +bool IDTagMatch(std::string tag, std::string id); +static bool GetTaggedItem(std::string tag, InventoryItem* item) { + InventoryItem temp; + int id; + bool isBlock; + bool found; + for(int blk = 0; blk < MAX_BLOCK_ID; blk++) { + if(IDTagMatch(tag, BLOCKS[blk].name)) { + isBlock = true; + id = GetNumericIDFromString(BLOCKS[blk].name); + found = true; + break; + }; + } + if(!found) { + for(Item item : registry) { + if(IDTagMatch(tag, item.id)) { + isBlock = false; + id = GetItemNumID(item.id); + found = true; + break; + }; + } + } + if(!found) { + return false; + } + if(isBlock) { + temp.item = &(registry[0]); + temp.damage = id; + } + else { + temp.item = &(registry[id]); + } + memcpy(item, &temp, sizeof(InventoryItem)); + return true; +} +static bool FindTaggedItem(std::string tag, InventoryItem* item) { + for(int blk = 0; blk < INVENTORY_SIZE; blk++) { + InventoryItem itm = inventory[blk]; + if(itm.item != nullptr) { + if(GetTaggedItem(tag, &itm)) { + memcpy(item, &itm, sizeof(InventoryItem)); + return true; + } + } + } + return false; +} +void DrawHotbar() { + if(inventoryOpen) UIBegin(); + const int width = 52 * ITEMS; + DrawRectangle(GetScreenWidth()/2-width/2, GetScreenHeight()-48-2, width, 50, ColorAlpha(BLACK, 0.25f)); + for (int i = 0; i < ITEMS; i++) + { + int x = GetScreenWidth()/2-width/2 + i * 52; + int y = GetScreenHeight()-50; + if(inventoryOpen) { + if(ButtonInventory(x, y, 52, 52)) { + if(movingItem != nullptr && hotbar[i].item == nullptr) { + memcpy(&hotbar[i], movingItem, sizeof(InventoryItem)); + movingItem->item = nullptr; + movingItem->amount = 0; + movingItem->damage = 0; + } + else { + movingItem = &hotbar[i]; + } + }; + } + + if(i == selected) { + DrawRectangle(x, y, 52, 52, BLACK); + } + if(&hotbar[i] != movingItem) { + DrawItem(hotbar[i], x, y); + } + } + if(inventoryOpen) { + const int width2 = 52 * INVENTORY_WIDTH; + const int inv_height = 52 * INVENTORY_HEIGHT; + DrawRectangle(GetScreenWidth()/2-width2/2, GetScreenHeight()-56-inv_height, width2, inv_height, ColorAlpha(BLACK, 0.25f)); + for (int i = 0; i < INVENTORY_SIZE; i++) + { + int x = GetScreenWidth()/2-width2/2 + (i % INVENTORY_WIDTH) * 52; + int y = GetScreenHeight()-56-inv_height + (i / INVENTORY_WIDTH) * 52; + if((i + (i / INVENTORY_WIDTH)) % 2 == 0) { + DrawRectangle(x, y, 52, 52, ColorAlpha(BLACK, 0.1f)); + } + if(ButtonInventory(x, y, 52, 52)) { + if(movingItem != nullptr && inventory[i].item == nullptr) { + memcpy(&inventory[i], movingItem, sizeof(InventoryItem)); + movingItem->item = nullptr; + movingItem->amount = 0; + movingItem->damage = 0; + } + else { + movingItem = &inventory[i]; + } + }; + if(&inventory[i] != movingItem) { + DrawItem(inventory[i], x, y); + } + } + std::list validRecipes; + for (Recipe r : recipes) + { + if(r.requiredStation == "hands") { + std::vector items; + for (RecipeItem ingredient : r.inputs) { + for (int i = 0; i < INVENTORY_SIZE; i++) + { + InventoryItem item = inventory[i]; + if(item.item != nullptr) { + bool blockCond = item.item->type == IType_Block && ingredient.type == IType_Block && item.damage == GetNumericIDFromString(ingredient.id); + bool genericCond = item.item->type != IType_Block && ingredient.type == IType_Generic && item.item->id == ingredient.id; + bool tagBlockCond = item.item->type == IType_Block && ingredient.type == IType_Special_Tag && IDTagMatch(ingredient.id, BLOCKS[item.damage].name); + bool tagItemCond = item.item->type != IType_Block && ingredient.type == IType_Special_Tag && IDTagMatch(ingredient.id, item.item->id); + if(blockCond || genericCond || tagBlockCond || tagItemCond) { + items.push_back(i); + } + } + } + } + if(items.size() == r.inputs.size()) { + validRecipes.push_back(r); + } + } + } + int _y = 0; + for (Recipe r : validRecipes) + { + int x = GetScreenWidth()/2-width2/2 - 300; + int y = GetScreenHeight()-56-inv_height - _y * 52; + DrawRectangle(x, y, 260, 52, Fade(BLACK, 0.25f)); + int _x = 0; + for (RecipeItem ingredient : r.inputs) { + InventoryItem temp; + if(ingredient.type == IType_Block) { + temp.item = &(registry[0]); + temp.damage = GetNumericIDFromString(ingredient.id); + } + else if (ingredient.type == IType_Generic) { + temp.item = GetItemByID(ingredient.id); + } + else { + GetTaggedItem(ingredient.id, &temp); + } + temp.amount = ingredient.quantity; + DrawItem(temp, x + _x * 52, y); + _x++; + } + _x = 1; + for (RecipeItem ingredient : r.outputs) { + InventoryItem temp; + if(ingredient.type == IType_Block) { + temp.item = &(registry[0]); + temp.damage = GetNumericIDFromString(ingredient.id); + } + else { + temp.item = GetItemByID(ingredient.id); + } + temp.amount = ingredient.quantity; + DrawItem(temp, x + 300 - 40 - _x * 52, y); + _x++; + } + if(ButtonInventory(x, y, 260, 52)) { + std::map items; + for (RecipeItem ingredient : r.inputs) { + for (int i = 0; i < INVENTORY_SIZE; i++) + { + InventoryItem item = inventory[i]; + if(item.item != nullptr) { + bool blockCond = item.item->type == IType_Block && ingredient.type == IType_Block && item.damage == GetNumericIDFromString(ingredient.id); + bool genericCond = item.item->type != IType_Block && ingredient.type == IType_Generic && item.item->id == ingredient.id; + bool tagBlockCond = item.item->type == IType_Block && ingredient.type == IType_Special_Tag && IDTagMatch(ingredient.id, BLOCKS[item.damage].name); + bool tagItemCond = item.item->type != IType_Block && ingredient.type == IType_Special_Tag && IDTagMatch(ingredient.id, item.item->id); + if(blockCond || genericCond || tagBlockCond || tagItemCond) { + items[i] = ingredient.quantity; + } + } + } + } + if(items.size() == r.inputs.size()) { + for(auto [i, amount] : items) { + for (int _ = 0; _ < amount; _++) + { + InventoryRemoveItem(i); + } + } + for (RecipeItem ingredient : r.outputs) { + if(ingredient.type == IType_Block) { + for (int _ = 0; _ < ingredient.quantity; _++) { + HotbarAddItem(®istry[0], GetNumericIDFromString(ingredient.id)); + } + } + else { + for (int _ = 0; _ < ingredient.quantity; _++) { + HotbarAddItem(GetItemByID(ingredient.id), 255); + } + } + } + } + } + _y++; + } + } + if(inventoryOpen) { + const int width2 = 52 * INVENTORY_WIDTH; + const int inv_height = 52 * INVENTORY_HEIGHT; + DrawRectangle(GetScreenWidth()/2-width2/2, 4, width2, inv_height, ColorAlpha(BLACK, 0.25f)); + for (int i = 0; i < INVENTORY_SIZE; i++) + { + InventoryItem item; + if(i < MAX_BLOCK_ID) { + item.item = ®istry[0]; + item.damage = i; + } + else if (i < registry.size()+MAX_BLOCK_ID-1) { + item.item = ®istry[i-MAX_BLOCK_ID+1]; + } + int x = GetScreenWidth()/2-width2/2 + (i % INVENTORY_WIDTH) * 52; + int y = 4 + (i / INVENTORY_WIDTH) * 52; + if((i + (i / INVENTORY_WIDTH)) % 2 == 0) { + DrawRectangle(x, y, 52, 52, ColorAlpha(BLACK, 0.1f)); + } + if(ButtonInventory(x, y, 52, 52)) { + if(movingItem != nullptr) { + movingItem = nullptr; + } + else { + HotbarAddItem(item.item, item.damage); + } + }; + DrawItem(item, x, y); + } + } + if(movingItem != nullptr && movingItem->item != nullptr) { + DrawItem(*movingItem, GetMouseX(), GetMouseY()); + std::string name = "item."+movingItem->item->id; + if(movingItem->item->type == IType_Block) { + name = "item."+BLOCKS[movingItem->damage].name; + } + DrawTextB(LocaleGet(name).c_str(), GetMouseX()-16, GetMouseY()+56, 16, WHITE); + } + if(inventoryOpen) UIHandleControls(); +} +InventoryItem HotbarGetSelected() { + return hotbar[selected]; +} +void DrawSelectedItem() { + InventoryItem item = hotbar[selected]; + Vector3 center = {-0.75f, -0.45f, 0.5f}; + rlPushMatrix(); + rlTranslatef(center.x, center.y-0.25f, center.z); + float swing = powf(player_attack_time,1); + float swing3 = sinf(swing * swing * PI); + float swing2 = sinf(sqrt(swing) * PI); + rlRotatef(-swing3 * 50, 1, 0, 0); + rlRotatef(-swing2 * 80, 0, 1, 0); + + swing = powf(player_place_time,2); + swing2 = sinf(swing * PI); + rlRotatef(swing2 * 80, 0, 1, 0); + + rlRotatef(-45, 1, 0, 0); + rlRotatef(25, 0, 0, 1); + DrawCubeTexture(playerTex, {0, -12*PIXUNIT/2, 0}, 4*PIXUNIT, 12*PIXUNIT, 4*PIXUNIT, WHITE, 44,20,4,4); + if(item.item != nullptr) { + if(item.item->type == IType_Block) { + DrawCubeBlock(terrain, {0, -12*PIXUNIT - 0.25f, 0}, 0.5f, 0.5f, 0.5f, BLOCKS[item.damage]); + } + else { + UVCorners left = GetUVTex(item.item->texture); + rlSetTexture(item_atlas.id); + rlBegin(RL_QUADS); + rlColor4ub(255, 255, 255, 255); + rlNormal3f(1.0f, 0.0f, 0.0f); + const float size = 0.5f; + rlTexCoord2f(left.corner0.x, left.corner0.y); rlVertex3f(0, -12*PIXUNIT - size, 0.25f - size); + rlTexCoord2f(left.corner1.x, left.corner1.y); rlVertex3f(0, -12*PIXUNIT + size, 0.25f - size); + rlTexCoord2f(left.corner2.x, left.corner2.y); rlVertex3f(0, -12*PIXUNIT + size, 0.25f + size); + rlTexCoord2f(left.corner3.x, left.corner3.y); rlVertex3f(0, -12*PIXUNIT - size, 0.25f + size); + rlEnd(); + } + } + rlPopMatrix(); +} +bool InputGetKeyDown(const std::string name); +bool InputGetKeyPressed(const std::string name); +void CloseInventory() { + inventoryOpen = false; + movingItem = nullptr; + DisableCursor(); +} +void UpdateHotbar() { + if(GetMouseWheelMoveV().y > 0.1f) { + selected++; + if(selected >= ITEMS) { + selected = 0; + } + } + if(GetMouseWheelMoveV().y < -0.1f) { + selected--; + if(selected >= ITEMS) { + selected = ITEMS-1; + } + } + if(IsKeyPressed(KEY_ONE)) { + selected = 0; + } + if(IsKeyPressed(KEY_TWO)) { + selected = 1; + } + if(IsKeyPressed(KEY_THREE)) { + selected = 2; + } + if(IsKeyPressed(KEY_FOUR)) { + selected = 3; + } + if(IsKeyPressed(KEY_FIVE)) { + selected = 4; + } + if(IsKeyPressed(KEY_SIX)) { + selected = 5; + } + if(IsKeyPressed(KEY_SEVEN)) { + selected = 6; + } + if(IsKeyPressed(KEY_EIGHT)) { + selected = 7; + } + if(IsKeyPressed(KEY_NINE)) { + selected = 8; + } + if(IsKeyPressed(KEY_KP_ADD)) { + hotbar[selected].damage++; + } + if(IsKeyPressed(KEY_KP_SUBTRACT)) { + hotbar[selected].damage--; + } + if(InputGetKeyPressed("inventory")) { + inventoryOpen = !inventoryOpen; + if(inventoryOpen) { + EnableCursor(); + } + else { + CloseInventory(); + } + } + if(IsKeyPressed(KEY_F9)) { + HotbarAddItem(GetItemByID("gold_pickaxe"), 255); + HotbarAddItemLots(GetItemByID("gold_ingot"), 255, INT32_MAX); + HotbarAddItemLots(GetItemByID("copper_ingot"), 255, INT32_MAX); + HotbarAddItemLots(GetItemByID("silver_ingot"), 255, INT32_MAX); + HotbarAddItemLots(GetItemByID("block"), GetNumericIDFromString("stone"), INT32_MAX); + HotbarAddItemLots(GetItemByID("block"), GetNumericIDFromString("planks"), INT32_MAX); + HotbarAddItemLots(GetItemByID("apple"), 255, INT32_MAX); + } +} +#endif \ No newline at end of file diff --git a/src/helpers/locale.cpp b/src/helpers/locale.cpp new file mode 100644 index 0000000..b17452b --- /dev/null +++ b/src/helpers/locale.cpp @@ -0,0 +1,29 @@ +#ifndef SERVER +#include "common.hpp" +#include "yaml-cpp/yaml.h" +#include +#include +#define LOCALE_LOC "locale.yaml" + +static std::map locale; + +void LocaleUpdate() { + YAML::Node _locale = YAML::LoadFile(Pathify(LOCALE_LOC)); + for(YAML::const_iterator it=_locale.begin();it!=_locale.end();++it) + { + locale[it->first.as()] = it->second.as(); + } +} +const std::string LocaleGet(const std::string loc) { + if(locale.find(loc) != locale.end()) { + return locale[loc]; + } + return loc; +} +const std::string GetKeycodeName(KeyboardKey key) { + const char* name = GetKeyName(key); + if(name == NULL) //fallback + name = KeyCodeToString((KeyCode)key); + return std::string(name); +} +#endif \ No newline at end of file diff --git a/src/helpers/menu.cpp b/src/helpers/menu.cpp new file mode 100644 index 0000000..a5e1862 --- /dev/null +++ b/src/helpers/menu.cpp @@ -0,0 +1,587 @@ +#ifndef SERVER +#include +#include +#include "common.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int currentlySelected = -1; +extern Font fnt; +extern GameState state; +extern bool online; +static Sound snd_click; +static Sound snd_hover; +extern Texture2D terrain; +Texture2D cubemap; +extern Camera camera; +static int MakeUIPOI(int x, int y) { + return x << 16 | y; +} + +std::unordered_set ui_pois; + +int TryConnect(const char* hostname); +void InitSingleplayer(); +void GameModeCleanup(); + +static int mouseX = -1; +static int mouseY = -1; +std::vector worldList; +static void LoadWorlds() { + worldList.clear(); + const std::filesystem::path worlds{PathifyUser("worlds")}; + for (auto const& dir_entry : std::filesystem::directory_iterator{worlds}) + worldList.push_back(dir_entry.path()); +} +void DrawBackground() +{ + ClearBackground(BLACK); + const float unit = 1; + camera.position = { 0 }; + BeginMode3D(camera); + rlSetTexture(cubemap.id); + rlColor4ub(128, 128, 128, 255); + rlRotatef(GetTime(), 0, 1, 0); + rlBegin(RL_QUADS); + rlTexCoord2f(2/6.0f, 0); rlVertex3f(-unit, unit, unit); + rlTexCoord2f(2/6.0f, 1); rlVertex3f(-unit, -unit, unit); + rlTexCoord2f(3/6.0f, 1); rlVertex3f(-unit, -unit, -unit); + rlTexCoord2f(3/6.0f, 0); rlVertex3f(-unit, unit, -unit); + + rlTexCoord2f(1/6.0f, 0); rlVertex3f(unit, unit, unit); + rlTexCoord2f(1/6.0f, 1); rlVertex3f(unit, -unit, unit); + rlTexCoord2f(2/6.0f, 1); rlVertex3f(-unit, -unit, unit); + rlTexCoord2f(2/6.0f, 0); rlVertex3f(-unit, unit, unit); + + rlTexCoord2f(0/6.0f, 0); rlVertex3f(unit, unit, -unit); + rlTexCoord2f(0/6.0f, 1); rlVertex3f(unit, -unit, -unit); + rlTexCoord2f(1/6.0f, 1); rlVertex3f(unit, -unit, unit); + rlTexCoord2f(1/6.0f, 0); rlVertex3f(unit, unit, unit); + + rlTexCoord2f(3/6.0f, 0); rlVertex3f(-unit, unit, -unit); + rlTexCoord2f(3/6.0f, 1); rlVertex3f(-unit, -unit, -unit); + rlTexCoord2f(4/6.0f, 1); rlVertex3f(unit, -unit, -unit); + rlTexCoord2f(4/6.0f, 0); rlVertex3f(unit, unit, -unit); + + rlTexCoord2f(4/6.0f, 0); rlVertex3f(unit, unit, -unit); + rlTexCoord2f(4/6.0f, 1); rlVertex3f(unit, unit, unit); + rlTexCoord2f(5/6.0f, 1); rlVertex3f(-unit, unit, unit); + rlTexCoord2f(5/6.0f, 0); rlVertex3f(-unit, unit, -unit); + + rlTexCoord2f(6/6.0f, 1); rlVertex3f(-unit, -unit, -unit); + rlTexCoord2f(6/6.0f, 0); rlVertex3f(-unit, -unit, unit); + rlTexCoord2f(5/6.0f, 0); rlVertex3f(unit, -unit, unit); + rlTexCoord2f(5/6.0f, 1); rlVertex3f(unit, -unit, -unit); + rlEnd(); + EndMode3D(); +} +void InitMenu() { + snd_click = LoadSound(Pathify("sound/select.wav")); + snd_hover = LoadSound(Pathify("sound/hover.wav")); + cubemap = LoadTexture(Pathify("cubemap.png")); +} +void DrawTextB(const char *text, int posX, int posY, int fontSize, Color color) +{ + DrawTextEx(fnt, text, (Vector2){ posX+2, posY+2 }, fontSize, 1, BLACK); + DrawTextEx(fnt, text, (Vector2){ posX, posY }, fontSize, 1, color); +} +static int lastHover = 0; +static int lastHover0 = 0; +bool Button(const char* text, int x, int y, int width, int id) { + int screenID = MakeUIPOI(x, y); + ui_pois.insert(screenID); + + Color shadow0 = BLACK; + Color shadow1 = WHITE; + Color fill = GRAY; + bool mouseOnButton = mouseX >= x && mouseY >= y && mouseX <= x+width && mouseY <= y+32; + if(mouseOnButton) { + lastHover = MakeUIPOI(x, y); + fill = BLACK; + shadow0 = WHITE; + shadow1 = GRAY; + } + else { + if(lastHover == screenID) { + lastHover = 0; + } + } + DrawRectangle(x-2, y-2, width, 32, shadow1); + DrawRectangle(x+2, y+2, width, 32, shadow0); + DrawRectangle(x, y, width, 32, fill); + DrawRectangleGradientV(x,y, width, 32, fill, ColorLerp(shadow0, fill, 0.5f)); + Vector2 measures = MeasureTextEx(fnt, text, DEFAULT_FONT_SIZE, 1); + DrawTextB(text, x + width / 2 - measures.x / 2, y + 32 / 2 - measures.y / 2, DEFAULT_FONT_SIZE, WHITE); + if((IsMouseButtonPressed(0) || IsGamepadButtonPressed(0, GAMEPAD_BUTTON_RIGHT_FACE_UP)) && mouseOnButton) { + PlaySound(snd_click); + return true; + } + return false; +} + +bool ButtonInventory(int x, int y, int width, int height) { + int screenID = MakeUIPOI(x, y); + ui_pois.insert(screenID); + + Color fill = Fade(WHITE, 0); + bool mouseOnButton = mouseX >= x && mouseY >= y && mouseX <= x+width && mouseY <= y+height; + if(mouseOnButton) { + lastHover = MakeUIPOI(x, y); + fill = Fade(WHITE, 0.25f); + } + else { + if(lastHover == screenID) { + lastHover = 0; + } + } + DrawRectangle(x, y, width, height, fill); + if((IsMouseButtonPressed(0) || IsGamepadButtonPressed(0, GAMEPAD_BUTTON_RIGHT_FACE_UP)) && mouseOnButton) { + PlaySound(snd_click); + return true; + } + return false; +} + +bool TextField(int id, char *buffer, int bufferSize, int x, int y, int width) { + Color shadow0 = BLACK; + Color shadow1 = WHITE; + Color fill = DARKGRAY; + static int focusedId = -1; // currently focused field + static int caretBlinkTimer = 0; // frames + static int caretPos = 0; // insertion index within buffer + + // Measure mouse-over and focus + bool mouseOnField = mouseX >= x && mouseY >= y && mouseX <= x+width && mouseY <= y+32; + if(mouseOnField) { + fill = BLACK; + shadow0 = WHITE; + shadow1 = GRAY; + } + if(focusedId == id) { + fill = BLACK; + shadow0 = WHITE; + shadow1 = GRAY; + } + + // Draw field + DrawRectangle(x-2, y-2, width, 32, shadow1); + DrawRectangle(x+2, y+2, width, 32, shadow0); + DrawRectangle(x, y, width, 32, GRAY); + DrawRectangle(x+2, y+2, width-4, 28, fill); + + // Click to focus + if(IsMouseButtonPressed(0) && mouseOnField) { + focusedId = id; + // position caret near mouse x: approximate by measuring characters + int len = (int)strlen(buffer); + caretPos = len; + float relX = mouseX - (x + 6); // 6px padding + // walk characters to determine caret index + float acc = 0; + for(int i = 0; i <= len; ++i) { + char temp = buffer[i]; + buffer[i] = '\0'; + Vector2 m = MeasureTextEx(fnt, buffer, DEFAULT_FONT_SIZE, 1); + buffer[i] = temp; + if(m.x >= relX) { caretPos = i; break; } + } + caretBlinkTimer = 0; + } else if(IsMouseButtonPressed(0) && !mouseOnField) { + // clicking elsewhere removes focus + // keep focusedId unless clicking outside any field; simplistic: clear focus + if(focusedId == id) focusedId = -1; + } + + // Draw text with padding + const int padding = 6; + Vector2 measures = MeasureTextEx(fnt, buffer, DEFAULT_FONT_SIZE, 1); + DrawTextB(buffer, x + padding, y + 32 / 2 - measures.y / 2, DEFAULT_FONT_SIZE, WHITE); + + bool edited = false; + + // If focused, handle keyboard input + if(focusedId == id) { + // Blink caret + caretBlinkTimer = (caretBlinkTimer + 1) % 60; // 60-frame cycle + if(caretBlinkTimer < 30) { + // compute caret x position by measuring substring + char tmp[256]; + int len = (int)strlen(buffer); + int cp = caretPos; + if(cp < 0) cp = 0; + if(cp > len) cp = len; + int copyLen = (cp < (int)sizeof(tmp)-1) ? cp : (int)sizeof(tmp)-1; + memcpy(tmp, buffer, copyLen); + tmp[copyLen] = '\0'; + Vector2 subMeas = MeasureTextEx(fnt, tmp, DEFAULT_FONT_SIZE, 1); + int cx = x + padding + (int)subMeas.x; + DrawRectangle(cx, y + 6, 2, 20, WHITE); // caret + } + + // Handle control keys: backspace, delete, left/right + int key; + while((key = GetKeyPressed()) != 0) { + std::cout << key << std::endl; + if(key == 259) { // Backspace + int len = (int)strlen(buffer); + if(caretPos > 0 && len > 0) { + // shift left + memmove(buffer + caretPos - 1, buffer + caretPos, len - caretPos + 1); + caretPos--; + edited = true; + } + } else if(key == 127) { // Delete + int len = (int)strlen(buffer); + if(caretPos < len) { + memmove(buffer + caretPos, buffer + caretPos + 1, len - caretPos); + edited = true; + } + } else if(key == 9) { // Tab (remove focus) + focusedId = -1; + } else if(key == 13 || key == 10) { + focusedId = -1; + } else if(key == 27) { + focusedId = -1; + } else if(key == 1270) { + + } else { + + } + } + + // Character input (UTF-8 aware limited) + int c; + while((c = GetCharPressed()) > 0) { + if(c >= 32 && c <= 125) { + int len = (int)strlen(buffer); + if(len + 1 < bufferSize) { + memmove(buffer + caretPos + 1, buffer + caretPos, len - caretPos + 1); + buffer[caretPos] = (char)c; + caretPos++; + edited = true; + } + } + } + int finalLen = (int)strlen(buffer); + if(caretPos < 0) caretPos = 0; + if(caretPos > finalLen) caretPos = finalLen; + } + + return edited; +} + +const char* error = ""; +void UIBegin() { + ui_pois.clear(); +} +void UIResetMousePos() { + mouseX = -1; + mouseY = -1; + ui_pois.clear(); +} +static int controlsDelay = 0; +void UIHandleControls() { + if(lastHover0 != lastHover) { + lastHover0 = lastHover; + PlaySound(snd_hover); + } + if(mouseX == -1 || mouseY == -1) { + std::vector> v; + for (int poi : ui_pois) { + int x = (poi >> 16) & 0xFFFF; + int y = poi & 0xFFFF; + v.push_back({y, x}); + } + std::sort(v.begin(), v.end(), [](const auto &a, const auto &b){ + if (a.first != b.first) return a.first < b.first; + return a.second < b.second; + }); + mouseX = v[0].second; + mouseY = v[0].first; + } + Vector2 dpad = InputGetDPAD(); + if(controlsDelay == 0) { + if(dpad.y > 0.1f) { + std::vector> v; + for (int poi : ui_pois) { + int x = (poi >> 16) & 0xFFFF; + int y = poi & 0xFFFF; + v.push_back({y, x}); + } + std::sort(v.begin(), v.end(), [](const auto &a, const auto &b){ + if (a.first != b.first) return a.first > b.first; + return a.second < b.second; + }); + for (const auto &poi : v) { + int x = poi.second; + int y = poi.first; + if(y < mouseY && x == mouseX) { + mouseX = x; + mouseY = y; + controlsDelay = 30; + return; + } + } + } + if(dpad.y < -0.1f) { + std::vector> v; + for (int poi : ui_pois) { + int x = (poi >> 16) & 0xFFFF; + int y = poi & 0xFFFF; + v.push_back({y, x}); + } + std::sort(v.begin(), v.end(), [](const auto &a, const auto &b){ + if (a.first != b.first) return a.first < b.first; + return a.second < b.second; + }); + for (const auto &poi : v) { + int x = poi.second; + int y = poi.first; + if(y > mouseY && x == mouseX) { + mouseX = x; + mouseY = y; + controlsDelay = 30; + return; + } + } + } + if(dpad.x > 0.1f) { + std::vector> v; + for (int poi : ui_pois) { + int x = (poi >> 16) & 0xFFFF; + int y = poi & 0xFFFF; + v.push_back({y, x}); + } + std::sort(v.begin(), v.end(), [](const auto &a, const auto &b){ + if (a.first != b.first) return a.first > b.first; + return a.second < b.second; + }); + for (const auto &poi : v) { + int x = poi.second; + int y = poi.first; + if(x > mouseX && y == mouseY) { + mouseX = x; + mouseY = y; + controlsDelay = 30; + return; + } + } + } + if(dpad.x < -0.1f) { + std::vector> v; + for (int poi : ui_pois) { + int x = (poi >> 16) & 0xFFFF; + int y = poi & 0xFFFF; + v.push_back({y, x}); + } + std::sort(v.begin(), v.end(), [](const auto &a, const auto &b){ + if (a.first != b.first) return a.first > b.first; + return a.second < b.second; + }); + for (const auto &poi : v) { + int x = poi.second; + int y = poi.first; + if(x < mouseX && y == mouseY) { + mouseX = x; + mouseY = y; + controlsDelay = 30; + return; + } + } + } + } + else { + controlsDelay--; + } + + if(!IsGamepadAvailable(0)) { + mouseX = GetMouseX(); + mouseY = GetMouseY(); + } +} +extern std::map controls; +const std::string LocaleGet(std::string loc); +const std::string GetKeycodeName(KeyboardKey key); +static int currently_selected_control = -1; +void DrawControls() { + UIBegin(); + BeginDrawing(); + DrawBackground(); + const int base_x = GetScreenWidth() / 2 - (160 + 128) / 2; + const int base_y = 64; + int n = 0; + for (const auto& [name, key] : controls) { + const char *text = "...\0"; + if(n != currently_selected_control) { + text = GetKeycodeName((KeyboardKey)key).c_str(); + } + else { + int _key = GetKeyPressed(); + if(_key != 0) { + controls[name] = _key; + currently_selected_control = -1; + } + } + DrawTextB(LocaleGet(name).c_str(), base_x, base_y + n * 36, DEFAULT_FONT_SIZE, WHITE); + if (Button(text, base_x + 160, base_y + n * 36, 128, 0)) { + currently_selected_control = n; + }; + n++; + } + const char *text = " "; + if(swap_mouse) { + text = "X"; + } + DrawTextB(LocaleGet("swap_mouse").c_str(), base_x, base_y + n * 36, DEFAULT_FONT_SIZE, WHITE); + if (Button(text, base_x + 160, base_y + n * 36, 32, 0)) { + swap_mouse = !swap_mouse; + }; + if (Button(LocaleGet("exit").c_str(), GetScreenWidth() / 2 - 64, GetScreenHeight() - 48, 128, 0)) { + InputWriteControls(); + state = GAME_STATE_MAINMENU; + }; + EndDrawing(); + UIHandleControls(); +} +void DrawMainMenu() { + UIBegin(); + BeginDrawing(); + DrawBackground(); + DrawText(GAME_NAME, 24, 24, 64, BLACK); + DrawText(GAME_NAME, 16, 16, 64, WHITE); + DrawTextB(GAME_VERSION, 16, 72, DEFAULT_FONT_SIZE, WHITE); + if (Button(LocaleGet("select_level").c_str(), 16, 128, 128, 0)) { + LoadWorlds(); + UIResetMousePos(); + state = GAME_STATE_WORLDSELECT; + return; + } + if (Button(LocaleGet("multiplayer").c_str(), 16, 128+48, 128, 0)) { + UIResetMousePos(); + state = GAME_STATE_MULTIPLAYERSELECT; + error = ""; + return; + } + if (Button(LocaleGet("options").c_str(), 16, 128+48+48, 128, 0)) { + UIResetMousePos(); + state = GAME_STATE_OPTIONS; + return; + } + if (Button(LocaleGet("exit").c_str(), 16, 128+48+48+48, 128, 0)) { + exit(0); + } + EndDrawing(); + UIHandleControls(); +} +void _LoadWorld(const char* path); +void DrawWorldSelect() { + UIBegin(); + BeginDrawing(); + DrawBackground(); + int i = 0; + for(std::filesystem::path path : worldList) { + if(Button(reinterpret_cast(path.filename().u8string().c_str()), GetScreenWidth()/2-64, i*48+8, 128, 0)) { + online = false; + SetTargetFPS(-1); + _LoadWorld(reinterpret_cast(path.u8string().c_str())); + return; + } + i++; + } + if (Button(LocaleGet("exit").c_str(), GetScreenWidth()/2-196, GetScreenHeight()-48, 128, 0)) { + UIResetMousePos(); + state = GAME_STATE_MAINMENU; + return; + } + if (Button(LocaleGet("create_world").c_str(), GetScreenWidth()/2+196-128, GetScreenHeight()-48, 128, 0)) { + UIResetMousePos(); + InitSingleplayer(); + return; + } + if (Button(LocaleGet("create_world").c_str(), GetScreenWidth()/2+196-128, GetScreenHeight()-48-48, 128, 0)) { + UIResetMousePos(); + InitWorldgen(2); + state = GAME_STATE_LOADING; + return; + } + EndDrawing(); + UIHandleControls(); +} +char buffer[64]; + +void DrawMultiplayer() { + UIBegin(); + BeginDrawing(); + DrawBackground(); + bool changed = TextField(1, buffer, sizeof(buffer), GetScreenWidth()/2-100, 32, 200); + if (Button(LocaleGet("join").c_str(), GetScreenWidth()/2-196, GetScreenHeight()-48, 128, 0)) { + int code = TryConnect(buffer); + if(code != -1) { + switch (code) + { + case NET_ERROR_NOTFOUND: + error = LocaleGet("error_notfound").c_str(); + break; + case NET_ERROR_INTERNAL: + error = LocaleGet("error_internal").c_str(); + break; + case NET_ERROR_FAILED: + error = LocaleGet("error_unknown").c_str(); + break; + default: + break; + } + } + } + if (Button(LocaleGet("exit").c_str(), GetScreenWidth()/2+196-128, GetScreenHeight()-48, 128, 0)) { + state = GAME_STATE_MAINMENU; + UIResetMousePos(); + return; + } + DrawTextB(error, GetScreenWidth()/2-100, 128, DEFAULT_FONT_SIZE, WHITE); + EndDrawing(); + UIHandleControls(); +} + +void DrawPauseMenu() { + UIBegin(); + DrawRectangle(0,0,GetScreenWidth(),GetScreenHeight(),Fade(GRAY,0.5f)); + if (Button(LocaleGet("exit").c_str(), GetScreenWidth()/2-64, 48, 128, 0)) { + GameModeCleanup(); + UIResetMousePos(); + return; + } + UIHandleControls(); +} +char bufferChat[MAX_MESSAGE_LENGTH]; +char* DrawChat() { + const int x = 0; + const int y = 0; + const int h = 240; + const int buttonWidth = 120; + UIBegin(); + DrawRectangle(x,y,GetScreenWidth(),h,Fade(GRAY,0.5f)); + bool changed = TextField(1, bufferChat, sizeof(bufferChat), x, y+h, GetScreenWidth()); + bool send = Button(LocaleGet("send").c_str(), x+GetScreenWidth()-buttonWidth, y+h, buttonWidth, 0) || IsKeyPressed(KEY_ENTER); + if(send) { + return bufferChat; + } + UIHandleControls(); + return nullptr; +} +void DrawLogoCenter() { + BeginDrawing(); + Vector2 m = MeasureTextEx(GetFontDefault(), GAME_NAME, 64, 0); + DrawText(GAME_NAME, GetScreenWidth()/2 - m.x / 2 - 8, GetScreenHeight()/2 - m.y / 2 - 8, 64, BLACK); + DrawText(GAME_NAME, GetScreenWidth()/2 - m.x / 2, GetScreenHeight()/2 - m.y / 2, 64, WHITE); + EndDrawing(); +} +#endif \ No newline at end of file diff --git a/src/helpers/mesher.cpp b/src/helpers/mesher.cpp new file mode 100644 index 0000000..8b78714 --- /dev/null +++ b/src/helpers/mesher.cpp @@ -0,0 +1,591 @@ +#include +#include +#include +#include +#include "common.hpp" +#include +#include +#include + +#ifndef SERVER +#ifndef LEGACY_GL +MeshData GenerateChunkMesh(ChunkRenderData chunkRenderData, Chunk chunk, int chunkWorldX, int chunkWorldY, uint8_t sideMask) +{ + MeshData out = {0}; + + out.vertexCount = 0; out.indexCount = 0; + + std::vector positions; + std::vector normals; + std::vector uvs; + std::vector colors; + std::vector indices; + + int face = 0; + for (int x = 0; x < CHUNK_SIZE; ++x) + for (int y = 0; y < WORLD_HEIGHT; ++y) + for (int z = 0; z < CHUNK_SIZE; ++z) + { + int index = GetIndexChunk(x,y,z); + uint8_t crdata = chunkRenderData.sides[index]; + if (crdata == 0) continue; + + int this_block = fetch_block(chunk, x,y,z); + + BlockDef definition = BLOCKS[this_block]; + + UVCorners UVside = GetUVTex(definition.textureSide); + UVCorners UVtop = GetUVTex(definition.textureTop); + UVCorners UVbtm = GetUVTex(definition.textureBottom); + + bool left, right, up, down, back, front; + unpack6(crdata & sideMask, &left, &right, &up, &down, &back, &front); + + bool shaded = chunk.highestBlock[GetIndexChunk2D(x, z)] != y; + Color color = {255, 255, 255, 255}; + Color colorShaded = {128, 128, 172, 255}; + + float wx = (float)(chunkWorldX * CHUNK_SIZE + x); + float wy = (float)(y); + float wz = (float)(chunkWorldY * CHUNK_SIZE + z); + /*if(GetCloud(wx, wz)) { + color = ColorLerp(color, colorShaded, 0.5f); + }*/ + if (left) { + Vector3 verts[4] = { + {wx - 0.5f, wy - 0.5f, wz - 0.5f}, // bottom-left + {wx - 0.5f, wy - 0.5f, wz + 0.5f}, // bottom-right + {wx - 0.5f, wy + 0.5f, wz + 0.5f}, // top-right + {wx - 0.5f, wy + 0.5f, wz - 0.5f} // top-left + }; + Vector3 normal = {-1.0f, 0.0f, 0.0f}; + Vector2 corners[4] = { + UVside.corner0, + UVside.corner1, + UVside.corner2, + UVside.corner3 + }; + positions.push_back(verts[0]); + positions.push_back(verts[1]); + positions.push_back(verts[2]); + positions.push_back(verts[3]); + + normals.push_back(normal); + colors.push_back(colorShaded); + + uvs.push_back(corners[0]); + uvs.push_back(corners[1]); + uvs.push_back(corners[2]); + uvs.push_back(corners[3]); + + unsigned short indbase = face*4; + unsigned short tri[6] = { + indbase + 0, indbase + 1, indbase + 2, + indbase + 0, indbase + 2, indbase + 3 + }; + + indices.insert(indices.end(), tri, tri + 6); + + face++; + } + + // Right face (+X) + if (right) { + Vector3 verts[4] = { + {wx + 0.5f, wy - 0.5f, wz + 0.5f}, // bottom-left (from +X side) + {wx + 0.5f, wy - 0.5f, wz - 0.5f}, // bottom-right + {wx + 0.5f, wy + 0.5f, wz - 0.5f}, // top-right + {wx + 0.5f, wy + 0.5f, wz + 0.5f} // top-left + }; + Vector3 normal = {1.0f, 0.0f, 0.0f}; + Vector2 corners[4] = { + UVside.corner0, + UVside.corner1, + UVside.corner2, + UVside.corner3 + }; + positions.push_back(verts[0]); + positions.push_back(verts[1]); + positions.push_back(verts[2]); + positions.push_back(verts[3]); + + normals.push_back(normal); + colors.push_back(colorShaded); + + uvs.push_back(corners[0]); + uvs.push_back(corners[1]); + uvs.push_back(corners[2]); + uvs.push_back(corners[3]); + + unsigned short indbase = face*4; + unsigned short tri[6] = { + indbase + 0, indbase + 1, indbase + 2, + indbase + 0, indbase + 2, indbase + 3 + }; + + indices.insert(indices.end(), tri, tri + 6); + + face++; + } + + // Down (-Y) + if (down) { + Vector3 verts[4] = { + {wx - 0.5f, wy - 0.5f, wz - 0.5f}, // bottom-left + {wx + 0.5f, wy - 0.5f, wz - 0.5f}, // bottom-right + {wx + 0.5f, wy - 0.5f, wz + 0.5f}, // top-right + {wx - 0.5f, wy - 0.5f, wz + 0.5f} // top-left + }; + Vector3 normal = {0.0f, -1.0f, 0.0f}; + Vector2 corners[4] = { + UVbtm.corner0, + UVbtm.corner1, + UVbtm.corner2, + UVbtm.corner3 + }; + positions.push_back(verts[0]); + positions.push_back(verts[1]); + positions.push_back(verts[2]); + positions.push_back(verts[3]); + + normals.push_back(normal); + colors.push_back(colorShaded); + + uvs.push_back(corners[0]); + uvs.push_back(corners[1]); + uvs.push_back(corners[2]); + uvs.push_back(corners[3]); + + unsigned short indbase = face*4; + unsigned short tri[6] = { + indbase + 0, indbase + 1, indbase + 2, + indbase + 0, indbase + 2, indbase + 3 + }; + + indices.insert(indices.end(), tri, tri + 6); + + face++; + } + + // Up (+Y) + if (up) { + Vector3 verts[4] = { + {wx - 0.5f, wy + 0.5f, wz + 0.5f}, // bottom-left (from +Y side) + {wx + 0.5f, wy + 0.5f, wz + 0.5f}, // bottom-right + {wx + 0.5f, wy + 0.5f, wz - 0.5f}, // top-right + {wx - 0.5f, wy + 0.5f, wz - 0.5f} // top-left + }; + Vector3 normal = {0.0f, 1.0f, 0.0f}; + Vector2 corners[4] = { + UVtop.corner0, + UVtop.corner1, + UVtop.corner2, + UVtop.corner3 + }; + positions.push_back(verts[0]); + positions.push_back(verts[1]); + positions.push_back(verts[2]); + positions.push_back(verts[3]); + + normals.push_back(normal); + if(shaded) { + colors.push_back(colorShaded); + } + else { + colors.push_back(color); + } + + uvs.push_back(corners[0]); + uvs.push_back(corners[1]); + uvs.push_back(corners[2]); + uvs.push_back(corners[3]); + + unsigned short indbase = face*4; + unsigned short tri[6] = { + indbase + 0, indbase + 1, indbase + 2, + indbase + 0, indbase + 2, indbase + 3 + }; + + indices.insert(indices.end(), tri, tri + 6); + + face++; + } + + // Front (+Z) + if (front) { + Vector3 verts[4] = { + {wx - 0.5f, wy - 0.5f, wz + 0.5f}, // bottom-left + {wx + 0.5f, wy - 0.5f, wz + 0.5f}, // bottom-right + {wx + 0.5f, wy + 0.5f, wz + 0.5f}, // top-right + {wx - 0.5f, wy + 0.5f, wz + 0.5f} // top-left + }; + Vector3 normal = {0.0f, 0.0f, 1.0f}; + Vector2 corners[4] = { + UVside.corner0, + UVside.corner1, + UVside.corner2, + UVside.corner3 + }; + positions.push_back(verts[0]); + positions.push_back(verts[1]); + positions.push_back(verts[2]); + positions.push_back(verts[3]); + + normals.push_back(normal); + colors.push_back(colorShaded); + + uvs.push_back(corners[0]); + uvs.push_back(corners[1]); + uvs.push_back(corners[2]); + uvs.push_back(corners[3]); + + unsigned short indbase = face*4; + unsigned short tri[6] = { + indbase + 0, indbase + 1, indbase + 2, + indbase + 0, indbase + 2, indbase + 3 + }; + + indices.insert(indices.end(), tri, tri + 6); + + face++; + } + + // Back (-Z) + if (back) { + Vector3 verts[4] = { + {wx + 0.5f, wy - 0.5f, wz - 0.5f}, // bottom-left (from -Z side) + {wx - 0.5f, wy - 0.5f, wz - 0.5f}, // bottom-right + {wx - 0.5f, wy + 0.5f, wz - 0.5f}, // top-right + {wx + 0.5f, wy + 0.5f, wz - 0.5f} // top-left + }; + Vector3 normal = {0.0f, 0.0f, -1.0f}; + Vector2 corners[4] = { + UVside.corner0, + UVside.corner1, + UVside.corner2, + UVside.corner3 + }; + positions.push_back(verts[0]); + positions.push_back(verts[1]); + positions.push_back(verts[2]); + positions.push_back(verts[3]); + + normals.push_back(normal); + colors.push_back(colorShaded); + + uvs.push_back(corners[0]); + uvs.push_back(corners[1]); + uvs.push_back(corners[2]); + uvs.push_back(corners[3]); + + unsigned short indbase = face*4; + unsigned short tri[6] = { + indbase + 0, indbase + 1, indbase + 2, + indbase + 0, indbase + 2, indbase + 3 + }; + + indices.insert(indices.end(), tri, tri + 6); + + face++; + } + } + + out.positions = (float*)malloc(sizeof(float) * 3 * positions.size()); + out.uvs = (float*)malloc(sizeof(float) * 2 * uvs.size()); + out.indices = (unsigned short*)malloc(sizeof(unsigned short) * indices.size()); + out.normals = (float*)malloc(sizeof(float) * 3 * 4 * normals.size()); + out.colors = (unsigned char*)malloc(sizeof(unsigned char) * 4 * 4 * colors.size()); + + int s = 0; + for (Vector3 v : positions) + { + out.positions[s] = v.x; + out.positions[s+1] = v.y; + out.positions[s+2] = v.z; + s += 3; + } + s = 0; + for (Vector3 n : normals) + { + for (size_t _ = 0; _ < 4; _++) + { + out.normals[s] = n.x; + out.normals[s+1] = n.y; + out.normals[s+2] = n.z; + s += 3; + } + } + s = 0; + for (Color c : colors) + { + for (size_t _ = 0; _ < 4; _++) + { + out.colors[s] = c.r; + out.colors[s+1] = c.g; + out.colors[s+2] = c.b; + out.colors[s+3] = c.a; + s += 4; + } + } + s = 0; + for (Vector2 uv : uvs) + { + out.uvs[s] = uv.x; + out.uvs[s+1] = uv.y; + s += 2; + } + s = 0; + for (unsigned short i : indices) + { + out.indices[s] = i; + s += 1; + } + out.vertexCount = positions.size(); + out.indexCount = indices.size(); + positions.clear(); + normals.clear(); + uvs.clear(); + indices.clear(); + return out; +} + + +MeshData MergeMeshData(const MeshData &a, const MeshData &b) +{ + MeshData out = {0}; + + // initial counts + int aPosCount = a.positions ? (a.vertexCount * 3) : 0; + int aNormCount = a.normals ? (a.vertexCount * 3) : 0; + int aUvCount = a.uvs ? (a.vertexCount * 2) : 0; + int aColCount = a.colors ? (a.vertexCount * 4) : 0; + int aIdxCount = a.indices ? (a.indexCount) : 0; + + int bPosCount = b.positions ? (b.vertexCount * 3) : 0; + int bNormCount = b.normals ? (b.vertexCount * 3) : 0; + int bUvCount = b.uvs ? (b.vertexCount * 2) : 0; + int bColCount = b.colors ? (b.vertexCount * 4) : 0; + int bIdxCount = b.indices ? (b.indexCount) : 0; + + // allocate combined buffers + int totalVert = a.vertexCount + b.vertexCount; + int totalPos = totalVert * 3; + int totalNorm = totalVert * 3; + int totalUv = totalVert * 2; + int totalCol = totalVert * 4; + int totalIdx = a.indexCount + b.indexCount; + + if (totalPos > 0) { + out.positions = (float*)malloc(totalPos * sizeof(float)); + memcpy(out.positions, a.positions ? a.positions : nullptr, aPosCount * sizeof(float)); + } else out.positions = nullptr; + + if (totalNorm > 0) { + out.normals = (float*)malloc(totalNorm * sizeof(float)); + memcpy(out.normals, a.normals ? a.normals : nullptr, aNormCount * sizeof(float)); + } else out.normals = nullptr; + + if (totalUv > 0) { + out.uvs = (float*)malloc(totalUv * sizeof(float)); + memcpy(out.uvs, a.uvs ? a.uvs : nullptr, aUvCount * sizeof(float)); + } else out.uvs = nullptr; + + if (totalCol > 0) { + out.colors = (unsigned char*)malloc(totalCol * sizeof(unsigned char)); + memcpy(out.colors, a.colors ? a.colors : nullptr, aColCount * sizeof(unsigned char)); + } else out.colors = nullptr; + + if (totalIdx > 0) { + out.indices = (unsigned short*)malloc(totalIdx * sizeof(unsigned short)); + if (a.indices && aIdxCount > 0) + memcpy(out.indices, a.indices, aIdxCount * sizeof(unsigned short)); + } else out.indices = nullptr; + + // copy B data appending with index offset + // positions + if (bPosCount > 0) { + memcpy(out.positions + aPosCount, b.positions, bPosCount * sizeof(float)); + } + // normals + if (bNormCount > 0) { + memcpy(out.normals + aNormCount, b.normals, bNormCount * sizeof(float)); + } + // uvs + if (bUvCount > 0) { + memcpy(out.uvs + aUvCount, b.uvs, bUvCount * sizeof(float)); + } + // colors + if (bColCount > 0) { + memcpy(out.colors + aColCount, b.colors, bColCount * sizeof(unsigned char)); + } + // indices (need to offset b indices by a.vertexCount) + if (bIdxCount > 0) { + unsigned short idxOffset = (unsigned short)(a.vertexCount); + for (int i = 0; i < bIdxCount; ++i) { + out.indices[aIdxCount + i] = (unsigned short)(b.indices[i] + idxOffset); + } + } + + out.vertexCount = totalVert; + out.indexCount = totalIdx; + + return out; +} + +MeshData CreateCubeMeshData(const BlockDef &def, float cx, float cy, float cz) +{ + MeshData out = {0}; + // 4 verts * 6 faces = 24 vertices + const int vertsPerFace = 4; + const int faces = 6; + const int totalVerts = vertsPerFace * faces; + const int totalPos = totalVerts * 3; + const int totalNorm = totalVerts * 3; + const int totalUv = totalVerts * 2; + const int totalCol = totalVerts * 4; + const int totalIdx = faces * 6; + + out.vertexCount = totalVerts; + out.indexCount = totalIdx; + + out.positions = (float*)malloc(totalPos * sizeof(float)); + out.normals = (float*)malloc(totalNorm * sizeof(float)); + out.uvs = (float*)malloc(totalUv * sizeof(float)); + out.colors = (unsigned char*)malloc(totalCol * sizeof(unsigned char)); + out.indices = (unsigned short*)malloc(totalIdx * sizeof(unsigned short)); + + int p = 0, n = 0, u = 0, c = 0, ix = 0; + Color baseColor = {255,255,255,255}; + + auto pushPos = [&](float x, float y, float z) { + out.positions[p++] = x; out.positions[p++] = y; out.positions[p++] = z; + }; + auto pushNorm = [&](float x, float y, float z) { + out.normals[n++] = x; out.normals[n++] = y; out.normals[n++] = z; + }; + auto pushUV = [&](float uu, float vv) { + out.uvs[u++] = uu; out.uvs[u++] = vv; + }; + auto pushCol = [&](unsigned char r, unsigned char g, unsigned char b, unsigned char a) { + out.colors[c++] = r; out.colors[c++] = g; out.colors[c++] = b; out.colors[c++] = a; + }; + auto pushTriIdx = [&](unsigned short a, unsigned short b, unsigned short c2) { + out.indices[ix++] = a; out.indices[ix++] = b; out.indices[ix++] = c2; + }; + + // get UV corners for faces + UVCorners UVside = GetUVTex(def.textureSide); + UVCorners UVtop = GetUVTex(def.textureTop); + UVCorners UVbtm = GetUVTex(def.textureBottom); + + // face builder: verts in CCW for the outside-facing side + auto buildFace = [&](const Vector3 faceVerts[4], const Vector3 &normal, const Vector2 faceUVs[4]) { + unsigned short base = (unsigned short)((p/3)); // current vertex index + Color clr = baseColor; + + for (int i=0;i<4;i++) { + pushPos(faceVerts[i].x, faceVerts[i].y, faceVerts[i].z); + pushNorm(normal.x, normal.y, normal.z); + pushUV(faceUVs[i].x, faceUVs[i].y); + pushCol(clr.r, clr.g, clr.b, clr.a); + } + // two tris (0,1,2) (0,2,3) + pushTriIdx(base + 0, base + 1, base + 2); + pushTriIdx(base + 0, base + 2, base + 3); + }; + + float hx = 0.5f, hy = 0.5f, hz = 0.5f; + // Left (-X) + { + Vector3 verts[4] = { + {cx - hx, cy - hy, cz - hz}, + {cx - hx, cy - hy, cz + hz}, + {cx - hx, cy + hy, cz + hz}, + {cx - hx, cy + hy, cz - hz} + }; + Vector2 uvs[4] = { UVside.corner0, UVside.corner1, UVside.corner2, UVside.corner3 }; + Vector3 normal = {-1,0,0}; + buildFace(verts, normal, uvs); + } + // Right (+X) + { + Vector3 verts[4] = { + {cx + hx, cy - hy, cz + hz}, + {cx + hx, cy - hy, cz - hz}, + {cx + hx, cy + hy, cz - hz}, + {cx + hx, cy + hy, cz + hz} + }; + Vector2 uvs[4] = { UVside.corner0, UVside.corner1, UVside.corner2, UVside.corner3 }; + Vector3 normal = {1,0,0}; + buildFace(verts, normal, uvs); + } + // Down (-Y) + { + Vector3 verts[4] = { + {cx - hx, cy - hy, cz - hz}, + {cx + hx, cy - hy, cz - hz}, + {cx + hx, cy - hy, cz + hz}, + {cx - hx, cy - hy, cz + hz} + }; + Vector2 uvs[4] = { UVbtm.corner0, UVbtm.corner1, UVbtm.corner2, UVbtm.corner3 }; + Vector3 normal = {0,-1,0}; + buildFace(verts, normal, uvs); + } + // Up (+Y) + { + Vector3 verts[4] = { + {cx - hx, cy + hy, cz + hz}, + {cx + hx, cy + hy, cz + hz}, + {cx + hx, cy + hy, cz - hz}, + {cx - hx, cy + hy, cz - hz} + }; + Vector2 uvs[4] = { UVtop.corner0, UVtop.corner1, UVtop.corner2, UVtop.corner3 }; + Vector3 normal = {0,1,0}; + buildFace(verts, normal, uvs); + } + // Front (+Z) + { + Vector3 verts[4] = { + {cx - hx, cy - hy, cz + hz}, + {cx + hx, cy - hy, cz + hz}, + {cx + hx, cy + hy, cz + hz}, + {cx - hx, cy + hy, cz + hz} + }; + Vector2 uvs[4] = { UVside.corner0, UVside.corner1, UVside.corner2, UVside.corner3 }; + Vector3 normal = {0,0,1}; + buildFace(verts, normal, uvs); + } + // Back (-Z) + { + Vector3 verts[4] = { + {cx + hx, cy - hy, cz - hz}, + {cx - hx, cy - hy, cz - hz}, + {cx - hx, cy + hy, cz - hz}, + {cx + hx, cy + hy, cz - hz} + }; + Vector2 uvs[4] = { UVside.corner0, UVside.corner1, UVside.corner2, UVside.corner3 }; + Vector3 normal = {0,0,-1}; + buildFace(verts, normal, uvs); + } + + return out; +} + + +Mesh GenChunkMesh(MeshData meshData) +{ + Mesh mesh = { 0 }; + mesh.indices = meshData.indices; + mesh.vertexCount = meshData.vertexCount; + mesh.triangleCount = meshData.indexCount / 3; + mesh.vertices = meshData.positions; + mesh.texcoords = meshData.uvs; + mesh.normals = meshData.normals; + mesh.colors = meshData.colors; + + UploadMesh(&mesh, false); + + return mesh; +} +#endif +#endif \ No newline at end of file diff --git a/src/helpers/multisound.cpp b/src/helpers/multisound.cpp new file mode 100644 index 0000000..4aa731e --- /dev/null +++ b/src/helpers/multisound.cpp @@ -0,0 +1,62 @@ +#include "common.hpp" + +struct ThreeDeeSound { + Sound ref; + float pitch; + float volume; + Vector3 position; + float seconds; +}; +std::vector sounds; + +static void SetSoundPosition(Sound sound, Vector3 position, float maxDist, Camera camera) +{ + // Calculate direction vector and distance between listener and sound source + Vector3 direction = Vector3Subtract(position, camera.position); + float distance = Vector3Length(direction); + + // Apply logarithmic distance attenuation and clamp between 0-1 + float attenuation = 1.0f/(1.0f + (distance/maxDist)); + attenuation = Clamp(attenuation, 0.0f, 1.0f); + + // Calculate normalized vectors for spatial positioning + Vector3 normalizedDirection = Vector3Normalize(direction); + Vector3 forward = Vector3Normalize(camera.target-camera.position); + Vector3 right = Vector3Normalize(Vector3CrossProduct({0, 1, 0}, forward)); + + // Reduce volume for sounds behind the listener + float dotProduct = Vector3DotProduct(forward, normalizedDirection); + if (dotProduct < 0.0f) attenuation *= (1.0f + dotProduct*0.5f); + + // Set stereo panning based on sound position relative to listener + float pan = -Vector3DotProduct(normalizedDirection, right); + + if(distance > maxDist) { + attenuation *= 1-Clamp((distance - maxDist) / 10.0f, 0, 1); + } + // Apply final sound properties + SetSoundVolume(sound, attenuation); + SetSoundPan(sound, pan); +} + +void MS_PlaySound(Sound src, float pitch, float volume, Vector3 position) { + ThreeDeeSound snd = {}; + snd.ref = LoadSoundAlias(src); + snd.pitch = pitch; + snd.volume = volume; + snd.position = position; + SetSoundVolume(snd.ref, 0); + PlaySound(snd.ref); + sounds.push_back(snd); +} + +void MS_Update(float dt, Camera camera) { + for (int i = 0; i < sounds.size(); i++) + { + ThreeDeeSound snd = sounds[i]; + if(snd.seconds > 3) sounds.erase(sounds.begin() + i); // TODO: make better + SetSoundPosition(snd.ref, snd.position, 40, camera); + SetSoundPitch(snd.ref, snd.pitch); + //SetSoundVolume(snd.ref, snd.volume); + } +} \ No newline at end of file diff --git a/src/helpers/networking.cpp b/src/helpers/networking.cpp new file mode 100644 index 0000000..3e8930c --- /dev/null +++ b/src/helpers/networking.cpp @@ -0,0 +1,109 @@ +#include +#include "common.hpp" +#include + + +int NetworkingStart() +{ + if (enet_initialize () < 0) + { + fprintf (stderr, "An error occurred while initializing ENet.\n"); + return EXIT_FAILURE; + } + atexit(enet_deinitialize); +} + +ENetHost* StartServer() { + ENetAddress address; + ENetHost * server; + + /* Bind the server to the default localhost. */ + /* A specific host address can be specified by */ + /* enet_address_set_host (& address, "x.x.x.x"); */ + + address.host = ENET_HOST_ANY; + /* Bind the server to port 1234. */ + address.port = PORT; + + server = enet_host_create(&address, 32, 1, 0, 0); + if (server == NULL) + { + fprintf (stderr, + "An error occurred while trying to create an ENet server host.\n"); + exit (EXIT_FAILURE); + } + return server; +} + +ENetHost* StartClient() { + ENetHost * client; + + client = enet_host_create (NULL /* create a client host */, + 1 /* 1 channel*/, + 1 /* only allow 1 outgoing connection */, + 0 /* assume any amount of incoming bandwidth */, + 0); + + if (client == NULL) + { + fprintf (stderr, + "An error occurred while trying to create an ENet client host.\n"); + exit (EXIT_FAILURE); + } + + return client; +} + +void SendPacket(ENetPeer* peer, void* data, int len) +{ + ENetPacket* packet = enet_packet_create(data, len, ENET_PACKET_FLAG_UNSEQUENCED); + + int c = enet_peer_send(peer, 0, packet); + if(c!=0) { + printf("Failed sending packet! "); + printf("%d\n", c); + enet_packet_destroy(packet); + } +} + +void SendPacketR(ENetPeer* peer, void* data, int len) +{ + ENetPacket* packet = enet_packet_create(data, len, ENET_PACKET_FLAG_RELIABLE); + + int c = enet_peer_send(peer, 0, packet); + if(c!=0) { + printf("Failed sending packet! "); + printf("%d\n", c); + enet_packet_destroy(packet); + } +} + +void BroadcastPacketR(ENetHost* server, void* data, int len) +{ + ENetPacket* packet = enet_packet_create(data, len, ENET_PACKET_FLAG_RELIABLE); + + enet_host_broadcast(server, 0, packet); +} + +void SerializeFloat2Data(float v, void *target, int offset) { + uint8_t *buf = (uint8_t*)target + offset; + if (!isfinite(v)) { // handle NaN/Inf as zero (choose policy) + buf[0] = 0; + buf[1] = 0; + return; + } + if (v < 0.0f) v = 0.0f; + if (v > 255.0f) v = 255.0f; // clamp to representable range + + int intpart = (int)floorf(v); + float frac = v - (float)intpart; + int frac_byte = (int)floorf(frac * 255.0f + 0.5f); // round to nearest + if (frac_byte < 0) frac_byte = 0; + if (frac_byte > 255) frac_byte = 255; + buf[0] = (uint8_t)intpart; + buf[1] = (uint8_t)frac_byte; +} + +float DeserializeFloat(int b0, int b1) { + return (float)(uint8_t)b0 + (float)((uint8_t)b1) / 255.0f; +} \ No newline at end of file diff --git a/src/helpers/particles.cpp b/src/helpers/particles.cpp new file mode 100644 index 0000000..eb894be --- /dev/null +++ b/src/helpers/particles.cpp @@ -0,0 +1,119 @@ +#include "common.hpp" +#include +#include + +std::list particles; +extern Camera3D camera; +extern Texture2D terrain; +static void DrawParticle(Particle particle, Color color) +{ + Vector3 position = particle.position; + float x = position.x; + float y = position.y; + float z = position.z; + float width = 0.25f; + float height = width; + float length = height; + UVCorners corners = GetUVTex(particle.data[0], width, width, width, width); + UVCorners front = corners; + UVCorners back = corners; + UVCorners top = corners; + UVCorners bottom = corners; + UVCorners left = corners; + UVCorners right = corners; + // Set desired texture to be enabled while drawing following vertex data + rlSetTexture(terrain.id); + rlBegin(RL_QUADS); + rlColor4ub(color.r, color.g, color.b, color.a); + // Front Face + rlNormal3f(0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer + rlTexCoord2f(front.corner0.x, front.corner0.y); rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left Of The Texture and Quad + rlTexCoord2f(front.corner1.x, front.corner1.y); rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right Of The Texture and Quad + rlTexCoord2f(front.corner2.x, front.corner2.y); rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(front.corner3.x, front.corner3.y); rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left Of The Texture and Quad + // Back Face + rlNormal3f(0.0f, 0.0f, - 1.0f); // Normal Pointing Away From Viewer + rlTexCoord2f(back.corner1.x, back.corner1.y); rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right Of The Texture and Quad + rlTexCoord2f(back.corner2.x, back.corner2.y); rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(back.corner3.x, back.corner3.y); rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Left Of The Texture and Quad + rlTexCoord2f(back.corner0.x, back.corner0.y); rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Left Of The Texture and Quad + // Top Face + rlNormal3f(0.0f, 1.0f, 0.0f); // Normal Pointing Up + rlTexCoord2f(top.corner3.x, top.corner3.y); rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left Of The Texture and Quad + rlTexCoord2f(top.corner0.x, top.corner0.y); rlVertex3f(x - width/2, y + height/2, z + length/2); // Bottom Left Of The Texture and Quad + rlTexCoord2f(top.corner1.x, top.corner1.y); rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right Of The Texture and Quad + rlTexCoord2f(top.corner2.x, top.corner2.y); rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right Of The Texture and Quad + // Bottom Face + rlNormal3f(0.0f, - 1.0f, 0.0f); // Normal Pointing Down + rlTexCoord2f(bottom.corner2.x, bottom.corner2.y); rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(bottom.corner3.x, bottom.corner3.y); rlVertex3f(x + width/2, y - height/2, z - length/2); // Top Left Of The Texture and Quad + rlTexCoord2f(bottom.corner0.x, bottom.corner0.y); rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left Of The Texture and Quad + rlTexCoord2f(bottom.corner1.x, bottom.corner1.y); rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Right Of The Texture and Quad + // Right face + rlNormal3f(1.0f, 0.0f, 0.0f); // Normal Pointing Right + rlTexCoord2f(right.corner1.x, right.corner1.y); rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right Of The Texture and Quad + rlTexCoord2f(right.corner2.x, right.corner2.y); rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(right.corner3.x, right.corner3.y); rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left Of The Texture and Quad + rlTexCoord2f(right.corner0.x, right.corner0.y); rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left Of The Texture and Quad + // Left Face + rlNormal3f( - 1.0f, 0.0f, 0.0f); // Normal Pointing Left + rlTexCoord2f(left.corner0.x, left.corner0.y); rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Left Of The Texture and Quad + rlTexCoord2f(left.corner1.x, left.corner1.y); rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Right Of The Texture and Quad + rlTexCoord2f(left.corner2.x, left.corner2.y); rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(left.corner3.x, left.corner3.y); rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left Of The Texture and Quad + rlEnd(); + + rlSetTexture(0); +} +void ParticlesUpdate(float dt, Chunk world[]) { + particles.remove_if([](Particle x) { return x.lifetime > 200; }); + for (Particle &part : particles) + { + part.lifetime++; + if(part.physicsType == Phys_Falling) { + Vector3 velocityTgt = Vector3Clamp(Vector3Add(part.velocity, {0, -dt * 9.8f, 0}), {-30.0f, -30.0f, 0-30.0f}, {30.0f, 30.0f, 30.0f}); + + Vector3 tgt = Vector3Add(part.position, Vector3Scale(velocityTgt, dt)); + Vector3 vis = Vector3Add(tgt, {0.5f, 0.4f, 0.5f}); + Vector3 final = part.position; + if(!GetBlock(world, (int)vis.x, (int)final.y, (int)final.z)) { + final.x = tgt.x; + } + if(!GetBlock(world, (int)final.x, (int)vis.y, (int)final.z)) { + final.y = tgt.y; + } + else { + velocityTgt.y = 0; + } + if(!GetBlock(world, (int)final.x, (int)final.y, (int)vis.z)) { + final.z = tgt.z; + } + for (Particle &part2 : particles) + { + if(Vector3DistanceSqr(part.position, part2.position) < 0.25f*0.25f) { + Vector3 force = Vector3Normalize(Vector3Subtract(part.position, part2.position)) * dt * 25; + part.velocity += force; + part2.velocity -= force; + } + } + part.position = final; + part.velocity = velocityTgt; + } + } +} +void ParticlesDraw() { + for (Particle part : particles) + { + DrawParticle(part, WHITE); + } +} +void AddParticle(Particle part) { + particles.push_back(part); +} +Particle NewParticle(Vector3 position) { + Particle part; + part.physicsType = Phys_Falling; + part.position = position; + part.texture = terrain; + return part; +} \ No newline at end of file diff --git a/src/helpers/player.cpp b/src/helpers/player.cpp new file mode 100644 index 0000000..ca216ab --- /dev/null +++ b/src/helpers/player.cpp @@ -0,0 +1,134 @@ +#include +#include +#include "common.hpp" +#include +#include +#include +#include +#include + +#define SAVE_FORMAT 1 + +bool creative_mode = false; +float player_health = MAX_HP; +float player_attack_time = 0; +float player_place_time = 0; +float player_break_parts_time = 0; +Vector3 player_position; +Vector3 player_velocity; +int GetItemNumID(std::string s); + +extern InventoryItem hotbar[ITEMS]; +extern InventoryItem inventory[INVENTORY_SIZE]; +extern std::vector registry; + +void SavePlayerData() { + std::ofstream data(PathifyUser("player.data"), std::ios::binary); + if (!data) return; + + data.write("PLR", 3); + char fmt = static_cast(SAVE_FORMAT); + data.write(&fmt, 1); + char creative = static_cast(creative_mode); + data.write(&creative, 1); + data.write(reinterpret_cast(&player_health), sizeof(player_health)); + + std::cout << "H" << std::endl; + std::vector temp; + + for (int i = 0; i < ITEMS; i++) + { + Item* item = hotbar[i].item; + int id = 0; + if(item != nullptr) + id = GetItemNumID(item->id); + std::cout << inventory[i].amount << std::endl; + temp.push_back((uint8_t)id); + temp.push_back((uint8_t)hotbar[i].damage); + temp.push_back((uint8_t)hotbar[i].amount); + } + for (int i = 0; i < INVENTORY_SIZE; i++) + { + Item* item = inventory[i].item; + int id = 0; + if(item != nullptr) + id = GetItemNumID(item->id); + temp.push_back((uint8_t)id); + temp.push_back((uint8_t)inventory[i].damage); + temp.push_back((uint8_t)inventory[i].amount); + } + uint16_t size = static_cast(temp.size()); + data.write(reinterpret_cast(&size), sizeof(size)); + if (!temp.empty()) { + data.write(reinterpret_cast(temp.data()), temp.size()); + } +} + +bool LoadPlayerData() { + std::ifstream data(PathifyUser("player.data"), std::ios::binary); + if (!data) return false; + + char header[3]; + if (!data.read(header, 3)) return false; + if (header[0] != 'P' || header[1] != 'L' || header[2] != 'R') return false; + + char fmt; + if (!data.read(&fmt, 1)) return false; + if (static_cast(fmt) != SAVE_FORMAT) { + return false; + } + + char creative; + if (!data.read(&creative, 1)) return false; + creative_mode = static_cast(creative); + + if (!data.read(reinterpret_cast(&player_health), sizeof(player_health))) return false; + + uint16_t size = 0; + if (!data.read(reinterpret_cast(&size), sizeof(size))) return false; + + std::vector temp; + if (size > 0) { + temp.resize(size); + if (!data.read(reinterpret_cast(temp.data()), size)) return false; + } + + // validate expected length: (ITEMS + INVENTORY_SIZE) * 3 bytes + const size_t expected = (static_cast(ITEMS) + static_cast(INVENTORY_SIZE)) * 3; + if (size != expected) { + // if older/newer formats may differ, handle gracefully; here we reject mismatch + return false; + } + + // parse into hotbar and inventory + size_t idx = 0; + for (int i = 0; i < ITEMS; ++i) { + uint8_t numid = temp[idx++]; + uint8_t damage = temp[idx++]; + uint8_t amount = temp[idx++]; + if (amount == 0) { + hotbar[i].item = nullptr; + } else { + Item* itemPtr = ®istry[static_cast(numid)]; + hotbar[i].item = itemPtr; + } + hotbar[i].damage = damage; + hotbar[i].amount = amount; + } + + for (int i = 0; i < INVENTORY_SIZE; ++i) { + uint8_t numid = temp[idx++]; + uint8_t damage = temp[idx++]; + uint8_t amount = temp[idx++]; + if (amount == 0) { + inventory[i].item = nullptr; + } else { + Item* itemPtr = ®istry[static_cast(numid)]; + inventory[i].item = itemPtr; + } + inventory[i].damage = damage; + inventory[i].amount = amount; + } + + return true; +} diff --git a/src/helpers/recipes.cpp b/src/helpers/recipes.cpp new file mode 100644 index 0000000..7b410ee --- /dev/null +++ b/src/helpers/recipes.cpp @@ -0,0 +1,80 @@ +#ifndef SERVER +#include +#include +#include "common.hpp" +#include +#include +#include "yaml-cpp/yaml.h" +#include +#include + +#include +#include +#include + +static std::map tags = {}; + +static bool ParseRecipe(const std::string &s, RecipeItem &out) { + static const std::regex re(R"(^([^:]+):([^*]+)(?:\*([0-9]+))?$)"); + std::smatch m; + if (!std::regex_match(s, m, re)) return false; + if(m[1].str() == "block") + out.type = IType_Block; + else if(m[1].str() == "tag") + out.type = IType_Special_Tag; + else + out.type = IType_Generic; + out.id = m[2].str(); + if (m[3].matched) out.quantity = std::stoi(m[3].str()); + else out.quantity = 1; + return true; +} + +bool IDTagMatch(std::string tag, std::string id) { + std::smatch m; + std::cout << tag << std::endl; + std::cout << id << std::endl; + return std::regex_match(id, m, tags[tag]); +} +std::list recipes; + +void LoadRecipes() { + tags["planks"] = std::regex(".*planks$"); + std::vector recipeList; + recipeList.clear(); + std::cout << "A" << std::endl; + std::filesystem::path recipes_path{Pathify("recipes")}; + std::cout << "A" << std::endl; + for (auto const& dir_entry : std::filesystem::directory_iterator{recipes_path}) { + try + { + Recipe _recipe; + const std::string path = dir_entry.path().u8string(); + YAML::Node recipe = YAML::LoadFile(path); + _recipe.requiredStation = recipe["station"].as(); + for (auto s : recipe["inputs"]) { + RecipeItem r; + if (ParseRecipe(s.as(), r)) { + _recipe.inputs.push_back(r); + } else { + std::cout << "parse failed for: " << s << std::endl; + } + } + for (auto s : recipe["outputs"]) { + RecipeItem r; + if (ParseRecipe(s.as(), r)) { + _recipe.outputs.push_back(r); + } else { + std::cout << "parse failed for: " << s << std::endl; + } + } + recipes.push_back(_recipe); + } + catch(const std::exception& e) + { + std::cerr << "Exception while loading recipe " << dir_entry << ": "<< e.what() << '!' << std::endl; + } + } +} + +#endif \ No newline at end of file diff --git a/src/helpers/svlib.cpp b/src/helpers/svlib.cpp new file mode 100644 index 0000000..8ee2ded --- /dev/null +++ b/src/helpers/svlib.cpp @@ -0,0 +1,358 @@ +#include "common.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static std::list players; +static Chunk world[MAX_WORLD_AREA]; +static ENetHost* server; + +struct LuaPlugin { + std::string name; + lua_State* L; + bool has_on_connect = false; + bool has_on_disconnect = false; + bool has_on_packet = false; +}; + +static std::vector> plugins; + +static int l_server_send(lua_State* L) { + int peer_id = (int)luaL_checkinteger(L, 1); + size_t len; + const char* data = luaL_checklstring(L, 2, &len); + for (const NetPlayer &p : players) { + if (p.id == peer_id) { + SendPacketR(p.peer, (void*)data, (int)len); + break; + } + } + return 0; +} + +static int l_server_broadcast(lua_State* L) { + size_t len; + const char* data = luaL_checklstring(L, 1, &len); + if (len > 0) { + BroadcastPacketR(server, (void*)data, (int)len); + } + return 0; +} + +static int l_get_players(lua_State* L) { + lua_newtable(L); + int idx = 1; + for (const NetPlayer &p : players) { + lua_pushinteger(L, p.id); + lua_rawseti(L, -2, idx++); + } + return 1; +} + +static void RegisterLuaAPI(lua_State* L) { + lua_newtable(L); + lua_pushcfunction(L, l_server_send); + lua_setfield(L, -2, "send"); + lua_pushcfunction(L, l_server_broadcast); + lua_setfield(L, -2, "broadcast"); + lua_pushcfunction(L, l_get_players); + lua_setfield(L, -2, "get_players"); + lua_setglobal(L, "server"); +} + +static bool LoadPluginFile(const std::filesystem::path& path) { + auto plugin = std::make_unique(); + plugin->name = path.filename().string(); + plugin->L = luaL_newstate(); + if (!plugin->L) return false; + luaL_openlibs(plugin->L); + + RegisterLuaAPI(plugin->L); + + int rc = luaL_dofile(plugin->L, path.string().c_str()); + if (rc != LUA_OK) { + const char* err = lua_tostring(plugin->L, -1); + std::cerr << "Error loading plugin " << plugin->name << ": " << (err ? err : "unknown") << std::endl; + lua_close(plugin->L); + return false; + } + + lua_getglobal(plugin->L, "on_connect"); + if (lua_isfunction(plugin->L, -1)) plugin->has_on_connect = true; + lua_pop(plugin->L, 1); + + lua_getglobal(plugin->L, "on_disconnect"); + if (lua_isfunction(plugin->L, -1)) plugin->has_on_disconnect = true; + lua_pop(plugin->L, 1); + + lua_getglobal(plugin->L, "on_packet"); + if (lua_isfunction(plugin->L, -1)) plugin->has_on_packet = true; + lua_pop(plugin->L, 1); + + plugins.push_back(std::move(plugin)); + std::cout << "Loaded plugin: " << path.filename().string() << std::endl; + return true; +} + +static void LoadPluginsFromFolder(const std::string& folder) { + try { + if (!std::filesystem::exists(folder)) { + std::cout << "Plugins folder does not exist; skipping: " << folder << std::endl; + return; + } + for (auto& p : std::filesystem::directory_iterator(folder)) { + if (!p.is_regular_file()) continue; + auto ext = p.path().extension().string(); + if (ext == ".lua") LoadPluginFile(p.path()); + } + } catch (const std::exception& e) { + std::cerr << "Error enumerating plugins: " << e.what() << std::endl; + } +} + +static void CallPluginOnConnect(int peer_id) { + for (auto &pl : plugins) { + if (!pl->has_on_connect) continue; + lua_State* L = pl->L; + lua_getglobal(L, "on_connect"); + lua_pushinteger(L, peer_id); + if (lua_pcall(L, 1, 0, 0) != LUA_OK) { + std::cerr << "Plugin " << pl->name << " on_connect error: " << lua_tostring(L, -1) << std::endl; + lua_pop(L, 1); + } + } +} + +static void CallPluginOnDisconnect(int peer_id) { + for (auto &pl : plugins) { + if (!pl->has_on_disconnect) continue; + lua_State* L = pl->L; + lua_getglobal(L, "on_disconnect"); + lua_pushinteger(L, peer_id); + if (lua_pcall(L, 1, 0, 0) != LUA_OK) { + std::cerr << "Plugin " << pl->name << " on_disconnect error: " << lua_tostring(L, -1) << std::endl; + lua_pop(L, 1); + } + } +} + +static void CallPluginOnPacket(int peer_id, const unsigned char* data, int len) { + for (auto &pl : plugins) { + if (!pl->has_on_packet) continue; + lua_State* L = pl->L; + lua_getglobal(L, "on_packet"); + lua_pushinteger(L, peer_id); + lua_pushlstring(L, (const char*)data, len); + if (lua_pcall(L, 2, 0, 0) != LUA_OK) { + std::cerr << "Plugin " << pl->name << " on_packet error: " << lua_tostring(L, -1) << std::endl; + lua_pop(L, 1); + } + } +} + +static int PeerToPlayerID(ENetPeer* peer) { + for (const NetPlayer &p : players) { + if (p.addr == peer->address.host && p.port == peer->address.port) return p.id; + } + return -1; +} + +static void ParseData(ENetPacket* packet, ENetPeer* peer) { + int len = packet->dataLength; + unsigned char data[len]; + memcpy(&data, packet->data, len); + + int pid = PeerToPlayerID(peer); + if (pid >= 0) CallPluginOnPacket(pid, data, len); + + unsigned char response[16]; + switch (data[0]) + { + case NET_ARG_REQWORLD: + puts("Client requested world; sending"); + for (int i = 0; i < 256; i++) + { + unsigned char responseChunk[2+CHUNK_DATA_SIZE+CHUNK_SIZE*CHUNK_SIZE]; + responseChunk[0] = NET_ARG_CHUNKF; + responseChunk[1] = i; + + memcpy(responseChunk+2, world[i].blocks, CHUNK_DATA_SIZE); + memcpy(responseChunk+2+CHUNK_DATA_SIZE, world[i].highestBlock, CHUNK_SIZE*CHUNK_SIZE); + SendPacket(peer, responseChunk, sizeof(responseChunk)); + } + break; + case NET_ARG_PLAYERMOVE: + response[0] = NET_ARG_ECHOMOVE; + for (NetPlayer &plr : players) + { + if(plr.addr == peer->address.host && plr.port == peer->address.port) { + float x = DeserializeFloat(data[1], data[2]); + float y = DeserializeFloat(data[3], data[4]); + float z = DeserializeFloat(data[5], data[6]); + plr.x = x; + plr.y = y; + plr.z = z; + response[1] = plr.id; + } + } + response[2] = data[1]; response[3] = data[2]; + response[4] = data[3]; response[5] = data[4]; + response[6] = data[5]; response[7] = data[6]; + response[8] = data[7]; + BroadcastPacketR(server, response, 9); + break; + case NET_ARG_PLAYERYAW: + response[0] = NET_ARG_ECHOYAW; + for (NetPlayer &plr : players) + { + if(plr.addr == peer->address.host && plr.port == peer->address.port) { + response[1] = plr.id; + plr.yaw = data[1]; + response[2] = plr.yaw; + } + } + BroadcastPacketR(server, response, 3); + break; + case NET_ARG_REQPLAYERS: + puts("Client requested players; sending"); + + for (NetPlayer &plr : players) + { + unsigned char responseP[9]; + responseP[0] = NET_ARG_PLRREG; + if(plr.addr != peer->address.host || plr.port != peer->address.port) { + SerializeFloat2Data(plr.x, responseP, 2); + SerializeFloat2Data(plr.y, responseP, 4); + SerializeFloat2Data(plr.z, responseP, 6); + responseP[1] = plr.id; + responseP[8] = plr.yaw; + SendPacketR(peer, responseP, 9); + } + } + break; + case NET_ARG_BLOCKDELTA: + response[0] = NET_ARG_BLOCK; + for (NetPlayer &plr : players) + { + if(plr.addr == peer->address.host || plr.port == peer->address.port) { + unsigned char responseB[6]; + responseB[0] = NET_ARG_BLOCK; + responseB[1] = plr.id; + responseB[2] = data[1]; + responseB[3] = data[2]; + responseB[4] = data[3]; + responseB[5] = data[4]; + SetBlock(world, data[1], data[2], data[3], data[4]); + BroadcastPacketR(server, responseB, 6); + } + } + break; + default: + break; + } +} + +static int FirstFreeID(const std::list& players) { + std::unordered_set used; + used.reserve(players.size()); + for (const auto &p : players) used.insert(p.id); + + int id = 0; + while (used.find(id) != used.end()) ++id; + return id; +} +static bool serverStarted = false; +void StartInternalServer(bool& started) { + int worldType = 0; + InitWorldgen(worldType); + std::cout << "Building chunks..." << std::endl; + Chunk chunk = GenerateChunk(8, 8); + for (int x = 0; x < MAX_WORLD_SIZE; x++) + { + for (int y = 0; y < MAX_WORLD_SIZE; y++) + { + memcpy(&world[x + y * MAX_WORLD_SIZE], &chunk, sizeof(Chunk)); + } + } + LoadPluginsFromFolder("plugins"); + std::cout << "Starting server..." << std::endl; + server = StartServer(); + std::cout << "Serving on port: " << PORT << std::endl; + serverStarted = true; + started = true; +} +void ServerNetworkUpdate() { + if(serverStarted) { + ENetEvent event; + while(enet_host_service (server, &event, 1) > 0) + { + switch (event.type) + { + int id; + char response[2]; + case ENET_EVENT_TYPE_CONNECT: + printf ("A new client connected from %x:%u.\n", + event.peer -> address.host, + event.peer -> address.port); + response[0] = NET_ARG_PLRID; + id = FirstFreeID(players); + response[1] = id; + SendPacketR(event.peer, response, sizeof(response)); + { + NetPlayer netPlayer; + netPlayer.id = id; + netPlayer.addr = event.peer->address.host; + netPlayer.port = event.peer->address.port; + netPlayer.peer = event.peer; + players.push_back(netPlayer); + + response[0] = NET_ARG_PLRCON; + response[1] = netPlayer.id; + BroadcastPacketR(server, response, sizeof(response)); + } + // notify plugins + CallPluginOnConnect(id); + break; + + case ENET_EVENT_TYPE_RECEIVE: + ParseData(event.packet, event.peer); + enet_packet_destroy (event.packet); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + printf ("%s disconnected.\n", event.peer -> data); + event.peer -> data = NULL; + for (auto it = players.begin(); it != players.end(); ++it) { + if (it->addr == event.peer->address.host && it->port == event.peer->address.port) { + char responseD[2]; + responseD[0] = NET_ARG_PLRDCN; + responseD[1] = it->id; + BroadcastPacketR(server, (unsigned char*)responseD, sizeof(responseD)); + + int disconnected_id = it->id; + it = players.erase(it); + + // notify plugins + CallPluginOnDisconnect(disconnected_id); + break; + } + } + + break; + } + } + } +} +void StopInternalServer() { + for (auto &pl : plugins) { + lua_close(pl->L); + } + + enet_host_destroy(server); +} \ No newline at end of file diff --git a/src/helpers/utils.cpp b/src/helpers/utils.cpp new file mode 100644 index 0000000..b1a999e --- /dev/null +++ b/src/helpers/utils.cpp @@ -0,0 +1,256 @@ +#include "raylib.h" +#include "math.h" +#include "raymath.h" +#include +#include +#include "common.hpp" +#include +#include + +const char* Pathify(const char* src) { + static std::string s; + s = std::string(PATH_APPEND) + src; + return s.c_str(); +} +const char* PathifyUser(const char* src) { + static std::string s; + s = std::string(PATH_APPEND_USER) + src; + return s.c_str(); +} +#ifndef SERVER +void DrawCubeTexture(Texture2D texture, Vector3 position, float width, float height, float length, Color color, int offsetX, int offsetY, int sizeX, int sizeY) +{ + float x = position.x; + float y = position.y; + float z = position.z; + + float sidesModX = length / width; + float topBottomModY = height / width; + + UVCorners front = GetUVTexFlipX(texture.width, texture.height, offsetX, offsetY, sizeX, sizeY * topBottomModY); + UVCorners back = GetUVTexFlipX(texture.width, texture.height, offsetX+sizeX+sizeX*sidesModX, offsetY, sizeX, sizeY * topBottomModY); + UVCorners top = GetUVTexFlipX(texture.width, texture.height, offsetX, offsetY-sizeY, sizeX, sizeY); + UVCorners bottom = GetUVTexFlipX(texture.width, texture.height, offsetX+sizeX, offsetY-sizeY, sizeX, sizeY); + UVCorners left = GetUVTexFlipX(texture.width, texture.height, offsetX-sizeX * sidesModX, offsetY, sizeX * sidesModX, sizeY * topBottomModY); + UVCorners right = GetUVTexFlipX(texture.width, texture.height, offsetX+sizeX * sidesModX, offsetY, sizeX * sidesModX, sizeY * topBottomModY); + // Set desired texture to be enabled while drawing following vertex data + rlSetTexture(texture.id); + + // Vertex data transformation can be defined with the commented lines, + // but in this example we calculate the transformed vertex data directly when calling rlVertex3f() + //rlPushMatrix(); + // NOTE: Transformation is applied in inverse order (scale -> rotate -> translate) + //rlTranslatef(2.0f, 0.0f, 0.0f); + //rlRotatef(45, 0, 1, 0); + //rlScalef(2.0f, 2.0f, 2.0f); + + rlBegin(RL_QUADS); + rlColor4ub(color.r, color.g, color.b, color.a); + // Front Face + rlNormal3f(0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer + rlTexCoord2f(front.corner0.x, front.corner0.y); rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left Of The Texture and Quad + rlTexCoord2f(front.corner1.x, front.corner1.y); rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right Of The Texture and Quad + rlTexCoord2f(front.corner2.x, front.corner2.y); rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(front.corner3.x, front.corner3.y); rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left Of The Texture and Quad + // Back Face + rlNormal3f(0.0f, 0.0f, - 1.0f); // Normal Pointing Away From Viewer + rlTexCoord2f(back.corner1.x, back.corner1.y); rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right Of The Texture and Quad + rlTexCoord2f(back.corner2.x, back.corner2.y); rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(back.corner3.x, back.corner3.y); rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Left Of The Texture and Quad + rlTexCoord2f(back.corner0.x, back.corner0.y); rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Left Of The Texture and Quad + // Top Face + rlNormal3f(0.0f, 1.0f, 0.0f); // Normal Pointing Up + rlTexCoord2f(top.corner3.x, top.corner3.y); rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left Of The Texture and Quad + rlTexCoord2f(top.corner0.x, top.corner0.y); rlVertex3f(x - width/2, y + height/2, z + length/2); // Bottom Left Of The Texture and Quad + rlTexCoord2f(top.corner1.x, top.corner1.y); rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right Of The Texture and Quad + rlTexCoord2f(top.corner2.x, top.corner2.y); rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right Of The Texture and Quad + // Bottom Face + rlNormal3f(0.0f, - 1.0f, 0.0f); // Normal Pointing Down + rlTexCoord2f(bottom.corner2.x, bottom.corner2.y); rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(bottom.corner3.x, bottom.corner3.y); rlVertex3f(x + width/2, y - height/2, z - length/2); // Top Left Of The Texture and Quad + rlTexCoord2f(bottom.corner0.x, bottom.corner0.y); rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left Of The Texture and Quad + rlTexCoord2f(bottom.corner1.x, bottom.corner1.y); rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Right Of The Texture and Quad + // Right face + rlNormal3f(1.0f, 0.0f, 0.0f); // Normal Pointing Right + rlTexCoord2f(right.corner1.x, right.corner1.y); rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right Of The Texture and Quad + rlTexCoord2f(right.corner2.x, right.corner2.y); rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(right.corner3.x, right.corner3.y); rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left Of The Texture and Quad + rlTexCoord2f(right.corner0.x, right.corner0.y); rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left Of The Texture and Quad + // Left Face + rlNormal3f( - 1.0f, 0.0f, 0.0f); // Normal Pointing Left + rlTexCoord2f(left.corner0.x, left.corner0.y); rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Left Of The Texture and Quad + rlTexCoord2f(left.corner1.x, left.corner1.y); rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Right Of The Texture and Quad + rlTexCoord2f(left.corner2.x, left.corner2.y); rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(left.corner3.x, left.corner3.y); rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left Of The Texture and Quad + rlEnd(); + //rlPopMatrix(); + + rlSetTexture(0); +} +void DrawCubeBlock(Texture2D texture, Vector3 position, float width, float height, float length, BlockDef def) +{ + float x = position.x; + float y = position.y; + float z = position.z; + + float sidesModX = length / width; + float topBottomModY = height / width; + + UVCorners front = GetUVTex(def.textureSide); + UVCorners back = GetUVTex(def.textureSide); + UVCorners top = GetUVTex(def.textureTop); + UVCorners bottom = GetUVTex(def.textureBottom); + UVCorners left = GetUVTex(def.textureSide); + UVCorners right = GetUVTex(def.textureSide); + rlSetTexture(texture.id); + rlBegin(RL_QUADS); + rlColor4ub(255, 255, 255, 255); + // Front Face + rlNormal3f(0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer + rlTexCoord2f(front.corner0.x, front.corner0.y); rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Left Of The Texture and Quad + rlTexCoord2f(front.corner1.x, front.corner1.y); rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Right Of The Texture and Quad + rlTexCoord2f(front.corner2.x, front.corner2.y); rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(front.corner3.x, front.corner3.y); rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Left Of The Texture and Quad + // Back Face + rlNormal3f(0.0f, 0.0f, - 1.0f); // Normal Pointing Away From Viewer + rlTexCoord2f(back.corner1.x, back.corner1.y); rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Right Of The Texture and Quad + rlTexCoord2f(back.corner2.x, back.corner2.y); rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(back.corner3.x, back.corner3.y); rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Left Of The Texture and Quad + rlTexCoord2f(back.corner0.x, back.corner0.y); rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Left Of The Texture and Quad + // Top Face + rlNormal3f(0.0f, 1.0f, 0.0f); // Normal Pointing Up + rlTexCoord2f(top.corner3.x, top.corner3.y); rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left Of The Texture and Quad + rlTexCoord2f(top.corner0.x, top.corner0.y); rlVertex3f(x - width/2, y + height/2, z + length/2); // Bottom Left Of The Texture and Quad + rlTexCoord2f(top.corner1.x, top.corner1.y); rlVertex3f(x + width/2, y + height/2, z + length/2); // Bottom Right Of The Texture and Quad + rlTexCoord2f(top.corner2.x, top.corner2.y); rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right Of The Texture and Quad + // Bottom Face + rlNormal3f(0.0f, - 1.0f, 0.0f); // Normal Pointing Down + rlTexCoord2f(bottom.corner2.x, bottom.corner2.y); rlVertex3f(x - width/2, y - height/2, z - length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(bottom.corner3.x, bottom.corner3.y); rlVertex3f(x + width/2, y - height/2, z - length/2); // Top Left Of The Texture and Quad + rlTexCoord2f(bottom.corner0.x, bottom.corner0.y); rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left Of The Texture and Quad + rlTexCoord2f(bottom.corner1.x, bottom.corner1.y); rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Right Of The Texture and Quad + // Right face + rlNormal3f(1.0f, 0.0f, 0.0f); // Normal Pointing Right + rlTexCoord2f(right.corner1.x, right.corner1.y); rlVertex3f(x + width/2, y - height/2, z - length/2); // Bottom Right Of The Texture and Quad + rlTexCoord2f(right.corner2.x, right.corner2.y); rlVertex3f(x + width/2, y + height/2, z - length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(right.corner3.x, right.corner3.y); rlVertex3f(x + width/2, y + height/2, z + length/2); // Top Left Of The Texture and Quad + rlTexCoord2f(right.corner0.x, right.corner0.y); rlVertex3f(x + width/2, y - height/2, z + length/2); // Bottom Left Of The Texture and Quad + // Left Face + rlNormal3f( - 1.0f, 0.0f, 0.0f); // Normal Pointing Left + rlTexCoord2f(left.corner0.x, left.corner0.y); rlVertex3f(x - width/2, y - height/2, z - length/2); // Bottom Left Of The Texture and Quad + rlTexCoord2f(left.corner1.x, left.corner1.y); rlVertex3f(x - width/2, y - height/2, z + length/2); // Bottom Right Of The Texture and Quad + rlTexCoord2f(left.corner2.x, left.corner2.y); rlVertex3f(x - width/2, y + height/2, z + length/2); // Top Right Of The Texture and Quad + rlTexCoord2f(left.corner3.x, left.corner3.y); rlVertex3f(x - width/2, y + height/2, z - length/2); // Top Left Of The Texture and Quad + rlEnd(); + rlSetTexture(0); +} +#endif + +#ifndef SERVER +void DrawTextCodepoint3D(Font font, int codepoint, Vector3 position, float fontSize, bool backface, Color tint) +{ + // Character index position in sprite font + // NOTE: In case a codepoint is not available in the font, index returned points to '?' + int index = GetGlyphIndex(font, codepoint); + float scale = fontSize/(float)font.baseSize; + + // Character destination rectangle on screen + // NOTE: We consider charsPadding on drawing + position.x += (float)(font.glyphs[index].offsetX - font.glyphPadding)*scale; + position.z += (float)(font.glyphs[index].offsetY - font.glyphPadding)*scale; + + // Character source rectangle from font texture atlas + // NOTE: We consider chars padding when drawing, it could be required for outline/glow shader effects + Rectangle srcRec = { font.recs[index].x - (float)font.glyphPadding, font.recs[index].y - (float)font.glyphPadding, + font.recs[index].width + 2.0f*font.glyphPadding, font.recs[index].height + 2.0f*font.glyphPadding }; + + float width = (float)(font.recs[index].width + 2.0f*font.glyphPadding)*scale; + float height = (float)(font.recs[index].height + 2.0f*font.glyphPadding)*scale; + + if (font.texture.id > 0) + { + const float x = 0.0f; + const float y = 0.0f; + const float z = 0.0f; + + // normalized texture coordinates of the glyph inside the font texture (0.0f -> 1.0f) + const float tx = srcRec.x/font.texture.width; + const float ty = srcRec.y/font.texture.height; + const float tw = (srcRec.x+srcRec.width)/font.texture.width; + const float th = (srcRec.y+srcRec.height)/font.texture.height; + + rlCheckRenderBatchLimit(4 + 4*backface); + rlSetTexture(font.texture.id); + + rlPushMatrix(); + rlTranslatef(position.x, position.y, position.z); + + rlBegin(RL_QUADS); + rlColor4ub(tint.r, tint.g, tint.b, tint.a); + + // Front Face + rlNormal3f(0.0f, 1.0f, 0.0f); // Normal Pointing Up + rlTexCoord2f(tx, ty); rlVertex3f(x, y, z); // Top Left Of The Texture and Quad + rlTexCoord2f(tx, th); rlVertex3f(x, y, z + height); // Bottom Left Of The Texture and Quad + rlTexCoord2f(tw, th); rlVertex3f(x + width, y, z + height); // Bottom Right Of The Texture and Quad + rlTexCoord2f(tw, ty); rlVertex3f(x + width, y, z); // Top Right Of The Texture and Quad + + if (backface) + { + // Back Face + rlNormal3f(0.0f, -1.0f, 0.0f); // Normal Pointing Down + rlTexCoord2f(tx, ty); rlVertex3f(x, y, z); // Top Right Of The Texture and Quad + rlTexCoord2f(tw, ty); rlVertex3f(x + width, y, z); // Top Left Of The Texture and Quad + rlTexCoord2f(tw, th); rlVertex3f(x + width, y, z + height); // Bottom Left Of The Texture and Quad + rlTexCoord2f(tx, th); rlVertex3f(x, y, z + height); // Bottom Right Of The Texture and Quad + } + rlEnd(); + rlPopMatrix(); + + rlSetTexture(0); + } +} + +// Draw a 2D text in 3D space +void DrawText3D(Font font, const char *text, Vector3 position, float fontSize, float fontSpacing, float lineSpacing, bool backface, Color tint) +{ + int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop + + float textOffsetY = 0.0f; // Offset between lines (on line break '\n') + float textOffsetX = 0.0f; // Offset X to next character to draw + + float scale = fontSize/(float)font.baseSize; + + for (int i = 0; i < length;) + { + // Get next codepoint from byte string and glyph index in font + int codepointByteCount = 0; + int codepoint = GetCodepoint(&text[i], &codepointByteCount); + int index = GetGlyphIndex(font, codepoint); + + // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) + // but we need to draw all of the bad bytes using the '?' symbol moving one byte + if (codepoint == 0x3f) codepointByteCount = 1; + + if (codepoint == '\n') + { + // NOTE: Fixed line spacing of 1.5 line-height + // TODO: Support custom line spacing defined by user + textOffsetY += fontSize + lineSpacing; + textOffsetX = 0.0f; + } + else + { + if ((codepoint != ' ') && (codepoint != '\t')) + { + DrawTextCodepoint3D(font, codepoint, (Vector3){ position.x + textOffsetX, position.y, position.z + textOffsetY }, fontSize, backface, tint); + } + + if (font.glyphs[index].advanceX == 0) textOffsetX += (float)font.recs[index].width*scale + fontSpacing; + else textOffsetX += (float)font.glyphs[index].advanceX*scale + fontSpacing; + } + + i += codepointByteCount; // Move text bytes counter to next codepoint + } +} +#endif \ No newline at end of file diff --git a/src/helpers/vars.hpp b/src/helpers/vars.hpp new file mode 100644 index 0000000..d4c6180 --- /dev/null +++ b/src/helpers/vars.hpp @@ -0,0 +1,312 @@ +#if INTERFACE + +#ifdef WIN32 +#include "external/fix_win32_compatibility.h" +#endif +#ifdef Escape + #undef Escape +#endif +#include "glfw_keycodes_to_string.h" +#include "../PerlinNoise.hpp" + +#include +#ifdef PlaySound +#undef PlaySound +#endif +#include "raylib.h" +#include "raymath.h" +#include +#include + +#endif + +#include +#include +#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 +//Networking +#define PORT 25565 +#define MAX_MESSAGE_LENGTH 64 +//Notes: +//The server uses a player ID of 255 +// +//Server->Client +// Byte 0 +#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) +//Client->Server +// Byte 0 +#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) +// +//Errors +#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 +//Rendering +#define RENDER_DISTANCE 16 +#define RENDER_DISTANCE_SQUARED RENDER_DISTANCE*RENDER_DISTANCE + +//Game stuff +#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 +}; + +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 BlockRendering { + R_None, + R_Normal, + R_TriSide, + R_Translucent, + R_TranslucentTriSide, + R_TranslucentAllSides +}; + +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 +}; + +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]; + +}; +#ifndef SERVER +struct ChunkRenderData { + uint8_t sides[CHUNK_DATA_SIZE]; +}; + +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; +}; + +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; +}; + +struct UVCorners { + Vector2 corner0; + Vector2 corner1; + Vector2 corner2; + Vector2 corner3; +}; + +struct Quad2D { + int x; + int y; + int w; + int h; +}; +#endif +struct Vector3I { + int x; + int y; + int z; +}; +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; +}; + +struct NetPlayer +{ + ENetPeer* peer; + int addr; + int port; + int id; + int yaw; + float x; + float y; + float z; +}; + +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 +}; +enum EntityPhysics { + Phys_Falling, + Phys_Floating +}; +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) + {} +}; +struct InventoryItem { + Item* item = nullptr; + int damage = -1; + int amount = 0; +}; +struct RecipeItem { + ItemType type; + std::string id; + int quantity = 1; +}; +struct Recipe { + std::string requiredStation; + std::list inputs; + std::list outputs; +}; +struct Particle { + Vector3 position; + Vector3 velocity; + EntityPhysics physicsType; + Texture2D texture; + int lifetime; + int data[8]; +}; \ No newline at end of file diff --git a/src/libraries/Lua b/src/libraries/Lua new file mode 160000 index 0000000..504ef66 --- /dev/null +++ b/src/libraries/Lua @@ -0,0 +1 @@ +Subproject commit 504ef66d500fa1fb4f1684b6617b01342eee704a diff --git a/src/libraries/enet b/src/libraries/enet new file mode 160000 index 0000000..8be2368 --- /dev/null +++ b/src/libraries/enet @@ -0,0 +1 @@ +Subproject commit 8be2368a8001f28db44e81d5939de5e613025023 diff --git a/src/libraries/raylib b/src/libraries/raylib new file mode 160000 index 0000000..cf9f27d --- /dev/null +++ b/src/libraries/raylib @@ -0,0 +1 @@ +Subproject commit cf9f27db545dd686b03f9f773cfbdb8a8dd9f72f diff --git a/src/libraries/yaml-cpp b/src/libraries/yaml-cpp new file mode 160000 index 0000000..4861d04 --- /dev/null +++ b/src/libraries/yaml-cpp @@ -0,0 +1 @@ +Subproject commit 4861d049534ed6f2c51c45b01d7c2926022e5f3f diff --git a/src/rcraft.cpp b/src/rcraft.cpp new file mode 100644 index 0000000..5dce420 --- /dev/null +++ b/src/rcraft.cpp @@ -0,0 +1,1604 @@ +#include "raylib.h" +#include "math.h" +#include "raymath.h" +#include +#include +#include "common.hpp" +#include +#include "rlgl.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define MAX_RAY_DISTANCE 8.0f +struct MessageData { + std::string message; + float lifetime; +}; +static inline int imax(int a, int b) { + if(a > b) { return a; } + return b; +} +extern std::vector registry; +void Debug_Write(const std::string &stuff); +Vector2 cameraMovementSmooth; +static float camYaw = 0.0f; +static float camPitch = -45.0f; +static const float acceleration = 48.0f; +static const float drag = 6.0f; // tuned for feel +Camera camera; +bool online = false; +bool flying = false; + +unsigned long long worldTime; + +static float entUpdateCounter; +int selectedBlockType = 1; + +bool hide_ui = false; +bool taking_panorama = false; +int panorama_step; + +Music currentMusic; +static float tilNextMusic; + +Font fnt; +Texture2D terrain; +Texture2D water; +Texture2D white_pixel; +Entity* ent; +const bool ROTATE_PLAYER_WITH_CAMERA = true; +static Sound wood_place; +static Sound stone_place; +static Sound dirt_place; +static Sound grass_place; +bool tick_entities; +const std::string LocaleGet(std::string loc); +bool InputGetKeyDown(const std::string name); +bool InputGetKeyPressed(const std::string name); +extern std::list entities; +std::bitset clouds; +std::queue tickedBlocks; +struct MeshUnit { + Model model; + int vertices; +}; +struct ProfilerPart { + std::string id; + double time; + Color color; +}; +std::vector profParts; +static void AddProfilerPart(std::string id, double time, Color color) { + ProfilerPart part = {}; + part.id = id; + part.time = time; + part.color = color; + profParts.push_back(part); +} +static Vector3 CameraForwardFromYawPitch(float yaw, float pitch) +{ + Vector3 f; + f.x = cosf(pitch) * cosf(yaw); + f.y = sinf(pitch); + f.z = cosf(pitch) * sinf(yaw); + return Vector3Normalize(f); +} +Chunk world[MAX_WORLD_AREA]; +static MeshUnit rendering[MAX_WORLD_AREA]; +static MeshUnit renderingT[MAX_WORLD_AREA]; + +GameState state = GAME_STATE_MAINMENU; +bool loaderParam_remeshOnly = false; +int worldCreationProgress = 0; +int worldCreationStep = 0; +static bool IsBlockSolid(int bx, int by, int bz) +{ + return GetBlock(world, bx, by, bz) != 0; +} +static bool IsBlockWater(int bx, int by, int bz) +{ + return GetBlock(world, bx, by, bz) == BLOCK_WATERS; +} + +typedef struct Plane { + float a, b, c, d; +} Plane; + +typedef struct Frustum { + Plane planes[6]; +} Frustum; + +static Frustum ExtractFrustumPlanes(Camera camera, float aspect) +{ + Frustum frustum; + + // Get current Projection and View matrices from raylib + Matrix proj = MatrixPerspective(camera.fovy * DEG2RAD, aspect, 0.01f, 1000.0f); + Matrix view = MatrixLookAt(camera.position, camera.target, camera.up); + Matrix mat = MatrixMultiply(view, proj); // View-Projection Matrix + + // Left Plane + frustum.planes[0] = (Plane){ mat.m3 + mat.m0, mat.m7 + mat.m4, mat.m11 + mat.m8, mat.m15 + mat.m12 }; + // Right Plane + frustum.planes[1] = (Plane){ mat.m3 - mat.m0, mat.m7 - mat.m4, mat.m11 - mat.m8, mat.m15 - mat.m12 }; + // Bottom Plane + frustum.planes[2] = (Plane){ mat.m3 + mat.m1, mat.m7 + mat.m5, mat.m11 + mat.m9, mat.m15 + mat.m13 }; + // Top Plane + frustum.planes[3] = (Plane){ mat.m3 - mat.m1, mat.m7 - mat.m5, mat.m11 - mat.m9, mat.m15 - mat.m13 }; + // Near Plane + frustum.planes[4] = (Plane){ mat.m3 + mat.m2, mat.m7 + mat.m6, mat.m11 + mat.m10, mat.m15 + mat.m14 }; + // Far Plane + frustum.planes[5] = (Plane){ mat.m3 - mat.m2, mat.m7 - mat.m6, mat.m11 - mat.m10, mat.m15 - mat.m14 }; + + // Normalize planes + for (int i = 0; i < 6; i++) { + float length = sqrtf(frustum.planes[i].a * frustum.planes[i].a + + frustum.planes[i].b * frustum.planes[i].b + + frustum.planes[i].c * frustum.planes[i].c); + frustum.planes[i].a /= length; + frustum.planes[i].b /= length; + frustum.planes[i].c /= length; + frustum.planes[i].d /= length; + } + + return frustum; +} + +static bool IsBoxInFrustum(Frustum frustum, BoundingBox box) +{ + for (int i = 0; i < 6; i++) { + // Find the positive vertex (farthest along the plane normal) + Vector3 p = box.min; + if (frustum.planes[i].a >= 0) p.x = box.max.x; + if (frustum.planes[i].b >= 0) p.y = box.max.y; + if (frustum.planes[i].c >= 0) p.z = box.max.z; + + // Calculate dot product of plane equation + float dot = (frustum.planes[i].a * p.x) + + (frustum.planes[i].b * p.y) + + (frustum.planes[i].c * p.z) + + frustum.planes[i].d; + + // If the positive vertex is behind the plane, the box is outside + if (dot < 0) return false; + } + return true; +} + +static bool IsChunkInFrustum(Frustum frustum, Chunk chunk, int x, int y) { + float highestBlock = 0; + for (int i = 0; i < CHUNK_SIZE*CHUNK_SIZE; i++) + { + if(highestBlock < chunk.highestBlock[i]) highestBlock = chunk.highestBlock[i]; + } + BoundingBox bb = {}; + bb.min = (Vector3){x, 0, y}; + bb.max = (Vector3){x+CHUNK_SIZE, highestBlock, y+CHUNK_SIZE}; + return IsBoxInFrustum(frustum, bb); +} + +static void ResolveCollisions(float dt) +{ + float minY = player_position.y; + float maxY = player_position.y + PLAYER_HEIGHT; + int minX = floorf(player_position.x - PLAYER_RADIUS); + int maxX = floorf(player_position.x + PLAYER_RADIUS); + int minZ = floorf(player_position.z - PLAYER_RADIUS); + int maxZ = floorf(player_position.z + PLAYER_RADIUS); + int minB = floorf(minY); + int maxB = floorf(maxY); + + for (int bx = minX; bx <= maxX; bx++) + { + for (int bz = minZ; bz <= maxZ; bz++) + { + for (int by = minB; by <= maxB; by++) + { + if (!IsBlockSolid(bx, by, bz)) continue; + if(IsBlockWater(bx, by, bz)) { + if(player_velocity.y < -5) { + player_velocity.y = -5; + } + if(player_velocity.y > 5) { + player_velocity.y = 5; + } + continue; + } + player_velocity.x *= fmaxf(0.0f, 1.0f - drag * dt); + player_velocity.z *= fmaxf(0.0f, 1.0f - drag * dt); + float bx0 = (float)bx; + float by0 = (float)by; + float bz0 = (float)bz; + float bx1 = bx0 + 1.0f; + float by1 = by0 + 1.0f; + float bz1 = bz0 + 1.0f; + + float px0 = player_position.x - PLAYER_RADIUS; + float py0 = player_position.y; + float pz0 = player_position.z - PLAYER_RADIUS; + float px1 = player_position.x + PLAYER_RADIUS; + float py1 = player_position.y + PLAYER_HEIGHT; + float pz1 = player_position.z + PLAYER_RADIUS; + + float ix = fminf(px1, bx1) - fmaxf(px0, bx0); + float iy = fminf(py1, by1) - fmaxf(py0, by0); + float iz = fminf(pz1, bz1) - fmaxf(pz0, bz0); + + if (ix > 0 && iy > 0 && iz > 0) + { + // find smallest penetration axis and push out along it + if (ix < iy && ix < iz) + { + // push on X + if (player_position.x < bx0) player_position.x -= ix; + else player_position.x += ix; + player_velocity.x = 0; + } + else if (iy < ix && iy < iz) + { + // push on Y + if (player_position.y < by0) player_position.y -= iy; + else player_position.y += iy; + if (player_velocity.y > 0) player_velocity.y = 0; + else player_velocity.y = 0; + } + else + { + // push on Z + if (player_position.z < bz0) player_position.z -= iz; + else player_position.z += iz; + player_velocity.z = 0; + } + } + } + } + } +} + +bool loadedChunks[MAX_WORLD_AREA] = { false }; +bool dirtyChunks[MAX_WORLD_AREA] = { false }; + +void Debug_EndMeasure(const std::string &stuff); +void Debug_EndMeasureLoops(const std::string &stuff, int loops); +static void RemeshChunk(int x, int y) { + int idx = x + y * MAX_WORLD_SIZE; + if(loadedChunks[idx]) { + UnloadModel(renderingT[idx].model); + UnloadModel(rendering[idx].model); + } + Chunk chunk = world[idx]; + RebuildOpaqueMask(&chunk); + + MeshData renderDataOpq = GenerateChunkMesh(GenerateCRDOpaque(chunk, world), chunk, x, y, 0xFF); + MeshData renderDataTsl = GenerateChunkMesh(GenerateCRDTranslucent(chunk, world), chunk, x, y, 0xFF); + + Model model = LoadModelFromMesh(GenChunkMesh(renderDataOpq)); + model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = terrain; + MeshUnit munit0; + munit0.model = model; + munit0.vertices += renderDataOpq.vertexCount; + rendering[idx] = munit0; + model = LoadModelFromMesh(GenChunkMesh(renderDataTsl)); + model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = terrain; + MeshUnit munit1; + munit1.model = model; + munit1.vertices += renderDataTsl.vertexCount; + renderingT[idx] = munit1; + loadedChunks[idx] = true; + +} + +static bool RaycastBlock(Vector3 origin, Vector3 dir, float maxDist, Vector3 *outBlock, Vector3 *outPrev) +{ + float t = 0.0f; + Vector3 pos = origin; + Vector3 step = Vector3Scale(dir, 0.1f); // step resolution (smaller => more accurate) + Vector3 last = origin; + while (t < maxDist) + { + pos = Vector3Add(origin, Vector3Scale(dir, t)); + int bx = floorf(pos.x); + int by = floorf(pos.y); + int bz = floorf(pos.z); + if (bx < 0 || by < 0 || bz < 0 || bx >= WORLD_SIZE_BLOCKS || by >= WORLD_HEIGHT || bz >= WORLD_SIZE_BLOCKS) { t += 0.1f; continue; } + if (IsBlockSolid(bx, by, bz) && !IsBlockWater(bx, by, bz)) + { + // hit block at bx,by,bz; last is previous pos (empty) + if (outBlock) *outBlock = (Vector3){ (float)bx, (float)by, (float)bz }; + if (outPrev) *outPrev = (Vector3){ (float)floorf(last.x), (float)floorf(last.y), (float)floorf(last.z) }; + return true; + } + last = pos; + t += 0.1f; + } + return false; +} +static Entity* RaycastEntity(Vector3 origin, Vector3 dir, float maxDist) +{ + float t = 0.0f; + Vector3 pos = origin; + Vector3 step = Vector3Scale(dir, 0.1f); + Vector3 last = origin; + while (t < maxDist) + { + pos = Vector3Add(origin, Vector3Scale(dir, t)); + int bx = floorf(pos.x); + int by = floorf(pos.y); + int bz = floorf(pos.z); + if (bx < 0 || by < 0 || bz < 0 || bx >= WORLD_SIZE_BLOCKS || by >= WORLD_HEIGHT || bz >= WORLD_SIZE_BLOCKS) { t += 0.1f; continue; } + + for(Entity &ent : entities) { + if(Vector3Distance(pos, ent.position) < 0.5f) { + return &ent; + } + } + last = pos; + t += 0.1f; + } + return nullptr; +} +static inline Vector3 BlockToWorldCenter(int bx, int by, int bz) +{ + return (Vector3){ bx , by , bz }; +} + +static inline void RemeshChunkForBlock(int bx, int by, int bz) +{ + int cx = bx / CHUNK_SIZE; + int cz = bz / CHUNK_SIZE; + if (cx >= 0 && cx < MAX_WORLD_SIZE && cz >= 0 && cz < MAX_WORLD_SIZE) RemeshChunk(cx, cz); +} + +static void AddNekit() { + Entity temp = NewEntity(); + temp.type = 2; + temp.health = 50; + temp.velocity = {0, 0, 0}; + temp.position = player_position + (Vector3){0, 10, 0}; + AddEntity(temp); +} + +ENetPeer *peer; +ENetHost* client; +static std::stack> worldPacketStack; +static int fetchedChunks; + +int plrId = -1; + +static bool pauseMenuActive; +void _LoadWorld(const char* path) { + Debug_Write("Loading world"); + LoadWorld(world, path); + Debug_Write("World loaded"); + state = GAME_STATE_LOADING; + worldCreationProgress = 0; + worldCreationStep = 2; +} +void PlayRandomMusic() { + currentMusic = LoadMusicStream(Pathify((std::string("music/calm0")+std::to_string(GetRandomValue(1,3))+std::string(".ogg")).c_str())); + PlayMusicStream(currentMusic); +} +static int _blockPlacedC = 0; +void SetBlockNetwork(Chunk world[], int bx, int by, int bz, int t) { + float pitch = GetRandomValue(-100, 100) / 100.0f * 0.25f + 1.0f; + int sound = B_Stone; + tickedBlocks.push({bx, by, bz}); + tickedBlocks.push({bx-1, by, bz}); + tickedBlocks.push({bx+1, by, bz}); + tickedBlocks.push({bx, by-1, bz}); + tickedBlocks.push({bx, by+1, bz}); + tickedBlocks.push({bx, by, bz-1}); + tickedBlocks.push({bx, by, bz+1}); + if(t != 0) { + sound = BLOCKS[t].type; + } + else { + int id = GetBlock(world, bx, by, bz); + BlockDef block = BLOCKS[id]; + if(id != 0) { + sound = block.type; + for (int x = 0; x < 4; x++) + { + for (int z = 0; z < 4; z++) + { + for (int y = 0; y < 4; y++) + { + if(GetRandomValue(0, 100) < 25) { + Particle part = NewParticle({bx + x / 4.0f - 0.375f, by + y / 4.0f - 0.375f, bz + z / 4.0f - 0.375f}); + part.data[0] = block.textureSide; + if(y == 3) { + part.data[0] = block.textureTop; + } + part.velocity = Vector3({(x <= 1) ? -1.0f : 1.0f, (y <= 1) ? -1.0f : 1.0f, (z <= 1) ? -1.0f : 1.0f}) * 1.5f; + AddParticle(part); + part.lifetime = GetRandomValue(0, 100); + } + } + } + } + } + else { + return; + } + } + switch (sound) + { + case B_Dirt: + MS_PlaySound(dirt_place, pitch, 1, (Vector3){bx, by, bz}); + break; + case B_Grass: + MS_PlaySound(grass_place, pitch, 1, (Vector3){bx, by, bz}); + break; + case B_Wood: + MS_PlaySound(wood_place, pitch, 1, (Vector3){bx, by, bz}); + break; + default: + MS_PlaySound(stone_place, pitch, 1, (Vector3){bx, by, bz}); + break; + } + if(online) { + char data[5]; + data[0] = NET_ARG_BLOCKDELTA; + data[1] = (uint8_t)bx; + data[2] = (uint8_t)by; + data[3] = (uint8_t)bz; + data[4] = (uint8_t)t; + SendPacketR(peer, data, 5); + } + else { + SetBlock(world, bx, by, bz, t); + int cx = bx / CHUNK_SIZE; + int cz = bz / CHUNK_SIZE; + int lx = bx - cx * CHUNK_SIZE; + int lz = bz - cz * CHUNK_SIZE; + + dirtyChunks[GetIndexWorld(cx, cz)] = true; + if (lx == 0) dirtyChunks[GetIndexWorld(cx - 1, cz)] = true; + if (lx == CHUNK_SIZE - 1) dirtyChunks[GetIndexWorld(cx + 1, cz)] = true; + if (lz == 0) dirtyChunks[GetIndexWorld(cx, cz - 1)] = true; + if (lz == CHUNK_SIZE - 1) dirtyChunks[GetIndexWorld(cx, cz + 1)] = true; + } + _blockPlacedC++; +} + +static void TickAvailableBlocks() { + if(tickedBlocks.size() > 0) { + Vector3I pos = tickedBlocks.front(); + if(CanTick(GetBlock(world, pos.x, pos.y, pos.z))) { + TickBlock(world, pos.x, pos.y, pos.z); + tickedBlocks.pop(); + } + else { + tickedBlocks.pop(); + TickAvailableBlocks(); + } + } +} +static bool serverStarted; +int TryConnect(const char* hostname) { + client = StartClient(); + + if(client == NULL) + { + fprintf(stderr, "An error occurred while trying to create an ENet client host!\n"); + return NET_ERROR_INTERNAL; + } + + ENetAddress address; + ENetEvent event; + + + int s = enet_address_set_host (& address, hostname); + address.port = PORT; + + peer = enet_host_connect(client, &address, 1, 0); + + if (peer == NULL) + { + fprintf (stderr, + "No available peers for initiating an ENet connection.\n"); + return NET_ERROR_NOTFOUND; + } + printf("Found a host\n"); + while (!serverStarted) + { + if(!online) serverStarted = true; + } + if (enet_host_service (client, & event, 10000) && event.type == ENET_EVENT_TYPE_CONNECT){ + puts ("Connection succeeded."); + char data[1]; + data[0] = NET_ARG_REQWORLD; + SendPacketR(peer, data, 1); // Tell the server to send all chunks + online = true; + state = GAME_STATE_LOADING; + return 0; + } + else + { + enet_peer_reset (peer); + + return NET_ERROR_FAILED; + } + return -1; +} + +std::thread serverThread; + +void ServerThread(bool& started) { + StartInternalServer(started); + while(true) { + ServerNetworkUpdate(); + } +} +void InitSingleplayer() { + online = true; + serverThread = std::thread(ServerThread, std::ref(serverStarted)); + int result = TryConnect("localhost"); + if(result == NET_ERROR_FAILED) { + state = GAME_STATE_MAINMENU; + } +} +std::vector messages; +bool survival = false; +bool chatOpen = false; +float oxygen = 100; +float blockBreakProgress; +void TakeDamage(float amount) { + player_health -= amount; +} +int currentlyTickedChunk = 0; +Vector3 highlightBlock = {-1, -1, -1}; + +static void GameUpdate(Camera camera, float &time) { + if (player_position.y < 0 || + player_position.x < 0 || + player_position.z < 0 || + player_position.z > WORLD_SIZE_BLOCKS || + player_position.x > WORLD_SIZE_BLOCKS) { + player_position = { MAX_WORLD_SIZE / 2 * CHUNK_SIZE, 128.0f, MAX_WORLD_SIZE / 2 * CHUNK_SIZE }; + player_velocity = { 0 }; + } + bool highlightValid = false; + + float dt = GetFrameTime(); + if (dt <= 0) dt = DELTA_SMOOTH; + if (dt > 0.1f) dt = 0.1f; + time += dt; + Vector3 forward = Vector3Normalize({ cosf(camYaw), 0.0f, sinf(camYaw) }); + Vector3 right = Vector3Normalize(Vector3CrossProduct(forward, camera.up)); + + camera.position = (Vector3){ player_position.x - 0.5f, player_position.y + 1.25f, player_position.z - 0.5f}; + camera.target = Vector3Add(camera.position, CameraForwardFromYawPitch(camYaw, camPitch)); + camera.up = (Vector3){0,1,0}; + float dt_faux = dt / 4.0f; + if(IsKeyPressed(KEY_ESCAPE)) { + if(chatOpen) chatOpen = false; + else if (inventoryOpen) CloseInventory(); + else { + pauseMenuActive = !pauseMenuActive; + if(!pauseMenuActive) { + DisableCursor(); + } + else { + EnableCursor(); + } + } + } + /*if(IsKeyPressed(KEY_F2)) { + taking_panorama = true; + panorama_step = -1; + } + if(taking_panorama) { + if(panorama_step == 6) { + taking_panorama = false; + } + else { + camYaw = (90 * panorama_step) * DEG2RAD; + if(panorama_step < 3) { + camPitch = 0; + } + else { + camPitch = 89.99f * DEG2RAD; + if(panorama_step == 4) { + camPitch = -89.99f * DEG2RAD; + } + camYaw = 0; + } + } + }*/ + if(InputGetKeyPressed("chat") && !pauseMenuActive) { + chatOpen = !chatOpen; + if(!chatOpen) { + DisableCursor(); + } + else { + EnableCursor(); + } + } + for (int _ = 0; _ < 4; _++) + { + player_position.x += player_velocity.x * dt_faux; + player_position.y += player_velocity.y * dt_faux; + player_position.z += player_velocity.z * dt_faux; + if(!flying) { + player_velocity.y -= GRAVITY * dt_faux; + } + ResolveCollisions(dt_faux); + } + if(!pauseMenuActive && !chatOpen && !inventoryOpen) { + float speedMod = 1; + if(InputGetKeyPressed("fly")) { + flying = !flying; + } + + Vector3 wish = {0,0,0}; + Vector2 walkAxis = InputGetWalkAxis(); + + wish = Vector3Add(Vector3Scale(forward, walkAxis.y), + Vector3Scale(right, walkAxis.x)); + + float inputMag = Vector3Length((Vector3){wish.x, 0, wish.z}); + Vector3 wishDir = (inputMag > 0.0001f) ? Vector3Scale(wish, 1.0f / inputMag) + : (Vector3){0,0,0}; + + if (flying) { + speedMod = InputGetKeyDown("speed_up") ? 6.0f : 3.0f; + float w_y = 0; + if (InputGetKeyDown("jump")) w_y += 1.0f; + if (InputGetKeyDown("shift")) w_y -= 1.0f; + player_velocity.y = w_y * MOVE_SPEED * speedMod; + } + + if (inputMag > 0.0001f) { + player_velocity.x += wishDir.x * acceleration * inputMag * speedMod * dt; + player_velocity.z += wishDir.z * acceleration * inputMag * speedMod * dt; + } + + float hSpeed = sqrtf(player_velocity.x*player_velocity.x + player_velocity.z*player_velocity.z); + float maxH = MOVE_SPEED * speedMod; + if (hSpeed > maxH) { + float s = maxH / hSpeed; + player_velocity.x *= s; + player_velocity.z *= s; + } + + if(!flying) { + bool onGround = false; + int belowY = floorf(player_position.y - 0.05f); + for (int bx = floorf(player_position.x - PLAYER_RADIUS); bx <= floorf(player_position.x + PLAYER_RADIUS); bx++) + for (int bz = floorf(player_position.z - PLAYER_RADIUS); bz <= floorf(player_position.z + PLAYER_RADIUS); bz++) + if (IsBlockSolid(bx, belowY, bz) && !IsBlockWater(bx, belowY, bz)) onGround = true; + + if (InputGetKeyPressed("jump") && onGround) player_velocity.y = JUMP_SPEED; + if (InputGetKeyDown("jump") && IsBlockWater(player_position.x, player_position.y, player_position.z)) player_velocity.y += dt * 30; + } + + Vector2 md = InputGetLookAxis(); + camYaw += md.x * MOUSE_SENS; + camPitch -= md.y * MOUSE_SENS; + cameraMovementSmooth += md * dt / 8.0f; + cameraMovementSmooth -= cameraMovementSmooth * dt * 16.0f; + if (camPitch < PITCH_MIN && !taking_panorama) camPitch = PITCH_MIN; + if (camPitch > PITCH_MAX && !taking_panorama) camPitch = PITCH_MAX; + + Vector3 camForward = CameraForwardFromYawPitch(camYaw, camPitch); + Vector3 rayOrigin = (Vector3){ player_position.x, player_position.y + 1.7f, player_position.z};; + + Vector3 hitBlock, prevBlock; + if(IsKeyDown(KEY_N) && !online) { + AddNekit(); + } + if(IsKeyPressed(KEY_F10)) { + tick_entities = !tick_entities; + } + if(IsKeyPressed(KEY_B) && !online) { + Entity temp = NewEntity(); + temp.type = 3; + temp.position = camera.position; + temp.velocity = (camera.target - camera.position) * 4.0f; + AddEntity(temp); + } + if (RaycastBlock(rayOrigin, Vector3Normalize(camForward), 5, &hitBlock, &prevBlock)) + { + if((int)hitBlock.x != (int)highlightBlock.x || (int)hitBlock.y != (int)highlightBlock.y || (int)hitBlock.z != (int)highlightBlock.z) + blockBreakProgress = 0.0f; + highlightBlock = hitBlock; + highlightValid = true; + } + else + { + blockBreakProgress = 0.0f; + highlightValid = false; + } + + if (InputGetLMBDown()) + { + player_attack_time += dt * 2; + if(player_attack_time > 1) { + player_attack_time = 0; + } + if(highlightValid) { + int bx = (int)highlightBlock.x; + int by = (int)highlightBlock.y; + int bz = (int)highlightBlock.z; + float speed = 1.0f; + int tier = 0; + Item* selected_item = HotbarGetSelected().item; + if(selected_item != nullptr) { + if(selected_item->type == IType_Mining) { + speed += selected_item->tier; + tier = selected_item->tier; + } + } + BlockDef block = BLOCKS[GetBlock(world, bx, by, bz)]; + float hardnessMultiplier = block.hardness / 4.0f; + if(player_break_parts_time >= 0.05f) { + Vector3 v = {0, 1, 0}; + int side = GetRandomValue(0, 3); + switch (side) + { + case 1: + v = {1, GetRandomValue(0, 15) / 16.0f, GetRandomValue(0, 15) / 16.0f}; + break; + + case 2: + v = {GetRandomValue(0, 15) / 16.0f, GetRandomValue(0, 15) / 16.0f, 1}; + break; + + default: + v = {GetRandomValue(0, 15) / 16.0f, 1, GetRandomValue(0, 15) / 16.0f}; + break; + } + Particle part = NewParticle({bx + v.x - 0.375f, by + v.y - 0.375f, bz + v.z - 0.375f}); + part.data[0] = block.textureSide; + part.velocity = {0}; + AddParticle(part); + part.lifetime = GetRandomValue(0, 100); + player_break_parts_time = 0; + } + player_break_parts_time += dt; + if(block.min_tier <= tier) + blockBreakProgress+=dt*speed/hardnessMultiplier; + if(blockBreakProgress > 1.0f) { + HotbarAddItem(®istry[0], GetBlock(world, bx, by, bz)); + SetBlockNetwork(world, bx, by, bz, 0); + blockBreakProgress = 0.0f; + } + } + } + else { + player_attack_time += (0.5f - player_attack_time) * dt * 16.0f; + blockBreakProgress = 0.0f; + } + + if (InputGetLMB()) + { + Entity* ent = RaycastEntity(rayOrigin, Vector3Normalize(camForward), 5); + int tier = 0; + Item* selected_item = HotbarGetSelected().item; + if(selected_item != nullptr) { + if(selected_item->type == IType_Weapon) { + tier = selected_item->tier; + } + } + if(ent != nullptr) { + ent->health -= 5.0f + tier * 5; + ent->damage_flash = 50; + ent->velocity += (camera.target - camera.position) * 50; + } + } + + if (InputGetRMB()) + { + InventoryItem selected = HotbarGetSelected(); + if(selected.item != nullptr) { + std::cout << selected.item->type << std::endl; + switch (selected.item->type) + { + case IType_Edible: + HotbarRemoveSelected(); + player_health += 10.0f; + break; + case IType_Block: + if(highlightValid) { + int px, py, pz; + px = (int)prevBlock.x; + py = (int)prevBlock.y; + pz = (int)prevBlock.z; + + if (px >= 0 && py >= 0 && pz >= 0 && px < WORLD_SIZE_BLOCKS && py < WORLD_HEIGHT && pz < WORLD_SIZE_BLOCKS) + { + if (!(abs(px - player_position.x + 0.5f) < PLAYER_RADIUS * 2 && abs(pz - player_position.z + 0.5f) < PLAYER_RADIUS * 2 && abs(py - player_position.y) < PLAYER_HEIGHT - 0.2f)) + { + SetBlockNetwork(world, px, py, pz, selected.damage); + HotbarRemoveSelected(); + player_place_time = 1.0f; + } + } + } + break; + default: + break; + } + } + } + if(player_place_time > 0) { + player_place_time -= dt * 3.0f; + } + if(player_place_time < 0) { + player_place_time = 0; + } + float y = GetMouseWheelMove(); + bool changed = abs(y) > 0.5f; + if(y > 0.5f) { + selectedBlockType++; + } + if(y < -0.5f) { + selectedBlockType--; + } + if(selectedBlockType > MAX_BLOCK_ID) { + selectedBlockType = 1; + } + if(selectedBlockType < 1) { + selectedBlockType = MAX_BLOCK_ID; + } + } + int bestIdx = -1; + int bestDist2 = INT_MAX; + int c = 0; + for (bool l : loadedChunks) { + int x, y; + GetXZFromIndex(c, &x, &y); + int dx = x - ((int)(player_position.x) >> 4); + int dy = y - ((int)(player_position.z) >> 4); + int dist2 = dx*dx + dy*dy; + if (dist2 > RENDER_DISTANCE_SQUARED) { + loadedChunks[c] = false; + } + else { + loadedChunks[c] = true; + } + c++; + } + c = 0; + for (bool b : dirtyChunks) { + if(b) { + int x, y; + GetXZFromIndex(c, &x, &y); + int dx = x - ((int)(player_position.x) >> 4); + int dy = y - ((int)(player_position.z) >> 4); + int dist2 = dx*dx + dy*dy; + if (dist2 <= RENDER_DISTANCE_SQUARED && dist2 < bestDist2) { + bestDist2 = dist2; + bestIdx = c; + } + } + c++; + } + double PROFCOUNT = GetTime(); + if (bestIdx != -1) { + RebuildOpaqueMask(&world[bestIdx]); + int bx, by; + GetXZFromIndex(bestIdx, &bx, &by); + RemeshChunk(bx, by); + dirtyChunks[bestIdx] = false; + } + AddProfilerPart("rebuild", GetTime()-PROFCOUNT, BLUE); + + + PROFCOUNT = GetTime(); + if(tick_entities) { + entUpdateCounter += dt; + if(entUpdateCounter > TPS_DIV) { + while (entUpdateCounter > 0) + { + UpdateEntities(world, TPS_DIV / 2.0f); + entUpdateCounter-= TPS_DIV; + } + TickAvailableBlocks(); + entUpdateCounter = 0.0f; + } + } + AddProfilerPart("entities", GetTime()-PROFCOUNT, MAGENTA); + + PROFCOUNT = GetTime(); + ParticlesUpdate(dt, world); + AddProfilerPart("particles", GetTime()-PROFCOUNT, ORANGE); + MS_Update(dt, camera); + UpdateMusicStream(currentMusic); + std::stack messages_local; + for(MessageData &msg : messages) { + if(msg.lifetime < 10.0f) { + messages_local.push(msg.message); + } + msg.lifetime += dt; + } + + UpdateCamera(&camera, CAMERA_CUSTOM); + + unsigned int debug_verts_opaque = 0; + unsigned int debug_verts_transparent = 0; + UpdateHotbar(); + Frustum frustum = ExtractFrustumPlanes(camera, (float)GetScreenWidth() / (float)GetScreenHeight()); + BeginDrawing(); + ClearBackground(RAYWHITE); + DrawRectangle(0, 0, GetScreenWidth(), GetScreenHeight(), BLUE); + + BeginMode3D(camera); + + PROFCOUNT = GetTime(); + double chunkRenderTime = 0; + for (int x = 0; x < MAX_WORLD_SIZE * MAX_WORLD_SIZE; x++) + { + int _x, _z; + GetXZFromIndex(x, &_x, &_z); + _x *= CHUNK_SIZE; + _z *= CHUNK_SIZE; + + if(!loadedChunks[x]) continue; + if(!IsChunkInFrustum(frustum, world[x], _x, _z)) continue; + DrawModel(rendering[x].model, {0, 0, 0}, 1, WHITE); + debug_verts_opaque += rendering[x].vertices; + } + AddProfilerPart("chunk render opq", GetTime()-PROFCOUNT, MAROON); + + DrawEntities(dt); + ParticlesDraw(); + //Transparent shit + rlDisableDepthMask(); + rlBegin(RL_QUADS); + rlSetTexture(white_pixel.id); + const int cloudMapLength = WORLD_SIZE_BLOCKS/8; + + PROFCOUNT = GetTime(); + for (int x = 0; x < cloudMapLength; x++) + { + for (int z = 0; z < cloudMapLength; z++) + { + rlColor4f(255, 255, 255, 255); + if(clouds.test(x * cloudMapLength + z)) { //TODO: Normal frustum culling + Vector3 A = Vector3Normalize((Vector3){x*8, 70, z*8}-(Vector3){camera.position.x, camera.position.y, camera.position.z}); + float dot = Vector3DotProduct(A, camera.target - camera.position); + if(dot > cosf(FOV*DEG2RAD)) { + int ex_x = (imax(-x*8, 0) + imax(x*8-WORLD_SIZE_BLOCKS, 0)) >> 5; + int ex_z = (imax(-z*8, 0) + imax(z*8-WORLD_SIZE_BLOCKS, 0)) >> 5; + rlVertex3f(x*8, 70, z*8); + rlVertex3f(x*8+8, 70, z*8); + rlVertex3f(x*8+8, 70, z*8+8); + rlVertex3f(x*8, 70, z*8+8); + } + } + } + } + rlSetTexture(water.id); + for (int x = -8; x < MAX_WORLD_SIZE+8; x++) + { + for (int z = -8; z < MAX_WORLD_SIZE+8; z++) + { + if((x < 0 || x >= MAX_WORLD_SIZE) || (z < 0 || z >= MAX_WORLD_SIZE)) { + Vector3 A = Vector3Normalize((Vector3){x * CHUNK_SIZE, 32, z * CHUNK_SIZE}-(Vector3){camera.position.x, camera.position.y, camera.position.z}); + float dot = Vector3DotProduct(A, camera.target - camera.position); + if(dot > cosf(FOV*DEG2RAD)) { + Vector3 A = Vector3Normalize((Vector3){x, 70, z}-(Vector3){camera.position.x, camera.position.y, camera.position.z}); + float dot = Vector3DotProduct(A, camera.target - camera.position); + int ex_x = (imax(-x, 0) + imax(x-MAX_WORLD_SIZE, 0)); + int ex_z = (imax(-z, 0) + imax(z-MAX_WORLD_SIZE, 0)); + rlColor4ub(255, 255, 255, 255); //(255 >> ex_x) >> ex_z + rlTexCoord2f(0, CHUNK_SIZE);rlVertex3f(x*CHUNK_SIZE - 0.5f, 32 + 0.5f, (z+1)*CHUNK_SIZE - 0.5f); + rlTexCoord2f(CHUNK_SIZE, CHUNK_SIZE);rlVertex3f((x+1)*CHUNK_SIZE - 0.5f, 32 + 0.5f, (z+1)*CHUNK_SIZE - 0.5f); + rlTexCoord2f(CHUNK_SIZE, 0);rlVertex3f((x+1)*CHUNK_SIZE - 0.5f, 32 + 0.5f, z*CHUNK_SIZE - 0.5f); + rlTexCoord2f(0, 0);rlVertex3f(x*CHUNK_SIZE - 0.5f, 32 + 0.5f, z*CHUNK_SIZE - 0.5f); + } + } + } + } + rlEnd(); + rlEnableDepthMask(); + AddProfilerPart("fancy", GetTime()-PROFCOUNT, BLACK); + + PROFCOUNT = GetTime(); + std::vector> distances; + distances.reserve(MAX_WORLD_SIZE * MAX_WORLD_SIZE); + + for (int i = 0; i < MAX_WORLD_SIZE * MAX_WORLD_SIZE; ++i) { + if (!loadedChunks[i]) continue; + int cx, cz; + GetXZFromIndex(i, &cx, &cz); + if (!IsChunkInFrustum(frustum, world[i], cx*CHUNK_SIZE, cz*CHUNK_SIZE)) continue; + float wx = cx * CHUNK_SIZE + CHUNK_SIZE * 0.5f; + float wz = cz * CHUNK_SIZE + CHUNK_SIZE * 0.5f; + float dx = wx - camera.position.x; + float dz = wz - camera.position.y; + float dist2 = dx*dx + dz*dz; + distances.emplace_back(dist2, i); + } + std::sort(distances.begin(), distances.end(), + [](auto &a, auto &b){ return a.first > b.first; }); + + for (auto &p : distances) { + int idx = p.second; + DrawModel(renderingT[idx].model, {0, 0, 0}, 1.0f, WHITE); + debug_verts_transparent += renderingT[idx].vertices; + } + AddProfilerPart("chunk render tsl", GetTime()-PROFCOUNT, RED); + if (highlightValid) + { + Vector3 center = BlockToWorldCenter((int)highlightBlock.x, (int)highlightBlock.y, (int)highlightBlock.z); + DrawCubeWires(center, 1.01f, 1.01f, 1.01f, BLACK); + DrawCube(center, 1.01f, 1.01f, 1.01f, Fade(WHITE, blockBreakProgress * 0.6f)); + } + EndMode3D(); + Vector3 camPos = camera.position; + Vector3 camTarget = camera.target; + camera.position = {0, 0, 0}; + + camera.target = {-cameraMovementSmooth.x, -cameraMovementSmooth.y, 1}; + + BeginMode3D(camera); + rlDisableDepthMask(); + rlDisableDepthTest(); + DrawSelectedItem(); + rlEnableDepthTest(); + rlEnableDepthMask(); + EndMode3D(); + + camera.position = camPos; + camera.target = camTarget; + + if(IsBlockWater(camera.position.x, camera.position.y+0.5f, camera.position.z)) { + oxygen -= dt * 10.0f; + DrawTexturePro(water, {0, 0, 16, 16}, {0, 0, (float)GetScreenWidth(), (float)GetScreenHeight()}, {0, 0}, 0, WHITE); + } + else { + oxygen += dt * 20.0f; + oxygen = Clamp(oxygen, 0, MAX_HP); + } + if(!hide_ui && !taking_panorama) { + char text[128]; + sprintf(text, "%s %s", GAME_NAME, GAME_VERSION); + DrawTextB(text, 20, 20, DEFAULT_FONT_SIZE, WHITE); + sprintf(text, "%.0f FPS", 1 / dt); + DrawTextB(text, 20, 40, DEFAULT_FONT_SIZE, WHITE); + sprintf(text, "Player %d %.1f %.1f %.1f", plrId, player_position.x, player_position.y, player_position.z); + DrawTextB(text, 20, 60, DEFAULT_FONT_SIZE, WHITE); + + MemInfo m; + Debug_GetProcessMemory(m); + float residentmem = m.resident / 1024.0f / 1024.0f; + float virtualmem = m.virtualSize / 1024.0f / 1024.0f; + sprintf(text, "Mem %.2fMiB/%.2fMiB", residentmem, virtualmem); + DrawTextB(text, 20, 80, DEFAULT_FONT_SIZE, WHITE); + sprintf(text, "Drawing %d verts", debug_verts_opaque+debug_verts_transparent); + DrawTextB(text, 120, 40, DEFAULT_FONT_SIZE, WHITE); + DrawRectangle(GetScreenWidth()/2-3, GetScreenHeight()/2-3, 6, 6, BLACK); + DrawRectangle(GetScreenWidth()/2-2, GetScreenHeight()/2-2, 4, 4, WHITE); + + int idx = 0; + while (messages_local.size() > 0) + { + std::string msg = messages_local.top(); + DrawTextB(msg.c_str(), 24, 96 + idx * 24, DEFAULT_FONT_SIZE, WHITE); + idx++; + messages_local.pop(); + } + + DrawRectangle(4-1, GetScreenHeight()-13, 2*(MAX_HP+2), 12, BLACK); + DrawRectangle(4+1, GetScreenHeight()-11, 2*player_health, 8, RED); + if(oxygen < MAX_HP) { + DrawRectangle(4-1, GetScreenHeight()-13-14, 2*(MAX_HP+2), 12, BLACK); + DrawRectangle(4+1, GetScreenHeight()-11-14, 2*oxygen, 8, BLUE); + } + DrawHotbar(); + + double totalTime = 0.0; + for (auto &p : profParts) totalTime += p.time; + if (totalTime <= 0.0) return; + + float startAngle = -PI/2.0f; + const int segmentsPerSlice = 24; + + const float cx = GetScreenWidth()-64; + const float cy = 64; + const float radius = 32; + for (auto &p : profParts) { + float frac = float(p.time / totalTime); + float sweep = frac * 2.0f * PI; + float endAngle = startAngle + sweep; + float angleA = startAngle; + for (int s = 1; s <= segmentsPerSlice; ++s) { + float angleB = startAngle + (s / (float)segmentsPerSlice) * sweep; + float x1 = cx; + float y1 = cy; + float x2 = cx + cosf(angleA) * radius; + float y2 = cy + sinf(angleA) * radius; + float x3 = cx + cosf(angleB) * radius; + float y3 = cy + sinf(angleB) * radius; + DrawTriangle({x3, y3}, {x2, y2}, {x1, y1}, p.color); + angleA = angleB; + } + /*float mid = (startAngle + endAngle) * 0.5f; + float lx = cx + cosf(mid) * (radius * 0.6f); + float ly = cy + sinf(mid) * (radius * 0.6f); + char label[128]; + sprintf(label, "%s (%.1f ms)", p.id.c_str(), p.time * 1000.0f); + DrawText(label, lx - 20, ly - 6, 10, WHITE);*/ + + startAngle = endAngle; + } + int T = 0; + std::sort(profParts.begin(), profParts.end(), + [](auto &a, auto &b){ return a.time > b.time; }); + for (auto &p : profParts) { + char label[128]; + sprintf(label, "%s (%.1f ms) (%.1f%%)", p.id.c_str(), p.time * 1000.0f, p.time / totalTime*100); + DrawText(label, cx-radius-180+1, cy+T*8-radius+1, 8, BLACK); + DrawText(label, cx-radius-180, cy+T*8-radius, 8, p.color); + T++; + }; + profParts.clear(); + } + + if(pauseMenuActive) { + DrawPauseMenu(); + } + + if(chatOpen) { + char* msg = DrawChat(); + if(msg != nullptr) { + chatOpen = false; + DisableCursor(); + char data[1+MAX_MESSAGE_LENGTH]; + data[0] = NET_ARG_MESSAGEC; + memcpy(data+1, msg, MAX_MESSAGE_LENGTH); + SendPacketR(peer, data, 1+MAX_MESSAGE_LENGTH); + memset(msg, 0, MAX_MESSAGE_LENGTH); + } + } + EndDrawing(); + if(oxygen < 0.1f) { + player_health -= dt * 20.0f; + } + if(taking_panorama) { + if(panorama_step >= 0) { + char name[16]; + sprintf(name, "%d.png", panorama_step); + TakeScreenshot(name); + } + panorama_step++; + } +} +static void GameStart() { + const int cloudMapSize = MAX_WORLD_SIZE*CHUNK_SIZE/8; + for (int x = 0; x < cloudMapSize; x++) + { + for (int y = 0; y < cloudMapSize; y++) { + clouds.set(y * cloudMapSize + x, GetCloud(x*8, y*8)); + } + } + LoadPlayerData(); + //PlayRandomMusic(); + char data[1]; + data[0] = NET_ARG_REQPLAYERS; + SendPacketR(peer, data, 1); + + camera = { 0 }; + camera.position = (Vector3){ MAX_WORLD_SIZE / 2 * CHUNK_SIZE, 12.0f, MAX_WORLD_SIZE / 2 * CHUNK_SIZE}; + camera.target = (Vector3){camera.position.x, camera.position.y, camera.position.z + 1}; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + camera.fovy = FOV; + camera.projection = CAMERA_PERSPECTIVE; + + int center = MAX_WORLD_SIZE / 2 * CHUNK_SIZE; + player_position = (Vector3){ MAX_WORLD_SIZE / 2 * CHUNK_SIZE, 128.0f, MAX_WORLD_SIZE / 2 * CHUNK_SIZE}; + player_position.y = world[GetIndexWorld(center / 16, center / 16)].highestBlock[0] + 2; + player_velocity = (Vector3){ 0, 0, 0 }; + + DisableCursor(); + InitHotbar(); + SetTargetFPS(-1); + + wood_place = LoadSound(Pathify("sound/block_wood.wav")); + stone_place = LoadSound(Pathify("sound/block_stone.wav")); + dirt_place = LoadSound(Pathify("sound/block_dirt.wav")); + grass_place = LoadSound(Pathify("sound/block_grass.wav")); +} + +static void CreateNetPlayer(int id) { + Entity temp = NewEntity(); + temp.type = 1; + temp.position = player_position; + temp.data[0] = id; + temp.data[1] = 0; + AddEntity(temp); +} +static void RemovePlayerEntity(int id) { + auto match = [&](const Entity ent){ + return ent.data[0] == id && ent.type == 1; + }; + auto it = std::find_if(entities.begin(), entities.end(), match); + if (it != entities.end()) { + entities.erase(it); + } +} +static void ParseMessagePacket(std::vector &a_data) { + std::string output; + if(a_data[1] == 255) { + output += std::string("[Server] "); + } + for (int i = 0; i < a_data[2]; ++i) { + output.push_back(static_cast(a_data[3 + i])); + } + MessageData msg; + msg.lifetime = 0; + msg.message = output; + messages.push_back(msg); +} +static void ParseData(ENetPacket* packet) { + int len = packet->dataLength; + std::vector a_data; + std::cout << "Processing packet: " << len << "b ;"; + for (int i = 0; i < len; i++) + { + a_data.push_back(packet->data[i]); + } + switch (a_data[0]) + { + case NET_ARG_CHUNKF: + puts("Server sent a chunk, caching for later use"); + worldPacketStack.push(a_data); + break; + case NET_ARG_PLRID: + plrId = a_data[1]; + std::cout << "Server assigned ID:" << plrId << std::endl; + break; + case NET_ARG_PLRCON: + puts("Player connected"); + if(a_data[1] != plrId) { + CreateNetPlayer(a_data[1]); + } + break; + case NET_ARG_PLRDCN: + puts("Player disconnected"); + RemovePlayerEntity(a_data[1]); + break; + case NET_ARG_ECHOMOVE: + std::cout << "Server requested to sync pos " << (int)(a_data[1]) << std::endl; + if(a_data[1] == plrId) { + float x = DeserializeFloat(a_data[2],a_data[3]); + float y = DeserializeFloat(a_data[4],a_data[5]); + float z = DeserializeFloat(a_data[6],a_data[7]); + player_position.x = x; + player_position.y = y; + player_position.z = z; + } + for (Entity &plr : entities) + { + std::cout << (int)(plr.data[0]) << " " << (int)(plr.type) << std::endl; + if(plr.data[0] == a_data[1] && plr.type == 1) { + float x = DeserializeFloat(a_data[2],a_data[3]); + float y = DeserializeFloat(a_data[4],a_data[5]); + float z = DeserializeFloat(a_data[6],a_data[7]); + plr.position.x = x; + plr.position.y = y; + plr.position.z = z; + //plr.yaw = (short)floorf(a_data[8] / 255.0f* 65536); + plr.data[1] = 255; + std::cout << x << " " << y << " " << z << " " << (float)(plr.yaw) << std::endl; + } + } + break; + case NET_ARG_ECHOYAW: + std::cout << "Server requested to sync yaw " << (int)(a_data[1]) << std::endl; + for (Entity &plr : entities) + { + if(plr.data[0] == a_data[1] && plr.type == 1) { + plr.yaw = (short)floorf(a_data[2] / 255.0f * 65536); + } + } + break; + case NET_ARG_PLRREG: + puts("Got a connected player"); + if(a_data[1] != plrId) { + CreateNetPlayer(a_data[1]); + } + for (Entity &plr : entities) + { + if(plr.data[0] == a_data[1] && plr.type == 1) { + float x = DeserializeFloat(a_data[2],a_data[3]); + float y = DeserializeFloat(a_data[4],a_data[5]); + float z = DeserializeFloat(a_data[6],a_data[7]); + plr.position.x = x; + plr.position.y = y; + plr.position.z = z; + plr.yaw = (short)floorf(a_data[8] / 255.0f * 65536); + } + } + break; + case NET_ARG_BLOCK: + SetBlock(world, a_data[2], a_data[3], a_data[4], a_data[5]); + dirtyChunks[GetIndexWorld(a_data[2] / 16, a_data[4] / 16)] = true; + if(a_data[2] == 0) { + dirtyChunks[GetIndexWorld(a_data[2] / 16-1, a_data[4] / 16)] = true; + } + if(a_data[2] == CHUNK_SIZE-1) { + dirtyChunks[GetIndexWorld(a_data[2] / 16+1, a_data[4] / 16)] = true; + } + if(a_data[4] == 0) { + dirtyChunks[GetIndexWorld(a_data[2] / 16, a_data[4] / 16-1)] = true; + } + if(a_data[4] == CHUNK_SIZE-1) { + dirtyChunks[GetIndexWorld(a_data[2] / 16, a_data[4] / 16+1)] = true; + } + break; + case NET_ARG_MESSAGEE: + ParseMessagePacket(a_data); + break; + default: + puts("Malformed data?"); + break; + } +} +Vector3 player_positionPrev; +float yawPrev; +void NetworkUpdate() { + if(!online) return; + if(state == GAME_STATE_GAME) { + float dist = Vector3Distance(player_positionPrev, player_position); + if(dist > 0.05f) { + char data[8]; + data[0] = NET_ARG_PLAYERMOVE; + SerializeFloat2Data(player_position.x, &data, 1); + SerializeFloat2Data(player_position.y, &data, 3); + SerializeFloat2Data(player_position.z, &data, 5); + float deg = camYaw * RAD2DEG; + int wrapped = (int)(deg + 180) % 360; + float scaled = wrapped / 360.0f * 255.0f; + int iv = (int)floorf(scaled + 0.5f); + if (iv < 0) iv = 0; + if (iv > 255) iv = 255; + data[7] = (uint8_t)iv; + + SendPacket(peer, &data, sizeof(data)); + player_positionPrev = player_position; + } + float yawDist = camYaw - yawPrev; + if(abs(yawDist) > 0.25f) { + char data[2]; + data[0] = NET_ARG_PLAYERYAW; + float deg = camYaw * RAD2DEG; + int wrapped = (int)(deg + 180) % 360; + float scaled = wrapped / 360.0f * 255.0f; + int iv = (int)floorf(scaled + 0.5f); + iv &= 0xFF; + data[1] = (uint8_t)iv; + SendPacket(peer, &data, sizeof(data)); + yawPrev = camYaw; + } + } + ENetEvent event; + while(enet_host_service(client, &event, 2) > 0) + { + switch(event.type) + { + case ENET_EVENT_TYPE_RECEIVE: + ParseData(event.packet); + enet_packet_destroy(event.packet); + break; + case ENET_EVENT_TYPE_DISCONNECT: + puts("Disconnection succeeded."); + break; + } + } +} + +void GameModeCleanupFull() { + if(online) { + enet_host_destroy(client); + online = false; + } + + for (int idx = 0; idx < MAX_WORLD_AREA; idx++) + { + if(loadedChunks[idx]) { + //UnloadModel(renderingT[idx]); + //UnloadModel(rendering[idx]); + } + } + memset(loadedChunks, 0, sizeof(loadedChunks)); + memset(dirtyChunks, 0, sizeof(dirtyChunks)); + entities.clear(); +} + +void GameModeCleanup() { + state = GAME_STATE_TRANSITION; + SaveWorld(world, "tempworld.data"); + SavePlayerData(); + pauseMenuActive = false; + EnableCursor(); + GameModeCleanupFull(); + state = GAME_STATE_MAINMENU; + worldCreationProgress = 0; + worldCreationStep = 0; +} + +extern void InitMenu(); +int main(void) +{ + SetTraceLogLevel(LOG_WARNING); + SetConfigFlags(FLAG_WINDOW_RESIZABLE); + InitWindow(WIDTH, HEIGHT, GAME_NAME); + InitWorldgen(0); + InitAudioDevice(); + InitMenu(); + InputInit(); + EntitiesInit(); + LocaleUpdate(); + LoadRecipes(); + + camera = { 0 }; + camera.position = (Vector3){0, 0, 0}; + camera.target = (Vector3){camera.position.x, camera.position.y, camera.position.z + 1}; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + camera.fovy = 90; + camera.projection = CAMERA_PERSPECTIVE; + + fnt = LoadFontEx(Pathify("font.ttf"), 32, NULL, 1024*8); + terrain = LoadTexture(Pathify("terrain.png")); + GenTextureMipmaps(&terrain); + Image temp = GenImageColor(1, 1, WHITE); + white_pixel = LoadTextureFromImage(temp); + Image src = LoadImageFromTexture(terrain); + + UVCorners uvs = GetUVTex(14); + Rectangle src0; + src0.x = uvs.corner2.x * terrain.width; + src0.y = uvs.corner2.y * terrain.height; + src0.width = (uvs.corner3.x - uvs.corner2.x) * terrain.width; + src0.height = (uvs.corner1.y - uvs.corner2.y) * terrain.height; + Image copied = ImageCopy(src); + ImageCrop(&copied, src0); + + water = LoadTextureFromImage(copied); + SetTextureWrap(water, TEXTURE_WRAP_REPEAT); + SetTargetFPS(-1); + float time; + + SetExitKey(KEY_NULL); + + while (!WindowShouldClose()) + { + NetworkUpdate(); + InputUpdate(); + + while (worldPacketStack.size() > 0) + { + std::vector data = worldPacketStack.top(); + + int idx = (int)(data[1]); + std::cout << "Processing chunk " << (int)idx << std::endl; + Chunk c; + int x, z; + GetXZFromIndex(idx, &x, &z); + c.x = (uint8_t)x; + c.z = (uint8_t)z; + std::cout << data.size() << std::endl; + for (int i = 2; i < CHUNK_DATA_SIZE+2; i++) + { + c.blocks[i-2] = data[i]; + } + for (int i = CHUNK_DATA_SIZE+2; i < CHUNK_DATA_SIZE+2+CHUNK_SIZE*CHUNK_SIZE; i++) + { + c.highestBlock[i-CHUNK_DATA_SIZE-2] = data[i]; + } + world[idx] = c; + dirtyChunks[idx] = true; + worldPacketStack.pop(); + fetchedChunks++; + } + + std::string text0 = LocaleGet("step_0"); + char text[64]; + switch (state) + { + case GAME_STATE_GAME: + GameUpdate(camera, time); + break; + + case GAME_STATE_LOADING: + // World creation crap + + BeginDrawing(); + DrawBackground(); + + if(worldCreationStep == 1) { + text0 = LocaleGet("step_1"); + } + if(worldCreationStep == 2) { + text0 = LocaleGet("step_2"); + } + if(worldCreationStep == 3) { + text0 = LocaleGet("step_3"); + } + DrawTextB(text0.c_str(), GetScreenWidth()/2 - 64, GetScreenHeight()/2 - 32, DEFAULT_FONT_SIZE, WHITE); + if(worldCreationStep != 1) { + DrawRectangle( GetScreenWidth()/2-64, GetScreenHeight()/2, 128, 16, BLACK); + DrawRectangle( GetScreenWidth()/2-62, GetScreenHeight()/2 + 2, 124 * ((float)worldCreationProgress / static_cast(MAX_WORLD_AREA)), 12, GREEN); + sprintf(text, LocaleGet("chunk_status").c_str(), worldCreationProgress, MAX_WORLD_AREA); + DrawTextB(text, GetScreenWidth()/2 - 32, GetScreenHeight()/2 + 32, DEFAULT_FONT_SIZE, WHITE); + } + EndDrawing(); + + // * Well, there was a loop here. + if(worldCreationProgress < MAX_WORLD_AREA) { + for (int _ = 0; _ < 16; _++) + { + int x = worldCreationProgress % MAX_WORLD_SIZE; + int y = worldCreationProgress / MAX_WORLD_SIZE; + int idx = x + y * MAX_WORLD_SIZE; + if(worldCreationStep == 0 && !online) { + Chunk chunk = GenerateChunk(x, y); + world[idx] = chunk; + } + if(worldCreationStep == 1 && !online) { + if(worldCreationProgress > 0) { + worldCreationStep++; + } + else { + GenerateWorldAdditional(world); + } + } + if (worldCreationStep == 2) + { + //RebuildOpaqueMask(&world[idx]); + } + if (worldCreationStep == 3) { + dirtyChunks[idx] = true; + } + if(online) + worldCreationProgress = fetchedChunks+1; + } + } + else { + if(worldCreationStep <= 2) { + worldCreationProgress = 0; + worldCreationStep++; + } + else { + state = GAME_STATE_GAME; + GameStart(); + break; + } + } + break; + case GAME_STATE_MAINMENU: + DrawMainMenu(); + break; + case GAME_STATE_WORLDSELECT: + DrawWorldSelect(); + break; + case GAME_STATE_MULTIPLAYERSELECT: + DrawMultiplayer(); + break; + case GAME_STATE_TRANSITION: + DrawLogoCenter(); + break; + case GAME_STATE_OPTIONS: + DrawControls(); + break; + } + } + + + GameModeCleanupFull(); + + UnloadTexture(terrain); + CloseWindow(); + return 0; +} \ No newline at end of file diff --git a/toolchains/mingw-w64.cmake b/toolchains/mingw-w64.cmake new file mode 100644 index 0000000..5ab0cb0 --- /dev/null +++ b/toolchains/mingw-w64.cmake @@ -0,0 +1,14 @@ +set(CMAKE_SYSTEM_NAME Windows) +set(CMAKE_SYSTEM_VERSION 1) + +set(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc) +set(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++) +set(CMAKE_RC_COMPILER x86_64-w64-mingw32-windres) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +set(CMAKE_C_FLAGS "-static-libgcc -static-libstdc++ ${CMAKE_C_FLAGS}") +set(CMAKE_CXX_FLAGS "-static-libgcc -static-libstdc++ ${CMAKE_CXX_FLAGS}")