9-2-26
This commit is contained in:
1048
.gitignore
vendored
1048
.gitignore
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,33 +1,64 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(factory_hole_core
|
||||
VERSION 0.1.0
|
||||
DESCRIPTION "High-performance ECS-based factory game engine core"
|
||||
LANGUAGES CXX
|
||||
)
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
project(factory-hole-core LANGUAGES CXX)
|
||||
|
||||
# C++17 standard
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
# Options
|
||||
option(FACTORY_CORE_BUILD_TESTS "Build tests" ON)
|
||||
option(FACTORY_CORE_BUILD_EXAMPLES "Build examples" ON)
|
||||
|
||||
# Export compile commands for IDE support
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
# Include dependency management
|
||||
include(cmake/FetchDependencies.cmake)
|
||||
include(FetchContent)
|
||||
|
||||
# Add subdirectories
|
||||
add_subdirectory(src)
|
||||
# Flecs ECS
|
||||
FetchContent_Declare(
|
||||
flecs
|
||||
GIT_REPOSITORY https://github.com/SanderMertens/flecs.git
|
||||
GIT_TAG v4.1.4
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
|
||||
if(FACTORY_CORE_BUILD_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
# Doctest
|
||||
FetchContent_Declare(
|
||||
doctest
|
||||
GIT_REPOSITORY https://github.com/doctest/doctest.git
|
||||
GIT_TAG v2.4.12
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
|
||||
if(FACTORY_CORE_BUILD_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
FetchContent_MakeAvailable(flecs doctest)
|
||||
|
||||
# Only compile sources needed for the core library
|
||||
set(SOURCES
|
||||
src/Components/Config/WorldConfig.cpp
|
||||
src/Core/WorldInstance.cpp
|
||||
)
|
||||
|
||||
add_library(factory-hole-core ${SOURCES})
|
||||
|
||||
# Main executable
|
||||
add_executable(factory-hole-app src/main.cpp)
|
||||
target_link_libraries(factory-hole-app PRIVATE factory-hole-core)
|
||||
|
||||
target_include_directories(factory-hole-core PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
target_link_libraries(factory-hole-core PUBLIC
|
||||
flecs::flecs_static
|
||||
doctest::doctest
|
||||
)
|
||||
|
||||
# Tests
|
||||
enable_testing()
|
||||
|
||||
file(GLOB_RECURSE TEST_SOURCES tests/*.cpp)
|
||||
|
||||
add_executable(factory-hole-tests ${TEST_SOURCES})
|
||||
|
||||
target_include_directories(factory-hole-tests PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
target_link_libraries(factory-hole-tests PRIVATE
|
||||
doctest::doctest_with_main
|
||||
)
|
||||
|
||||
add_test(NAME factory-hole-tests COMMAND factory-hole-tests)
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
include(FetchContent)
|
||||
|
||||
# Fetch flecs - high-performance ECS library
|
||||
FetchContent_Declare(
|
||||
flecs
|
||||
GIT_REPOSITORY https://github.com/SanderMertens/flecs.git
|
||||
GIT_TAG v4.0.4
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
|
||||
# Fetch doctest - testing framework (same as Godot uses)
|
||||
FetchContent_Declare(
|
||||
doctest
|
||||
GIT_REPOSITORY https://github.com/doctest/doctest.git
|
||||
GIT_TAG v2.4.11
|
||||
GIT_SHALLOW TRUE
|
||||
)
|
||||
|
||||
# Make dependencies available
|
||||
FetchContent_MakeAvailable(flecs doctest)
|
||||
40
include/Components/Chute.h
Normal file
40
include/Components/Chute.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include "Types/Item.hpp"
|
||||
#include "Util/SharedBuffer.h"
|
||||
|
||||
struct Chute
|
||||
{
|
||||
struct ChuteLink
|
||||
{
|
||||
int8_t RelativeX{};
|
||||
int8_t RelativeY{};
|
||||
uint16_t Tick{};
|
||||
};
|
||||
struct ChuteItem
|
||||
{
|
||||
Item Item{};
|
||||
uint16_t ChuteEntered{};
|
||||
};
|
||||
struct ChuteData
|
||||
{
|
||||
std::deque<ChuteItem> ItemsInChute{};
|
||||
};
|
||||
|
||||
public:
|
||||
Chute() = default;
|
||||
Chute(Vector2i position, const Vector<Vector2i>& chuteLinks)
|
||||
: Data{static_cast<int>(chuteLinks.size()), ChuteData{}}
|
||||
{
|
||||
for (int i{}; i < chuteLinks.size(); ++i)
|
||||
{
|
||||
Data.GetData()[i].RelativeX = position.x - chuteLinks[i].x;
|
||||
Data.GetData()[i].RelativeY = position.y - chuteLinks[i].y;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
SharedBuffer<ChuteLink, ChuteData> Data{};
|
||||
};
|
||||
13
include/Components/Configs/ItemConfig.hpp
Normal file
13
include/Components/Configs/ItemConfig.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "Types/Item.hpp"
|
||||
|
||||
struct ItemConfig
|
||||
{
|
||||
Item ItemID{};
|
||||
std::string Name{};
|
||||
};
|
||||
13
include/Components/Configs/RecipeConfig.hpp
Normal file
13
include/Components/Configs/RecipeConfig.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ItemConfig.hpp"
|
||||
|
||||
struct RecipeConfig
|
||||
{
|
||||
std::vector<ItemAmount32> Ingredients{};
|
||||
std::vector<ItemAmount32> Results{};
|
||||
float ProcessingTime{};
|
||||
uint32_t ID{ std::numeric_limits<uint32_t>::max() };
|
||||
};
|
||||
51
include/Components/Configs/WorldConfig.hpp
Normal file
51
include/Components/Configs/WorldConfig.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "flecs.h"
|
||||
|
||||
#include "ItemConfig.hpp"
|
||||
#include "RecipeConfig.hpp"
|
||||
#include "Components/Misc.hpp"
|
||||
|
||||
class WorldConfig
|
||||
{
|
||||
public:
|
||||
void RegisterItem(const std::string& name);
|
||||
|
||||
private:
|
||||
std::vector<ItemConfig> Items{};
|
||||
std::vector<RecipeConfig> Recipes{};
|
||||
|
||||
};
|
||||
|
||||
inline void Flecs_Configs(flecs::world& world)
|
||||
{
|
||||
// std::string opaque support
|
||||
world.component<std::string>()
|
||||
.opaque(flecs::String)
|
||||
.serialize([](const flecs::serializer *s, const std::string *data) {
|
||||
const char *str = data->c_str();
|
||||
return s->value(flecs::String, &str);
|
||||
})
|
||||
.assign_string([](std::string *data, const char *value) {
|
||||
*data = value;
|
||||
});
|
||||
|
||||
// std::vector<ItemAmount32> opaque support
|
||||
world.component<std::vector<ItemAmount32>>()
|
||||
.opaque(std_vector_support<ItemAmount32>);
|
||||
|
||||
// ItemConfig
|
||||
world.component<ItemConfig>()
|
||||
.member<Item>("ItemID")
|
||||
.member<std::string>("Name");
|
||||
|
||||
// RecipeConfig
|
||||
world.component<RecipeConfig>()
|
||||
.member<std::vector<ItemAmount32>>("Ingredients")
|
||||
.member<std::vector<ItemAmount32>>("Results")
|
||||
.member<float>("ProcessingTime")
|
||||
.member<uint32_t>("ID");
|
||||
}
|
||||
218
include/Components/Inventory.hpp
Normal file
218
include/Components/Inventory.hpp
Normal file
@@ -0,0 +1,218 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "flecs.h"
|
||||
|
||||
#include "Types/Item.hpp"
|
||||
#include "Types/Filter.h"
|
||||
#include "Util/SharedBuffer.h"
|
||||
#include "Util/Span.h"
|
||||
|
||||
template <typename IntegralType>
|
||||
struct InventoryT
|
||||
{
|
||||
struct InventoryMeta
|
||||
{
|
||||
IntegralType MaxSize{ std::numerics_limits<IntegralType>::max() };
|
||||
};
|
||||
|
||||
public:
|
||||
InventoryT() = default;
|
||||
InventoryT(const Vector<Ref<ItemConfig>>& items)
|
||||
{
|
||||
Slots = { static_cast<int>(items.size()), InventoryMeta{} };
|
||||
|
||||
for (int i{}; i < items.size(); ++i)
|
||||
{
|
||||
DEV_ASSERT(items[i]->Item.ItemID != Item::null);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
IntegralType GetItemsAmount(uint16_t id) const { Assert(id); return Slots[id]; }
|
||||
IntegralType GetItemsAmount(Item item) const { return GetItemsAmount(item.ItemID); }
|
||||
|
||||
void RemoveItems(uint16_t id, IntegralType amount) { Assert(id); Slots[id]-= amount; }
|
||||
void RemoveItems(Item id, IntegralType amount) { RemoveItems(id.ItemID, amount); }
|
||||
template <typename OtherIntegralType>
|
||||
void RemoveItems(ItemAmountT<OtherIntegralType> amount) { RemoveItems(amount.Item, amount.Amount); }
|
||||
|
||||
void AddItems(uint16_t id, IntegralType amount) { Assert(id); Slots[id]+= amount; }
|
||||
void AddItems(Item id, IntegralType amount) { AddItems(id.ItemID, amount); }
|
||||
template <typename OtherIntegralType>
|
||||
void AddItems(ItemAmountT<OtherIntegralType> item) { AddItems(item.Item.ItemID, item.Amount); }
|
||||
template <typename OtherIntegralType>
|
||||
void AddItems(const InventoryT<OtherIntegralType> other)
|
||||
{
|
||||
DEV_ASSERT(Slots.GetSize() == other.Slots.GetSize());
|
||||
for (uint32_t i{}; i < Slots.GetSize(); ++i)
|
||||
Slots[i] += other.Slots[i];
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
for (IntegralType i{}; i < Slots.GetSize(); ++i)
|
||||
Slots[i] = 0;
|
||||
}
|
||||
|
||||
void Assert(uint16_t id)
|
||||
{
|
||||
DEV_ASSERT(id < Slots.GetSize() && id != Item::null);
|
||||
}
|
||||
|
||||
public:
|
||||
operator bool() const { return Slots; }
|
||||
|
||||
public:
|
||||
SharedBuffer<IntegralType, InventoryMeta> Slots;
|
||||
};
|
||||
|
||||
typedef InventoryT<uint16_t> Inventory16;
|
||||
typedef InventoryT<uint32_t> Inventory32;
|
||||
typedef InventoryT<uint64_t> Inventory64;
|
||||
|
||||
typedef Inventory32 Inventory;
|
||||
typedef Inventory64 WorldInventory;
|
||||
|
||||
struct FixedInventoryEntry
|
||||
{
|
||||
FixedInventoryEntry() = default;
|
||||
FixedInventoryEntry(Item item, uint16_t amount, uint16_t maxAmount) : Item{ item }, Amount{ amount }, MaxAmount{ maxAmount } {};
|
||||
|
||||
Item Item{};
|
||||
uint16_t Amount{};
|
||||
uint16_t MaxAmount{std::numeric_limits<uint16_t>::max()};
|
||||
};
|
||||
|
||||
template <uint8_t Size>
|
||||
struct FixedInventoryBase
|
||||
{
|
||||
FixedInventoryBase() = default;
|
||||
|
||||
template <typename It>
|
||||
FixedInventoryBase(const It& it)
|
||||
{
|
||||
int counter{};
|
||||
for (const auto& val : it)
|
||||
Data[counter++] = val;
|
||||
}
|
||||
|
||||
tcb::span<FixedInventoryEntry> GetInventoryData()
|
||||
{
|
||||
return {&Data[0], InventorySize);
|
||||
}
|
||||
tcb::span<const FixedInventoryEntry> GetInventoryData() const
|
||||
{
|
||||
return {&Data[0], InventorySize};
|
||||
}
|
||||
|
||||
uint8_t InventorySize{ Size };
|
||||
std::array<FixedInventoryEntry, Size> Data{};
|
||||
};
|
||||
|
||||
typedef FixedInventoryBase<1> FixedInventory1;
|
||||
typedef FixedInventoryBase<2> FixedInventory2;
|
||||
typedef FixedInventoryBase<3> FixedInventory3;
|
||||
typedef FixedInventoryBase<4> FixedInventory4;
|
||||
typedef FixedInventoryBase<5> FixedInventory5;
|
||||
typedef FixedInventoryBase<6> FixedInventory6;
|
||||
typedef FixedInventoryBase<7> FixedInventory7;
|
||||
typedef FixedInventoryBase<8> FixedInventory8;
|
||||
|
||||
struct InventoryAreaOfEffect
|
||||
{
|
||||
InventoryAreaOfEffect(bool isCircle, uint8_t size)
|
||||
: IsCircle{ isCircle }
|
||||
, Size{ size }
|
||||
{
|
||||
}
|
||||
|
||||
uint8_t IsCircle : 1;
|
||||
uint8_t Size : 7;
|
||||
};
|
||||
static_assert(sizeof(InventoryAreaOfEffect) == 1);
|
||||
|
||||
struct ItemProcessor
|
||||
{
|
||||
ItemProcessor() = default;
|
||||
ItemProcessor(uint32_t ticks) : ProcessedTicks{ ticks } {};
|
||||
|
||||
uint32_t ProcessedTicks;
|
||||
};
|
||||
|
||||
|
||||
inline void Flecs_Inventory(flecs::world& world)
|
||||
{
|
||||
world.component<FixedInventoryEntry>()
|
||||
.member<Item>("Item")
|
||||
.member<uint16_t>("Amount")
|
||||
.member<uint16_t>("MaxAmount");
|
||||
|
||||
world.component<FixedInventory1>()
|
||||
.add(flecs::Inheritable)
|
||||
.opaque(world.vector<FixedInventoryEntry>())
|
||||
.serialize([](const flecs::serializer *s, const FixedInventory1 *data) {
|
||||
for (uint8_t i = 0; i < data->InventorySize; ++i)
|
||||
s->value(data->Data[i]);
|
||||
return 0;
|
||||
})
|
||||
.count([](const FixedInventory1 *data) -> size_t {
|
||||
return data->InventorySize;
|
||||
})
|
||||
.ensure_element([](FixedInventory1 *data, size_t elem) -> FixedInventoryEntry* {
|
||||
return &data->Data[elem];
|
||||
});
|
||||
|
||||
world.component<FixedInventory2>().is_a<FixedInventory1>();
|
||||
world.component<FixedInventory3>().is_a<FixedInventory1>();
|
||||
world.component<FixedInventory4>().is_a<FixedInventory1>();
|
||||
world.component<FixedInventory5>().is_a<FixedInventory1>();
|
||||
world.component<FixedInventory6>().is_a<FixedInventory1>();
|
||||
world.component<FixedInventory7>().is_a<FixedInventory1>();
|
||||
world.component<FixedInventory8>().is_a<FixedInventory1>();
|
||||
|
||||
world.component<Inventory>()
|
||||
.opaque(world.vector<uint32_t>())
|
||||
.serialize([](const flecs::serializer *s, const Inventory *data) {
|
||||
if (!data->Slots) return 0;
|
||||
for (uint32_t i = 0; i < data->Slots.GetSize(); ++i)
|
||||
s->value(data->Slots[i]);
|
||||
return 0;
|
||||
})
|
||||
.count([](const Inventory *data) -> size_t {
|
||||
if (!data->Slots) return 0;
|
||||
return data->Slots.GetSize();
|
||||
});
|
||||
|
||||
world.component<WorldInventory>()
|
||||
.add(flecs::Singleton)
|
||||
.opaque(world.vector<uint64_t>())
|
||||
.serialize([](const flecs::serializer *s, const Inventory *data) {
|
||||
if (!data->Slots) return 0;
|
||||
for (uint64_t i = 0; i < data->Slots.GetSize(); ++i)
|
||||
s->value(data->Slots[i]);
|
||||
return 0;
|
||||
})
|
||||
.count([](const Inventory *data) -> size_t {
|
||||
if (!data->Slots) return 0;
|
||||
return data->Slots.GetSize();
|
||||
});
|
||||
|
||||
world.component<InventoryAreaOfEffect>()
|
||||
.opaque(world.component()
|
||||
.member<uint8_t>("IsCircle")
|
||||
.member<uint8_t>("Size"))
|
||||
.serialize([](const flecs::serializer *s, const InventoryAreaOfEffect *data) {
|
||||
uint8_t isCircle = data->IsCircle;
|
||||
uint8_t size = data->Size;
|
||||
s->member("IsCircle");
|
||||
s->value(isCircle);
|
||||
s->member("Size");
|
||||
s->value(size);
|
||||
return 0;
|
||||
});
|
||||
|
||||
world.component<ItemProcessor>()
|
||||
.member<uint32_t>("ProcessedTicks");
|
||||
}
|
||||
64
include/Components/Misc.hpp
Normal file
64
include/Components/Misc.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include "flecs.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct Vector2
|
||||
{
|
||||
Vector2() = default;
|
||||
Vector2(int32_t x, int32_t y): X{ x }, Y{ y } {};
|
||||
|
||||
int32_t X{}, Y{};
|
||||
|
||||
Vector2 operator+(const Vector2& other) const { return { X + other.X, Y + other.Y }; }
|
||||
Vector2 operator-(const Vector2& other) const { return { X - other.X, Y - other.Y }; }
|
||||
};
|
||||
|
||||
struct TilePosition
|
||||
{
|
||||
Vector2 Position;
|
||||
};
|
||||
|
||||
struct Level
|
||||
{
|
||||
Level() = default;
|
||||
Level(uint8_t level) : Val{ level } {};
|
||||
|
||||
uint8_t Val;
|
||||
};
|
||||
|
||||
template <typename Elem, typename Vector = std::vector<Elem>>
|
||||
flecs::opaque<Vector, Elem> std_vector_support(flecs::world& world) {
|
||||
return flecs::opaque<Vector, Elem>()
|
||||
.as_type(world.vector<Elem>())
|
||||
.serialize([](const flecs::serializer *s, const Vector *data) {
|
||||
for (const auto& el : *data)
|
||||
s->value(el);
|
||||
return 0;
|
||||
})
|
||||
.count([](const Vector *data) {
|
||||
return data->size();
|
||||
})
|
||||
.resize([](Vector *data, size_t size) {
|
||||
data->resize(size);
|
||||
})
|
||||
.ensure_element([](Vector *data, size_t elem) {
|
||||
if (data->size() <= elem)
|
||||
data->resize(elem + 1);
|
||||
return &data->data()[elem];
|
||||
});
|
||||
}
|
||||
|
||||
inline void Flecs_Misc(flecs::world& world)
|
||||
{
|
||||
world.component<Vector2>()
|
||||
.member<int32_t>("x")
|
||||
.member<int32_t>("y");
|
||||
|
||||
world.component<TilePosition>()
|
||||
.member<Vector2>("Position");
|
||||
|
||||
world.component<Level>()
|
||||
.member<uint8_t>("Val");
|
||||
}
|
||||
50
include/Components/Resource.hpp
Normal file
50
include/Components/Resource.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "flecs.h"
|
||||
|
||||
#include "Tick.hpp"
|
||||
|
||||
struct ResourceInfo
|
||||
{
|
||||
uint16_t ResourceID;
|
||||
};
|
||||
|
||||
struct ResourceHealth
|
||||
{
|
||||
uint16_t MaxHealth{};
|
||||
uint16_t Health{};
|
||||
uint16_t RenewalTicks{};
|
||||
uint16_t Renewal{};
|
||||
};
|
||||
|
||||
struct ResourceTick : public TickAccumulator
|
||||
{};
|
||||
|
||||
struct Renewing
|
||||
{};
|
||||
|
||||
|
||||
inline void Flecs_Resource(flecs::world& world)
|
||||
{
|
||||
world.component<ResourceInfo>()
|
||||
.member<uint16_t>("ResourceID");
|
||||
|
||||
world.component<ResourceHealth>()
|
||||
.member<uint16_t>("MaxHealth")
|
||||
.member<uint16_t>("Health")
|
||||
.member<uint16_t>("RenewalTicks")
|
||||
.member<uint16_t>("Renewal");
|
||||
|
||||
world.component<ResourceTick>()
|
||||
.is_a<TickAccumulator>();
|
||||
|
||||
world.component<Renewing>()
|
||||
.add<Freezes, ResourceTick>();
|
||||
|
||||
world.system<ResourceInfo>()
|
||||
.without<ResourceHealth>()
|
||||
.each([](ResourceInfo) {});
|
||||
|
||||
}
|
||||
19
include/Components/Support.h
Normal file
19
include/Components/Support.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct Support
|
||||
{
|
||||
uint8_t MaxSupport : 5;
|
||||
bool SupportsUp : 1;
|
||||
bool SupportsRight : 1;
|
||||
bool SupportsLeft : 1;
|
||||
uint8_t SupportsAvailable : 5;
|
||||
bool SupportedByBottom : 1;
|
||||
bool SupportedByRight : 1;
|
||||
bool SupportedByLeft : 1;
|
||||
};
|
||||
|
||||
struct RequiresSupport
|
||||
{
|
||||
};
|
||||
33
include/Components/Sync.h
Normal file
33
include/Components/Sync.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// #pragma once
|
||||
// #include "core/object/ref_counted.h"
|
||||
|
||||
// struct Sync
|
||||
// { };
|
||||
|
||||
// class FactoryEntity;
|
||||
|
||||
// struct NodePtr
|
||||
// {
|
||||
// NodePtr() = default;
|
||||
// NodePtr(FactoryEntity* node) : Node{ node } {}
|
||||
|
||||
// template <typename T>
|
||||
// T* AsNode() const
|
||||
// {
|
||||
// static_assert(std::is_base_of_v<FactoryEntity, T>);
|
||||
// DEV_ASSERT(Object::cast_to<T>(Node));
|
||||
// return static_cast<T*>(Node);
|
||||
// }
|
||||
|
||||
// FactoryEntity* Node{};
|
||||
// };
|
||||
|
||||
// class Archetype;
|
||||
|
||||
// struct ArchetypePtr
|
||||
// {
|
||||
// ArchetypePtr() = default;
|
||||
// ArchetypePtr(Ref<Archetype>& archetpye) : Archetype{ archetpye } {}
|
||||
|
||||
// Ref<Archetype> Archetype;
|
||||
// };
|
||||
44
include/Components/Tick.hpp
Normal file
44
include/Components/Tick.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
#include "flecs.h"
|
||||
|
||||
struct TickAccumulator
|
||||
{
|
||||
uint16_t MaxTick{1};
|
||||
uint16_t AccumulatedTick;
|
||||
|
||||
constexpr bool Finished() const
|
||||
{
|
||||
return AccumulatedTick >= MaxTick;
|
||||
}
|
||||
};
|
||||
|
||||
struct Freezes
|
||||
{};
|
||||
|
||||
inline void Flecs_Tick(flecs::world& world)
|
||||
{
|
||||
world.component<TickAccumulator>()
|
||||
.member<uint16_t>("MaxTick")
|
||||
.member<uint16_t>("AccumulatedTick")
|
||||
.add(flecs::Inheritable);
|
||||
|
||||
world.component<Freezes>();
|
||||
|
||||
world.system<TickAccumulator>("Tick Increase")
|
||||
.kind(flecs::PreUpdate)
|
||||
.without<Freezes, TickAccumulator>()
|
||||
.each([] (TickAccumulator& tick)
|
||||
{
|
||||
++tick.AccumulatedTick;
|
||||
});
|
||||
|
||||
world.system<TickAccumulator>("Tick Reset")
|
||||
.kind(flecs::PostUpdate)
|
||||
.without<Freezes, TickAccumulator>()
|
||||
.each([] (TickAccumulator& tick)
|
||||
{
|
||||
tick.AccumulatedTick -= tick.MaxTick * tick.Finished();
|
||||
});
|
||||
}
|
||||
177
include/Core/Chunk.h
Normal file
177
include/Core/Chunk.h
Normal file
@@ -0,0 +1,177 @@
|
||||
#pragma once
|
||||
|
||||
#include "modules/factory/include/Data/Tile.h"
|
||||
#include "core/math/vector2i.h"
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
struct Chunk
|
||||
{
|
||||
public:
|
||||
typedef uint8_t CoordinateType;
|
||||
static constexpr int ChunkSizePowerOfTwo = 6;
|
||||
static constexpr int ChunkSize = 1 << ChunkSizePowerOfTwo;
|
||||
static constexpr int TotalChunkTiles = ChunkSize * ChunkSize;
|
||||
static constexpr int ChunkMask = (1 << ChunkSizePowerOfTwo) - 1;
|
||||
static_assert(sizeof(CoordinateType) * 8 >= ChunkSizePowerOfTwo, "CoordinateType is too small to support chunk size");
|
||||
|
||||
static constexpr uint8_t WorldToLocal(int world) { return world & ChunkMask; }
|
||||
|
||||
std::array<Tile, TotalChunkTiles> Tiles;
|
||||
|
||||
public:
|
||||
static void Assert(int x, int y) { DEV_ASSERT(x < ChunkSize && x >= 0 && y < ChunkSize && y >= 0); }
|
||||
public:
|
||||
Tile GetTile(int x, int y) const { Assert(x, y); return Tiles[y * ChunkSize + x]; }
|
||||
Tile GetTile(Vector2i pos) const { return GetTile(pos.x, pos.y); }
|
||||
const Tile& GetTileRef(int x, int y) const { Assert(x, y); return Tiles[y * ChunkSize + x]; }
|
||||
Tile& GetTile(int x, int y) { Assert(x, y); return Tiles[y * ChunkSize + x]; }
|
||||
Tile& GetTile(Vector2i pos) { return GetTile(pos.x, pos.y); }
|
||||
};
|
||||
|
||||
struct ChunkCoordinate
|
||||
{
|
||||
ChunkCoordinate() = default;
|
||||
ChunkCoordinate(int x, int y) : X{ static_cast<uint8_t>(x) }, Y{ static_cast<uint8_t>(y) } { DEV_ASSERT(x >= 0 && x < Chunk::ChunkSize && y >= 0 && y < Chunk::ChunkSize); }
|
||||
ChunkCoordinate(Vector2i pos) : ChunkCoordinate{pos.x, pos.y} {}
|
||||
|
||||
uint8_t X{};
|
||||
uint8_t Y{};
|
||||
|
||||
operator Vector2i() const { return Vector2i(X, Y); }
|
||||
};
|
||||
|
||||
static_assert(sizeof(ChunkCoordinate) / 2 >= Chunk::ChunkSizePowerOfTwo / 8);
|
||||
|
||||
struct EntityTile final
|
||||
{
|
||||
public:
|
||||
EntityTile() = default;
|
||||
EntityTile(entt::entity entity, int worldX, int worldY)
|
||||
: Entity{ entity }
|
||||
, ChunkX{ Chunk::WorldToLocal(worldX) }
|
||||
, ChunkY{ Chunk::WorldToLocal(worldY) }
|
||||
{}
|
||||
|
||||
public:
|
||||
entt::entity Entity{};
|
||||
Chunk::CoordinateType ChunkX{};
|
||||
Chunk::CoordinateType ChunkY{};
|
||||
};
|
||||
|
||||
struct ChunkKey
|
||||
{
|
||||
public:
|
||||
static constexpr int16_t WorldToChunk(int pos) { return static_cast<int16_t>(pos >> Chunk::ChunkSizePowerOfTwo); }
|
||||
static constexpr int32_t ChunkToWorld(int16_t pos) { return pos << Chunk::ChunkSizePowerOfTwo; }
|
||||
|
||||
public:
|
||||
int16_t X{}, Y{};
|
||||
|
||||
public:
|
||||
ChunkKey() = default;
|
||||
ChunkKey(int WorldX, int WorldY) : X{ WorldToChunk(WorldX) }, Y{ WorldToChunk(WorldY) } {}
|
||||
|
||||
public:
|
||||
uint64_t hash64() const { static_assert(sizeof(uint32_t) == sizeof(ChunkKey)); return std::hash<uint32_t>{}(*reinterpret_cast<const uint32_t*>(this)); }
|
||||
uint32_t hash() const { return static_cast<uint32_t>(hash64()); }
|
||||
static uint32_t hash(ChunkKey key) { return key.hash(); }
|
||||
|
||||
Rect2i GetBounds() const { return Rect2i{ChunkToWorld(X), ChunkToWorld(Y), 1 << Chunk::ChunkSizePowerOfTwo, 1 << Chunk::ChunkSizePowerOfTwo}; }
|
||||
|
||||
public:
|
||||
bool operator==(const ChunkKey& rhs) const { return hash() == rhs.hash(); }
|
||||
};
|
||||
|
||||
static_assert(sizeof(ChunkKey) == 4);
|
||||
|
||||
struct ChunkData
|
||||
{
|
||||
public:
|
||||
void MarkAsPersistant(entt::entity entity);
|
||||
void RemovePersistance(entt::entity entity);
|
||||
void Clear()
|
||||
{
|
||||
Chunk = {};
|
||||
Entities.resize(0);
|
||||
}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Chunk> Chunk{};
|
||||
std::vector<EntityTile> Entities{};
|
||||
std::vector<EntityTile> PersistantEntities{};
|
||||
};
|
||||
|
||||
struct ChunkCollection
|
||||
{
|
||||
public:
|
||||
const Chunk& GetChunk(int x, int y);
|
||||
const Chunk& GetChunk(ChunkKey key);
|
||||
Chunk const* TryGetChunk(int x, int y) const;
|
||||
Chunk const* TryGetChunk(ChunkKey key) const;
|
||||
ChunkData& GetChunkData(int x, int y);
|
||||
ChunkData& GetChunkData(ChunkKey key);
|
||||
|
||||
Tile GetTile(int x, int y);
|
||||
Tile const* TryGetTile(int x, int y) const;
|
||||
entt::entity GetEntity(int x, int y) const;
|
||||
|
||||
void SetChunkTiles(int x, int y, std::unique_ptr<Chunk>&& chunk);
|
||||
void SetChunkTiles(ChunkKey key, std::unique_ptr<Chunk>&& chunk);
|
||||
void AddEntity(entt::entity entity, const Vector<Vector2i>& claimedPositions);
|
||||
void AddPersistantEntity(entt::entity entity, const Vector<Vector2i>& claimedPositions);
|
||||
|
||||
void MarkAsPersistant(entt::entity entity);
|
||||
void RemovePersistance(entt::entity entity);
|
||||
|
||||
void RemoveEntity(entt::entity entity);
|
||||
void RemoveChunk(int x, int y);
|
||||
void RemoveChunk(ChunkKey key);
|
||||
|
||||
private:
|
||||
int GetChunkIndex(int x, int y);
|
||||
int GetChunkIndex(ChunkKey key);
|
||||
int TryGetChunkIndex(int x, int y) const;
|
||||
int TryGetChunkIndex(ChunkKey key) const;
|
||||
Chunk& GetChunkInternal(int x, int y);
|
||||
Chunk& GetChunkInternal(ChunkKey key);
|
||||
Tile& GetTileInternal(int x, int y);
|
||||
|
||||
void SetTile(Tile tile, int x, int y);
|
||||
|
||||
void InvalidateCachedChunk();
|
||||
|
||||
private:
|
||||
std::vector<ChunkData> ChunkDatas;
|
||||
HashMap<ChunkKey, int, ChunkKey> ChunkMap{};
|
||||
ChunkKey CachedChunkKey{};
|
||||
int CachedChunk{-1};
|
||||
};
|
||||
|
||||
// struct LightValue
|
||||
// {
|
||||
// constexpr static uint8_t LightLevelBits = 5;
|
||||
// constexpr static uint8_t PenetrationBits = 8 - LightLevelBits;
|
||||
// constexpr static uint8_t MaxLightVal = (1 << LightLevelBits) - 1;
|
||||
// constexpr static uint8_t MaxPenetration = (1 << PenetrationBits) - 1;
|
||||
|
||||
// uint8_t Penetration : PenetrationBits;
|
||||
// uint8_t Val : LightLevelBits;
|
||||
|
||||
// LightValue()
|
||||
// {
|
||||
// Penetration = 1;
|
||||
// Val = 0;
|
||||
// }
|
||||
// };
|
||||
|
||||
//static_assert(sizeof(LightValue) == 1);
|
||||
|
||||
// struct LightChunk
|
||||
// {
|
||||
// std::array<LightValue, Chunk::TotalChunkTiles> Tiles;
|
||||
|
||||
// public:
|
||||
// LightValue GetTile(int x, int y) const { Chunk::Assert(x, y); return Tiles[y * Chunk::ChunkSize + x]; }
|
||||
// LightValue& GetTile(int x, int y) { Chunk::Assert(x, y); return Tiles[y * Chunk::ChunkSize + x]; }
|
||||
// };
|
||||
197
include/Core/FactoryCommandQueue.h
Normal file
197
include/Core/FactoryCommandQueue.h
Normal file
@@ -0,0 +1,197 @@
|
||||
#pragma once
|
||||
#include "Util/StackAllocator.h"
|
||||
#include "Util/Span.h"
|
||||
#include "EnTT/entity/registry.hpp"
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include "Components/Sync.h"
|
||||
#include "Core/FactoryWorld.h"
|
||||
|
||||
struct FactoryCommand
|
||||
{
|
||||
void* Data;
|
||||
entt::entity Entity;
|
||||
void(*Command)(FactoryWorld& world, entt::entity entity, void* data);
|
||||
};
|
||||
|
||||
class FactoryCommandQueue final
|
||||
{
|
||||
static constexpr int DefaultAllocatorSize = 1024 * 1024;
|
||||
|
||||
public:
|
||||
FactoryCommandQueue(size_t memorySize)
|
||||
: Allocator{ memorySize }
|
||||
{}
|
||||
FactoryCommandQueue()
|
||||
: Allocator{ DefaultAllocatorSize }
|
||||
{}
|
||||
|
||||
public:
|
||||
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
void SetComponentData(entt::entity entity, const T& component);
|
||||
|
||||
template <typename T>
|
||||
void SetOrAddComponentData(entt::entity entity, const T& component);
|
||||
|
||||
template <typename T>
|
||||
void AddIfNone(entt::entity entity, const T& component);
|
||||
|
||||
template <typename T>
|
||||
void AddComponent(entt::entity entity, const T& component);
|
||||
|
||||
template <typename T>
|
||||
void AddComponent(entt::entity entity);
|
||||
|
||||
template <typename T>
|
||||
void RemoveComponent(entt::entity entity);
|
||||
|
||||
//void RemoveEntity(entt::entity entity);
|
||||
|
||||
void SyncEntity(entt::entity entity) { AddComponent<Sync>(entity); }
|
||||
void StopSyncingEntity(entt::entity entity) { RemoveComponent<Sync>(entity); }
|
||||
|
||||
void ExecuteAll(FactoryWorld& world);
|
||||
|
||||
void Clear();
|
||||
|
||||
template <typename T>
|
||||
T* AllocateData()
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>);
|
||||
return Allocator.allocate<T>(1);
|
||||
}
|
||||
|
||||
template <typename T, typename ... Args>
|
||||
T* AllocateData(Args... arguments)
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>);
|
||||
T* data = AllocateData<T>();
|
||||
new (data) T(arguments...);
|
||||
return data;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
tcb::span<T>* AllocateBuffer(uint32_t size)
|
||||
{
|
||||
static_assert(std::is_trivially_copyable_v<T>);
|
||||
auto spanData = Allocator.allocate<tcb::span<T>>();
|
||||
auto data = Allocator.allocate<T>(size);
|
||||
for (uint32_t i{}; i < size; ++i)
|
||||
data[i] = {};
|
||||
*spanData = tcb::span<T>(data, size);
|
||||
return spanData;
|
||||
}
|
||||
|
||||
private:
|
||||
void ClearUnsafe()
|
||||
{
|
||||
Commands.clear();
|
||||
Allocator.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
StackAllocator Allocator;
|
||||
std::mutex Mutex;
|
||||
std::vector<FactoryCommand> Commands;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline void FactoryCommandQueue::SetComponentData(entt::entity entity, const T& component)
|
||||
{
|
||||
FactoryCommand command;
|
||||
command.Entity = entity;
|
||||
command.Data = AllocateData<T>(component);
|
||||
command.Command = [](entt::registry& registry, entt::entity entity, void* data)
|
||||
{
|
||||
registry.get<T>(entity) = *static_cast<T*>(data);
|
||||
};
|
||||
std::scoped_lock lock(Mutex);
|
||||
Commands.push_back(command);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void FactoryCommandQueue::SetOrAddComponentData(entt::entity entity, const T& component)
|
||||
{
|
||||
FactoryCommand command;
|
||||
command.Entity = entity;
|
||||
command.Data = AllocateData<T>(component);
|
||||
command.Command = [](entt::registry& registry, entt::entity entity, void* data)
|
||||
{
|
||||
registry.emplace_or_replace<T>(entity, *static_cast<T*>(data));
|
||||
};
|
||||
std::scoped_lock lock(Mutex);
|
||||
Commands.push_back(command);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void FactoryCommandQueue::AddIfNone(entt::entity entity, const T& component)
|
||||
{
|
||||
FactoryCommand command;
|
||||
command.Entity = entity;
|
||||
command.Data = AllocateData<T>(component);
|
||||
command.Command = [](entt::registry& registry, entt::entity entity, void* data)
|
||||
{
|
||||
if (!registry.all_of<T>(entity))
|
||||
registry.emplace<T>(entity, *static_cast<T*>(data));
|
||||
};
|
||||
std::scoped_lock lock(Mutex);
|
||||
Commands.push_back(command);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void FactoryCommandQueue::AddComponent(entt::entity entity, const T& component)
|
||||
{
|
||||
FactoryCommand command;
|
||||
command.Entity = entity;
|
||||
command.Data = AllocateData<T>(component);
|
||||
command.Command = [](entt::registry& registry, entt::entity entity, void* data)
|
||||
{
|
||||
registry.emplace<T>(entity, *static_cast<T*>(data));
|
||||
};
|
||||
std::scoped_lock lock(Mutex);
|
||||
Commands.push_back(command);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void FactoryCommandQueue::AddComponent(entt::entity entity)
|
||||
{
|
||||
FactoryCommand command;
|
||||
command.Entity = entity;
|
||||
command.Data = nullptr;
|
||||
command.Command = [](entt::registry& registry, entt::entity entity, void* data)
|
||||
{
|
||||
registry.emplace<T>(entity);
|
||||
};
|
||||
std::scoped_lock lock(Mutex);
|
||||
Commands.push_back(command);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void FactoryCommandQueue::RemoveComponent(entt::entity entity)
|
||||
{
|
||||
FactoryCommand command;
|
||||
command.Entity = entity;
|
||||
command.Data = nullptr;
|
||||
command.Command = [](entt::registry& registry, entt::entity entity, void* data)
|
||||
{
|
||||
registry.remove<T>(entity);
|
||||
};
|
||||
std::scoped_lock lock(Mutex);
|
||||
Commands.push_back(command);
|
||||
}
|
||||
|
||||
// inline void FactoryCommandQueue::RemoveEntity(entt::entity entity)
|
||||
// {
|
||||
// FactoryCommand command;
|
||||
// command.Entity = entity;
|
||||
// command.Data = nullptr;
|
||||
// command.Command = [](entt::registry& registry, entt::entity entity, void* data)
|
||||
// {
|
||||
// registry.destroy(entity);
|
||||
// };
|
||||
// std::scoped_lock lock(Mutex);
|
||||
// Commands.push_back(command);
|
||||
// }
|
||||
159
include/Core/FactoryWorld.h
Normal file
159
include/Core/FactoryWorld.h
Normal file
@@ -0,0 +1,159 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "core/templates/vector.h"
|
||||
#include "core/templates/hash_map.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "modules/noise/fastnoise_lite.h"
|
||||
#include "core/io/resource.h"
|
||||
#include "scene/2d/tile_map_layer.h"
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/templates/hash_set.h"
|
||||
#include "core/math/vector2i.h"
|
||||
|
||||
#include "Components/Position.h"
|
||||
#include "Components/Inventory.h"
|
||||
#include "Components/Support.h"
|
||||
|
||||
#include "Data/Tile.h"
|
||||
#include "Util/ResourceAccess.h"
|
||||
#include "Chunk.h"
|
||||
#include "SystemBase.h"
|
||||
#include "Data/WorldSettings.h"
|
||||
#include "FactoryCommandQueue.h"
|
||||
#include "WorldThreadData.h"
|
||||
|
||||
class FactoryWorld;
|
||||
class FactoryWorldInterface;
|
||||
|
||||
// typedef ResourceAccess<FactoryWorld, Mutex> FactoryWorldAccess;
|
||||
|
||||
enum FactoryError
|
||||
{
|
||||
FACTORY_ERROR_NONE = 0,
|
||||
FACTORY_ERROR_NOT_ENTITY,
|
||||
FACTORY_ERROR_CANT_UPGRADE,
|
||||
FACTORY_ERROR_FULLY_UPGRADED,
|
||||
FACTORY_ERROR_NOT_ENOUGH_ITEMS,
|
||||
FACTORY_ERROR_NO_SPACE,
|
||||
FACTORY_ERROR_REQUIRES_SUPPORT,
|
||||
FACTORY_ERROR_PATH_TOO_LONG,
|
||||
FACTORY_ERROR_INVALID_PATH,
|
||||
FACTORY_ERROR_INVALID_POS,
|
||||
FACTORY_ERROR_MISC_ERROR,
|
||||
};
|
||||
|
||||
struct ChunkUnlock final
|
||||
{
|
||||
ChunkKey ChunkID{};
|
||||
Vector<ItemAmount64> Items{};
|
||||
};
|
||||
|
||||
class FactoryWorld final
|
||||
{
|
||||
friend class WorldSerializer;
|
||||
friend class WorldLoader;
|
||||
public:
|
||||
FactoryWorld() = default;
|
||||
~FactoryWorld() = default;
|
||||
|
||||
public:
|
||||
void Tick(int amount = 1);
|
||||
void Initialize(FactoryWorldInterface* worldInterface);
|
||||
void Save();
|
||||
|
||||
public:
|
||||
int32_t GetSeed() const { return Seed; }
|
||||
|
||||
FactoryError CanPlaceEntity(int x, int y, Ref<Archetype> archetype);
|
||||
FactoryError AddEntity(int x, int y, Ref<Archetype> archetype);
|
||||
void RemoveEntity(FactoryEntity* node);
|
||||
void RemoveEntity(int x, int y);
|
||||
void RemoveEntity(entt::entity entity);
|
||||
|
||||
// FactoryWorldAccess GetAccess() { return FactoryWorldAccess{ *this, WorldAccessMutex }; }
|
||||
|
||||
entt::registry& GetRegistry() { return Registry; };
|
||||
const entt::registry& GetRegistry() const { return Registry; };
|
||||
auto& GetChunks() { return Chunks; }
|
||||
const auto& GetChunks() const { return Chunks; }
|
||||
auto& GetSettings() const { return WorldSettings; }
|
||||
|
||||
bool IsValidCameraPos(Rect2i viewport) const;
|
||||
|
||||
public: // Chunks
|
||||
FactoryError TryUnlockChunk(ChunkKey chunk);
|
||||
|
||||
private:
|
||||
void RefreshUnlockedChunks();
|
||||
|
||||
private:
|
||||
entt::entity CreateEntity();
|
||||
FactoryError AddEntity(int x, int y, Ref<Archetype> archetype, entt::entity entityID);
|
||||
void InvalidateCachedChunk();
|
||||
|
||||
public: // UPGRADING
|
||||
FactoryError TryUpgradeEntity(FactoryEntity* entity);
|
||||
FactoryError TryUpgradeEntity(entt::entity entity);
|
||||
FactoryError TryUpgradeEntity(const Vector2i& position);
|
||||
|
||||
FactoryError CanUpgradeEntity(entt::entity entity) const;
|
||||
FactoryError CanUpgradeEntity(FactoryEntity* entity) const;
|
||||
FactoryError CanUpgradeEntity(const Vector2i& position);
|
||||
|
||||
private:
|
||||
void UpgradeEntity(entt::entity entity);
|
||||
void UpgradeEntity(entt::entity entity, Ref<Archetype> archetype);
|
||||
void SetEntityLevel(entt::entity entity, uint8_t level);
|
||||
void SetEntityLevel(entt::entity entity, Ref<Archetype> archetype, uint8_t level);
|
||||
|
||||
public: // QUERY
|
||||
void HighlightUpgradableEntities(TileMapLayer* tilemap) const;
|
||||
FactoryError FindChutePath(Vector<Vector2i>& path, Vector2i startPos, Vector2i endPos) const;
|
||||
Tile const* Raycast(Vector2i startPos, Vector2i endPos) const;
|
||||
bool IsSupport(int x, int y) const;
|
||||
bool IsSupport(entt::entity entity) const;
|
||||
|
||||
public: // INVENTORY
|
||||
void SetInventory(const Vector<Ref<ItemConfig>>& items);
|
||||
Inventory& GetInventory() { return WorldInventory; }
|
||||
const Inventory& GetInventory() const { return WorldInventory; }
|
||||
Inventory GetWorldInventory(Vector2 position) const;
|
||||
Inventory GetWorldInventory(entt::entity entity) const;
|
||||
|
||||
public: // DATA
|
||||
const Recipe& GetRecipe(Ref<RecipeConfig> config) { return WorldInstanceData.GetRecipe(config); }
|
||||
|
||||
public: // SUPPORT
|
||||
// FactoryError CanPlaceSupport(int x, int y) const;
|
||||
// FactoryError CanRemoveSupport(int x, int y) const;
|
||||
// void RegisterSupport(int x, int y, Support& support);
|
||||
// void RemoveSupport(int x, int y);
|
||||
|
||||
private:
|
||||
bool SupportCheckerHelper(entt::entity entity) const;
|
||||
uint8_t SupportValueHelper(entt::entity entity) const;
|
||||
uint8_t GetSupportValue(int x, int y) const;
|
||||
bool CheckIfSupportHelper(entt::entity entity, Support& support) const;
|
||||
|
||||
void ConnectSupports(Vector2i pos, Support& support, Vector2i direction);
|
||||
|
||||
private:
|
||||
Mutex WorldAccessMutex;
|
||||
// std::shared_ptr<FactoryCommandQueue> CommandQueue = std::make_shared<FactoryCommandQueue>();
|
||||
// FactoryWorldInterface* Interface;
|
||||
|
||||
ChunkCollection Chunks{};
|
||||
|
||||
entt::registry Registry{};
|
||||
|
||||
Ref<FactoryWorldSettings> WorldSettings{};
|
||||
|
||||
int32_t Seed{};
|
||||
int32_t LastDrawnFrame{};
|
||||
|
||||
Inventory64 WorldInventory;
|
||||
WorldData WorldInstanceData;
|
||||
Vector<ChunkKey> UnlockedChunks{ ChunkKey{} };
|
||||
Vector<ChunkUnlock> UnlockableChunks{};
|
||||
};
|
||||
95
include/Core/WorldGenerator.h
Normal file
95
include/Core/WorldGenerator.h
Normal file
@@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
|
||||
#include "Data/WorldSettings.h"
|
||||
#include "Chunk.h"
|
||||
|
||||
class FactoryWorld;
|
||||
|
||||
// class WorldGenerator final
|
||||
// {
|
||||
// public:
|
||||
|
||||
|
||||
// public:
|
||||
// WorldGenerator() = default;
|
||||
// WorldGenerator(Ref<FactoryWorldSettings> settings, int32_t seed);
|
||||
|
||||
// public:
|
||||
// // bool GenerateChunk(ChunkKey chunkKey, Chunk& chunk) const;
|
||||
// // bool GenerateChunk(ChunkKey chunkKey, Chunk& chunk, Ref<LayerConfig> layer, Ref<LayerConfig> nextLayer = {}) const;
|
||||
|
||||
// // Vector<SpawnedEntities> SpawnEntities(ChunkKey chunkKey, Chunk& chunk, const std::vector<EntityTile>& persistantEntities = {}) const;
|
||||
// // Vector<SpawnedEntities> SpawnEntities(ChunkKey chunkKey, Chunk& chunk, Ref<LayerConfig> layer, Ref<LayerConfig> nextLayer = {}, const std::vector<EntityTile>& persistantEntities = {}) const;
|
||||
|
||||
// // std::unique_ptr<CreatedVisualsChunk> CreateChunkVisuals(ChunkKey chunkKey, Chunk& chunk);
|
||||
|
||||
// public:
|
||||
// void ThreadedGenerateChunk(ChunkKey chunkKey, std::function<void(ChunkData&&)> callback, const std::vector<EntityTile>& persistantEntities = {});
|
||||
|
||||
// private:
|
||||
|
||||
// public:
|
||||
// Ref<FactoryWorldSettings> Settings{};
|
||||
// WorldGraph Graph{};
|
||||
// int32_t Seed{};
|
||||
// };
|
||||
|
||||
class ChunkGenerator final
|
||||
{
|
||||
public:
|
||||
struct SpawnedEntities
|
||||
{
|
||||
Ref<Archetype> Archetype{};
|
||||
Vector2i SpawnPosition{};
|
||||
Vector<Vector2i> ClaimedPositions{};
|
||||
};
|
||||
|
||||
struct CreatedVisualsTile
|
||||
{
|
||||
CreatedVisualsTile() = default;
|
||||
CreatedVisualsTile(uint16_t atlasCoordinateX, uint16_t atlasCoordinateY, uint16_t atlasIndex) : AtlasCoordinateX{ atlasCoordinateX }, AtlasCoordinateY{ atlasCoordinateY }, AtlasIndex{ atlasIndex } {};
|
||||
|
||||
uint16_t AtlasCoordinateX{};
|
||||
uint16_t AtlasCoordinateY{};
|
||||
uint16_t AtlasIndex{};
|
||||
};
|
||||
|
||||
|
||||
typedef std::array<CreatedVisualsTile, Chunk::TotalChunkTiles> CreatedVisualsChunk;
|
||||
typedef std::array<int8_t, Chunk::TotalChunkTiles> ChunkShadowValues;
|
||||
|
||||
typedef std::function<void(std::unique_ptr<Chunk>&&)> CreatedChunkCallback;
|
||||
typedef std::function<void(const Vector<SpawnedEntities>&)> SpawnedEntitiesCallback;
|
||||
typedef std::function<void(CreatedVisualsChunk*)> VisualizedChunkCallback;
|
||||
typedef std::function<void(ChunkShadowValues*)> ShadowsCallback;
|
||||
|
||||
ChunkGenerator() = default;
|
||||
private:
|
||||
ChunkGenerator(Ref<FactoryWorldSettings> settings, ChunkKey chunk, int32_t seed) : Settings{ settings }, ChunkInfo{ chunk }, Seed{ seed } {};
|
||||
|
||||
public:
|
||||
static void GenerateChunk(Ref<FactoryWorldSettings> settings, ChunkKey chunkInfo, int seed, CreatedChunkCallback chunkCallback, SpawnedEntitiesCallback entitiesCallback = {}, VisualizedChunkCallback visualsCallback = {}, ShadowsCallback shadowsCallback = {});
|
||||
std::unique_ptr<Chunk> GenerateChunk();
|
||||
|
||||
private:
|
||||
static void GenerateChunk(void* pData);
|
||||
void GenerateChunkInternal(CreatedChunkCallback chunkCallback, SpawnedEntitiesCallback entitiesCallback, VisualizedChunkCallback visualsCallback, ShadowsCallback shadowsCallback);
|
||||
|
||||
void GenerateChunkTiles() const;
|
||||
Vector<SpawnedEntities> SpawnEntities(const std::vector<EntityTile>& persistantEntities = {}) const;
|
||||
std::unique_ptr<CreatedVisualsChunk> CreateVisuals();
|
||||
std::unique_ptr<ChunkShadowValues> CascadeShadows();
|
||||
|
||||
Pair<Ref<LayerConfig>, Ref<LayerConfig>> GetLayers() const;
|
||||
void FillChunkCollection(int relativeX, int relativeY, ChunkCollection& collection) const;
|
||||
void CascadeShadows_Recursive(std::array<int8_t, WorldNodeParameters::PaddedChunkSize>& values, int posX, int posY, int value);
|
||||
|
||||
Tile GetTile(int x, int y) const;
|
||||
bool InBounds(int x, int y) const;
|
||||
|
||||
private:
|
||||
Ref<FactoryWorldSettings> Settings{};
|
||||
ChunkKey ChunkInfo{};
|
||||
int32_t Seed{};
|
||||
std::unique_ptr<WorldNodeParameters::TileArray> TileArray{};
|
||||
};
|
||||
20
include/Core/WorldInstance.h
Normal file
20
include/Core/WorldInstance.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "flecs.h"
|
||||
|
||||
#include "Components/Configs/WorldConfig.hpp"
|
||||
|
||||
|
||||
class WorldInstance
|
||||
{
|
||||
public:
|
||||
WorldInstance() = default;
|
||||
WorldInstance(const WorldConfig& worldConfig);
|
||||
|
||||
private:
|
||||
static void RegisterTypes(flecs::world& world);
|
||||
|
||||
|
||||
private:
|
||||
flecs::world EcsWorld{};
|
||||
};
|
||||
155
include/Types/Archetype.h
Normal file
155
include/Types/Archetype.h
Normal file
@@ -0,0 +1,155 @@
|
||||
// #pragma once
|
||||
|
||||
// #include "core/io/resource.h"
|
||||
// #include "scene/resources/packed_scene.h"
|
||||
// #include "scene/resources/texture.h"
|
||||
// #include "UpgradeLevel.h"
|
||||
// #include "modules/factory/include/Data/Tile.h"
|
||||
// #include "Core/Chunk.h"
|
||||
|
||||
|
||||
// class SpawnDescription : public Resource
|
||||
// {
|
||||
// GDCLASS(SpawnDescription, Resource);
|
||||
// public:
|
||||
// static void _bind_methods();
|
||||
|
||||
// public:
|
||||
// virtual bool IsValid(const ChunkCollection& chunk, Vector2i pos) = 0;
|
||||
// virtual void ClaimTiles(Vector<Vector2i>& tiles, const ChunkCollection& chunk, Vector2i pos) {};
|
||||
|
||||
// public:
|
||||
// int GetFilter() const { return Filter; }
|
||||
// auto GetTile() const { return Tile; }
|
||||
|
||||
// void SetFilter(int filter) { Filter = static_cast<TILE_TYPE>(filter); }
|
||||
// void SetTile(Ref<TileConfig> tile) { Tile = tile; }
|
||||
|
||||
// protected:
|
||||
// bool FilterTile(Tile tile) { return Tile.is_valid() ? (Tile->TileData == tile) : (tile.GetType() == Filter); }
|
||||
// bool TryFilterTile(const ChunkCollection& chunks, int x, int y);
|
||||
// bool TryFilterTile(const ChunkCollection& chunks, Vector2i pos);
|
||||
|
||||
// protected:
|
||||
// TILE_TYPE Filter{ TILE_NONE };
|
||||
// Ref<TileConfig> Tile{};
|
||||
// };
|
||||
|
||||
// class SpawnNearby : public SpawnDescription
|
||||
// {
|
||||
// GDCLASS(SpawnNearby, SpawnDescription);
|
||||
// public:
|
||||
// static void _bind_methods();
|
||||
|
||||
// protected:
|
||||
// virtual bool IsValid(const ChunkCollection& chunk, Vector2i pos) override;
|
||||
|
||||
// public:
|
||||
// int GetRange() const { return Range; }
|
||||
|
||||
// void SetRange(int range) { Range = static_cast<int8_t>(range); }
|
||||
|
||||
// private:
|
||||
// int8_t Range{};
|
||||
// };
|
||||
|
||||
// class OccupiedTiles : public SpawnDescription
|
||||
// {
|
||||
// GDCLASS(OccupiedTiles, SpawnDescription);
|
||||
// public:
|
||||
// static void _bind_methods();
|
||||
|
||||
// protected:
|
||||
// virtual bool IsValid(const ChunkCollection& chunk, Vector2i pos) override;
|
||||
// virtual void ClaimTiles(Vector<Vector2i>& tiles, const ChunkCollection& chunk, Vector2i pos) override;
|
||||
|
||||
// public:
|
||||
// TypedArray<Vector2i> GetOffset() const { return VectorToTypedArrayVariant(Offsets); }
|
||||
|
||||
// void SetOffset(TypedArray<Vector2i> offsets) { Offsets = TypedArrayToVectorVariant(offsets); }
|
||||
|
||||
// public:
|
||||
// Vector<Vector2i> Offsets;
|
||||
// };
|
||||
|
||||
// class RequiredTiles : public SpawnDescription
|
||||
// {
|
||||
// GDCLASS(RequiredTiles, SpawnDescription);
|
||||
// public:
|
||||
// static void _bind_methods();
|
||||
|
||||
// protected:
|
||||
// virtual bool IsValid(const ChunkCollection& chunk, Vector2i pos) override;
|
||||
|
||||
// public:
|
||||
// TypedArray<Vector2i> GetOffset() const { return VectorToTypedArrayVariant(Offsets); }
|
||||
|
||||
// void SetOffset(TypedArray<Vector2i> offsets) { Offsets = TypedArrayToVectorVariant(offsets); }
|
||||
|
||||
// public:
|
||||
// Vector<Vector2i> Offsets;
|
||||
// };
|
||||
|
||||
// class LinkedTiles : public SpawnDescription
|
||||
// {
|
||||
// GDCLASS(LinkedTiles, SpawnDescription);
|
||||
// public:
|
||||
// static void _bind_methods();
|
||||
|
||||
// protected:
|
||||
// virtual bool IsValid(const ChunkCollection& chunk, Vector2i pos) override;
|
||||
// virtual void ClaimTiles(Vector<Vector2i>& tiles, const ChunkCollection& chunk, Vector2i pos) override;
|
||||
|
||||
// public:
|
||||
// void ClaimTiles_Recursive(Vector<Vector2i>& tiles, const ChunkCollection& chunk, Vector2i pos);
|
||||
|
||||
// };
|
||||
|
||||
// class Archetype : public Resource
|
||||
// {
|
||||
// GDCLASS(Archetype, Resource);
|
||||
|
||||
// public:
|
||||
// static void _bind_methods();
|
||||
|
||||
// public:
|
||||
// Ref<PackedScene> GetScene() const { return Scene; }
|
||||
// Ref<PackedScene> GetPreviewScene() const { return PreviewScene; }
|
||||
// Ref<Texture2D> GetPreviewTexture() const { return PreviewTexture; }
|
||||
// TypedArray<SpawnDescription> GetOccupiedTiles() const { return VectorToTypedArray(SpawnConditions); }
|
||||
// TypedArray<UpgradeLevelConfig> GetUpgrades() const { return VectorToTypedArray(Upgrades); }
|
||||
// int GetID() const { return ID; }
|
||||
|
||||
// void SetScene(Ref<PackedScene> scene) { Scene = scene; }
|
||||
// void SetPreviewScene(Ref<PackedScene> scene) { PreviewScene = scene; }
|
||||
// void SetPreviewTetxture(Ref<Texture2D> preview) { PreviewTexture = preview; }
|
||||
// void SetOccupiedTiles(TypedArray<SpawnDescription> tiles) { SpawnConditions = TypedArrayToVector(tiles); }
|
||||
// void SetUpgrades(TypedArray<UpgradeLevelConfig> upgrades) { Upgrades = TypedArrayToVector(upgrades); }
|
||||
// void SetID(int id) { ID = id; }
|
||||
|
||||
// public:
|
||||
// Ref<PackedScene> Scene{};
|
||||
// Ref<PackedScene> PreviewScene{};
|
||||
// Ref<Texture2D> PreviewTexture{};
|
||||
// Vector<Ref<UpgradeLevelConfig>> Upgrades{};
|
||||
// Vector<Ref<SpawnDescription>> SpawnConditions{};
|
||||
// int ID{ -1 };
|
||||
// };
|
||||
|
||||
// class PlaceableArchetype : public Resource
|
||||
// {
|
||||
// GDCLASS(PlaceableArchetype, Resource);
|
||||
// public:
|
||||
// static void _bind_methods();
|
||||
|
||||
// public:
|
||||
// auto GetArchetype() const { return Archetype; }
|
||||
// auto GetPlaceCosts() const { return VectorToTypedArray(PlaceCosts); }
|
||||
|
||||
// void SetArchetype(Ref<Archetype> archetype) { Archetype = archetype; }
|
||||
// void SetPlaceCosts(TypedArray<ItemAmountConfig> costs) { PlaceCosts = TypedArrayToVector(costs); }
|
||||
|
||||
// public:
|
||||
// Ref<Archetype> Archetype{};
|
||||
// Vector<Ref<ItemAmountConfig>> PlaceCosts{};
|
||||
// };
|
||||
20
include/Types/Filter.h
Normal file
20
include/Types/Filter.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "Item.hpp"
|
||||
|
||||
struct alignas(4) ItemFilter final
|
||||
{
|
||||
uint16_t FilterItem0;
|
||||
uint16_t FilterItem1;
|
||||
uint16_t FilterItem2;
|
||||
//Item::ItemFlags FilterFlags;
|
||||
|
||||
public:
|
||||
inline bool ApplyFilter(Item item)
|
||||
{
|
||||
return //(item.Flags & FilterFlags) &&
|
||||
(FilterItem0 == 0 || FilterItem0 == item.ItemID) &&
|
||||
(FilterItem1 == 0 || FilterItem1 == item.ItemID) &&
|
||||
(FilterItem2 == 0 || FilterItem2 == item.ItemID);
|
||||
}
|
||||
};
|
||||
52
include/Types/Item.hpp
Normal file
52
include/Types/Item.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <limits>
|
||||
|
||||
#include "flecs.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
struct Item final
|
||||
{
|
||||
public:
|
||||
static constexpr UnderlyingItemT null = std::numeric_limits<UnderlyingItemT>::max();
|
||||
|
||||
public:
|
||||
UnderlyingItemT ItemID{ null };
|
||||
|
||||
public:
|
||||
Item() = default;
|
||||
Item(int id)
|
||||
{
|
||||
DEV_ASSERT(id != null);
|
||||
ItemID = static_cast<UnderlyingItemT>(id);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename IntegralType>
|
||||
struct ItemAmountT final
|
||||
{
|
||||
public:
|
||||
ItemAmountT(Item item, IntegralType amount) : ItemId{ item }, Amount{ amount } {}
|
||||
ItemAmountT() = default;
|
||||
public:
|
||||
Item ItemId;
|
||||
IntegralType Amount;
|
||||
};
|
||||
|
||||
typedef ItemAmountT<uint16_t> ItemAmount16;
|
||||
typedef ItemAmountT<uint32_t> ItemAmount32;
|
||||
typedef ItemAmountT<uint64_t> ItemAmount64;
|
||||
typedef ItemAmount16 ItemAmount;
|
||||
|
||||
inline void Flecs_Item(flecs::world& world)
|
||||
{
|
||||
world.component<Item>()
|
||||
.member<uint16_t>("ItemID");
|
||||
|
||||
world.component<ItemAmount32>()
|
||||
.member<Item>("ItemRef")
|
||||
.member<uint32_t>("Amount");
|
||||
}
|
||||
152
include/Types/LayerConfigs.h
Normal file
152
include/Types/LayerConfigs.h
Normal file
@@ -0,0 +1,152 @@
|
||||
#pragma once
|
||||
|
||||
// #include "Tile.h"
|
||||
// #include "core/object/ref_counted.h"
|
||||
// #include "modules/noise/fastnoise_lite.h"
|
||||
// #include "core/io/resource.h"
|
||||
// #include "modules/factory/include/Data/Archetype.h"
|
||||
// #include "modules/factory/include/Util/Helpers.h"
|
||||
// #include "modules/factory/include/Data/WorldGraph/WorldGraphVisualNode.h"
|
||||
// #include "scene/resources/image_texture.h"
|
||||
|
||||
// struct LayerTileConfig final : public Resource
|
||||
// {
|
||||
// GDCLASS(LayerTileConfig, Resource);
|
||||
|
||||
// public:
|
||||
// static void _bind_methods();
|
||||
|
||||
// public:
|
||||
// Ref<WorldGraphVisualNodeBase> GetTileGenerator() const { return TileGenerator; }
|
||||
// Ref<TileConfig> GetTile() const { return Tile; }
|
||||
// Color GetPreviewColor() const { return PreviewColor; }
|
||||
|
||||
// void SetTileGenerator(Ref<WorldGraphVisualNodeBase> gen) { TileGenerator = gen; }
|
||||
// void SetTile(Ref<TileConfig> tile) { Tile = tile; }
|
||||
// void SetPreviewColor(Color color) { PreviewColor = color; }
|
||||
|
||||
// public:
|
||||
// Ref<WorldGraphVisualNodeBase> TileGenerator{};
|
||||
// Ref<TileConfig> Tile{};
|
||||
// Color PreviewColor{};
|
||||
// };
|
||||
|
||||
// struct LayerConfig final : public Resource
|
||||
// {
|
||||
// GDCLASS(LayerConfig, Resource);
|
||||
|
||||
// public:
|
||||
// static void _bind_methods();
|
||||
|
||||
// public:
|
||||
// int GetStartChunk() const { return StartChunk; }
|
||||
// auto GetTiles() const { return VectorToTypedArray(Tiles); }
|
||||
// auto GetRegisteredNodes() const { return VectorToTypedArray(AllNodes); }
|
||||
// auto GetArchetypes() const { return VectorToTypedArray(SpawningArchetypes); }
|
||||
|
||||
// void SetStartChunk(int height) { StartChunk = height; }
|
||||
// void SetTiles(TypedArray<LayerTileConfig> tiles);
|
||||
// void SetRegisteredNodes(TypedArray<WorldGraphVisualNodeBase> allNodes) { AllNodes = TypedArrayToVector(allNodes); }
|
||||
// void SetArchetypes(TypedArray<Archetype> archetypes) { SpawningArchetypes = TypedArrayToVector(archetypes); }
|
||||
|
||||
// void RegisterVisualNode(Ref<WorldGraphVisualNodeBase> node) { if (!HasVisualNode(node)) AllNodes.push_back(node); }
|
||||
// void RemoveVisualNode(Ref<WorldGraphVisualNodeBase> node) { AllNodes.erase(node);}
|
||||
// bool HasVisualNode(Ref<WorldGraphVisualNodeBase> node) const { return AllNodes.has(node); }
|
||||
|
||||
// bool IsValid() const;
|
||||
// bool CanConnect(Ref<WorldGraphVisualNodeBase> from, Ref<WorldGraphVisualNodeBase> to) const;
|
||||
|
||||
// Ref<ImageTexture> CreateTexture(Vector2i chunk, int seed) const;
|
||||
|
||||
// public:
|
||||
// int StartChunk{};
|
||||
// Vector<Ref<LayerTileConfig>> Tiles{};
|
||||
// Vector<Ref<WorldGraphVisualNodeBase>> AllNodes{};
|
||||
// Vector<Ref<Archetype>> SpawningArchetypes{};
|
||||
// Vector<Ref<ItemConfig>> UnlockedItems{};
|
||||
// Vector<Ref<Archetype>> UnlockedBuildings{};
|
||||
// Vector<Ref<RecipeConfig>> UnlockedRecipes{};
|
||||
// };
|
||||
|
||||
// struct LayerConfig_Filler final : public Resource
|
||||
// {
|
||||
// GDCLASS(LayerConfig_Filler, Resource);
|
||||
// public:
|
||||
// static void _bind_methods();
|
||||
|
||||
// public:
|
||||
// float GetPassagewayWidthRatio() const { return PassagewayWidthRatio; }
|
||||
// Ref<FastNoiseLite> GetNoise() const { return NoiseGenerator; }
|
||||
// Ref<TileConfig> GetTile() const { return Tile; }
|
||||
|
||||
// void SetPassagewayWidthRatio(float ratio) { PassagewayWidthRatio = ratio; }
|
||||
// void SetNoise(Ref<FastNoiseLite> noise) { NoiseGenerator = noise; }
|
||||
// void SetTile(Ref<TileConfig> tile) { Tile = tile; }
|
||||
|
||||
// public:
|
||||
// float PassagewayWidthRatio = 0.05f;
|
||||
// Ref<FastNoiseLite> NoiseGenerator{};
|
||||
// Ref<TileConfig> Tile{};
|
||||
// };
|
||||
|
||||
// struct LayerConfig_Ore final : public Resource
|
||||
// {
|
||||
// GDCLASS(LayerConfig_Ore, Resource);
|
||||
// public:
|
||||
// static void _bind_methods();
|
||||
|
||||
// public:
|
||||
// float GetRatio() const { return Ratio; }
|
||||
// Ref<Archetype> GetOre() { return OreArchetype; }
|
||||
// Ref<FastNoiseLite> GetNoise() const { return NoiseGenerator; }
|
||||
|
||||
// void SetRatio(float ratio) { Ratio = ratio; }
|
||||
// void SetOre(Ref<Archetype> archetype) { OreArchetype = archetype; }
|
||||
// void SetNoise(Ref<FastNoiseLite> noise) { NoiseGenerator = noise; }
|
||||
|
||||
// public:
|
||||
// float Ratio = 0.05f;
|
||||
// Ref<Archetype> OreArchetype;
|
||||
// Ref<FastNoiseLite> NoiseGenerator{};
|
||||
// };
|
||||
|
||||
// struct LayerConfig_Plant final : public Resource
|
||||
// {
|
||||
// GDCLASS(LayerConfig_Plant, Resource);
|
||||
// public:
|
||||
// static void _bind_methods();
|
||||
|
||||
// public:
|
||||
// auto GetGrowTile() const { return GrowTileID; }
|
||||
// float GetGrowChance() const { return GrowChance; }
|
||||
// Ref<Archetype> GetPlantArchetype() const { return PlantArchetype; }
|
||||
|
||||
// void SetGrowTile(const Ref<TileConfig>& growTile) { GrowTileID = growTile; }
|
||||
// void SetGrowChance(float chance) { GrowChance = chance; }
|
||||
// void SetPlantArchetype(const Ref<Archetype>& archetype) { PlantArchetype = archetype; }
|
||||
|
||||
// public:
|
||||
// Ref<TileConfig> GrowTileID{}; // Tile it can grow on
|
||||
// float GrowChance{}; // Value from 0 to 1
|
||||
// Ref<Archetype> PlantArchetype{};
|
||||
// };
|
||||
|
||||
// struct LayerConfig_Liquid final : public Resource
|
||||
// {
|
||||
// GDCLASS(LayerConfig_Liquid, Resource);
|
||||
// public:
|
||||
// static void _bind_methods() {};
|
||||
// public:
|
||||
// };
|
||||
|
||||
// struct LayerConfig_NPC final : public Resource
|
||||
// {
|
||||
// GDCLASS(LayerConfig_NPC, Resource);
|
||||
// public:
|
||||
// static void _bind_methods() {};
|
||||
// };
|
||||
|
||||
// template <typename T>
|
||||
// struct CompareLayerHeights {
|
||||
// bool operator()(const Ref<T> p_a, const Ref<T> p_b) const { return p_b->MaxHeight < p_a->MaxHeight; }
|
||||
// };
|
||||
46
include/Types/Recipe.h
Normal file
46
include/Types/Recipe.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "Item.hpp"
|
||||
#include "Util/SharedBuffer.h"
|
||||
#include "Util/Span.h"
|
||||
|
||||
struct RecipeConfig;
|
||||
|
||||
struct Recipe final
|
||||
{
|
||||
struct RecipeMeta final
|
||||
{
|
||||
uint8_t IngredientsAmount;
|
||||
uint8_t ResultsAmount;
|
||||
uint16_t ProcessingTime;
|
||||
uint32_t RecipeID{};
|
||||
};
|
||||
|
||||
public:
|
||||
Recipe() = default;
|
||||
Recipe(const RecipeConfig& recipe);
|
||||
|
||||
public:
|
||||
tcb::span<const ItemAmount> GetIngredients() const { assert(Data); return tcb::span<const ItemAmount>(Data.Ptr(), Data.GetMetaData()->IngredientsAmount); };
|
||||
tcb::span<const ItemAmount> GetResults() const { assert(Data); return tcb::span<const ItemAmount>(Data.Ptr() + Data.GetMetaData()->IngredientsAmount, Data.GetMetaData()->ResultsAmount); };
|
||||
uint32_t GetRecipeID() const { return Data.GetMetaData()->RecipeID; }
|
||||
uint16_t GetProcessingTime() const { return Data.GetMetaData()->ProcessingTime; }
|
||||
|
||||
public:
|
||||
operator bool() const { return Data; }
|
||||
|
||||
public:
|
||||
SharedBuffer<ItemAmount, RecipeMeta> Data;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
176
include/Types/Tile.h
Normal file
176
include/Types/Tile.h
Normal file
@@ -0,0 +1,176 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/string/string_name.h"
|
||||
#include "core/io/resource.h"
|
||||
#include "scene/resources/texture.h"
|
||||
#include "modules/factory/thirdParty/EnTT/entity/entity.hpp"
|
||||
#include "modules/factory/include/Util/Helpers.h"
|
||||
|
||||
// If tile data uses all 4 bytes, it is an entity,
|
||||
// if it uses only 2 bytes it is a tile
|
||||
// if it is 0, it is air
|
||||
// struct Tile final
|
||||
// {
|
||||
// private:
|
||||
// union TileData
|
||||
// {
|
||||
// uint32_t Data;
|
||||
// entt::entity Entity;
|
||||
// uint16_t TileID;
|
||||
// };
|
||||
// static_assert(sizeof(TileData) == sizeof(uint32_t));
|
||||
// TileData Data{};
|
||||
|
||||
// static constexpr uint32_t TileMask = 0x80000000;
|
||||
|
||||
// public:
|
||||
// Tile() = default;
|
||||
// Tile(uint16_t id) { Data.Data = id | TileMask; }
|
||||
// Tile(entt::entity entity) { DEV_ASSERT(IsValidEntity(entity)); Data.Entity = entity; }
|
||||
|
||||
// public:
|
||||
// constexpr bool IsAir() const { return Data.Data == 0; }
|
||||
// constexpr bool IsTile() const { return (Data.Data & TileMask) != 0; }
|
||||
// constexpr bool IsEntity() const { return !IsAir() && !IsTile(); }
|
||||
// static constexpr bool IsValidEntity(entt::entity entity) { return (((uint32_t)entity) & TileMask) == 0; }
|
||||
|
||||
// public:
|
||||
// entt::entity GetEntity() const { DEV_ASSERT(IsEntity()); return Data.Entity; }
|
||||
// uint16_t GetTileID() const { DEV_ASSERT(IsTile()); return Data.TileID; }
|
||||
// };
|
||||
|
||||
enum TILE_TYPE : uint8_t
|
||||
{
|
||||
TILE_AIR,
|
||||
TILE_FILLER,
|
||||
TILE_LIQUID,
|
||||
TILE_ORE,
|
||||
TILE_NPC,
|
||||
TILE_PLANT,
|
||||
TILE_MAX,
|
||||
|
||||
TILE_NONE = 0b111,
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(TILE_TYPE);
|
||||
static_assert(TILE_MAX <= TILE_NONE);
|
||||
const char* TileTypeEnumString = "Air,Filler,Liquid,Ore,Npc,Plant,,None";
|
||||
|
||||
struct Tile final
|
||||
{
|
||||
typedef uint16_t TileDataType;
|
||||
static constexpr int BytesID = 12;
|
||||
static constexpr int BytesType = 3;
|
||||
static constexpr int BytesEntity = 1;
|
||||
static constexpr TileDataType MaskID = 0b0000111111111111;
|
||||
static constexpr TileDataType MaskType = 0b0111000000000000;
|
||||
static constexpr TileDataType MaskEntity = 0b1000000000000000;
|
||||
static constexpr TileDataType InvalidID = MaskID;
|
||||
|
||||
public:
|
||||
Tile() = default;
|
||||
explicit Tile(TileDataType id)
|
||||
{
|
||||
Data = id;
|
||||
}
|
||||
|
||||
public:
|
||||
void FlagAsEntity() { Data |= MaskEntity; }
|
||||
void RemoveEntityFlag() { Data &= ~MaskEntity; }
|
||||
constexpr bool HasEntity() const { return Data & MaskEntity; }
|
||||
|
||||
constexpr uint16_t GetID() const { return Data & MaskID; }
|
||||
constexpr bool IsAir() const { return GetType() == TILE_AIR; }
|
||||
constexpr bool IsFiller() const { return GetType() == TILE_FILLER; }
|
||||
constexpr bool IsLiquid() const { return GetType() == TILE_LIQUID; }
|
||||
constexpr bool IsOre() const { return GetType() == TILE_ORE; }
|
||||
constexpr bool IsPlant() const { return GetType() == TILE_PLANT; }
|
||||
constexpr bool IsNPC() const { return GetType() == TILE_NPC; }
|
||||
|
||||
constexpr TILE_TYPE GetType() const { return static_cast<TILE_TYPE>((Data & MaskType) >> BytesID); }
|
||||
constexpr bool IsType(TILE_TYPE type) const { return GetType() == type; }
|
||||
|
||||
void SetID(uint16_t id) { Data = (Data & ~MaskID) | (id & MaskID); }
|
||||
void SetAir() { SetType(TILE_AIR); }
|
||||
void SetFiller() { SetType(TILE_FILLER); }
|
||||
void SetLiquid() { SetType(TILE_LIQUID); }
|
||||
void SetOre() { SetType(TILE_ORE); }
|
||||
void SetPlant() { SetType(TILE_PLANT); }
|
||||
void SetNPC() { SetType(TILE_NPC); }
|
||||
|
||||
void SetType(TILE_TYPE type) { Data = (Data & ~MaskType) | (type << BytesID); }
|
||||
|
||||
constexpr uint16_t AsInt() const { return Data; }
|
||||
constexpr bool IsValid() const { return Data == MaskID; }
|
||||
|
||||
private:
|
||||
TileDataType Data{ MaskID };
|
||||
};
|
||||
|
||||
inline constexpr bool operator==(Tile lhs, Tile rhs) { return lhs.AsInt() == rhs.AsInt(); }
|
||||
inline constexpr bool operator!=(Tile lhs, Tile rhs) { return lhs.AsInt() != rhs.AsInt(); }
|
||||
|
||||
static_assert(sizeof(Tile) == 2);
|
||||
|
||||
class TextureWeight final : public Resource
|
||||
{
|
||||
GDCLASS(TextureWeight, Resource);
|
||||
static void _bind_methods();
|
||||
public:
|
||||
Ref<Texture2D> GetTexture() const { return Texture; }
|
||||
uint16_t GetWeight() const { return Weight; }
|
||||
|
||||
void SetTexture(Ref<Texture2D> texture) { Texture = texture; }
|
||||
void SetWeight(uint16_t weight) { Weight = weight; }
|
||||
|
||||
public:
|
||||
Ref<Texture2D> Texture{};
|
||||
uint16_t Weight{1};
|
||||
uint16_t AtlasIndex{};
|
||||
uint16_t AtlasX{};
|
||||
uint16_t AtlasY{};
|
||||
};
|
||||
|
||||
class TileConfig;
|
||||
|
||||
class TileTransitionConfig final : public Resource
|
||||
{
|
||||
GDCLASS(TileTransitionConfig, Resource);
|
||||
static void _bind_methods();
|
||||
public:
|
||||
auto GetNeighbor() const { return Neighbor; }
|
||||
auto GetPossibleTextures() const { return VectorToTypedArray(PossibleTextures); }
|
||||
|
||||
void SetNeighbor(Ref<TileConfig> neighbor) { Neighbor = neighbor; }
|
||||
void SetPossibleTextures(TypedArray<TextureWeight> textures) { PossibleTextures = TypedArrayToVector(textures); }
|
||||
|
||||
public:
|
||||
Ref<TileConfig> Neighbor;
|
||||
Vector<Ref<TextureWeight>> PossibleTextures;
|
||||
};
|
||||
|
||||
class TileConfig final : public Resource
|
||||
{
|
||||
GDCLASS(TileConfig, Resource);
|
||||
public:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
uint16_t GetID() const { return TileData.GetID(); }
|
||||
TILE_TYPE GetType() const { return TileData.GetType(); }
|
||||
auto GetTextures() const { return VectorToTypedArray(PossibleTextures); }
|
||||
auto GetTransitions() const { return VectorToTypedArray(NeighborTransitions); }
|
||||
auto GetLightResistance() const { return VectorToTypedArray(NeighborTransitions); }
|
||||
|
||||
void SetID(uint16_t id) { TileData.SetID(id); }
|
||||
void SetTextures(TypedArray<TextureWeight> val) { PossibleTextures = TypedArrayToVector(val); }
|
||||
void SetTransitions(TypedArray<TileTransitionConfig> val) { NeighborTransitions = TypedArrayToVector(val); }
|
||||
void SetType(TILE_TYPE type) { TileData.SetType(type); }
|
||||
void SetLightResistance(int resistance) { LightResistance = resistance; }
|
||||
|
||||
public:
|
||||
Tile TileData{};
|
||||
// Vector<Ref<TextureWeight>> PossibleTextures;
|
||||
// Vector<Ref<TileTransitionConfig>> NeighborTransitions;
|
||||
int LightResistance{};
|
||||
};
|
||||
25
include/Types/UpgradeLevel.h
Normal file
25
include/Types/UpgradeLevel.h
Normal file
@@ -0,0 +1,25 @@
|
||||
// #pragma once
|
||||
|
||||
// #include "core/io/resource.h"
|
||||
// #include "core/templates/vector.h"
|
||||
// #include "core/variant/dictionary.h"
|
||||
// #include "modules/factory/include/Util/Helpers.h"
|
||||
// #include "Item.h"
|
||||
|
||||
// class UpgradeLevelConfig : public Resource
|
||||
// {
|
||||
// GDCLASS(UpgradeLevelConfig, Resource);
|
||||
// public:
|
||||
// static void _bind_methods();
|
||||
|
||||
// public:
|
||||
// TypedArray<ItemAmountConfig> GetUpgradeCosts() const { return VectorToTypedArray(UpgradeCost); }
|
||||
// Dictionary GetUpgradeResults() const { return UpgradeResults; }
|
||||
|
||||
// void SetUpgradeCosts(TypedArray<ItemAmountConfig> upgradeCosts) { UpgradeCost = TypedArrayToVector<ItemAmountConfig>(upgradeCosts); }
|
||||
// void SetUpgradeResults(Dictionary results) { UpgradeResults = results; }
|
||||
|
||||
// public:
|
||||
// Vector<Ref<ItemAmountConfig>> UpgradeCost{};
|
||||
// Dictionary UpgradeResults{};
|
||||
// };
|
||||
30
include/Types/WorldGraph/WorldGraph.h
Normal file
30
include/Types/WorldGraph/WorldGraph.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "WorldGraphVisualNode.h"
|
||||
|
||||
class WorldGraph final
|
||||
{
|
||||
public:
|
||||
WorldGraph() = default;
|
||||
WorldGraph(const Vector<Ref<WorldGraphVisualNodeBase>>& nodes);
|
||||
|
||||
WorldGraph(const WorldGraph& other);
|
||||
WorldGraph(WorldGraph&& other) noexcept = default;
|
||||
|
||||
WorldGraph& operator=(const WorldGraph& other);
|
||||
WorldGraph& operator=(WorldGraph&& other) noexcept = default;
|
||||
|
||||
public:
|
||||
Variant Execute(Ref<WorldGraphVisualNodeBase> node, const WorldNodeParameters& params) const;
|
||||
WorldNodeBase* GetNode(Ref<WorldGraphVisualNodeBase> node) const;
|
||||
|
||||
private:
|
||||
void Compile(const Vector<Ref<WorldGraphVisualNodeBase>>& nodes);
|
||||
std::unique_ptr<WorldNodeBase*[]> CopyMemory(HashMap<Ref<WorldGraphVisualNodeBase>, WorldNodeBase*>& nodeMap) const;
|
||||
|
||||
private:
|
||||
int MemorySize{};
|
||||
std::unique_ptr<WorldNodeBase*[]> CompiledData{};
|
||||
HashMap<Ref<WorldGraphVisualNodeBase>, WorldNodeBase*> NodeMap{};
|
||||
};
|
||||
|
||||
70
include/Types/WorldGraph/WorldGraphAllocator.h
Normal file
70
include/Types/WorldGraph/WorldGraphAllocator.h
Normal file
@@ -0,0 +1,70 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include "modules/factory/include/Util/Span.h"
|
||||
|
||||
struct WorldGraphAllocatorBase
|
||||
{
|
||||
virtual ~WorldGraphAllocatorBase() = default;
|
||||
|
||||
virtual void* Allocate(tcb::span<const uint8_t> data) = 0;
|
||||
virtual void Clear() = 0;
|
||||
|
||||
template <typename T>
|
||||
T* Allocate(const T& val)
|
||||
{
|
||||
return static_cast<T*>(Allocate(tcb::span<const uint8_t>(reinterpret_cast<uint8_t const*>(&val), sizeof(T))));
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldGraphAllocator : public WorldGraphAllocatorBase
|
||||
{
|
||||
virtual ~WorldGraphAllocator() = default;
|
||||
WorldGraphAllocator() = default;
|
||||
WorldGraphAllocator(uint32_t totalSize) : Data {std::make_unique<uint8_t[]>(totalSize)}, Size{totalSize} {}
|
||||
|
||||
virtual void* Allocate(tcb::span<const uint8_t> data) override
|
||||
{
|
||||
auto size = (data.size_bytes() + sizeof(void*) - 1) / sizeof(void*) * sizeof(void*); // make sure aligment is 8/4 bytes for pointers
|
||||
if (CurrentOffset + size > Size)
|
||||
throw std::exception{};
|
||||
|
||||
std::memcpy(Data.get() + CurrentOffset, data.data(), data.size_bytes());
|
||||
CurrentOffset += size;
|
||||
|
||||
return Data.get() + CurrentOffset - size;
|
||||
}
|
||||
|
||||
virtual void Clear() override
|
||||
{
|
||||
CurrentOffset = 0;
|
||||
}
|
||||
|
||||
void* GetCurrentAddress() const
|
||||
{
|
||||
return Data.get() + CurrentOffset;
|
||||
}
|
||||
|
||||
std::unique_ptr<uint8_t[]> Data{};
|
||||
uint32_t Size{};
|
||||
uint32_t CurrentOffset{};
|
||||
};
|
||||
|
||||
struct WorldGraphSizeMeasurer : public WorldGraphAllocatorBase
|
||||
{
|
||||
virtual ~WorldGraphSizeMeasurer() = default;
|
||||
WorldGraphSizeMeasurer() = default;
|
||||
|
||||
virtual void* Allocate(tcb::span<const uint8_t> data) override
|
||||
{
|
||||
TotalSize += (data.size_bytes() + sizeof(void*) - 1) / sizeof(void*) * sizeof(void*);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual void Clear() override
|
||||
{
|
||||
TotalSize = 0;
|
||||
}
|
||||
|
||||
uint32_t TotalSize{};
|
||||
};
|
||||
665
include/Types/WorldGraph/WorldGraphNode.h
Normal file
665
include/Types/WorldGraph/WorldGraphNode.h
Normal file
@@ -0,0 +1,665 @@
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
#include <array>
|
||||
|
||||
#include "WorldGraphAllocator.h"
|
||||
#include "Util/FastNoiseLite.h"
|
||||
#include "core/variant/variant.h"
|
||||
#include "modules/factory/include/Core/Chunk.h"
|
||||
|
||||
// // last bit determines if it is a boolean or a float
|
||||
// // if it is a float, last bit of precision will be lost (not that bad)
|
||||
// // if it is a bool, the bool value will be stored on the second bit
|
||||
// // for floats the last bit is set
|
||||
// // for bools the last bit is unset
|
||||
// class BoolFloat
|
||||
// {
|
||||
// public:
|
||||
// BoolFloat() = default;
|
||||
// BoolFloat(bool val) {}
|
||||
// BoolFloat(float val) {}
|
||||
|
||||
// public:
|
||||
// void SetFloat(float val) { Data = reinterpret_cast<uint32_t&>(val) | 0b1u; }
|
||||
// void SetBool(bool val) { Data = (val << 1) & (~0b1u); }
|
||||
|
||||
// constexpr float IsFloat() const { return Data & 0b1u; }
|
||||
// constexpr float IsBool() const { return Data & (~0b1u); }
|
||||
|
||||
// float GetFloat() const { _ASSERT(IsFloat()); return reinterpret_cast<const float&>(Data); }
|
||||
// float GetBool() const { _ASSERT(IsBool()); return Data; }
|
||||
|
||||
// public:
|
||||
// operator float() const { return GetFloat(); }
|
||||
|
||||
// private:
|
||||
// uint32_t Data{};
|
||||
// };
|
||||
|
||||
struct WorldNodeParameters;
|
||||
|
||||
struct alignas(void*) WorldNodeBase
|
||||
{
|
||||
virtual Variant Evaluate(const WorldNodeParameters& params) const = 0;
|
||||
virtual Variant::Type GetReturnType() const = 0;
|
||||
virtual Vector<Variant::Type> GetInputTypes() const = 0;
|
||||
virtual bool IsValid() const = 0;
|
||||
virtual void Allocate(WorldGraphAllocatorBase* Allocator) const = 0;
|
||||
virtual void SetInput(int index, WorldNodeBase* input) = 0;
|
||||
};
|
||||
|
||||
struct WorldNodeParameters
|
||||
{
|
||||
static constexpr int MaxQueryOffset = 4;
|
||||
static constexpr int PaddedChunkSide = Chunk::ChunkSize + MaxQueryOffset * 2;
|
||||
static constexpr int PaddedChunkSize = PaddedChunkSide * PaddedChunkSide;
|
||||
|
||||
typedef std::array<Tile, PaddedChunkSize> TileArray;
|
||||
|
||||
int X{ };
|
||||
int Y{ };
|
||||
int Seed{ };
|
||||
float FinalValueSubstract{ };
|
||||
|
||||
ChunkKey ChunkInfo{ };
|
||||
TileArray* GeneratedTiles{ };
|
||||
|
||||
Tile GetTile(int x, int y) const
|
||||
{
|
||||
auto bounds = GetGenerationBounds();
|
||||
if (unlikely(!bounds.has_point(Vector2i{x, y})))
|
||||
return {};
|
||||
// DEV_ASSERT(bounds.has_point(Vector2i{x, y}));
|
||||
|
||||
return (*GeneratedTiles)[(y - bounds.position.y) * PaddedChunkSide + (x - bounds.position.x)];
|
||||
}
|
||||
|
||||
static int GetArrayIndex(int x, int y)
|
||||
{
|
||||
return (y + MaxQueryOffset) * PaddedChunkSide + (x + MaxQueryOffset);
|
||||
}
|
||||
|
||||
Rect2i GetGenerationBounds() const
|
||||
{
|
||||
return ChunkInfo.GetBounds().grow(MaxQueryOffset);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct TtoVariant
|
||||
{
|
||||
typedef Variant TVariant;
|
||||
};
|
||||
|
||||
template <typename Return, typename ... Inputs>
|
||||
struct WorldNodeTemplated : public WorldNodeBase
|
||||
{
|
||||
std::array<WorldNodeBase*, sizeof...(Inputs)> InputNodes{};
|
||||
|
||||
virtual Variant::Type GetReturnType() const override { return Variant::get_type_t<Return>(); }
|
||||
virtual Vector<Variant::Type> GetInputTypes() const override
|
||||
{
|
||||
return { Variant::get_type_t<Inputs>()... };
|
||||
};
|
||||
virtual bool IsValid() const override
|
||||
{
|
||||
bool valid{ true };
|
||||
for (auto input : InputNodes)
|
||||
{
|
||||
valid = valid && input;
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
virtual void Allocate(WorldGraphAllocatorBase* Allocator) const override { Allocator->Allocate(*this); }
|
||||
|
||||
virtual Variant Evaluate(const WorldNodeParameters& params) const override
|
||||
{
|
||||
std::array<Variant, sizeof...(Inputs)> results{};
|
||||
for (int i{}; i < sizeof...(Inputs); ++i)
|
||||
{
|
||||
results[i] = InputNodes[i]->Evaluate(params);
|
||||
}
|
||||
|
||||
auto EvaluateFunctor = [this](typename TtoVariant<Inputs>::TVariant... args) -> Variant
|
||||
{
|
||||
return EvaluateT(args.get_unsafe_t<Inputs>()...);
|
||||
};
|
||||
|
||||
return std::apply(EvaluateFunctor, results);
|
||||
}
|
||||
|
||||
virtual void SetInput(int index, WorldNodeBase* input) override
|
||||
{
|
||||
InputNodes[index] = input;
|
||||
}
|
||||
|
||||
|
||||
virtual Return EvaluateT(Inputs...) const = 0;
|
||||
};
|
||||
|
||||
struct WorldNode_Add : public WorldNodeTemplated<float, float, float>
|
||||
{
|
||||
virtual float EvaluateT(float v0, float v1) const override
|
||||
{
|
||||
return v0 + v1;
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Subtract : public WorldNodeTemplated<float, float, float>
|
||||
{
|
||||
virtual float EvaluateT(float v0, float v1) const override
|
||||
{
|
||||
return v0 - v1;
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Multiply : public WorldNodeTemplated<float, float, float>
|
||||
{
|
||||
virtual float EvaluateT(float v0, float v1) const override
|
||||
{
|
||||
return v0 * v1;
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Divide : public WorldNodeTemplated<float, float, float>
|
||||
{
|
||||
virtual float EvaluateT(float v0, float v1) const override
|
||||
{
|
||||
return v0 / v1;
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Modulo : public WorldNodeTemplated<float, float, float>
|
||||
{
|
||||
virtual float EvaluateT(float v0, float v1) const override
|
||||
{
|
||||
return fmod(v0, v1);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Equal : public WorldNodeTemplated<bool, float, float>
|
||||
{
|
||||
virtual bool EvaluateT(float v0, float v1) const override
|
||||
{
|
||||
return v0 == v1;
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Smaller : public WorldNodeTemplated<bool, float, float>
|
||||
{
|
||||
virtual bool EvaluateT(float v0, float v1) const override
|
||||
{
|
||||
return v0 < v1;
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Greater : public WorldNodeTemplated<bool, float, float>
|
||||
{
|
||||
virtual bool EvaluateT(float v0, float v1) const override
|
||||
{
|
||||
return v0 > v1;
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_SmallerEqual : public WorldNodeTemplated<bool, float, float>
|
||||
{
|
||||
virtual bool EvaluateT(float v0, float v1) const override
|
||||
{
|
||||
return v0 <= v1;
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_GreaterEqual : public WorldNodeTemplated<bool, float, float>
|
||||
{
|
||||
virtual bool EvaluateT(float v0, float v1) const override
|
||||
{
|
||||
return v0 >= v1;
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Negate : public WorldNodeTemplated<float, float>
|
||||
{
|
||||
virtual float EvaluateT(float v) const override
|
||||
{
|
||||
return -v;
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Abs : public WorldNodeTemplated<float, float>
|
||||
{
|
||||
virtual float EvaluateT(float v) const override
|
||||
{
|
||||
return std::abs(v);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Ceil : public WorldNodeTemplated<float, float>
|
||||
{
|
||||
virtual float EvaluateT(float v) const override
|
||||
{
|
||||
return std::ceil(v);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Floor : public WorldNodeTemplated<float, float>
|
||||
{
|
||||
virtual float EvaluateT(float v) const override
|
||||
{
|
||||
return std::floor(v);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Sin : public WorldNodeTemplated<float, float>
|
||||
{
|
||||
virtual float EvaluateT(float v) const override
|
||||
{
|
||||
return std::sin(v);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Cos : public WorldNodeTemplated<float, float>
|
||||
{
|
||||
virtual float EvaluateT(float v) const override
|
||||
{
|
||||
return std::cos(v);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Tan : public WorldNodeTemplated<float, float>
|
||||
{
|
||||
virtual float EvaluateT(float v) const override
|
||||
{
|
||||
return std::tan(v);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Exp : public WorldNodeTemplated<float, float>
|
||||
{
|
||||
virtual float EvaluateT(float v) const override
|
||||
{
|
||||
return std::exp(v);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Pow : public WorldNodeTemplated<float, float, float>
|
||||
{
|
||||
virtual float EvaluateT(float v0, float v1) const override
|
||||
{
|
||||
return std::pow(v0, v1);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Max : public WorldNodeTemplated<float, float, float>
|
||||
{
|
||||
virtual float EvaluateT(float v0, float v1) const override
|
||||
{
|
||||
return std::max(v0, v1);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Min : public WorldNodeTemplated<float, float, float>
|
||||
{
|
||||
virtual float EvaluateT(float v0, float v1) const override
|
||||
{
|
||||
return std::min(v0, v1);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Clamp : public WorldNodeTemplated<float, float, float, float>
|
||||
{
|
||||
virtual float EvaluateT(float v0, float v1, float v2) const override
|
||||
{
|
||||
return std::clamp(v0, v1, v2);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Round : public WorldNodeTemplated<float, float>
|
||||
{
|
||||
virtual float EvaluateT(float v) const override
|
||||
{
|
||||
return std::round(v);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Log : public WorldNodeTemplated<float, float>
|
||||
{
|
||||
virtual float EvaluateT(float v) const override
|
||||
{
|
||||
return std::log(v);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Square : public WorldNodeTemplated<float, float>
|
||||
{
|
||||
virtual float EvaluateT(float v) const override
|
||||
{
|
||||
return v * v;
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Lerp : public WorldNodeTemplated<float, float, float, float>
|
||||
{
|
||||
virtual float EvaluateT(float from, float to, float weight) const override
|
||||
{
|
||||
return Math::lerp(from, to, weight);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_OneMinus : public WorldNodeTemplated<float, float>
|
||||
{
|
||||
virtual float EvaluateT(float val) const override
|
||||
{
|
||||
return 1 - val;
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_And : public WorldNodeTemplated<bool, bool, bool>
|
||||
{
|
||||
virtual bool EvaluateT(bool val0, bool val1) const override
|
||||
{
|
||||
return val0 && val1;
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Or : public WorldNodeTemplated<bool, bool, bool>
|
||||
{
|
||||
virtual bool EvaluateT(bool val0, bool val1) const override
|
||||
{
|
||||
return val0 || val1;
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Constant : public WorldNodeBase
|
||||
{
|
||||
float Value{};
|
||||
virtual Variant Evaluate(const WorldNodeParameters& params) const override
|
||||
{
|
||||
return Value;
|
||||
}
|
||||
virtual Variant::Type GetReturnType() const override { return Variant::FLOAT; }
|
||||
virtual Vector<Variant::Type> GetInputTypes() const override
|
||||
{
|
||||
return { };
|
||||
};
|
||||
virtual bool IsValid() const override
|
||||
{
|
||||
return !std::_Is_nan(Value);
|
||||
}
|
||||
virtual void Allocate(WorldGraphAllocatorBase* Allocator) const override
|
||||
{
|
||||
Allocator->Allocate(*this);
|
||||
}
|
||||
virtual void SetInput(int index, WorldNodeBase* input) override
|
||||
{
|
||||
DEV_ASSERT(false);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Branch : public WorldNodeBase
|
||||
{
|
||||
WorldNodeBase* InputBool{};
|
||||
WorldNodeBase* InputTrue{};
|
||||
WorldNodeBase* InputFalse{};
|
||||
|
||||
virtual Variant Evaluate(const WorldNodeParameters& params) const override
|
||||
{
|
||||
bool condition = InputBool->Evaluate(params).get_unsafe_bool();
|
||||
return condition ? InputTrue->Evaluate(params) : InputFalse->Evaluate(params);
|
||||
}
|
||||
virtual Variant::Type GetReturnType() const override { return Variant::Type::FLOAT; }
|
||||
virtual Vector<Variant::Type> GetInputTypes() const override
|
||||
{
|
||||
return { Variant::Type::BOOL, Variant::Type::FLOAT, Variant::Type::FLOAT };
|
||||
};
|
||||
virtual bool IsValid() const override
|
||||
{
|
||||
return InputBool && InputTrue && InputFalse;
|
||||
}
|
||||
virtual void Allocate(WorldGraphAllocatorBase* Allocator) const override
|
||||
{
|
||||
Allocator->Allocate(*this);
|
||||
}
|
||||
virtual void SetInput(int index, WorldNodeBase* input) override
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0: InputBool = input;
|
||||
case 1: InputTrue = input;
|
||||
case 2: InputFalse = input;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_NoiseBase : public WorldNodeBase
|
||||
{
|
||||
float Frequency{ 1.f };
|
||||
|
||||
virtual Variant Evaluate(const WorldNodeParameters& params) const override
|
||||
{
|
||||
float x = params.X * Frequency;
|
||||
float y = params.Y * Frequency;
|
||||
return EvaluateNoise(params.Seed, x, y);
|
||||
}
|
||||
virtual Variant::Type GetReturnType() const override { return Variant::FLOAT; }
|
||||
virtual Vector<Variant::Type> GetInputTypes() const override
|
||||
{
|
||||
return { };
|
||||
};
|
||||
virtual bool IsValid() const override
|
||||
{
|
||||
return Frequency != 0;
|
||||
}
|
||||
virtual void Allocate(WorldGraphAllocatorBase* Allocator) const override
|
||||
{
|
||||
Allocator->Allocate(*this);
|
||||
}
|
||||
|
||||
virtual void SetInput(int index, WorldNodeBase* input) override
|
||||
{
|
||||
DEV_ASSERT(false);
|
||||
}
|
||||
|
||||
virtual float EvaluateNoise(int seed, float x, float y) const = 0;
|
||||
};
|
||||
|
||||
struct WorldNode_Simplex : public WorldNode_NoiseBase
|
||||
{
|
||||
virtual float EvaluateNoise(int seed, float x, float y) const override
|
||||
{
|
||||
const float SQRT3 = (float)1.7320508075688772935274463415059;
|
||||
const float F2 = 0.5f * (SQRT3 - 1);
|
||||
float t = (x + y) * F2;
|
||||
x += t;
|
||||
y += t;
|
||||
|
||||
return fastnoiselitestatic::SingleSimplex<float>(seed, x, y);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_OpenSimplex : public WorldNode_NoiseBase
|
||||
{
|
||||
virtual float EvaluateNoise(int seed, float x, float y) const override
|
||||
{
|
||||
const float SQRT3 = (float)1.7320508075688772935274463415059;
|
||||
const float F2 = 0.5f * (SQRT3 - 1);
|
||||
float t = (x + y) * F2;
|
||||
x += t;
|
||||
y += t;
|
||||
|
||||
return fastnoiselitestatic::SingleOpenSimplex2S<float>(seed, x, y);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Perlin : public WorldNode_NoiseBase
|
||||
{
|
||||
virtual float EvaluateNoise(int seed, float x, float y) const override
|
||||
{
|
||||
return fastnoiselitestatic::SinglePerlin<float>(seed, x, y);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_ValueCubic : public WorldNode_NoiseBase
|
||||
{
|
||||
virtual float EvaluateNoise(int seed, float x, float y) const override
|
||||
{
|
||||
return fastnoiselitestatic::SingleValueCubic<float>(seed, x, y);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_Value : public WorldNode_NoiseBase
|
||||
{
|
||||
virtual float EvaluateNoise(int seed, float x, float y) const override
|
||||
{
|
||||
return fastnoiselitestatic::SingleValue<float>(seed, x, y);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_IsTile : public WorldNodeBase
|
||||
{
|
||||
int8_t RelativeX{};
|
||||
int8_t RelativeY{};
|
||||
TILE_TYPE TileType{};
|
||||
|
||||
virtual Variant Evaluate(const WorldNodeParameters& params) const override
|
||||
{
|
||||
return params.GetTile(params.X + RelativeX, params.Y + RelativeY).GetType() == TileType;
|
||||
}
|
||||
virtual Variant::Type GetReturnType() const override { return Variant::BOOL; }
|
||||
virtual Vector<Variant::Type> GetInputTypes() const override
|
||||
{
|
||||
return { };
|
||||
};
|
||||
virtual bool IsValid() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
virtual void Allocate(WorldGraphAllocatorBase* Allocator) const override
|
||||
{
|
||||
Allocator->Allocate(*this);
|
||||
}
|
||||
|
||||
virtual void SetInput(int index, WorldNodeBase* input) override
|
||||
{
|
||||
DEV_ASSERT(false);
|
||||
}
|
||||
};
|
||||
|
||||
struct WorldNode_TileDistance : public WorldNodeBase
|
||||
{
|
||||
int8_t Range{};
|
||||
TILE_TYPE TileType{};
|
||||
|
||||
private:
|
||||
struct SmallVectorI2{
|
||||
int8_t X; int8_t Y;
|
||||
SmallVectorI2(int8_t x, int8_t y) : X{ x }, Y{ y } {};
|
||||
SmallVectorI2() = default;
|
||||
};
|
||||
|
||||
static constexpr int ArraySize{(WorldNodeParameters::MaxQueryOffset * 2 + 1) * (WorldNodeParameters::MaxQueryOffset * 2 + 1) - 1};
|
||||
|
||||
static std::array<SmallVectorI2, ArraySize> CreateSortedOffsets()
|
||||
{
|
||||
std::array<SmallVectorI2, ArraySize> offsets{};
|
||||
int counter{};
|
||||
for (int y{-WorldNodeParameters::MaxQueryOffset}; y <= WorldNodeParameters::MaxQueryOffset; ++y)
|
||||
for (int x{-WorldNodeParameters::MaxQueryOffset}; x <= WorldNodeParameters::MaxQueryOffset; ++x)
|
||||
if (y != 0 && x != 0)
|
||||
{
|
||||
offsets[counter] = SmallVectorI2{ static_cast<int8_t>(x), static_cast<int8_t>(y) };
|
||||
++counter;
|
||||
}
|
||||
std::sort(offsets.begin(), offsets.end(), [] (SmallVectorI2 lhs, SmallVectorI2 rhs)
|
||||
{
|
||||
return rhs.X * rhs.X + rhs.Y * rhs.Y > lhs.X * lhs.X + lhs.Y * lhs.Y;
|
||||
});
|
||||
return offsets;
|
||||
}
|
||||
|
||||
inline static const std::array<SmallVectorI2, ArraySize> SortedOffsets{ CreateSortedOffsets() };
|
||||
|
||||
static std::array<int, WorldNodeParameters::MaxQueryOffset> CreateRangeOffsets()
|
||||
{
|
||||
std::array<int, WorldNodeParameters::MaxQueryOffset> offsets{};
|
||||
|
||||
for (int i{}; i < WorldNodeParameters::MaxQueryOffset; ++i)
|
||||
{
|
||||
offsets[i] = ArraySize - (((WorldNodeParameters::MaxQueryOffset - i) * 2 + 1) * ((WorldNodeParameters::MaxQueryOffset - i) * 2 + 1) - 1);
|
||||
}
|
||||
|
||||
return offsets;
|
||||
}
|
||||
|
||||
inline static const std::array<int, WorldNodeParameters::MaxQueryOffset> RangeOffsets{ CreateRangeOffsets() };
|
||||
|
||||
// inline static const SmallVectorI2 SortedOffset[] =
|
||||
// {
|
||||
// {+3, +3}, {-3, -3}, {+3, -3}, {-3, +3}, // 3
|
||||
// {+2, +3}, {+3, +2}, {-2, -3}, {-3, -2}, {-2, +3}, {-3, +2}, {+2, -3}, {+3, -2},
|
||||
// {+1, +3}, {+3, +1}, {-1, -3}, {-3, -1}, {-1, +3}, {-3, +1}, {+1, -3}, {+3, -1},
|
||||
// {+3, +0}, {-3, -0}, {+0, -3}, {-0, +3},
|
||||
// {+2, +2}, {-2, -2}, {+2, -2}, {-2, +2}, // 2
|
||||
// {+1, +2}, {+2, +1}, {-1, -2}, {-2, -1}, {-1, +2}, {-2, +1}, {+1, -2}, {+2, -1},
|
||||
// {+2, +0}, {-2, -0}, {+0, -2}, {-0, +2},
|
||||
// {+1, +1}, {-1, -1}, {+1, -1}, {-1, +1}, // 1
|
||||
// {+1, +0}, {-1, -0}, {+0, -1}, {-0, +1},
|
||||
// };
|
||||
|
||||
// inline static const int8_t RangeOffsets[] =
|
||||
// {
|
||||
// 0, 24, 40
|
||||
// };
|
||||
|
||||
public:
|
||||
virtual Variant Evaluate(const WorldNodeParameters& params) const override
|
||||
{
|
||||
if (!params.ChunkInfo.GetBounds().has_point(Vector2i{params.X, params.Y})) return 16'384.f;
|
||||
|
||||
int maxRangeSQ = 16'384;
|
||||
for (int i{RangeOffsets[Range]}; i < 48; ++i)
|
||||
{
|
||||
auto offset = SortedOffsets[i];
|
||||
if (params.GetTile(params.X + offset.X, params.Y + offset.Y).GetType() == TileType)
|
||||
{
|
||||
maxRangeSQ = offset.X * offset.X + offset.Y * offset.Y;
|
||||
}
|
||||
}
|
||||
return sqrtf(static_cast<float>(maxRangeSQ));
|
||||
}
|
||||
virtual Variant::Type GetReturnType() const override { return Variant::FLOAT; }
|
||||
virtual Vector<Variant::Type> GetInputTypes() const override
|
||||
{
|
||||
return { };
|
||||
};
|
||||
virtual bool IsValid() const override
|
||||
{
|
||||
return Range >= 1 && Range <= 3;
|
||||
}
|
||||
virtual void Allocate(WorldGraphAllocatorBase* Allocator) const override
|
||||
{
|
||||
Allocator->Allocate(*this);
|
||||
}
|
||||
|
||||
virtual void SetInput(int index, WorldNodeBase* input) override
|
||||
{
|
||||
DEV_ASSERT(false);
|
||||
}
|
||||
};
|
||||
|
||||
// struct WorldNode_Cellular : public WorldNode_NoiseBase
|
||||
// {
|
||||
// virtual float EvaluateNoise(int seed, float x, float y) const override
|
||||
// {
|
||||
// const float SQRT3 = (float)1.7320508075688772935274463415059;
|
||||
// const float F2 = 0.5f * (SQRT3 - 1);
|
||||
// float t = (x + y) * F2;
|
||||
// x += t;
|
||||
// y += t;
|
||||
|
||||
// return fastnoiselite::FastNoiseLite::SingleCellular<float>(seed, x, y);
|
||||
// }
|
||||
// };
|
||||
174
include/Types/WorldGraph/WorldGraphVisualNode.h
Normal file
174
include/Types/WorldGraph/WorldGraphVisualNode.h
Normal file
@@ -0,0 +1,174 @@
|
||||
#pragma once
|
||||
|
||||
#include "WorldGraphNode.h"
|
||||
#include "core/io/resource.h"
|
||||
#include "modules/factory/include/Util/Helpers.h"
|
||||
|
||||
class WorldGraphVisualNodeBase : public Resource
|
||||
{
|
||||
GDCLASS(WorldGraphVisualNodeBase, Resource);
|
||||
public:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual ~WorldGraphVisualNodeBase() = default;
|
||||
|
||||
public:
|
||||
Vector2i GetPosition() const { return Position; }
|
||||
TypedArray<WorldGraphVisualNodeBase> GetInputs() const
|
||||
{
|
||||
return VectorToTypedArray(InputNodes);
|
||||
}
|
||||
|
||||
void SetPosition(Vector2i pos) { Position = pos; }
|
||||
void SetInputNodes(TypedArray<WorldGraphVisualNodeBase> inputNodes)
|
||||
{
|
||||
InputNodes = TypedArrayToVector(inputNodes);
|
||||
RefreshInputs();
|
||||
}
|
||||
|
||||
bool NodeIsValid() const { return IsValid(); }
|
||||
TypedArray<int> NodeGetInputTypes() const { return VectorToTypedArrayCast<int>(GetInputTypes()); }
|
||||
int NodeGetOutputType() const { return GetOutputType(); }
|
||||
void NodeSetInput(int index, Ref<WorldGraphVisualNodeBase> input) { SetInput(index, input); }
|
||||
bool HasInternalNode() const { return InternalNode.get(); };
|
||||
|
||||
bool CanExecuteNode();
|
||||
|
||||
void SetInternalNode(std::unique_ptr<WorldNodeBase>&& node);
|
||||
WorldNodeBase* GetInternalNode() const { return InternalNode.get(); }
|
||||
void RefreshInputs();
|
||||
|
||||
public:
|
||||
virtual Vector<Variant::Type> GetInputTypes() const { return InternalNode ? InternalNode->GetInputTypes() : Vector<Variant::Type>{}; };
|
||||
virtual Variant::Type GetOutputType() const { return InternalNode ? InternalNode->GetReturnType() : Variant::Type{}; };
|
||||
virtual void SetInput(int index, Ref<WorldGraphVisualNodeBase> input)
|
||||
{
|
||||
InputNodes.set(index, input);
|
||||
InternalNode->SetInput(index, input.is_valid() ? input->InternalNode.get() : nullptr);
|
||||
}
|
||||
virtual bool IsValid() const
|
||||
{
|
||||
if (!InternalNode)
|
||||
print_error(String("No internal node for ") + get_class_name());
|
||||
if (!InternalNode->IsValid())
|
||||
print_error(String("node is invalid ") + get_class_name());
|
||||
return InternalNode && InternalNode->IsValid();
|
||||
}
|
||||
virtual void RefreshValues() {};
|
||||
|
||||
public:
|
||||
Vector2i Position{};
|
||||
Vector<Ref<WorldGraphVisualNodeBase>> InputNodes{};
|
||||
private:
|
||||
std::unique_ptr<WorldNodeBase> InternalNode{};
|
||||
};
|
||||
|
||||
class WorldGraphVisualNode_Math : public WorldGraphVisualNodeBase
|
||||
{
|
||||
GDCLASS(WorldGraphVisualNode_Math, WorldGraphVisualNodeBase);
|
||||
public:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
WorldGraphVisualNode_Math();
|
||||
virtual ~WorldGraphVisualNode_Math() = default;
|
||||
|
||||
public:
|
||||
TypedArray<String> GetNodeNames() const;
|
||||
void SetNode(String nodeName);
|
||||
String GetNode() const { return NodeID; }
|
||||
|
||||
private:
|
||||
String NodeID{};
|
||||
|
||||
};
|
||||
|
||||
class WorldGraphVisualNode_Constant : public WorldGraphVisualNodeBase
|
||||
{
|
||||
GDCLASS(WorldGraphVisualNode_Constant, WorldGraphVisualNodeBase);
|
||||
public:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
WorldGraphVisualNode_Constant();
|
||||
virtual ~WorldGraphVisualNode_Constant() = default;
|
||||
|
||||
private:
|
||||
void SetValue(float val);
|
||||
float GetValue() const;
|
||||
};
|
||||
|
||||
class WorldGraphVisualNode_If : public WorldGraphVisualNodeBase
|
||||
{
|
||||
GDCLASS(WorldGraphVisualNode_If, WorldGraphVisualNodeBase);
|
||||
public:
|
||||
static void _bind_methods() {};
|
||||
|
||||
public:
|
||||
WorldGraphVisualNode_If();
|
||||
virtual ~WorldGraphVisualNode_If() = default;
|
||||
};
|
||||
|
||||
class WorldGraphVisualNode_Noise : public WorldGraphVisualNodeBase
|
||||
{
|
||||
GDCLASS(WorldGraphVisualNode_Noise, WorldGraphVisualNodeBase);
|
||||
public:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void SetNoiseType(String noiseType);
|
||||
String GetNoiseType() const { return NoiseType; }
|
||||
float GetFrequency() const { return Frequency; }
|
||||
|
||||
TypedArray<String> GetNoiseTypes() const;
|
||||
void SetFrequency(float val);
|
||||
|
||||
virtual void RefreshValues() override;
|
||||
|
||||
public:
|
||||
WorldGraphVisualNode_Noise();
|
||||
virtual ~WorldGraphVisualNode_Noise() = default;
|
||||
|
||||
private:
|
||||
String NoiseType{};
|
||||
float Frequency{};
|
||||
};
|
||||
|
||||
class WorldGraphVisualNode_Tile : public WorldGraphVisualNodeBase
|
||||
{
|
||||
GDCLASS(WorldGraphVisualNode_Tile, WorldGraphVisualNodeBase);
|
||||
public:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
WorldGraphVisualNode_Tile();
|
||||
virtual ~WorldGraphVisualNode_Tile() = default;
|
||||
|
||||
public:
|
||||
void SetType(int type);
|
||||
void SetRelativeX(int offset);
|
||||
void SetRelativeY(int offset);
|
||||
|
||||
int GetType() const;
|
||||
int GetRelativeX() const;
|
||||
int GetRelativeY() const;
|
||||
};
|
||||
|
||||
class WorldGraphVisualNode_TileDistance : public WorldGraphVisualNodeBase
|
||||
{
|
||||
GDCLASS(WorldGraphVisualNode_TileDistance, WorldGraphVisualNodeBase);
|
||||
public:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
WorldGraphVisualNode_TileDistance();
|
||||
virtual ~WorldGraphVisualNode_TileDistance() = default;
|
||||
|
||||
public:
|
||||
void SetType(int type);
|
||||
void SetRange(int range);
|
||||
|
||||
int GetType() const;
|
||||
int GetRange() const;
|
||||
};
|
||||
63
include/Types/WorldSettings.h
Normal file
63
include/Types/WorldSettings.h
Normal file
@@ -0,0 +1,63 @@
|
||||
// #pragma once
|
||||
|
||||
// #include "Data/Tile.h"
|
||||
// #include "Data/Recipe.h"
|
||||
// #include "core/object/ref_counted.h"
|
||||
// #include "modules/noise/fastnoise_lite.h"
|
||||
// #include "core/io/resource.h"
|
||||
// #include "Data/Archetype.h"
|
||||
// #include "LayerConfigs.h"
|
||||
// #include "scene/resources/2d/tile_set.h"
|
||||
// #include "Data/WorldGraph/WorldGraph.h"
|
||||
|
||||
// struct FactoryWorldSettings : public Resource
|
||||
// {
|
||||
// public:
|
||||
// GDCLASS(FactoryWorldSettings, Resource);
|
||||
|
||||
// public:
|
||||
// static void _bind_methods();
|
||||
|
||||
// public:
|
||||
// void Merge(Ref<FactoryWorldSettings> settings);
|
||||
// void Initialize();
|
||||
|
||||
// public:
|
||||
// TypedArray<RecipeConfig> GetRecipes() const { return VectorToTypedArray(Recipes); }
|
||||
// TypedArray<Archetype> GetPlaceableArchetypes() const { return VectorToTypedArray(PlaceableArchetypes); }
|
||||
// TypedArray<TileConfig> GetTileConfigs() const { return VectorToTypedArray(TileConfigs); }
|
||||
// TypedArray<LayerConfig> GetLayerConfigs() const { return VectorToTypedArray(LayerConfigs); }
|
||||
|
||||
// void SetRecipes(TypedArray<RecipeConfig> recipes) { Recipes = TypedArrayToVector(recipes); }
|
||||
// void SetPlaceableArchetypes(TypedArray<PlaceableArchetype> archetypes) { PlaceableArchetypes = TypedArrayToVector(archetypes); }
|
||||
// void SetTileConfigs(TypedArray<TileConfig> tiles) { TileConfigs = TypedArrayToVector(tiles); }
|
||||
// void SetLayerConfigs(TypedArray<LayerConfig> layers) { LayerConfigs = TypedArrayToVector(layers); }
|
||||
|
||||
// public:
|
||||
// int GetStartHeight() const;
|
||||
// Vector<ItemAmount> GetChunkUnlockCosts(int x, int y) const;
|
||||
// Vector<ItemAmount> GetChunkUnlockCosts(ChunkKey chunk) const;
|
||||
// Ref<LayerConfig> GetLayer(ChunkKey chunk) const;
|
||||
|
||||
// private:
|
||||
// void InitializeResources();
|
||||
// void InitializeLayers();
|
||||
// void InitializeWorldGenerators();
|
||||
// void InitializeTexturesSheets();
|
||||
// void InitializeItemGraph();
|
||||
|
||||
// public:
|
||||
// Vector<Ref<TileConfig>> TileConfigs{};
|
||||
// Vector<Ref<RecipeConfig>> Recipes{};
|
||||
// Vector<Ref<ItemConfig>> Items{};
|
||||
// Vector<Ref<Archetype>> Archetypes{};
|
||||
// Vector<Ref<PlaceableArchetype>> PlaceableArchetypes{};
|
||||
// Vector<Ref<LayerConfig>> LayerConfigs{};
|
||||
// HashMap<Ref<ItemConfig>,int> ItemComplexity{};
|
||||
|
||||
// Vector<Ref<Texture2D>> TileSheets{};
|
||||
// Ref<TileSet> TileSet{};
|
||||
|
||||
// WorldGraph WorldGenerator{};
|
||||
// };
|
||||
|
||||
837
include/Util/AStar.h
Normal file
837
include/Util/AStar.h
Normal file
@@ -0,0 +1,837 @@
|
||||
/*
|
||||
A* Algorithm Implementation using STL is
|
||||
Copyright (C)2001-2005 Justin Heyes-Jones
|
||||
|
||||
Permission is given by the author to freely redistribute and
|
||||
include this code in any program as long as this credit is
|
||||
given where due.
|
||||
|
||||
COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
|
||||
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
|
||||
INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE
|
||||
IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
|
||||
OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND
|
||||
PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED
|
||||
CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL
|
||||
DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY
|
||||
NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF
|
||||
WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE
|
||||
OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
|
||||
THIS DISCLAIMER.
|
||||
|
||||
Use at your own risk!
|
||||
|
||||
*/
|
||||
|
||||
#ifndef STLASTAR_H
|
||||
#define STLASTAR_H
|
||||
// used for text debugging
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
//#include <conio.h>
|
||||
#include <assert.h>
|
||||
|
||||
// stl includes
|
||||
#include <algorithm>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <cfloat>
|
||||
|
||||
// fast fixed size memory allocator, used for fast node memory management
|
||||
#include "fsa.h"
|
||||
|
||||
// Fixed size memory allocator can be disabled to compare performance
|
||||
// Uses std new and delete instead if you turn it off
|
||||
#define USE_FSA_MEMORY 1
|
||||
|
||||
// disable warning that debugging information has lines that are truncated
|
||||
// occurs in stl headers
|
||||
#if defined(WIN32) && defined(_WINDOWS)
|
||||
#pragma warning( disable : 4786 )
|
||||
#endif
|
||||
|
||||
template <class T> class AStarState;
|
||||
|
||||
// The AStar search class. UserState is the users state space type
|
||||
template <class UserState> class AStarSearch
|
||||
{
|
||||
|
||||
public: // data
|
||||
|
||||
enum
|
||||
{
|
||||
SEARCH_STATE_NOT_INITIALISED,
|
||||
SEARCH_STATE_SEARCHING,
|
||||
SEARCH_STATE_SUCCEEDED,
|
||||
SEARCH_STATE_FAILED,
|
||||
SEARCH_STATE_OUT_OF_MEMORY,
|
||||
SEARCH_STATE_INVALID
|
||||
};
|
||||
|
||||
|
||||
// A node represents a possible state in the search
|
||||
// The user provided state type is included inside this type
|
||||
|
||||
public:
|
||||
|
||||
class Node
|
||||
{
|
||||
public:
|
||||
|
||||
Node* parent; // used during the search to record the parent of successor nodes
|
||||
Node* child; // used after the search for the application to view the search in reverse
|
||||
|
||||
float g; // cost of this node + its predecessors
|
||||
float h; // heuristic estimate of distance to goal
|
||||
float f; // sum of cumulative cost of predecessors and self and heuristic
|
||||
|
||||
Node() :
|
||||
parent(0),
|
||||
child(0),
|
||||
g(0.0f),
|
||||
h(0.0f),
|
||||
f(0.0f)
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const Node& otherNode) const
|
||||
{
|
||||
return m_UserState.IsSameState(otherNode.m_UserState);
|
||||
}
|
||||
|
||||
UserState m_UserState;
|
||||
};
|
||||
|
||||
// For sorting the heap the STL needs compare function that lets us compare
|
||||
// the f value of two nodes
|
||||
|
||||
class HeapCompare_f
|
||||
{
|
||||
public:
|
||||
|
||||
bool operator() (const Node* x, const Node* y) const
|
||||
{
|
||||
return x->f > y->f;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public: // methods
|
||||
|
||||
|
||||
// constructor just initialises private data
|
||||
AStarSearch() :
|
||||
m_State(SEARCH_STATE_NOT_INITIALISED),
|
||||
m_CurrentSolutionNode(NULL),
|
||||
#if USE_FSA_MEMORY
|
||||
m_FixedSizeAllocator(1000),
|
||||
#endif
|
||||
m_AllocateNodeCount(0),
|
||||
m_CancelRequest(false)
|
||||
{
|
||||
}
|
||||
|
||||
AStarSearch(int MaxNodes) :
|
||||
m_State(SEARCH_STATE_NOT_INITIALISED),
|
||||
m_CurrentSolutionNode(NULL),
|
||||
#if USE_FSA_MEMORY
|
||||
m_FixedSizeAllocator(MaxNodes),
|
||||
#endif
|
||||
m_AllocateNodeCount(0),
|
||||
m_CancelRequest(false)
|
||||
{
|
||||
}
|
||||
|
||||
// call at any time to cancel the search and free up all the memory
|
||||
void CancelSearch()
|
||||
{
|
||||
m_CancelRequest = true;
|
||||
}
|
||||
|
||||
// Set Start and goal states
|
||||
void SetStartAndGoalStates(const UserState& Start, const UserState& Goal)
|
||||
{
|
||||
m_CancelRequest = false;
|
||||
|
||||
m_Start = AllocateNode();
|
||||
m_Goal = AllocateNode();
|
||||
|
||||
assert((m_Start != NULL && m_Goal != NULL));
|
||||
|
||||
m_Start->m_UserState = Start;
|
||||
m_Goal->m_UserState = Goal;
|
||||
|
||||
m_State = SEARCH_STATE_SEARCHING;
|
||||
|
||||
// Initialise the AStar specific parts of the Start Node
|
||||
// The user only needs fill out the state information
|
||||
|
||||
m_Start->g = 0;
|
||||
m_Start->h = m_Start->m_UserState.GoalDistanceEstimate(m_Goal->m_UserState);
|
||||
m_Start->f = m_Start->g + m_Start->h;
|
||||
m_Start->parent = 0;
|
||||
|
||||
// Push the start node on the Open list
|
||||
|
||||
m_OpenList.push_back(m_Start); // heap now unsorted
|
||||
|
||||
// Sort back element into heap
|
||||
push_heap(m_OpenList.begin(), m_OpenList.end(), HeapCompare_f());
|
||||
|
||||
// Initialise counter for search steps
|
||||
m_Steps = 0;
|
||||
}
|
||||
|
||||
// Advances search one step
|
||||
unsigned int SearchStep()
|
||||
{
|
||||
// Firstly break if the user has not initialised the search
|
||||
assert((m_State > SEARCH_STATE_NOT_INITIALISED) &&
|
||||
(m_State < SEARCH_STATE_INVALID));
|
||||
|
||||
// Next I want it to be safe to do a searchstep once the search has succeeded...
|
||||
if ((m_State == SEARCH_STATE_SUCCEEDED) ||
|
||||
(m_State == SEARCH_STATE_FAILED)
|
||||
)
|
||||
{
|
||||
return m_State;
|
||||
}
|
||||
|
||||
// Failure is defined as emptying the open list as there is nothing left to
|
||||
// search...
|
||||
// New: Allow user abort
|
||||
if (m_OpenList.empty() || m_CancelRequest)
|
||||
{
|
||||
FreeAllNodes();
|
||||
m_State = SEARCH_STATE_FAILED;
|
||||
return m_State;
|
||||
}
|
||||
|
||||
// Incremement step count
|
||||
m_Steps++;
|
||||
|
||||
// Pop the best node (the one with the lowest f)
|
||||
Node* n = m_OpenList.front(); // get pointer to the node
|
||||
pop_heap(m_OpenList.begin(), m_OpenList.end(), HeapCompare_f());
|
||||
m_OpenList.pop_back();
|
||||
|
||||
// Check for the goal, once we pop that we're done
|
||||
if (n->m_UserState.IsGoal(m_Goal->m_UserState))
|
||||
{
|
||||
// The user is going to use the Goal Node he passed in
|
||||
// so copy the parent pointer of n
|
||||
m_Goal->parent = n->parent;
|
||||
m_Goal->g = n->g;
|
||||
|
||||
// A special case is that the goal was passed in as the start state
|
||||
// so handle that here
|
||||
if (false == n->m_UserState.IsSameState(m_Start->m_UserState))
|
||||
{
|
||||
FreeNode(n);
|
||||
|
||||
// set the child pointers in each node (except Goal which has no child)
|
||||
Node* nodeChild = m_Goal;
|
||||
Node* nodeParent = m_Goal->parent;
|
||||
|
||||
do
|
||||
{
|
||||
nodeParent->child = nodeChild;
|
||||
|
||||
nodeChild = nodeParent;
|
||||
nodeParent = nodeParent->parent;
|
||||
|
||||
} while (nodeChild != m_Start); // Start is always the first node by definition
|
||||
|
||||
}
|
||||
|
||||
// delete nodes that aren't needed for the solution
|
||||
FreeUnusedNodes();
|
||||
|
||||
m_State = SEARCH_STATE_SUCCEEDED;
|
||||
|
||||
return m_State;
|
||||
}
|
||||
else // not goal
|
||||
{
|
||||
|
||||
// We now need to generate the successors of this node
|
||||
// The user helps us to do this, and we keep the new nodes in
|
||||
// m_Successors ...
|
||||
|
||||
m_Successors.clear(); // empty vector of successor nodes to n
|
||||
|
||||
// User provides this functions and uses AddSuccessor to add each successor of
|
||||
// node 'n' to m_Successors
|
||||
bool ret = n->m_UserState.GetSuccessors(this, n->parent ? &n->parent->m_UserState : NULL);
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
|
||||
typename std::vector<Node*>::iterator successor;
|
||||
|
||||
// free the nodes that may previously have been added
|
||||
for (successor = m_Successors.begin(); successor != m_Successors.end(); successor++)
|
||||
{
|
||||
FreeNode((*successor));
|
||||
}
|
||||
|
||||
m_Successors.clear(); // empty vector of successor nodes to n
|
||||
|
||||
// free up everything else we allocated
|
||||
FreeNode((n));
|
||||
FreeAllNodes();
|
||||
|
||||
m_State = SEARCH_STATE_OUT_OF_MEMORY;
|
||||
return m_State;
|
||||
}
|
||||
|
||||
// Now handle each successor to the current node ...
|
||||
for (typename std::vector<Node*>::iterator successor = m_Successors.begin(); successor != m_Successors.end(); successor++)
|
||||
{
|
||||
// The g value for this successor ...
|
||||
float newg = n->g + n->m_UserState.GetCost((*successor)->m_UserState);
|
||||
|
||||
// Now we need to find whether the node is on the open or closed lists
|
||||
// If it is but the node that is already on them is better (lower g)
|
||||
// then we can forget about this successor
|
||||
|
||||
// First linear search of open list to find node
|
||||
|
||||
typename std::vector<Node*>::iterator openlist_result;
|
||||
|
||||
for (openlist_result = m_OpenList.begin(); openlist_result != m_OpenList.end(); openlist_result++)
|
||||
{
|
||||
if ((*openlist_result)->m_UserState.IsSameState((*successor)->m_UserState))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (openlist_result != m_OpenList.end())
|
||||
{
|
||||
|
||||
// we found this state on open
|
||||
|
||||
if ((*openlist_result)->g <= newg)
|
||||
{
|
||||
FreeNode((*successor));
|
||||
|
||||
// the one on Open is cheaper than this one
|
||||
continue;
|
||||
}
|
||||
}
|
||||
typename std::unordered_set<Node*, NodeHash, NodeEqual>::iterator closedlist_result;
|
||||
|
||||
closedlist_result = m_ClosedList.find(*successor);
|
||||
|
||||
if (closedlist_result != m_ClosedList.end())
|
||||
{
|
||||
|
||||
// we found this state on closed
|
||||
|
||||
if ((*closedlist_result)->g <= newg)
|
||||
{
|
||||
// the one on Closed is cheaper than this one
|
||||
FreeNode((*successor));
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// This node is the best node so far with this particular state
|
||||
// so lets keep it and set up its AStar specific data ...
|
||||
|
||||
(*successor)->parent = n;
|
||||
(*successor)->g = newg;
|
||||
(*successor)->h = (*successor)->m_UserState.GoalDistanceEstimate(m_Goal->m_UserState);
|
||||
(*successor)->f = (*successor)->g + (*successor)->h;
|
||||
|
||||
// Successor in closed list
|
||||
// 1 - Update old version of this node in closed list
|
||||
// 2 - Move it from closed to open list
|
||||
// 3 - Sort heap again in open list
|
||||
|
||||
if (closedlist_result != m_ClosedList.end())
|
||||
{
|
||||
// Update closed node with successor node AStar data
|
||||
//*(*closedlist_result) = *(*successor);
|
||||
(*closedlist_result)->parent = (*successor)->parent;
|
||||
(*closedlist_result)->g = (*successor)->g;
|
||||
(*closedlist_result)->h = (*successor)->h;
|
||||
(*closedlist_result)->f = (*successor)->f;
|
||||
|
||||
// Free successor node
|
||||
FreeNode((*successor));
|
||||
|
||||
// Push closed node into open list
|
||||
m_OpenList.push_back((*closedlist_result));
|
||||
|
||||
// Remove closed node from closed list
|
||||
m_ClosedList.erase(closedlist_result);
|
||||
|
||||
// Sort back element into heap
|
||||
push_heap(m_OpenList.begin(), m_OpenList.end(), HeapCompare_f());
|
||||
|
||||
// Fix thanks to ...
|
||||
// Greg Douglas <gregdouglasmail@gmail.com>
|
||||
// who noticed that this code path was incorrect
|
||||
// Here we have found a new state which is already CLOSED
|
||||
|
||||
}
|
||||
|
||||
// Successor in open list
|
||||
// 1 - Update old version of this node in open list
|
||||
// 2 - sort heap again in open list
|
||||
|
||||
else if (openlist_result != m_OpenList.end())
|
||||
{
|
||||
// Update open node with successor node AStar data
|
||||
//*(*openlist_result) = *(*successor);
|
||||
(*openlist_result)->parent = (*successor)->parent;
|
||||
(*openlist_result)->g = (*successor)->g;
|
||||
(*openlist_result)->h = (*successor)->h;
|
||||
(*openlist_result)->f = (*successor)->f;
|
||||
|
||||
// Free successor node
|
||||
FreeNode((*successor));
|
||||
|
||||
// re-make the heap
|
||||
// make_heap rather than sort_heap is an essential bug fix
|
||||
// thanks to Mike Ryynanen for pointing this out and then explaining
|
||||
// it in detail. sort_heap called on an invalid heap does not work
|
||||
make_heap(m_OpenList.begin(), m_OpenList.end(), HeapCompare_f());
|
||||
}
|
||||
|
||||
// New successor
|
||||
// 1 - Move it from successors to open list
|
||||
// 2 - sort heap again in open list
|
||||
|
||||
else
|
||||
{
|
||||
// Push successor node into open list
|
||||
m_OpenList.push_back((*successor));
|
||||
|
||||
// Sort back element into heap
|
||||
push_heap(m_OpenList.begin(), m_OpenList.end(), HeapCompare_f());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// push n onto Closed, as we have expanded it now
|
||||
|
||||
m_ClosedList.insert(n);
|
||||
|
||||
} // end else (not goal so expand)
|
||||
|
||||
return m_State; // Succeeded bool is false at this point.
|
||||
|
||||
}
|
||||
|
||||
// User calls this to add a successor to a list of successors
|
||||
// when expanding the search frontier
|
||||
bool AddSuccessor(const UserState& State)
|
||||
{
|
||||
Node* node = AllocateNode();
|
||||
|
||||
if (node)
|
||||
{
|
||||
node->m_UserState = State;
|
||||
|
||||
m_Successors.push_back(node);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Free the solution nodes
|
||||
// This is done to clean up all used Node memory when you are done with the
|
||||
// search
|
||||
void FreeSolutionNodes()
|
||||
{
|
||||
Node* n = m_Start;
|
||||
|
||||
if (m_Start->child)
|
||||
{
|
||||
do
|
||||
{
|
||||
Node* del = n;
|
||||
n = n->child;
|
||||
FreeNode(del);
|
||||
|
||||
del = NULL;
|
||||
|
||||
} while (n != m_Goal);
|
||||
|
||||
FreeNode(n); // Delete the goal
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the start node is the solution we need to just delete the start and goal
|
||||
// nodes
|
||||
FreeNode(m_Start);
|
||||
FreeNode(m_Goal);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Functions for traversing the solution
|
||||
|
||||
// Get start node
|
||||
UserState* GetSolutionStart()
|
||||
{
|
||||
m_CurrentSolutionNode = m_Start;
|
||||
if (m_Start)
|
||||
{
|
||||
return &m_Start->m_UserState;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Get next node
|
||||
UserState* GetSolutionNext()
|
||||
{
|
||||
if (m_CurrentSolutionNode)
|
||||
{
|
||||
if (m_CurrentSolutionNode->child)
|
||||
{
|
||||
|
||||
Node* child = m_CurrentSolutionNode->child;
|
||||
|
||||
m_CurrentSolutionNode = m_CurrentSolutionNode->child;
|
||||
|
||||
return &child->m_UserState;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get end node
|
||||
UserState* GetSolutionEnd()
|
||||
{
|
||||
m_CurrentSolutionNode = m_Goal;
|
||||
if (m_Goal)
|
||||
{
|
||||
return &m_Goal->m_UserState;
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Step solution iterator backwards
|
||||
UserState* GetSolutionPrev()
|
||||
{
|
||||
if (m_CurrentSolutionNode)
|
||||
{
|
||||
if (m_CurrentSolutionNode->parent)
|
||||
{
|
||||
|
||||
Node* parent = m_CurrentSolutionNode->parent;
|
||||
|
||||
m_CurrentSolutionNode = m_CurrentSolutionNode->parent;
|
||||
|
||||
return &parent->m_UserState;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get final cost of solution
|
||||
// Returns FLT_MAX if goal is not defined or there is no solution
|
||||
float GetSolutionCost()
|
||||
{
|
||||
if (m_Goal && m_State == SEARCH_STATE_SUCCEEDED)
|
||||
{
|
||||
return m_Goal->g;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FLT_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
// For educational use and debugging it is useful to be able to view
|
||||
// the open and closed list at each step, here are two functions to allow that.
|
||||
|
||||
UserState* GetOpenListStart()
|
||||
{
|
||||
float f, g, h;
|
||||
return GetOpenListStart(f, g, h);
|
||||
}
|
||||
|
||||
UserState* GetOpenListStart(float& f, float& g, float& h)
|
||||
{
|
||||
iterDbgOpen = m_OpenList.begin();
|
||||
if (iterDbgOpen != m_OpenList.end())
|
||||
{
|
||||
f = (*iterDbgOpen)->f;
|
||||
g = (*iterDbgOpen)->g;
|
||||
h = (*iterDbgOpen)->h;
|
||||
return &(*iterDbgOpen)->m_UserState;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UserState* GetOpenListNext()
|
||||
{
|
||||
float f, g, h;
|
||||
return GetOpenListNext(f, g, h);
|
||||
}
|
||||
|
||||
UserState* GetOpenListNext(float& f, float& g, float& h)
|
||||
{
|
||||
iterDbgOpen++;
|
||||
if (iterDbgOpen != m_OpenList.end())
|
||||
{
|
||||
f = (*iterDbgOpen)->f;
|
||||
g = (*iterDbgOpen)->g;
|
||||
h = (*iterDbgOpen)->h;
|
||||
return &(*iterDbgOpen)->m_UserState;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UserState* GetClosedListStart()
|
||||
{
|
||||
float f, g, h;
|
||||
return GetClosedListStart(f, g, h);
|
||||
}
|
||||
|
||||
UserState* GetClosedListStart(float& f, float& g, float& h)
|
||||
{
|
||||
iterDbgClosed = m_ClosedList.begin();
|
||||
if (iterDbgClosed != m_ClosedList.end())
|
||||
{
|
||||
f = (*iterDbgClosed)->f;
|
||||
g = (*iterDbgClosed)->g;
|
||||
h = (*iterDbgClosed)->h;
|
||||
|
||||
return &(*iterDbgClosed)->m_UserState;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
UserState* GetClosedListNext()
|
||||
{
|
||||
float f, g, h;
|
||||
return GetClosedListNext(f, g, h);
|
||||
}
|
||||
|
||||
UserState* GetClosedListNext(float& f, float& g, float& h)
|
||||
{
|
||||
iterDbgClosed++;
|
||||
if (iterDbgClosed != m_ClosedList.end())
|
||||
{
|
||||
f = (*iterDbgClosed)->f;
|
||||
g = (*iterDbgClosed)->g;
|
||||
h = (*iterDbgClosed)->h;
|
||||
|
||||
return &(*iterDbgClosed)->m_UserState;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get the number of steps
|
||||
|
||||
int GetStepCount() { return m_Steps; }
|
||||
|
||||
void EnsureMemoryFreed()
|
||||
{
|
||||
#if USE_FSA_MEMORY
|
||||
assert(m_AllocateNodeCount == 0);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
private: // methods
|
||||
|
||||
// This is called when a search fails or is cancelled to free all used
|
||||
// memory
|
||||
void FreeAllNodes()
|
||||
{
|
||||
// iterate open list and delete all nodes
|
||||
typename std::vector<Node*>::iterator iterOpen = m_OpenList.begin();
|
||||
|
||||
while (iterOpen != m_OpenList.end())
|
||||
{
|
||||
Node* n = (*iterOpen);
|
||||
FreeNode(n);
|
||||
|
||||
iterOpen++;
|
||||
}
|
||||
|
||||
m_OpenList.clear();
|
||||
|
||||
// iterate closed list and delete unused nodes
|
||||
typename std::unordered_set<Node*, NodeHash, NodeEqual>::iterator iterClosed;
|
||||
|
||||
for (iterClosed = m_ClosedList.begin(); iterClosed != m_ClosedList.end(); iterClosed++)
|
||||
{
|
||||
Node* n = (*iterClosed);
|
||||
FreeNode(n);
|
||||
}
|
||||
|
||||
m_ClosedList.clear();
|
||||
|
||||
// delete the goal
|
||||
|
||||
FreeNode(m_Goal);
|
||||
}
|
||||
|
||||
|
||||
// This call is made by the search class when the search ends. A lot of nodes may be
|
||||
// created that are still present when the search ends. They will be deleted by this
|
||||
// routine once the search ends
|
||||
void FreeUnusedNodes()
|
||||
{
|
||||
// iterate open list and delete unused nodes
|
||||
typename std::vector< Node* >::iterator iterOpen = m_OpenList.begin();
|
||||
|
||||
while (iterOpen != m_OpenList.end())
|
||||
{
|
||||
Node* n = (*iterOpen);
|
||||
|
||||
if (!n->child)
|
||||
{
|
||||
FreeNode(n);
|
||||
|
||||
n = NULL;
|
||||
}
|
||||
|
||||
iterOpen++;
|
||||
}
|
||||
|
||||
m_OpenList.clear();
|
||||
|
||||
// iterate closed list and delete unused nodes
|
||||
typename std::unordered_set<Node*, NodeHash, NodeEqual>::iterator iterClosed;
|
||||
|
||||
for (iterClosed = m_ClosedList.begin(); iterClosed != m_ClosedList.end(); iterClosed++)
|
||||
{
|
||||
Node* n = (*iterClosed);
|
||||
|
||||
if (!n->child)
|
||||
{
|
||||
FreeNode(n);
|
||||
n = NULL;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
m_ClosedList.clear();
|
||||
|
||||
}
|
||||
|
||||
// Node memory management
|
||||
Node* AllocateNode()
|
||||
{
|
||||
|
||||
#if !USE_FSA_MEMORY
|
||||
m_AllocateNodeCount++;
|
||||
Node* p = new Node;
|
||||
return p;
|
||||
#else
|
||||
Node* address = m_FixedSizeAllocator.alloc();
|
||||
|
||||
if (!address)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
m_AllocateNodeCount++;
|
||||
Node* p = new (address) Node;
|
||||
return p;
|
||||
#endif
|
||||
}
|
||||
|
||||
void FreeNode(Node* node)
|
||||
{
|
||||
|
||||
m_AllocateNodeCount--;
|
||||
|
||||
#if !USE_FSA_MEMORY
|
||||
delete node;
|
||||
#else
|
||||
node->~Node();
|
||||
m_FixedSizeAllocator.free(node);
|
||||
#endif
|
||||
}
|
||||
|
||||
private: // data
|
||||
|
||||
// Heap (simple vector but used as a heap, cf. Steve Rabin's game gems article)
|
||||
std::vector<Node*> m_OpenList;
|
||||
|
||||
// Closed is an unordered_set
|
||||
struct NodeHash {
|
||||
size_t operator() (Node* const& n) const {
|
||||
return n->m_UserState.Hash();
|
||||
}
|
||||
};
|
||||
struct NodeEqual {
|
||||
bool operator()(Node* a, Node* b) const {
|
||||
return a->m_UserState.IsSameState(b->m_UserState);
|
||||
}
|
||||
};
|
||||
std::unordered_set<Node*, NodeHash, NodeEqual> m_ClosedList;
|
||||
|
||||
|
||||
// Successors is a vector filled out by the user each type successors to a node
|
||||
// are generated
|
||||
std::vector< Node* > m_Successors;
|
||||
|
||||
// State
|
||||
unsigned int m_State;
|
||||
|
||||
// Counts steps
|
||||
int m_Steps;
|
||||
|
||||
// Start and goal state pointers
|
||||
Node* m_Start;
|
||||
Node* m_Goal;
|
||||
|
||||
Node* m_CurrentSolutionNode;
|
||||
|
||||
#if USE_FSA_MEMORY
|
||||
// Memory
|
||||
FixedSizeAllocator<Node> m_FixedSizeAllocator;
|
||||
#endif
|
||||
|
||||
//Debug : need to keep these two iterators around
|
||||
// for the user Dbg functions
|
||||
typename std::vector< Node* >::iterator iterDbgOpen;
|
||||
typename std::vector< Node* >::iterator iterDbgClosed;
|
||||
|
||||
// debugging : count memory allocation and free's
|
||||
int m_AllocateNodeCount;
|
||||
|
||||
bool m_CancelRequest;
|
||||
|
||||
};
|
||||
|
||||
template <class T> class AStarState
|
||||
{
|
||||
public:
|
||||
virtual ~AStarState() {}
|
||||
virtual float GoalDistanceEstimate(T& nodeGoal) = 0; // Heuristic function which computes the estimated cost to the goal node
|
||||
virtual bool IsGoal(T& nodeGoal) = 0; // Returns true if this node is the goal node
|
||||
virtual bool GetSuccessors(AStarSearch<T>* astarsearch, T* parent_node) = 0; // Retrieves all successors to this node and adds them via astarsearch.addSuccessor()
|
||||
virtual float GetCost(T& successor) = 0; // Computes the cost of travelling from this node to the successor node
|
||||
virtual bool IsSameState(T& rhs) = 0; // Returns true if this node is the same as the rhs node
|
||||
virtual size_t Hash() = 0; // Returns a hash for the state
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
1191
include/Util/FastNoiseLite.h
Normal file
1191
include/Util/FastNoiseLite.h
Normal file
File diff suppressed because it is too large
Load Diff
208
include/Util/Helpers.h
Normal file
208
include/Util/Helpers.h
Normal file
@@ -0,0 +1,208 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/templates/vector.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
#include "core/variant/variant.h"
|
||||
#include "core/variant/typed_array.h"
|
||||
|
||||
template <typename T>
|
||||
TypedArray<T> VectorToTypedArray(const Vector<Ref<T>>& vector)
|
||||
{
|
||||
TypedArray<T> arr;
|
||||
arr.resize(vector.size());
|
||||
|
||||
for (int i{}; i < vector.size(); ++i)
|
||||
{
|
||||
arr[i] = Variant(vector[i]);
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Vector<Ref<T>> TypedArrayToVector(const TypedArray<T>& arr)
|
||||
{
|
||||
Vector<Ref<T>> vector;
|
||||
vector.resize(arr.size());
|
||||
|
||||
for (int i{}; i < vector.size(); ++i)
|
||||
{
|
||||
vector.set(i, Ref<T>(arr[i]));
|
||||
}
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
TypedArray<T> VectorToTypedArrayVariant(const Vector<T>& vector)
|
||||
{
|
||||
TypedArray<T> arr;
|
||||
arr.resize(vector.size());
|
||||
|
||||
for (int i{}; i < vector.size(); ++i)
|
||||
{
|
||||
arr[i] = Variant(vector[i]);
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
Vector<T> TypedArrayToVectorVariant(const TypedArray<T>& arr)
|
||||
{
|
||||
Vector<T> vector;
|
||||
vector.resize(arr.size());
|
||||
|
||||
for (int i{}; i < vector.size(); ++i)
|
||||
{
|
||||
vector.set(i, arr[i]);
|
||||
}
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
template <typename To, typename From>
|
||||
TypedArray<To> VectorToTypedArrayCast(const Vector<From>& vector)
|
||||
{
|
||||
TypedArray<To> arr;
|
||||
arr.resize(vector.size());
|
||||
|
||||
for (int i{}; i < vector.size(); ++i)
|
||||
{
|
||||
arr[i] = Variant(static_cast<To>(vector[i]));
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// template <typename T>
|
||||
// static Type get_type_t();
|
||||
|
||||
// template <> static Type get_type_t<bool>() { return Type::BOOL; }
|
||||
// template <> static Type get_type_t<int>() { return Type::INT; }
|
||||
// template <> static Type get_type_t<float>() { return Type::FLOAT; }
|
||||
// template <> static Type get_type_t<String>() { return Type::STRING; }
|
||||
|
||||
// template <> static Type get_type_t<Vector2>() { return Type::VECTOR2; }
|
||||
// template <> static Type get_type_t<Vector2i>() { return Type::VECTOR2I; }
|
||||
// template <> static Type get_type_t<Rect2>() { return Type::RECT2; }
|
||||
// template <> static Type get_type_t<Rect2i>() { return Type::RECT2I; }
|
||||
// template <> static Type get_type_t<Vector3>() { return Type::VECTOR3; }
|
||||
// template <> static Type get_type_t<Vector3i>() { return Type::VECTOR3I; }
|
||||
// template <> static Type get_type_t<Transform2D>() { return Type::TRANSFORM2D; }
|
||||
// template <> static Type get_type_t<Vector4>() { return Type::VECTOR4; }
|
||||
// template <> static Type get_type_t<Vector4i>() { return Type::VECTOR4I; }
|
||||
// template <> static Type get_type_t<Plane>() { return Type::PLANE; }
|
||||
// template <> static Type get_type_t<Quaternion>() { return Type::QUATERNION; }
|
||||
// template <> static Type get_type_t<::AABB>() { return Type::AABB; }
|
||||
// template <> static Type get_type_t<Basis>() { return Type::BASIS; }
|
||||
// template <> static Type get_type_t<Transform3D>() { return Type::TRANSFORM3D; }
|
||||
// template <> static Type get_type_t<Projection>() { return Type::PROJECTION; }
|
||||
|
||||
// template <> static Type get_type_t<Color>() { return Type::COLOR; }
|
||||
// template <> static Type get_type_t<StringName>() { return Type::STRING_NAME; }
|
||||
// template <> static Type get_type_t<NodePath>() { return Type::NODE_PATH; }
|
||||
// template <> static Type get_type_t<::RID>() { return Type::RID; }
|
||||
// template <> static Type get_type_t<Object*>() { return Type::OBJECT; }
|
||||
// template <> static Type get_type_t<Callable>() { return Type::CALLABLE; }
|
||||
// template <> static Type get_type_t<Signal>() { return Type::SIGNAL; }
|
||||
// template <> static Type get_type_t<Dictionary>() { return Type::DICTIONARY; }
|
||||
// template <> static Type get_type_t<Array>() { return Type::ARRAY; }
|
||||
|
||||
// template <> static Type get_type_t<PackedByteArray>() { return Type::PACKED_BYTE_ARRAY; }
|
||||
// template <> static Type get_type_t<PackedInt32Array>() { return Type::PACKED_INT32_ARRAY; }
|
||||
// template <> static Type get_type_t<PackedInt64Array>() { return Type::PACKED_INT64_ARRAY; }
|
||||
// template <> static Type get_type_t<PackedFloat32Array>() { return Type::PACKED_FLOAT32_ARRAY; }
|
||||
// template <> static Type get_type_t<PackedFloat64Array>() { return Type::PACKED_FLOAT64_ARRAY; }
|
||||
// template <> static Type get_type_t<PackedStringArray>() { return Type::PACKED_STRING_ARRAY; }
|
||||
// template <> static Type get_type_t<PackedVector2Array>() { return Type::PACKED_VECTOR2_ARRAY; }
|
||||
// template <> static Type get_type_t<PackedVector3Array>() { return Type::PACKED_VECTOR3_ARRAY; }
|
||||
// template <> static Type get_type_t<PackedColorArray>() { return Type::PACKED_COLOR_ARRAY; }
|
||||
// template <> static Type get_type_t<PackedVector4Array>() { return Type::PACKED_VECTOR4_ARRAY; }
|
||||
|
||||
// //template <typename T> static Type get_type_t() { return Type::NIL; }
|
||||
|
||||
// bool get_unsafe_bool() const { DEV_ASSERT(type == Type::BOOL); return _data._bool; }
|
||||
// int get_unsafe_int() const { DEV_ASSERT(type == Type::INT); return _data._int; }
|
||||
// float get_unsafe_float() const { DEV_ASSERT(type == Type::FLOAT); return _data._float; }
|
||||
// String get_unsafe_string() const { DEV_ASSERT(type == Type::STRING); return *reinterpret_cast<const String *>(_data._mem); }
|
||||
|
||||
// Vector2 get_unsafe_vector2() const { DEV_ASSERT(type == Type::VECTOR2); return *reinterpret_cast<const Vector2 *>(_data._mem); }
|
||||
// Vector2i get_unsafe_vector2i() const { DEV_ASSERT(type == Type::VECTOR2I); return *reinterpret_cast<const Vector2i *>(_data._mem); }
|
||||
// Vector3 get_unsafe_vector3() const { DEV_ASSERT(type == Type::VECTOR3); return *reinterpret_cast<const Vector3 *>(_data._mem); }
|
||||
// Vector3i get_unsafe_vector3i() const { DEV_ASSERT(type == Type::VECTOR3I); return *reinterpret_cast<const Vector3i *>(_data._mem); }
|
||||
// Transform2D get_unsafe_transform2d() const { DEV_ASSERT(type == Type::TRANSFORM2D); return *reinterpret_cast<const Transform2D *>(_data._mem); }
|
||||
// Vector4 get_unsafe_vector4() const { DEV_ASSERT(type == Type::VECTOR4); return *reinterpret_cast<const Vector4 *>(_data._mem); }
|
||||
// Vector4i get_unsafe_vector4i() const { DEV_ASSERT(type == Type::VECTOR4I); return *reinterpret_cast<const Vector4i *>(_data._mem); }
|
||||
// Plane get_unsafe_plane() const { DEV_ASSERT(type == Type::PLANE); return *reinterpret_cast<const Plane *>(_data._mem); }
|
||||
// Quaternion get_unsafe_quaternion() const { DEV_ASSERT(type == Type::QUATERNION); return *reinterpret_cast<const Quaternion *>(_data._mem); }
|
||||
// ::AABB get_unsafe_aabb() const { DEV_ASSERT(type == Type::AABB); return *reinterpret_cast<const ::AABB *>(_data._mem); }
|
||||
// Basis get_unsafe_basis() const { DEV_ASSERT(type == Type::BASIS); return *reinterpret_cast<const Basis *>(_data._mem); }
|
||||
// Transform3D get_unsafe_transform3d() const { DEV_ASSERT(type == Type::TRANSFORM3D); return *reinterpret_cast<const Transform3D *>(_data._mem); }
|
||||
// Projection get_unsafe_projection() const { DEV_ASSERT(type == Type::PROJECTION); return *reinterpret_cast<const Projection *>(_data._mem); }
|
||||
|
||||
// Color get_unsafe_color() const { DEV_ASSERT(type == Type::COLOR); return *reinterpret_cast<const Color *>(_data._mem); }
|
||||
// StringName get_unsafe_string_name() const { DEV_ASSERT(type == Type::STRING_NAME); return *reinterpret_cast<const StringName *>(_data._mem); }
|
||||
// NodePath get_unsafe_node_path() const { DEV_ASSERT(type == Type::NODE_PATH); return *reinterpret_cast<const NodePath *>(_data._mem); }
|
||||
// ::RID get_unsafe_rid() const { DEV_ASSERT(type == Type::RID); return *reinterpret_cast<const ::RID *>(_data._mem); }
|
||||
// Object* get_unsafe_object() const { DEV_ASSERT(type == Type::OBJECT); return reinterpret_cast<const ObjData *>(&_data._mem[0])->obj; }
|
||||
// Callable get_unsafe_callable() const { DEV_ASSERT(type == Type::CALLABLE); return *reinterpret_cast<const Callable *>(_data._mem); }
|
||||
// Signal get_unsafe_signal() const { DEV_ASSERT(type == Type::SIGNAL); return *reinterpret_cast<const Signal *>(_data._mem); }
|
||||
// Dictionary get_unsafe_dictionary() const { DEV_ASSERT(type == Type::DICTIONARY); return *reinterpret_cast<const Dictionary *>(_data._mem); }
|
||||
// Array get_unsafe_array() const { DEV_ASSERT(type == Type::ARRAY); return *reinterpret_cast<const Array *>(_data._mem); }
|
||||
|
||||
// PackedByteArray get_unsafe_packed_byte_array() const { DEV_ASSERT(type == Type::PACKED_BYTE_ARRAY); return static_cast<PackedArrayRef<uint8_t> *>(_data.packed_array)->array; }
|
||||
// PackedInt32Array get_unsafe_packed_int32_array() const { DEV_ASSERT(type == Type::PACKED_INT32_ARRAY); return static_cast<PackedArrayRef<int32_t> *>(_data.packed_array)->array; }
|
||||
// PackedInt64Array get_unsafe_packed_int64_array() const { DEV_ASSERT(type == Type::PACKED_INT64_ARRAY); return static_cast<PackedArrayRef<int64_t> *>(_data.packed_array)->array; }
|
||||
// PackedFloat32Array get_unsafe_packed_float32_array() const { DEV_ASSERT(type == Type::PACKED_FLOAT32_ARRAY); return static_cast<PackedArrayRef<float> *>(_data.packed_array)->array; }
|
||||
// PackedFloat64Array get_unsafe_packed_float64_array() const { DEV_ASSERT(type == Type::PACKED_FLOAT64_ARRAY); return static_cast<PackedArrayRef<double> *>(_data.packed_array)->array; }
|
||||
// PackedStringArray get_unsafe_packed_string_array() const { DEV_ASSERT(type == Type::PACKED_STRING_ARRAY); return static_cast<PackedArrayRef<String> *>(_data.packed_array)->array; }
|
||||
// PackedVector2Array get_unsafe_packed_vector2_array() const { DEV_ASSERT(type == Type::PACKED_VECTOR2_ARRAY); return static_cast<PackedArrayRef<Vector2> *>(_data.packed_array)->array; }
|
||||
// PackedVector3Array get_unsafe_packed_vector3_array() const { DEV_ASSERT(type == Type::PACKED_VECTOR3_ARRAY); return static_cast<PackedArrayRef<Vector3> *>(_data.packed_array)->array; }
|
||||
// PackedColorArray get_unsafe_packed_color_array() const { DEV_ASSERT(type == Type::PACKED_COLOR_ARRAY); return static_cast<PackedArrayRef<Color> *>(_data.packed_array)->array; }
|
||||
// PackedVector4Array get_unsafe_packed_vector4_array() const { DEV_ASSERT(type == Type::PACKED_VECTOR4_ARRAY); return static_cast<PackedArrayRef<Vector4> *>(_data.packed_array)->array; }
|
||||
|
||||
// template <typename T>
|
||||
// T get_unsafe_t() const;
|
||||
|
||||
// template <> bool get_unsafe_t<bool>() const { return get_unsafe_bool(); }
|
||||
// template <> int get_unsafe_t<int>() const { return get_unsafe_int(); }
|
||||
// template <> float get_unsafe_t<float>() const { return get_unsafe_float(); }
|
||||
// template <> String get_unsafe_t<String>() const { return get_unsafe_string(); }
|
||||
|
||||
// template <> Vector2 get_unsafe_t<Vector2>() const { return get_unsafe_vector2(); }
|
||||
// template <> Vector2i get_unsafe_t<Vector2i>() const { return get_unsafe_vector2i(); }
|
||||
// template <> Vector3 get_unsafe_t<Vector3>() const { return get_unsafe_vector3(); }
|
||||
// template <> Vector3i get_unsafe_t<Vector3i>() const { return get_unsafe_vector3i(); }
|
||||
// template <> Transform2D get_unsafe_t<Transform2D>() const { return get_unsafe_transform2d(); }
|
||||
// template <> Vector4 get_unsafe_t<Vector4>() const { return get_unsafe_vector4(); }
|
||||
// template <> Vector4i get_unsafe_t<Vector4i>() const { return get_unsafe_vector4i(); }
|
||||
// template <> Plane get_unsafe_t<Plane>() const { return get_unsafe_plane(); }
|
||||
// template <> Quaternion get_unsafe_t<Quaternion>() const { return get_unsafe_quaternion(); }
|
||||
// template <> ::AABB get_unsafe_t<::AABB>() const { return get_unsafe_aabb(); }
|
||||
// template <> Basis get_unsafe_t<Basis>() const { return get_unsafe_basis(); }
|
||||
// template <> Transform3D get_unsafe_t<Transform3D>() const { return get_unsafe_transform3d(); }
|
||||
// template <> Projection get_unsafe_t<Projection>() const { return get_unsafe_projection(); }
|
||||
|
||||
// template <> Color get_unsafe_t<Color>() const { return get_unsafe_color(); }
|
||||
// template <> StringName get_unsafe_t<StringName>() const { return get_unsafe_string_name(); }
|
||||
// template <> NodePath get_unsafe_t<NodePath>() const { return get_unsafe_node_path(); }
|
||||
// template <> ::RID get_unsafe_t<::RID>() const { return get_unsafe_rid(); }
|
||||
// template <> Object* get_unsafe_t<Object*>() const { return get_unsafe_object(); }
|
||||
// template <> Callable get_unsafe_t<Callable>() const { return get_unsafe_callable(); }
|
||||
// template <> Signal get_unsafe_t<Signal>() const { return get_unsafe_signal(); }
|
||||
// template <> Dictionary get_unsafe_t<Dictionary>() const { return get_unsafe_dictionary(); }
|
||||
// template <> Array get_unsafe_t<Array>() const { return get_unsafe_array(); }
|
||||
|
||||
// template <> PackedByteArray get_unsafe_t<PackedByteArray>() const { return get_unsafe_packed_byte_array(); }
|
||||
// template <> PackedInt32Array get_unsafe_t<PackedInt32Array>() const { return get_unsafe_packed_int32_array(); }
|
||||
// template <> PackedInt64Array get_unsafe_t<PackedInt64Array>() const { return get_unsafe_packed_int64_array(); }
|
||||
// template <> PackedFloat32Array get_unsafe_t<PackedFloat32Array>() const { return get_unsafe_packed_float32_array(); }
|
||||
// template <> PackedFloat64Array get_unsafe_t<PackedFloat64Array>() const { return get_unsafe_packed_float64_array(); }
|
||||
// template <> PackedStringArray get_unsafe_t<PackedStringArray>() const { return get_unsafe_packed_string_array(); }
|
||||
// template <> PackedVector2Array get_unsafe_t<PackedVector2Array>() const { return get_unsafe_packed_vector2_array(); }
|
||||
// template <> PackedVector3Array get_unsafe_t<PackedVector3Array>() const { return get_unsafe_packed_vector3_array(); }
|
||||
// template <> PackedColorArray get_unsafe_t<PackedColorArray>() const { return get_unsafe_packed_color_array(); }
|
||||
// template <> PackedVector4Array get_unsafe_t<PackedVector4Array>() const { return get_unsafe_packed_vector4_array(); }
|
||||
732
include/Util/InplaceVector.h
Normal file
732
include/Util/InplaceVector.h
Normal file
@@ -0,0 +1,732 @@
|
||||
/*
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
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 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.
|
||||
|
||||
For more information, please refer to <https://unlicense.org>
|
||||
*/
|
||||
// Original: https://github.com/TedLyngmo/inplace_vector
|
||||
|
||||
// NOLINTNEXTLINE(llvm-header-guard)
|
||||
#ifndef LYNIPV_F4BA9AA8_99CD_11EF_8916_90B11C0C0FF8
|
||||
#define LYNIPV_F4BA9AA8_99CD_11EF_8916_90B11C0C0FF8
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#if __cplusplus >= 202002L
|
||||
# include <ranges>
|
||||
#endif
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#if __cplusplus >= 201402L
|
||||
# define LYNIPV_CXX14_CONSTEXPR constexpr
|
||||
#else
|
||||
# define LYNIPV_CXX14_CONSTEXPR
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
# define LYNIPV_CXX20_CONSTEXPR constexpr
|
||||
# define LYNIPV_CONSTRUCT_AT(p, ...) std::construct_at(p __VA_OPT__(, ) __VA_ARGS__)
|
||||
#else
|
||||
# define LYNIPV_CXX20_CONSTEXPR
|
||||
# define LYNIPV_CONSTRUCT_AT(p, ...) ::new(static_cast<void*>(p)) T(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
namespace lyn {
|
||||
|
||||
template<class, std::size_t>
|
||||
class inplace_vector;
|
||||
namespace lyn_inplace_vector_detail {
|
||||
#if __cplusplus >= 201703L
|
||||
using std::is_nothrow_swappable;
|
||||
#else
|
||||
template<typename U>
|
||||
struct is_nothrow_swappable : std::integral_constant<bool, noexcept(swap(std::declval<U&>(), std::declval<U&>()))> {};
|
||||
#endif
|
||||
#if __cplusplus >= 202002L
|
||||
template<class R, class T>
|
||||
concept container_compatiblel_range = std::ranges::input_range<R> && std::convertible_to<std::ranges::range_reference_t<R>, T>;
|
||||
#endif
|
||||
template<class T, std::size_t N>
|
||||
struct aligned_storage_non_trivial {
|
||||
constexpr aligned_storage_non_trivial() noexcept {}
|
||||
|
||||
using value_type = typename std::remove_const<T>::type;
|
||||
using size_type = std::size_t;
|
||||
using reference = value_type&;
|
||||
using const_reference = value_type const&;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = value_type const*;
|
||||
|
||||
// destructor
|
||||
LYNIPV_CXX20_CONSTEXPR ~aligned_storage_non_trivial() noexcept { static_cast<inplace_vector<T, N>*>(this)->clear(); }
|
||||
|
||||
LYNIPV_CXX14_CONSTEXPR pointer ptr(size_type idx) noexcept { return std::addressof(m_data[idx].data); }
|
||||
LYNIPV_CXX14_CONSTEXPR const_pointer ptr(size_type idx) const noexcept { return std::addressof(m_data[idx].data); }
|
||||
LYNIPV_CXX14_CONSTEXPR reference ref(size_type idx) noexcept { return m_data[idx].data; }
|
||||
LYNIPV_CXX14_CONSTEXPR const_reference ref(size_type idx) const noexcept { return m_data[idx].data; }
|
||||
|
||||
template<class... Args>
|
||||
LYNIPV_CXX20_CONSTEXPR reference construct(size_type idx, Args&&... args) {
|
||||
return *LYNIPV_CONSTRUCT_AT(ptr(idx), std::forward<Args>(args)...);
|
||||
}
|
||||
LYNIPV_CXX14_CONSTEXPR void destroy(size_type idx) noexcept { ref(idx).~T(); }
|
||||
|
||||
LYNIPV_CXX14_CONSTEXPR reference operator[](size_type idx) noexcept { return ref(idx); }
|
||||
constexpr const_reference operator[](size_type idx) const noexcept { return ref(idx); }
|
||||
|
||||
constexpr size_type size() const noexcept { return m_size; }
|
||||
|
||||
LYNIPV_CXX14_CONSTEXPR size_type inc() noexcept { return ++m_size; }
|
||||
LYNIPV_CXX14_CONSTEXPR size_type dec(size_type count = 1) noexcept { return m_size -= count; }
|
||||
|
||||
private:
|
||||
union raw {
|
||||
LYNIPV_CXX20_CONSTEXPR ~raw() {}
|
||||
char dummy{};
|
||||
value_type data;
|
||||
};
|
||||
std::array<raw, N> m_data;
|
||||
static_assert(sizeof m_data == sizeof(T[N]), "erroneous size");
|
||||
size_type m_size = 0;
|
||||
};
|
||||
|
||||
template<class T, std::size_t N>
|
||||
struct aligned_storage_trivial {
|
||||
static_assert(std::is_trivially_destructible<T>::value, "T must be trivially destructible");
|
||||
constexpr aligned_storage_trivial() noexcept {}
|
||||
|
||||
using value_type = typename std::remove_const<T>::type;
|
||||
using size_type = std::size_t;
|
||||
using reference = value_type&;
|
||||
using const_reference = value_type const&;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = value_type const*;
|
||||
|
||||
LYNIPV_CXX14_CONSTEXPR pointer ptr(size_type idx) noexcept { return std::addressof(m_data[idx].data); }
|
||||
LYNIPV_CXX14_CONSTEXPR const_pointer ptr(size_type idx) const noexcept { return std::addressof(m_data[idx].data); }
|
||||
LYNIPV_CXX14_CONSTEXPR reference ref(size_type idx) noexcept { return m_data[idx].data; }
|
||||
LYNIPV_CXX14_CONSTEXPR const_reference ref(size_type idx) const noexcept { return m_data[idx].data; }
|
||||
|
||||
template<class... Args>
|
||||
LYNIPV_CXX20_CONSTEXPR reference construct(size_type idx, Args&&... args) {
|
||||
return *LYNIPV_CONSTRUCT_AT(ptr(idx), std::forward<Args>(args)...);
|
||||
}
|
||||
LYNIPV_CXX14_CONSTEXPR void destroy(size_type idx) noexcept { ref(idx).~T(); }
|
||||
|
||||
LYNIPV_CXX14_CONSTEXPR reference operator[](size_type idx) noexcept { return ref(idx); }
|
||||
constexpr const_reference operator[](size_type idx) const noexcept { return ref(idx); }
|
||||
|
||||
constexpr size_type size() const noexcept { return m_size; }
|
||||
|
||||
LYNIPV_CXX14_CONSTEXPR size_type inc() noexcept { return ++m_size; }
|
||||
LYNIPV_CXX14_CONSTEXPR size_type dec(size_type count = 1) noexcept { return m_size -= count; }
|
||||
|
||||
private:
|
||||
union raw {
|
||||
constexpr raw() : dummy{} {}
|
||||
char dummy;
|
||||
value_type data;
|
||||
};
|
||||
std::array<raw, N> m_data;
|
||||
static_assert(sizeof m_data == sizeof(T[N]), "erroneous size");
|
||||
size_type m_size = 0;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct aligned_storage_empty { // specialization for 0 elements
|
||||
using value_type = typename std::remove_const<T>::type;
|
||||
using size_type = std::size_t;
|
||||
using reference = value_type&;
|
||||
using const_reference = value_type const&;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = value_type const*;
|
||||
|
||||
LYNIPV_CXX14_CONSTEXPR pointer ptr(size_type) { return nullptr; }
|
||||
LYNIPV_CXX14_CONSTEXPR const_pointer ptr(size_type) const { return nullptr; }
|
||||
LYNIPV_CXX14_CONSTEXPR reference ref(size_type) { return *ptr(0); }
|
||||
LYNIPV_CXX14_CONSTEXPR const_reference ref(size_type) const { return *ptr(0); }
|
||||
|
||||
template<class... Args>
|
||||
LYNIPV_CXX20_CONSTEXPR reference construct(size_type, Args&&...) {
|
||||
return *ptr(0);
|
||||
}
|
||||
LYNIPV_CXX14_CONSTEXPR void destroy(size_type) {}
|
||||
|
||||
LYNIPV_CXX14_CONSTEXPR reference operator[](size_type) { return *ptr(0); }
|
||||
constexpr const_reference operator[](size_type) const { return *ptr(0); }
|
||||
|
||||
constexpr size_type size() const noexcept { return 0; }
|
||||
|
||||
LYNIPV_CXX14_CONSTEXPR size_type inc() { return 0; }
|
||||
LYNIPV_CXX14_CONSTEXPR size_type dec(size_type = 1) { return 0; }
|
||||
};
|
||||
|
||||
template<class T, std::size_t N>
|
||||
struct base_selector {
|
||||
using type =
|
||||
typename std::conditional<N == 0, aligned_storage_empty<T>,
|
||||
typename std::conditional<std::is_trivially_copyable<T>::value, aligned_storage_trivial<T, N>,
|
||||
aligned_storage_non_trivial<T, N>>::type>::type;
|
||||
};
|
||||
} // namespace lyn_inplace_vector_detail
|
||||
|
||||
template<class T, std::size_t N>
|
||||
class inplace_vector : public lyn_inplace_vector_detail::base_selector<T, N>::type {
|
||||
static_assert(std::is_nothrow_destructible<T>::value,
|
||||
"inplace_vector: classes with potentially throwing destructors are prohibited");
|
||||
using base = typename lyn_inplace_vector_detail::base_selector<T, N>::type;
|
||||
using base::construct;
|
||||
using base::destroy;
|
||||
using base::ptr;
|
||||
using base::ref;
|
||||
|
||||
public:
|
||||
using base::size;
|
||||
using base::operator[];
|
||||
|
||||
using value_type = T;
|
||||
using size_type = std::size_t;
|
||||
using reference = T&;
|
||||
using const_reference = T const&;
|
||||
using pointer = T*;
|
||||
using const_pointer = T const*;
|
||||
using iterator = T*;
|
||||
using const_iterator = T const*;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||
using difference_type = typename std::iterator_traits<iterator>::difference_type;
|
||||
|
||||
private:
|
||||
LYNIPV_CXX14_CONSTEXPR void shrink_to(const size_type count) noexcept {
|
||||
while (count != size()) {
|
||||
pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// constructors
|
||||
constexpr inplace_vector() noexcept = default;
|
||||
|
||||
template<bool D = std::is_default_constructible<T>::value, typename std::enable_if<D, int>::type = 0>
|
||||
LYNIPV_CXX14_CONSTEXPR explicit inplace_vector(size_type count) {
|
||||
if (count > N) throw std::bad_alloc();
|
||||
while (count != size()) unchecked_emplace_back();
|
||||
}
|
||||
|
||||
template<bool C = std::is_copy_constructible<T>::value, typename std::enable_if<C, int>::type = 0>
|
||||
LYNIPV_CXX14_CONSTEXPR inplace_vector(size_type count, const T& value) {
|
||||
if (count > N) throw std::bad_alloc();
|
||||
while (count != size()) unchecked_push_back(value);
|
||||
}
|
||||
|
||||
template<class InputIt, typename std::enable_if<
|
||||
std::is_constructible<typename std::iterator_traits<InputIt>::value_type>::value, int>::type = 0>
|
||||
LYNIPV_CXX14_CONSTEXPR inplace_vector(InputIt first, InputIt last) {
|
||||
std::copy(first, last, std::back_inserter(*this));
|
||||
}
|
||||
|
||||
LYNIPV_CXX14_CONSTEXPR inplace_vector(const inplace_vector& other) = default; // for trivial types
|
||||
|
||||
template<class U = T,
|
||||
typename std::enable_if<std::is_copy_constructible<U>::value &&
|
||||
not std::is_trivially_copy_constructible<typename std::remove_reference<T>::type>::value,
|
||||
int>::type = 0>
|
||||
LYNIPV_CXX14_CONSTEXPR inplace_vector(const inplace_vector& other) {
|
||||
for (size_type idx = 0; idx != other.size(); ++idx) {
|
||||
unchecked_push_back(other[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
LYNIPV_CXX14_CONSTEXPR inplace_vector(inplace_vector&& other) noexcept = default; // for trivial types
|
||||
|
||||
template<class U = T,
|
||||
typename std::enable_if<std::is_move_constructible<U>::value &&
|
||||
not std::is_trivially_move_constructible<typename std::remove_reference<T>::type>::value,
|
||||
int>::type = 0>
|
||||
LYNIPV_CXX14_CONSTEXPR inplace_vector(inplace_vector&& other) noexcept(N == 0 || std::is_nothrow_move_constructible<T>::value) {
|
||||
for (size_type idx = 0; idx != other.size(); ++idx) {
|
||||
unchecked_push_back(std::move(other[idx]));
|
||||
}
|
||||
other.clear();
|
||||
}
|
||||
|
||||
template<bool C = std::is_copy_constructible<T>::value, typename std::enable_if<C, int>::type = 0>
|
||||
constexpr inplace_vector(std::initializer_list<T> init) : inplace_vector(init.begin(), init.end()) {}
|
||||
|
||||
#if __cplusplus >= 202302L && defined(__cpp_lib_containers_ranges)
|
||||
template<lyn_inplace_vector_detail::container_compatiblel_range<T> R>
|
||||
constexpr inplace_vector(std::from_range_t, R&& rg) {
|
||||
if constexpr (std::ranges::sized_range<R>) {
|
||||
if (std::ranges::size(rg) > N) throw std::bad_alloc();
|
||||
for (auto&& val : rg) unchecked_emplace_back(std::forward<decltype(val)>(val));
|
||||
}
|
||||
else {
|
||||
for (auto&& val : rg) emplace_back(std::forward<decltype(val)>(val));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// assignment
|
||||
LYNIPV_CXX14_CONSTEXPR inplace_vector& operator=(const inplace_vector& other) = default; // for trivial types
|
||||
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto operator=(const inplace_vector& other) ->
|
||||
typename std::enable_if<not(std::is_trivially_destructible<U>::value&& std::is_trivially_copy_constructible<U>::value&&
|
||||
std::is_trivially_copy_assignable<U>::value),
|
||||
inplace_vector&>::type {
|
||||
assign(other.begin(), other.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
LYNIPV_CXX14_CONSTEXPR inplace_vector& operator=(inplace_vector&& other) noexcept(
|
||||
N == 0 || (std::is_nothrow_move_assignable<T>::value)) = default; // for trivial types
|
||||
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto operator=(inplace_vector&& other) noexcept(
|
||||
N == 0 || (std::is_nothrow_move_assignable<T>::value && std::is_nothrow_move_constructible<T>::value &&
|
||||
not std::is_trivially_copyable<typename std::remove_reference<T>::type>::value)) ->
|
||||
typename std::enable_if<not(std::is_trivially_destructible<U>::value&& std::is_trivially_move_constructible<U>::value&&
|
||||
std::is_trivially_move_assignable<U>::value),
|
||||
inplace_vector&>::type {
|
||||
clear();
|
||||
std::move(other.begin(), other.end(), std::back_inserter(*this));
|
||||
other.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto operator=(std::initializer_list<T> init) ->
|
||||
typename std::enable_if<std::is_copy_constructible<U>::value, inplace_vector&>::type {
|
||||
if (init.size() > capacity()) throw std::bad_alloc();
|
||||
assign(init.begin(), init.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto assign(size_type count, const T& value) ->
|
||||
typename std::enable_if<std::is_copy_constructible<U>::value>::type {
|
||||
if (count > capacity()) throw std::bad_alloc();
|
||||
clear();
|
||||
while (count != size()) push_back(value);
|
||||
}
|
||||
|
||||
template<class InputIt>
|
||||
LYNIPV_CXX14_CONSTEXPR auto assign(InputIt first, InputIt last) ->
|
||||
typename std::enable_if<std::is_constructible<T, typename std::iterator_traits<InputIt>::value_type>::value>::type {
|
||||
clear();
|
||||
std::copy(first, last, std::back_inserter(*this));
|
||||
}
|
||||
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto assign(std::initializer_list<T> ilist) ->
|
||||
typename std::enable_if<std::is_copy_constructible<U>::value>::type {
|
||||
if (ilist.size() > capacity()) throw std::bad_alloc();
|
||||
clear();
|
||||
std::copy(ilist.begin(), ilist.end(), std::back_inserter(*this));
|
||||
}
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
template<lyn_inplace_vector_detail::container_compatiblel_range<T> R>
|
||||
constexpr void assign_range(R&& rg)
|
||||
requires std::constructible_from<T&, std::ranges::range_reference_t<R>>
|
||||
{
|
||||
clear();
|
||||
append_range(std::forward<R>(rg));
|
||||
}
|
||||
|
||||
template<lyn_inplace_vector_detail::container_compatiblel_range<T> R>
|
||||
constexpr void append_range(R&& rg)
|
||||
requires std::constructible_from<T&, std::ranges::range_reference_t<R>>
|
||||
{
|
||||
if constexpr (std::ranges::sized_range<R>) {
|
||||
if (size() + std::ranges::size(rg) > capacity()) throw std::bad_alloc();
|
||||
for (auto&& val : rg) {
|
||||
unchecked_emplace_back(std::forward<decltype(val)>(val));
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (auto&& val : rg) {
|
||||
emplace_back(std::forward<decltype(val)>(val));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<lyn_inplace_vector_detail::container_compatiblel_range<T> R>
|
||||
constexpr std::ranges::borrowed_iterator_t<R> try_append_range(R&& rg)
|
||||
requires std::constructible_from<T&, std::ranges::range_reference_t<R>>
|
||||
{
|
||||
auto it = std::ranges::begin(rg);
|
||||
for (auto end = std::ranges::end(rg); it != end; std::ranges::advance(it, 1)) {
|
||||
if (size() == capacity()) break;
|
||||
unchecked_emplace_back(*it);
|
||||
}
|
||||
return it;
|
||||
}
|
||||
#endif
|
||||
|
||||
// element access
|
||||
LYNIPV_CXX14_CONSTEXPR reference at(size_type idx) {
|
||||
if (idx >= size()) throw std::out_of_range("");
|
||||
return ref(idx);
|
||||
}
|
||||
LYNIPV_CXX14_CONSTEXPR const_reference at(size_type idx) const {
|
||||
if (idx >= size()) throw std::out_of_range("");
|
||||
return ref(idx);
|
||||
}
|
||||
LYNIPV_CXX14_CONSTEXPR reference front() noexcept { return ref(0); }
|
||||
constexpr const_reference front() const noexcept { return ref(0); }
|
||||
LYNIPV_CXX14_CONSTEXPR reference back() noexcept { return ref(size() - 1); }
|
||||
constexpr const_reference back() const noexcept { return ref(size() - 1); }
|
||||
|
||||
LYNIPV_CXX14_CONSTEXPR pointer data() noexcept { return ptr(0); }
|
||||
LYNIPV_CXX14_CONSTEXPR const_pointer data() const noexcept { return ptr(0); }
|
||||
|
||||
// iterators
|
||||
constexpr const_iterator cbegin() const noexcept { return data(); }
|
||||
constexpr const_iterator cend() const noexcept { return std::next(cbegin(), static_cast<difference_type>(size())); }
|
||||
constexpr const_iterator begin() const noexcept { return cbegin(); }
|
||||
constexpr const_iterator end() const noexcept { return cend(); }
|
||||
LYNIPV_CXX14_CONSTEXPR iterator begin() noexcept { return data(); }
|
||||
LYNIPV_CXX14_CONSTEXPR iterator end() noexcept { return std::next(begin(), static_cast<difference_type>(size())); }
|
||||
|
||||
constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); }
|
||||
constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); }
|
||||
constexpr const_reverse_iterator rbegin() const noexcept { return crbegin(); }
|
||||
constexpr const_reverse_iterator rend() const noexcept { return crend(); }
|
||||
LYNIPV_CXX14_CONSTEXPR reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
|
||||
LYNIPV_CXX14_CONSTEXPR reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
|
||||
|
||||
// size and capacity
|
||||
constexpr bool empty() const noexcept { return size() == 0; }
|
||||
static constexpr size_type max_size() noexcept { return N; }
|
||||
static constexpr size_type capacity() noexcept { return N; }
|
||||
|
||||
private:
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto unchecked_resize(size_type count) ->
|
||||
typename std::enable_if<std::is_default_constructible<U>::value>::type {
|
||||
if (count < size()) {
|
||||
shrink_to(count);
|
||||
}
|
||||
else {
|
||||
while (count != size()) {
|
||||
unchecked_emplace_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto unchecked_resize(size_type count, const value_type& value) ->
|
||||
typename std::enable_if<std::is_copy_constructible<U>::value>::type {
|
||||
if (count < size()) {
|
||||
shrink_to(count);
|
||||
}
|
||||
else {
|
||||
while (count != size()) {
|
||||
unchecked_push_back(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto resize(size_type count) -> typename std::enable_if<std::is_default_constructible<U>::value>::type {
|
||||
if (count > capacity()) throw std::bad_alloc();
|
||||
unchecked_resize(count);
|
||||
}
|
||||
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto resize(size_type count, const value_type& value) ->
|
||||
typename std::enable_if<std::is_copy_constructible<U>::value>::type {
|
||||
if (count > capacity()) throw std::bad_alloc();
|
||||
unchecked_resize(count, value);
|
||||
}
|
||||
|
||||
static LYNIPV_CXX14_CONSTEXPR void reserve(size_type new_cap) {
|
||||
if (new_cap > capacity()) throw std::bad_alloc();
|
||||
}
|
||||
static LYNIPV_CXX14_CONSTEXPR void shrink_to_fit() noexcept {}
|
||||
|
||||
// modifiers
|
||||
private:
|
||||
/*
|
||||
// optimization idea for all insert() functions to get away from constructing and rotating:
|
||||
LYNIPV_CXX14_CONSTEXPR size_type make_room_at(const_iterator pos, size_type count) {
|
||||
// - move construct some T's at current end().
|
||||
// - move assign some T's before current end().
|
||||
// - destroy the old host for those "moved from" but not "moved to".
|
||||
//
|
||||
// This should leave a nice gap to construct the new range in without the need for move assigning via rotate afterwards.
|
||||
//
|
||||
// I don't know what to do about exception guarantees with that implementation though so I'll leave it to something to think
|
||||
// about. Perhaps it can be used for T's with a non-throwing move assignment operator and move constructor.
|
||||
// It will at least be ok for trivial types.
|
||||
}
|
||||
*/
|
||||
|
||||
public:
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto insert(const_iterator pos, const T& value) ->
|
||||
typename std::enable_if<std::is_copy_constructible<U>::value, iterator>::type {
|
||||
// static_assert(std::is_nothrow_move_assignable<T>::value, "only nothrow move assignable types may be used for now");
|
||||
if (size() == capacity()) throw std::bad_alloc();
|
||||
const auto ncpos = const_cast<iterator>(pos);
|
||||
unchecked_push_back(value);
|
||||
std::rotate(ncpos, std::prev(end()), end());
|
||||
return ncpos;
|
||||
}
|
||||
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto insert(const_iterator pos, T&& value) ->
|
||||
typename std::enable_if<std::is_move_constructible<U>::value, iterator>::type {
|
||||
// static_assert(std::is_nothrow_move_assignable<T>::value, "only nothrow move assignable types may be used for now");
|
||||
if (size() == capacity()) throw std::bad_alloc();
|
||||
const auto ncpos = const_cast<iterator>(pos);
|
||||
unchecked_push_back(std::move(value));
|
||||
std::rotate(ncpos, std::prev(end()), end());
|
||||
return ncpos;
|
||||
}
|
||||
|
||||
template<class U = T>
|
||||
LYNIPV_CXX20_CONSTEXPR auto insert(const_iterator pos, size_type count, const T& value) ->
|
||||
typename std::enable_if<std::is_copy_constructible<U>::value, iterator>::type {
|
||||
// static_assert(std::is_nothrow_move_assignable<T>::value, "only nothrow move assignable types may be used for now");
|
||||
if (size() + count > capacity()) throw std::bad_alloc();
|
||||
const auto ncpos = const_cast<iterator>(pos);
|
||||
auto oldsize = size();
|
||||
auto first_inserted = end();
|
||||
try {
|
||||
while (count--) {
|
||||
unchecked_push_back(value);
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
shrink_to(oldsize);
|
||||
throw;
|
||||
}
|
||||
std::rotate(ncpos, first_inserted, end());
|
||||
return ncpos;
|
||||
}
|
||||
template<class InputIt, class U = T>
|
||||
LYNIPV_CXX20_CONSTEXPR auto insert(const_iterator pos, InputIt first, InputIt last) ->
|
||||
typename std::enable_if<std::is_constructible<typename std::iterator_traits<InputIt>::value_type>::value &&
|
||||
!std::is_const<U>::value,
|
||||
iterator>::type {
|
||||
// static_assert(std::is_nothrow_move_assignable<T>::value, "only nothrow move assignable types may be used for now");
|
||||
const auto ncpos = const_cast<iterator>(pos);
|
||||
auto oldsize = size();
|
||||
auto first_inserted = end();
|
||||
try {
|
||||
for (; first != last; std::advance(first, 1)) {
|
||||
push_back(*first);
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
shrink_to(oldsize);
|
||||
throw;
|
||||
}
|
||||
std::rotate(ncpos, first_inserted, end());
|
||||
return ncpos;
|
||||
}
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto insert(const_iterator pos, std::initializer_list<T> ilist) ->
|
||||
typename std::enable_if<std::is_copy_constructible<U>::value && !std::is_const<U>::value, iterator>::type {
|
||||
return insert(pos, ilist.begin(), ilist.end());
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
LYNIPV_CXX14_CONSTEXPR auto emplace(const_iterator pos, Args&&... args) ->
|
||||
typename std::enable_if<std::is_constructible<T, Args...>::value, iterator>::type {
|
||||
// static_assert(std::is_nothrow_move_assignable<T>::value, "only nothrow move assignable types may be used for now");
|
||||
const auto ncpos = const_cast<iterator>(pos);
|
||||
emplace_back(std::forward<Args>(args)...);
|
||||
std::rotate(ncpos, std::prev(end()), end());
|
||||
return ncpos;
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
LYNIPV_CXX14_CONSTEXPR auto unchecked_emplace_back(Args&&... args) ->
|
||||
typename std::enable_if<std::is_constructible<T, Args...>::value, reference>::type {
|
||||
auto& rv = construct(size(), std::forward<Args>(args)...);
|
||||
this->inc();
|
||||
return rv;
|
||||
}
|
||||
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto unchecked_push_back(T const& value) ->
|
||||
typename std::enable_if<std::is_copy_constructible<U>::value, reference>::type {
|
||||
auto& rv = construct(size(), value);
|
||||
this->inc();
|
||||
return rv;
|
||||
}
|
||||
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto unchecked_push_back(T&& value) ->
|
||||
typename std::enable_if<std::is_move_constructible<U>::value, reference>::type {
|
||||
auto& rv = construct(size(), std::move(value));
|
||||
this->inc();
|
||||
return rv;
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
LYNIPV_CXX14_CONSTEXPR auto emplace_back(Args&&... args) ->
|
||||
typename std::enable_if<std::is_constructible<T, Args...>::value, reference>::type {
|
||||
if (size() == N) throw std::bad_alloc();
|
||||
return unchecked_emplace_back(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
LYNIPV_CXX14_CONSTEXPR auto try_emplace_back(Args&&... args) ->
|
||||
typename std::enable_if<std::is_constructible<T, Args...>::value, pointer>::type {
|
||||
if (size() == N) return nullptr;
|
||||
return std::addressof(unchecked_emplace_back(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto push_back(T const& value) ->
|
||||
typename std::enable_if<std::is_copy_constructible<U>::value, reference>::type {
|
||||
if (size() == N) throw std::bad_alloc();
|
||||
return unchecked_push_back(value);
|
||||
}
|
||||
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto push_back(T&& value) ->
|
||||
typename std::enable_if<std::is_move_constructible<U>::value, reference>::type {
|
||||
if (size() == N) throw std::bad_alloc();
|
||||
return unchecked_push_back(std::move(value));
|
||||
}
|
||||
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto try_push_back(T const& value) ->
|
||||
typename std::enable_if<std::is_copy_constructible<U>::value, pointer>::type {
|
||||
if (size() == N) return nullptr;
|
||||
return std::addressof(unchecked_push_back(value));
|
||||
}
|
||||
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto try_push_back(T&& value) ->
|
||||
typename std::enable_if<std::is_move_constructible<U>::value, pointer>::type {
|
||||
if (size() == N) return nullptr;
|
||||
return std::addressof(unchecked_push_back(std::move(value)));
|
||||
}
|
||||
|
||||
LYNIPV_CXX14_CONSTEXPR void pop_back() noexcept { destroy(this->dec()); }
|
||||
LYNIPV_CXX14_CONSTEXPR void clear() noexcept { shrink_to(0); }
|
||||
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto erase(const_iterator first, const_iterator last) ->
|
||||
typename std::enable_if<!std::is_const<U>::value, iterator>::type {
|
||||
auto ncfirst = const_cast<iterator>(first);
|
||||
auto nclast = const_cast<iterator>(last);
|
||||
auto removed = static_cast<std::size_t>(std::distance(ncfirst, nclast));
|
||||
std::move(nclast, end(), ncfirst);
|
||||
for (size_type idx = size() - removed; idx < size(); ++idx) {
|
||||
destroy(idx);
|
||||
}
|
||||
this->dec(removed);
|
||||
return ncfirst;
|
||||
}
|
||||
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto erase(const_iterator pos) -> typename std::enable_if<!std::is_const<U>::value, iterator>::type {
|
||||
return erase(pos, std::next(pos));
|
||||
}
|
||||
|
||||
template<class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR auto swap(inplace_vector& other) noexcept(N == 0 ||
|
||||
(lyn_inplace_vector_detail::is_nothrow_swappable<T>::value &&
|
||||
std::is_nothrow_move_constructible<T>::value)) ->
|
||||
typename std::enable_if<!std::is_const<U>::value>::type {
|
||||
auto&& p = (size() < other.size()) ? std::pair<inplace_vector&, inplace_vector&>(*this, other)
|
||||
: std::pair<inplace_vector&, inplace_vector&>(other, *this);
|
||||
auto& small = p.first;
|
||||
auto& large = p.second;
|
||||
size_type idx = 0, small_size = small.size();
|
||||
for (; idx < small_size; ++idx) {
|
||||
using std::swap;
|
||||
swap(small[idx], large[idx]);
|
||||
}
|
||||
for (; idx < large.size(); ++idx) {
|
||||
small.push_back(std::move(large[idx]));
|
||||
}
|
||||
large.shrink_to(small_size);
|
||||
}
|
||||
|
||||
LYNIPV_CXX14_CONSTEXPR void friend swap(inplace_vector& lhs, inplace_vector& rhs) noexcept(
|
||||
N == 0 || (lyn_inplace_vector_detail::is_nothrow_swappable<T>::value && std::is_nothrow_move_constructible<T>::value)) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
#if __cplusplus >= 202002L
|
||||
constexpr friend auto operator<=>(const inplace_vector& lhs, const inplace_vector& rhs) {
|
||||
return std::lexicographical_compare_three_way(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
|
||||
}
|
||||
#else
|
||||
friend bool operator<(const inplace_vector& lhs, const inplace_vector& rhs) {
|
||||
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
|
||||
}
|
||||
friend bool operator>(const inplace_vector& lhs, const inplace_vector& rhs) { return rhs < lhs; }
|
||||
friend bool operator<=(const inplace_vector& lhs, const inplace_vector& rhs) { return !(rhs < lhs); }
|
||||
friend bool operator>=(const inplace_vector& lhs, const inplace_vector& rhs) { return rhs <= lhs; }
|
||||
friend bool operator!=(const inplace_vector& lhs, const inplace_vector& rhs) { return !(lhs == rhs); }
|
||||
#endif
|
||||
friend bool operator==(const inplace_vector& lhs, const inplace_vector& rhs) {
|
||||
if (lhs.size() != rhs.size()) return false;
|
||||
return std::equal(lhs.cbegin(), lhs.cend(), rhs.cbegin());
|
||||
}
|
||||
};
|
||||
|
||||
template<class T, size_t N, class U = T>
|
||||
LYNIPV_CXX14_CONSTEXPR typename inplace_vector<T, N>::size_type erase(inplace_vector<T, N>& c, const U& value) {
|
||||
auto it = std::remove(c.begin(), c.end(), value);
|
||||
auto r = static_cast<typename inplace_vector<T, N>::size_type>(std::distance(it, c.end()));
|
||||
c.erase(it, it.end());
|
||||
return r;
|
||||
}
|
||||
|
||||
template<class T, size_t N, class Predicate>
|
||||
LYNIPV_CXX14_CONSTEXPR typename inplace_vector<T, N>::size_type erase_if(inplace_vector<T, N>& c, Predicate pred) {
|
||||
auto it = std::remove_if(c.begin(), c.end(), pred);
|
||||
auto r = static_cast<typename inplace_vector<T, N>::size_type>(std::distance(it, c.end()));
|
||||
c.erase(it, c.end());
|
||||
return r;
|
||||
}
|
||||
|
||||
} // namespace lyn
|
||||
|
||||
// clean up defines
|
||||
#undef LYNIPV_CXX14_CONSTEXPR
|
||||
#undef LYNIPV_CXX20_CONSTEXPR
|
||||
#undef LYNIPV_CONSTRUCT_AT
|
||||
|
||||
#endif
|
||||
198
include/Util/RandomPicker.h
Normal file
198
include/Util/RandomPicker.h
Normal file
@@ -0,0 +1,198 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
template <typename T, typename WeightType>
|
||||
class RandomPickerT
|
||||
{
|
||||
public:
|
||||
struct Entry
|
||||
{
|
||||
Entry() = default;
|
||||
Entry(const T& val, WeightType weight) : Val{ val }, Weight{ weight } {}
|
||||
|
||||
T Val{};
|
||||
const WeightType Weight{};
|
||||
WeightType AccumulatedWeight{};
|
||||
WeightType GetWeightSum() { return AccumulatedWeight + Weight; }
|
||||
};
|
||||
|
||||
public:
|
||||
std::unique_ptr<Entry[]> Entries{};
|
||||
WeightType TotalWeight{};
|
||||
uint32_t TotalEntries{};
|
||||
const uint32_t DataSize{};
|
||||
|
||||
public:
|
||||
RandomPickerT() = default;
|
||||
|
||||
template <typename Container, typename WeightGetter>
|
||||
RandomPickerT(const Container& container, WeightGetter getter)
|
||||
: Entries{ std::make_unique<Entry[]>(container.size()) }
|
||||
, TotalEntries{ container.size() }
|
||||
, DataSize{ container.size() }
|
||||
{
|
||||
int counter{};
|
||||
for (const auto& entry : container)
|
||||
{
|
||||
auto weight = getter(entry);
|
||||
Entries[counter++] = Entry{entry, weight};
|
||||
}
|
||||
|
||||
RecalculateWeights();
|
||||
}
|
||||
|
||||
template <typename EntriesT>
|
||||
RandomPickerT(const EntriesT& entries)
|
||||
: Entries{ std::make_unique<Entry[]>(entries.size()) }
|
||||
, TotalEntries{ entries.size() }
|
||||
, DataSize{ entries.size() }
|
||||
{
|
||||
int counter{};
|
||||
for (const auto& entry : entries)
|
||||
{
|
||||
Entries[counter++] = entry;
|
||||
}
|
||||
|
||||
RecalculateWeights();
|
||||
}
|
||||
|
||||
public:
|
||||
T GetRandom(int& index, WeightType randomVal) const
|
||||
{
|
||||
WeightType weight = GetRandomWeight(randomVal);
|
||||
index = GetIndex(weight);
|
||||
|
||||
return Entries[index].Val;
|
||||
}
|
||||
|
||||
int GetIndex(WeightType weight) const
|
||||
{
|
||||
if (weight < 0 || weight > TotalWeight || TotalEntries == 0)
|
||||
return -1;
|
||||
if (TotalEntries == 1)
|
||||
return 0;
|
||||
return BinarySearchRecursive(weight, 0, TotalEntries - 1);
|
||||
}
|
||||
|
||||
void RemoveEntry(int index)
|
||||
{
|
||||
if (ValidateIndex(index)) RemoveEntryInternal(index);
|
||||
}
|
||||
|
||||
T GetAndRemoveRandom(int& index, WeightType randomVal)
|
||||
{
|
||||
WeightType weight = GetRandomWeight(randomVal);
|
||||
int index = GetIndex(weight);
|
||||
|
||||
auto returnVal = Entries[index].Val;
|
||||
RemoveEntryInternal(index);
|
||||
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
T GetAndRemoveRandom(WeightType randomVal)
|
||||
{
|
||||
int index{};
|
||||
return GetAndRemoveRandom(index, randomVal);
|
||||
}
|
||||
|
||||
T Peek(int index) const
|
||||
{
|
||||
_ASSERT(ValidateIndex(index));
|
||||
return Entries[index];
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
TotalEntries = DataSize;
|
||||
RecalculateWeights();
|
||||
}
|
||||
|
||||
template <typename WeightGetter>
|
||||
void SetObjectAtIndex(int index, const T& val, WeightGetter getter)
|
||||
{
|
||||
if (!ValidateIndex(index)) return;
|
||||
|
||||
auto entry = Entries[index];
|
||||
|
||||
auto newWeight = getter(val);
|
||||
bool recalculateWeights = newWeight != entry.Weight;
|
||||
|
||||
Entries[index] = Entry{ val, newWeight };
|
||||
|
||||
if (recalculateWeights) RecalculateWeights();
|
||||
}
|
||||
|
||||
void SetEntryAtIndex(int index, const Entry& entry)
|
||||
{
|
||||
if (!ValidateIndex(index)) return;
|
||||
|
||||
bool recalculateWeights = entry.Weight != Entries[index].Weight;
|
||||
Entries[index] = entry;
|
||||
|
||||
if (recalculateWeights) RecalculateWeights();
|
||||
}
|
||||
|
||||
private:
|
||||
void RecalculateWeights()
|
||||
{
|
||||
WeightType accumulatedWeight = 0;
|
||||
for (uint32_t i{}; i < TotalEntries; ++i)
|
||||
{
|
||||
Entries[i].AccumulatedWeight = accumulatedWeight;
|
||||
accumulatedWeight += Entries[i].Weight;
|
||||
}
|
||||
TotalWeight = accumulatedWeight;
|
||||
}
|
||||
|
||||
WeightType GetRandomWeight(WeightType randomVal) const
|
||||
{
|
||||
if constexpr (std::is_integral_v<WeightType>)
|
||||
{
|
||||
return randomVal % TotalWeight;
|
||||
}
|
||||
if constexpr (std::is_floating_point_v<WeightType>)
|
||||
{
|
||||
return std::fmod(randomVal, TotalWeight);
|
||||
}
|
||||
}
|
||||
|
||||
int BinarySearchRecursive(WeightType weight, int min, int max) const
|
||||
{
|
||||
int middle = (min + max) >> 1;
|
||||
|
||||
auto entry = Entries[middle];
|
||||
if (weight >= entry.AccumulatedWeight && weight < entry.GetWeightSum())
|
||||
return middle;
|
||||
|
||||
return (weight < entry.AccumulatedWeight) ? BinarySearchRecursive(weight, min, middle - 1) : BinarySearchRecursive(weight, middle + 1, max);
|
||||
}
|
||||
|
||||
void RemoveEntryInternal(int index)
|
||||
{
|
||||
if (!ValidateIndex(index))
|
||||
return;
|
||||
|
||||
std::swap(Entries[index], Entries.back());
|
||||
--TotalEntries;
|
||||
|
||||
if (TotalEntries != 0) RecalculateWeights();
|
||||
else TotalWeight = 0;
|
||||
}
|
||||
|
||||
bool ValidateIndex(int index) const
|
||||
{
|
||||
return index >= 0 && index < TotalEntries;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using RandomPicker32 = RandomPickerT<T, uint32_t>;
|
||||
template <typename T>
|
||||
using RandomPicker64 = RandomPickerT<T, uint64_t>;
|
||||
template <typename T>
|
||||
using RandomPickerF = RandomPickerT<T, float>;
|
||||
template <typename T>
|
||||
using RandomPickerD = RandomPickerT<T, double>;
|
||||
41
include/Util/ResourceAccess.h
Normal file
41
include/Util/ResourceAccess.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/os/mutex.h"
|
||||
#include "core/object/ref_counted.h"
|
||||
|
||||
template <typename T, typename LockT>
|
||||
class ResourceAccess final
|
||||
{
|
||||
public:
|
||||
ResourceAccess(T& resource, LockT& lock)
|
||||
: Resource{ resource }
|
||||
, Lock{ lock }
|
||||
{
|
||||
Lock.lock();
|
||||
}
|
||||
~ResourceAccess()
|
||||
{
|
||||
if (validLock) Lock.unlock();
|
||||
}
|
||||
|
||||
ResourceAccess(const ResourceAccess& other) = delete;
|
||||
ResourceAccess& operator=(const ResourceAccess& other) = delete;
|
||||
ResourceAccess& operator=(ResourceAccess&& other) noexcept = delete;
|
||||
|
||||
ResourceAccess(ResourceAccess&& other) noexcept
|
||||
: Resource{ other.Resource }
|
||||
, Lock{ other.Lock }
|
||||
, validLock{ true }
|
||||
{
|
||||
other.validLock = false;
|
||||
}
|
||||
|
||||
public:
|
||||
T& GetResource() { return Resource; }
|
||||
T* operator->() { return &Resource; }
|
||||
|
||||
private:
|
||||
T& Resource;
|
||||
LockT& Lock;
|
||||
bool validLock{ true };
|
||||
};
|
||||
112
include/Util/SharedBuffer.h
Normal file
112
include/Util/SharedBuffer.h
Normal file
@@ -0,0 +1,112 @@
|
||||
#pragma once
|
||||
|
||||
#include "stdint.h"
|
||||
#include "cassert"
|
||||
#include "Util/Span.h"
|
||||
|
||||
template <typename T>
|
||||
inline T const* ByteToData(uint8_t const* data) { return reinterpret_cast<T const*>(data); }
|
||||
|
||||
template <typename T>
|
||||
inline T* ByteToData(uint8_t* data) { return reinterpret_cast<T*>(data); }
|
||||
|
||||
template <typename T, typename MetaData>
|
||||
class SharedBuffer final
|
||||
{
|
||||
private:
|
||||
struct ControlBlock
|
||||
{
|
||||
ControlBlock(uint32_t size, const MetaData& data) : Size{ size }, Data { data } {}
|
||||
|
||||
std::atomic<uint32_t> Count{ 1 };
|
||||
uint32_t Size{};
|
||||
MetaData Data{};
|
||||
};
|
||||
|
||||
static constexpr size_t ControlBlockOffset = 0;
|
||||
static constexpr size_t DataOffset = sizeof(ControlBlock);
|
||||
|
||||
public:
|
||||
SharedBuffer() = default;
|
||||
SharedBuffer(int size, const MetaData& metaData)
|
||||
{
|
||||
Data = new uint8_t[sizeof(ControlBlock) + size * sizeof(T)]{};
|
||||
|
||||
new (GetControlBlock()) ControlBlock(size, metaData);
|
||||
for (size_t i{}; i < size; ++i)
|
||||
{
|
||||
new (&Ptr()[i]) T{};
|
||||
}
|
||||
}
|
||||
|
||||
~SharedBuffer()
|
||||
{
|
||||
Destruct();
|
||||
}
|
||||
|
||||
SharedBuffer(const SharedBuffer& other)
|
||||
: Data{ other.Data }
|
||||
{
|
||||
if (Data) ++GetControlBlock()->Count;
|
||||
}
|
||||
SharedBuffer(SharedBuffer&& other) noexcept
|
||||
: Data{ other.Data }
|
||||
{
|
||||
other.Data = nullptr;
|
||||
}
|
||||
|
||||
SharedBuffer& operator=(const SharedBuffer& other)
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
Destruct();
|
||||
Data = other.Data;
|
||||
if (Data) ++GetControlBlock()->Count;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
SharedBuffer& operator=(SharedBuffer&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
Destruct();
|
||||
Data = other.Data;
|
||||
other.Data = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
ControlBlock const* GetControlBlock() const { assert(Data); return ByteToData<ControlBlock>(Data + ControlBlockOffset); }
|
||||
ControlBlock* GetControlBlock() { assert(Data); return ByteToData<ControlBlock>(Data + ControlBlockOffset); }
|
||||
|
||||
void Destruct()
|
||||
{
|
||||
if (Data != nullptr && --GetControlBlock()->Count == 0)
|
||||
{
|
||||
uint32_t size = GetSize();
|
||||
GetControlBlock()->~ControlBlock();
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
Ptr()[i].~T();
|
||||
delete[] Data;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
T const* Ptr() const { assert(Data); return ByteToData<T>(Data + DataOffset); }
|
||||
T* Ptr() { assert(Data); return ByteToData<T>(Data + DataOffset); }
|
||||
tcb::span<const T> GetData() const { return tcb::span<const T>(Ptr(), GetSize()); }
|
||||
tcb::span<T> GetData() { return tcb::span<T>(Ptr(), GetSize()); }
|
||||
MetaData const* GetMetaData() const { assert(Data); return &GetControlBlock()->Data; }
|
||||
MetaData* GetMetaData() { assert(Data); return &GetControlBlock()->Data; }
|
||||
|
||||
T& operator[] (uint32_t index) { assert(index < GetControlBlock()->Size); return GetData()[index]; }
|
||||
const T& operator[] (uint32_t index) const { assert(index < GetControlBlock()->Size); return GetData()[index]; }
|
||||
uint32_t GetSize() const { return GetControlBlock()->Size; }
|
||||
|
||||
operator bool() const { return Data; }
|
||||
|
||||
private:
|
||||
uint8_t* Data{};
|
||||
};
|
||||
630
include/Util/Span.h
Normal file
630
include/Util/Span.h
Normal file
@@ -0,0 +1,630 @@
|
||||
|
||||
/*
|
||||
This is an implementation of C++20's std::span
|
||||
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/n4820.pdf
|
||||
*/
|
||||
|
||||
// Copyright Tristan Brindle 2018.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file ../../LICENSE_1_0.txt or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef TCB_SPAN_HPP_INCLUDED
|
||||
#define TCB_SPAN_HPP_INCLUDED
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
#ifndef TCB_SPAN_NO_EXCEPTIONS
|
||||
// Attempt to discover whether we're being compiled with exception support
|
||||
#if !(defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND))
|
||||
#define TCB_SPAN_NO_EXCEPTIONS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef TCB_SPAN_NO_EXCEPTIONS
|
||||
#include <cstdio>
|
||||
#include <stdexcept>
|
||||
#endif
|
||||
|
||||
// Various feature test macros
|
||||
|
||||
#ifndef TCB_SPAN_NAMESPACE_NAME
|
||||
#define TCB_SPAN_NAMESPACE_NAME tcb
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
|
||||
#define TCB_SPAN_HAVE_CPP17
|
||||
#endif
|
||||
|
||||
#if __cplusplus >= 201402L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L)
|
||||
#define TCB_SPAN_HAVE_CPP14
|
||||
#endif
|
||||
|
||||
namespace TCB_SPAN_NAMESPACE_NAME {
|
||||
|
||||
// Establish default contract checking behavior
|
||||
#if !defined(TCB_SPAN_THROW_ON_CONTRACT_VIOLATION) && \
|
||||
!defined(TCB_SPAN_TERMINATE_ON_CONTRACT_VIOLATION) && \
|
||||
!defined(TCB_SPAN_NO_CONTRACT_CHECKING)
|
||||
#if defined(NDEBUG) || !defined(TCB_SPAN_HAVE_CPP14)
|
||||
#define TCB_SPAN_NO_CONTRACT_CHECKING
|
||||
#else
|
||||
#define TCB_SPAN_TERMINATE_ON_CONTRACT_VIOLATION
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(TCB_SPAN_THROW_ON_CONTRACT_VIOLATION)
|
||||
struct contract_violation_error : std::logic_error {
|
||||
explicit contract_violation_error(const char* msg) : std::logic_error(msg)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline void contract_violation(const char* msg)
|
||||
{
|
||||
throw contract_violation_error(msg);
|
||||
}
|
||||
|
||||
#elif defined(TCB_SPAN_TERMINATE_ON_CONTRACT_VIOLATION)
|
||||
[[noreturn]] inline void contract_violation(const char* /*unused*/)
|
||||
{
|
||||
std::terminate();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(TCB_SPAN_NO_CONTRACT_CHECKING)
|
||||
#define TCB_SPAN_STRINGIFY(cond) #cond
|
||||
#define TCB_SPAN_EXPECT(cond) \
|
||||
cond ? (void) 0 : contract_violation("Expected " TCB_SPAN_STRINGIFY(cond))
|
||||
#else
|
||||
#define TCB_SPAN_EXPECT(cond)
|
||||
#endif
|
||||
|
||||
#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_inline_variables)
|
||||
#define TCB_SPAN_INLINE_VAR inline
|
||||
#else
|
||||
#define TCB_SPAN_INLINE_VAR
|
||||
#endif
|
||||
|
||||
#if defined(TCB_SPAN_HAVE_CPP14) || \
|
||||
(defined(__cpp_constexpr) && __cpp_constexpr >= 201304)
|
||||
#define TCB_SPAN_HAVE_CPP14_CONSTEXPR
|
||||
#endif
|
||||
|
||||
#if defined(TCB_SPAN_HAVE_CPP14_CONSTEXPR)
|
||||
#define TCB_SPAN_CONSTEXPR14 constexpr
|
||||
#else
|
||||
#define TCB_SPAN_CONSTEXPR14
|
||||
#endif
|
||||
|
||||
#if defined(TCB_SPAN_HAVE_CPP14_CONSTEXPR) && \
|
||||
(!defined(_MSC_VER) || _MSC_VER > 1900)
|
||||
#define TCB_SPAN_CONSTEXPR_ASSIGN constexpr
|
||||
#else
|
||||
#define TCB_SPAN_CONSTEXPR_ASSIGN
|
||||
#endif
|
||||
|
||||
#if defined(TCB_SPAN_NO_CONTRACT_CHECKING)
|
||||
#define TCB_SPAN_CONSTEXPR11 constexpr
|
||||
#else
|
||||
#define TCB_SPAN_CONSTEXPR11 TCB_SPAN_CONSTEXPR14
|
||||
#endif
|
||||
|
||||
#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_deduction_guides)
|
||||
#define TCB_SPAN_HAVE_DEDUCTION_GUIDES
|
||||
#endif
|
||||
|
||||
#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_lib_byte)
|
||||
#define TCB_SPAN_HAVE_STD_BYTE
|
||||
#endif
|
||||
|
||||
#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_lib_array_constexpr)
|
||||
#define TCB_SPAN_HAVE_CONSTEXPR_STD_ARRAY_ETC
|
||||
#endif
|
||||
|
||||
#if defined(TCB_SPAN_HAVE_CONSTEXPR_STD_ARRAY_ETC)
|
||||
#define TCB_SPAN_ARRAY_CONSTEXPR constexpr
|
||||
#else
|
||||
#define TCB_SPAN_ARRAY_CONSTEXPR
|
||||
#endif
|
||||
|
||||
#ifdef TCB_SPAN_HAVE_STD_BYTE
|
||||
using byte = std::byte;
|
||||
#else
|
||||
using byte = unsigned char;
|
||||
#endif
|
||||
|
||||
#if defined(TCB_SPAN_HAVE_CPP17)
|
||||
#define TCB_SPAN_NODISCARD [[nodiscard]]
|
||||
#else
|
||||
#define TCB_SPAN_NODISCARD
|
||||
#endif
|
||||
|
||||
TCB_SPAN_INLINE_VAR constexpr std::size_t dynamic_extent = SIZE_MAX;
|
||||
|
||||
template <typename ElementType, std::size_t Extent = dynamic_extent>
|
||||
class span;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename E, std::size_t S>
|
||||
struct span_storage {
|
||||
constexpr span_storage() noexcept = default;
|
||||
|
||||
constexpr span_storage(E* p_ptr, std::size_t /*unused*/) noexcept
|
||||
: ptr(p_ptr)
|
||||
{
|
||||
}
|
||||
|
||||
E* ptr = nullptr;
|
||||
static constexpr std::size_t size = S;
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
struct span_storage<E, dynamic_extent> {
|
||||
constexpr span_storage() noexcept = default;
|
||||
|
||||
constexpr span_storage(E* p_ptr, std::size_t p_size) noexcept
|
||||
: ptr(p_ptr), size(p_size)
|
||||
{
|
||||
}
|
||||
|
||||
E* ptr = nullptr;
|
||||
std::size_t size = 0;
|
||||
};
|
||||
|
||||
// Reimplementation of C++17 std::size() and std::data()
|
||||
#if defined(TCB_SPAN_HAVE_CPP17) || \
|
||||
defined(__cpp_lib_nonmember_container_access)
|
||||
using std::data;
|
||||
using std::size;
|
||||
#else
|
||||
template <class C>
|
||||
constexpr auto size(const C& c) -> decltype(c.size())
|
||||
{
|
||||
return c.size();
|
||||
}
|
||||
|
||||
template <class T, std::size_t N>
|
||||
constexpr std::size_t size(const T(&)[N]) noexcept
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
template <class C>
|
||||
constexpr auto data(C& c) -> decltype(c.data())
|
||||
{
|
||||
return c.data();
|
||||
}
|
||||
|
||||
template <class C>
|
||||
constexpr auto data(const C& c) -> decltype(c.data())
|
||||
{
|
||||
return c.data();
|
||||
}
|
||||
|
||||
template <class T, std::size_t N>
|
||||
constexpr T* data(T(&array)[N]) noexcept
|
||||
{
|
||||
return array;
|
||||
}
|
||||
|
||||
template <class E>
|
||||
constexpr const E* data(std::initializer_list<E> il) noexcept
|
||||
{
|
||||
return il.begin();
|
||||
}
|
||||
#endif // TCB_SPAN_HAVE_CPP17
|
||||
|
||||
#if defined(TCB_SPAN_HAVE_CPP17) || defined(__cpp_lib_void_t)
|
||||
using std::void_t;
|
||||
#else
|
||||
template <typename...>
|
||||
using void_t = void;
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
using uncvref_t =
|
||||
typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
||||
|
||||
template <typename>
|
||||
struct is_span : std::false_type {};
|
||||
|
||||
template <typename T, std::size_t S>
|
||||
struct is_span<span<T, S>> : std::true_type {};
|
||||
|
||||
template <typename>
|
||||
struct is_std_array : std::false_type {};
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
struct is_std_array<std::array<T, N>> : std::true_type {};
|
||||
|
||||
template <typename, typename = void>
|
||||
struct has_size_and_data : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_size_and_data<T, void_t<decltype(detail::size(std::declval<T>())),
|
||||
decltype(detail::data(std::declval<T>()))>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename C, typename U = uncvref_t<C>>
|
||||
struct is_container {
|
||||
static constexpr bool value =
|
||||
!is_span<U>::value && !is_std_array<U>::value &&
|
||||
!std::is_array<U>::value && has_size_and_data<C>::value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using remove_pointer_t = typename std::remove_pointer<T>::type;
|
||||
|
||||
template <typename, typename, typename = void>
|
||||
struct is_container_element_type_compatible : std::false_type {};
|
||||
|
||||
template <typename T, typename E>
|
||||
struct is_container_element_type_compatible<
|
||||
T, E,
|
||||
typename std::enable_if<
|
||||
!std::is_same<
|
||||
typename std::remove_cv<decltype(detail::data(std::declval<T>()))>::type,
|
||||
void>::value&&
|
||||
std::is_convertible<
|
||||
remove_pointer_t<decltype(detail::data(std::declval<T>()))>(*)[],
|
||||
E(*)[]>::value
|
||||
>::type>
|
||||
: std::true_type {
|
||||
};
|
||||
|
||||
template <typename, typename = size_t>
|
||||
struct is_complete : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_complete<T, decltype(sizeof(T))> : std::true_type {};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename ElementType, std::size_t Extent>
|
||||
class span {
|
||||
static_assert(std::is_object<ElementType>::value,
|
||||
"A span's ElementType must be an object type (not a "
|
||||
"reference type or void)");
|
||||
static_assert(detail::is_complete<ElementType>::value,
|
||||
"A span's ElementType must be a complete type (not a forward "
|
||||
"declaration)");
|
||||
static_assert(!std::is_abstract<ElementType>::value,
|
||||
"A span's ElementType cannot be an abstract class type");
|
||||
|
||||
using storage_type = detail::span_storage<ElementType, Extent>;
|
||||
|
||||
public:
|
||||
// constants and types
|
||||
using element_type = ElementType;
|
||||
using value_type = typename std::remove_cv<ElementType>::type;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = element_type*;
|
||||
using const_pointer = const element_type*;
|
||||
using reference = element_type&;
|
||||
using const_reference = const element_type&;
|
||||
using iterator = pointer;
|
||||
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||
|
||||
static constexpr size_type extent = Extent;
|
||||
|
||||
// [span.cons], span constructors, copy, assignment, and destructor
|
||||
template <
|
||||
std::size_t E = Extent,
|
||||
typename std::enable_if<(E == dynamic_extent || E <= 0), int>::type = 0>
|
||||
constexpr span() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
TCB_SPAN_CONSTEXPR11 span(pointer ptr, size_type count)
|
||||
: storage_(ptr, count)
|
||||
{
|
||||
TCB_SPAN_EXPECT(extent == dynamic_extent || count == extent);
|
||||
}
|
||||
|
||||
TCB_SPAN_CONSTEXPR11 span(pointer first_elem, pointer last_elem)
|
||||
: storage_(first_elem, last_elem - first_elem)
|
||||
{
|
||||
TCB_SPAN_EXPECT(extent == dynamic_extent ||
|
||||
last_elem - first_elem ==
|
||||
static_cast<std::ptrdiff_t>(extent));
|
||||
}
|
||||
|
||||
template <std::size_t N, std::size_t E = Extent,
|
||||
typename std::enable_if<
|
||||
(E == dynamic_extent || N == E) &&
|
||||
detail::is_container_element_type_compatible<
|
||||
element_type(&)[N], ElementType>::value,
|
||||
int>::type = 0>
|
||||
constexpr span(element_type(&arr)[N]) noexcept : storage_(arr, N)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N, std::size_t E = Extent,
|
||||
typename std::enable_if<
|
||||
(E == dynamic_extent || N == E) &&
|
||||
detail::is_container_element_type_compatible<
|
||||
std::array<T, N>&, ElementType>::value,
|
||||
int>::type = 0>
|
||||
TCB_SPAN_ARRAY_CONSTEXPR span(std::array<T, N>& arr) noexcept
|
||||
: storage_(arr.data(), N)
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N, std::size_t E = Extent,
|
||||
typename std::enable_if<
|
||||
(E == dynamic_extent || N == E) &&
|
||||
detail::is_container_element_type_compatible<
|
||||
const std::array<T, N>&, ElementType>::value,
|
||||
int>::type = 0>
|
||||
TCB_SPAN_ARRAY_CONSTEXPR span(const std::array<T, N>& arr) noexcept
|
||||
: storage_(arr.data(), N)
|
||||
{
|
||||
}
|
||||
|
||||
template <
|
||||
typename Container, std::size_t E = Extent,
|
||||
typename std::enable_if<
|
||||
E == dynamic_extent && detail::is_container<Container>::value&&
|
||||
detail::is_container_element_type_compatible<
|
||||
Container&, ElementType>::value,
|
||||
int>::type = 0>
|
||||
constexpr span(Container & cont)
|
||||
: storage_(detail::data(cont), detail::size(cont))
|
||||
{
|
||||
}
|
||||
|
||||
template <
|
||||
typename Container, std::size_t E = Extent,
|
||||
typename std::enable_if<
|
||||
E == dynamic_extent && detail::is_container<Container>::value&&
|
||||
detail::is_container_element_type_compatible<
|
||||
const Container&, ElementType>::value,
|
||||
int>::type = 0>
|
||||
constexpr span(const Container & cont)
|
||||
: storage_(detail::data(cont), detail::size(cont))
|
||||
{
|
||||
}
|
||||
|
||||
constexpr span(const span& other) noexcept = default;
|
||||
|
||||
template <typename OtherElementType, std::size_t OtherExtent,
|
||||
typename std::enable_if<
|
||||
(Extent == dynamic_extent || OtherExtent == dynamic_extent ||
|
||||
Extent == OtherExtent) &&
|
||||
std::is_convertible<OtherElementType(*)[],
|
||||
ElementType(*)[]>::value,
|
||||
int>::type = 0>
|
||||
constexpr span(const span<OtherElementType, OtherExtent>& other) noexcept
|
||||
: storage_(other.data(), other.size())
|
||||
{
|
||||
}
|
||||
|
||||
~span() noexcept = default;
|
||||
|
||||
TCB_SPAN_CONSTEXPR_ASSIGN span&
|
||||
operator=(const span& other) noexcept = default;
|
||||
|
||||
// [span.sub], span subviews
|
||||
template <std::size_t Count>
|
||||
TCB_SPAN_CONSTEXPR11 span<element_type, Count> first() const
|
||||
{
|
||||
TCB_SPAN_EXPECT(Count <= size());
|
||||
return { data(), Count };
|
||||
}
|
||||
|
||||
template <std::size_t Count>
|
||||
TCB_SPAN_CONSTEXPR11 span<element_type, Count> last() const
|
||||
{
|
||||
TCB_SPAN_EXPECT(Count <= size());
|
||||
return { data() + (size() - Count), Count };
|
||||
}
|
||||
|
||||
template <std::size_t Offset, std::size_t Count = dynamic_extent>
|
||||
using subspan_return_t =
|
||||
span<ElementType, Count != dynamic_extent
|
||||
? Count
|
||||
: (Extent != dynamic_extent ? Extent - Offset
|
||||
: dynamic_extent)>;
|
||||
|
||||
template <std::size_t Offset, std::size_t Count = dynamic_extent>
|
||||
TCB_SPAN_CONSTEXPR11 subspan_return_t<Offset, Count> subspan() const
|
||||
{
|
||||
TCB_SPAN_EXPECT(Offset <= size() &&
|
||||
(Count == dynamic_extent || Offset + Count <= size()));
|
||||
return { data() + Offset,
|
||||
Count != dynamic_extent ? Count : size() - Offset };
|
||||
}
|
||||
|
||||
TCB_SPAN_CONSTEXPR11 span<element_type, dynamic_extent>
|
||||
first(size_type count) const
|
||||
{
|
||||
TCB_SPAN_EXPECT(count <= size());
|
||||
return { data(), count };
|
||||
}
|
||||
|
||||
TCB_SPAN_CONSTEXPR11 span<element_type, dynamic_extent>
|
||||
last(size_type count) const
|
||||
{
|
||||
TCB_SPAN_EXPECT(count <= size());
|
||||
return { data() + (size() - count), count };
|
||||
}
|
||||
|
||||
TCB_SPAN_CONSTEXPR11 span<element_type, dynamic_extent>
|
||||
subspan(size_type offset, size_type count = dynamic_extent) const
|
||||
{
|
||||
TCB_SPAN_EXPECT(offset <= size() &&
|
||||
(count == dynamic_extent || offset + count <= size()));
|
||||
return { data() + offset,
|
||||
count == dynamic_extent ? size() - offset : count };
|
||||
}
|
||||
|
||||
// [span.obs], span observers
|
||||
constexpr size_type size() const noexcept { return storage_.size; }
|
||||
|
||||
constexpr size_type size_bytes() const noexcept
|
||||
{
|
||||
return size() * sizeof(element_type);
|
||||
}
|
||||
|
||||
TCB_SPAN_NODISCARD constexpr bool empty() const noexcept
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
// [span.elem], span element access
|
||||
TCB_SPAN_CONSTEXPR11 reference operator[](size_type idx) const
|
||||
{
|
||||
TCB_SPAN_EXPECT(idx < size());
|
||||
return *(data() + idx);
|
||||
}
|
||||
|
||||
TCB_SPAN_CONSTEXPR11 reference front() const
|
||||
{
|
||||
TCB_SPAN_EXPECT(!empty());
|
||||
return *data();
|
||||
}
|
||||
|
||||
TCB_SPAN_CONSTEXPR11 reference back() const
|
||||
{
|
||||
TCB_SPAN_EXPECT(!empty());
|
||||
return *(data() + (size() - 1));
|
||||
}
|
||||
|
||||
constexpr pointer data() const noexcept { return storage_.ptr; }
|
||||
|
||||
// [span.iterators], span iterator support
|
||||
constexpr iterator begin() const noexcept { return data(); }
|
||||
|
||||
constexpr iterator end() const noexcept { return data() + size(); }
|
||||
|
||||
TCB_SPAN_ARRAY_CONSTEXPR reverse_iterator rbegin() const noexcept
|
||||
{
|
||||
return reverse_iterator(end());
|
||||
}
|
||||
|
||||
TCB_SPAN_ARRAY_CONSTEXPR reverse_iterator rend() const noexcept
|
||||
{
|
||||
return reverse_iterator(begin());
|
||||
}
|
||||
|
||||
private:
|
||||
storage_type storage_{};
|
||||
};
|
||||
|
||||
#ifdef TCB_SPAN_HAVE_DEDUCTION_GUIDES
|
||||
|
||||
/* Deduction Guides */
|
||||
template <class T, size_t N>
|
||||
span(T(&)[N]) -> span<T, N>;
|
||||
|
||||
template <class T, size_t N>
|
||||
span(std::array<T, N>&) -> span<T, N>;
|
||||
|
||||
template <class T, size_t N>
|
||||
span(const std::array<T, N>&) -> span<const T, N>;
|
||||
|
||||
template <class Container>
|
||||
span(Container&) -> span<typename std::remove_reference<
|
||||
decltype(*detail::data(std::declval<Container&>()))>::type>;
|
||||
|
||||
template <class Container>
|
||||
span(const Container&) -> span<const typename Container::value_type>;
|
||||
|
||||
#endif // TCB_HAVE_DEDUCTION_GUIDES
|
||||
|
||||
template <typename ElementType, std::size_t Extent>
|
||||
constexpr span<ElementType, Extent>
|
||||
make_span(span<ElementType, Extent> s) noexcept
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
constexpr span<T, N> make_span(T(&arr)[N]) noexcept
|
||||
{
|
||||
return { arr };
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
TCB_SPAN_ARRAY_CONSTEXPR span<T, N> make_span(std::array<T, N>& arr) noexcept
|
||||
{
|
||||
return { arr };
|
||||
}
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
TCB_SPAN_ARRAY_CONSTEXPR span<const T, N>
|
||||
make_span(const std::array<T, N>& arr) noexcept
|
||||
{
|
||||
return { arr };
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
constexpr span<typename std::remove_reference<
|
||||
decltype(*detail::data(std::declval<Container&>()))>::type>
|
||||
make_span(Container& cont)
|
||||
{
|
||||
return { cont };
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
constexpr span<const typename Container::value_type>
|
||||
make_span(const Container& cont)
|
||||
{
|
||||
return { cont };
|
||||
}
|
||||
|
||||
template <typename ElementType, std::size_t Extent>
|
||||
span<const byte, ((Extent == dynamic_extent) ? dynamic_extent
|
||||
: sizeof(ElementType) * Extent)>
|
||||
as_bytes(span<ElementType, Extent> s) noexcept
|
||||
{
|
||||
return { reinterpret_cast<const byte*>(s.data()), s.size_bytes() };
|
||||
}
|
||||
|
||||
template <
|
||||
class ElementType, size_t Extent,
|
||||
typename std::enable_if<!std::is_const<ElementType>::value, int>::type = 0>
|
||||
span<byte, ((Extent == dynamic_extent) ? dynamic_extent
|
||||
: sizeof(ElementType) * Extent)>
|
||||
as_writable_bytes(span<ElementType, Extent> s) noexcept
|
||||
{
|
||||
return { reinterpret_cast<byte*>(s.data()), s.size_bytes() };
|
||||
}
|
||||
|
||||
template <std::size_t N, typename E, std::size_t S>
|
||||
constexpr auto get(span<E, S> s) -> decltype(s[N])
|
||||
{
|
||||
return s[N];
|
||||
}
|
||||
|
||||
} // namespace TCB_SPAN_NAMESPACE_NAME
|
||||
|
||||
namespace std {
|
||||
|
||||
template <typename ElementType, size_t Extent>
|
||||
class tuple_size<TCB_SPAN_NAMESPACE_NAME::span<ElementType, Extent>>
|
||||
: public integral_constant<size_t, Extent> {
|
||||
};
|
||||
|
||||
template <typename ElementType>
|
||||
class tuple_size<TCB_SPAN_NAMESPACE_NAME::span<
|
||||
ElementType, TCB_SPAN_NAMESPACE_NAME::dynamic_extent>>; // not defined
|
||||
|
||||
template <size_t I, typename ElementType, size_t Extent>
|
||||
class tuple_element<I, TCB_SPAN_NAMESPACE_NAME::span<ElementType, Extent>> {
|
||||
public:
|
||||
static_assert(Extent != TCB_SPAN_NAMESPACE_NAME::dynamic_extent &&
|
||||
I < Extent,
|
||||
"");
|
||||
using type = ElementType;
|
||||
};
|
||||
|
||||
} // end namespace std
|
||||
|
||||
#endif // TCB_SPAN_HPP_INCLUDED
|
||||
53
include/Util/StackAllocator.h
Normal file
53
include/Util/StackAllocator.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include "core/error/error_macros.h"
|
||||
|
||||
class StackAllocator {
|
||||
public:
|
||||
StackAllocator() noexcept = default;
|
||||
StackAllocator(size_t size) noexcept
|
||||
{
|
||||
m_memory = new char[size];
|
||||
m_size = size;
|
||||
m_offset = 0;
|
||||
}
|
||||
|
||||
~StackAllocator()
|
||||
{
|
||||
delete[] m_memory;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* allocate(size_t size = 1)
|
||||
{
|
||||
return static_cast<T*>(allocate(size * sizeof(T)));
|
||||
}
|
||||
|
||||
void* allocate(size_t size = 1)
|
||||
{
|
||||
size = std::max<size_t>(size, 8);
|
||||
|
||||
if (m_offset + size > m_size)
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
void* ptr = m_memory + m_offset;
|
||||
m_offset += size;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void deallocate(T* p, size_t size) noexcept
|
||||
{}
|
||||
|
||||
void reset() {
|
||||
m_offset = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
char* m_memory;
|
||||
size_t m_size;
|
||||
size_t m_offset;
|
||||
};
|
||||
252
include/Util/fsa.h
Normal file
252
include/Util/fsa.h
Normal file
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
|
||||
A* Algorithm Implementation using STL is
|
||||
Copyright (C)2001-2005 Justin Heyes-Jones
|
||||
|
||||
Permission is given by the author to freely redistribute and
|
||||
include this code in any program as long as this credit is
|
||||
given where due.
|
||||
|
||||
COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
|
||||
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
|
||||
INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE
|
||||
IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
|
||||
OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND
|
||||
PERFORMANCE OF THE COVERED CODE IS WITH YOU. SHOULD ANY COVERED
|
||||
CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL
|
||||
DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY
|
||||
NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF
|
||||
WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE
|
||||
OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
|
||||
THIS DISCLAIMER.
|
||||
|
||||
Use at your own risk!
|
||||
|
||||
|
||||
|
||||
FixedSizeAllocator class
|
||||
Copyright 2001 Justin Heyes-Jones
|
||||
|
||||
This class is a constant time O(1) memory manager for objects of
|
||||
a specified type. The type is specified using a template class.
|
||||
|
||||
Memory is allocated from a fixed size buffer which you can specify in the
|
||||
class constructor or use the default.
|
||||
|
||||
Using GetFirst and GetNext it is possible to iterate through the elements
|
||||
one by one, and this would be the most common use for the class.
|
||||
|
||||
I would suggest using this class when you want O(1) add and delete
|
||||
and you don't do much searching, which would be O(n). Structures such as binary
|
||||
trees can be used instead to get O(logn) access time.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef FSA_H
|
||||
#define FSA_H
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
template <class USER_TYPE> class FixedSizeAllocator
|
||||
{
|
||||
|
||||
public:
|
||||
// Constants
|
||||
enum
|
||||
{
|
||||
FSA_DEFAULT_SIZE = 100
|
||||
};
|
||||
|
||||
// This class enables us to transparently manage the extra data
|
||||
// needed to enable the user class to form part of the double-linked
|
||||
// list class
|
||||
struct FSA_ELEMENT
|
||||
{
|
||||
USER_TYPE UserType;
|
||||
|
||||
FSA_ELEMENT* pPrev;
|
||||
FSA_ELEMENT* pNext;
|
||||
};
|
||||
|
||||
public: // methods
|
||||
FixedSizeAllocator(unsigned int MaxElements = FSA_DEFAULT_SIZE) :
|
||||
m_pFirstUsed(NULL),
|
||||
m_MaxElements(MaxElements)
|
||||
{
|
||||
// Allocate enough memory for the maximum number of elements
|
||||
|
||||
char* pMem = new char[m_MaxElements * sizeof(FSA_ELEMENT)];
|
||||
|
||||
m_pMemory = (FSA_ELEMENT*)pMem;
|
||||
|
||||
// Set the free list first pointer
|
||||
m_pFirstFree = m_pMemory;
|
||||
|
||||
// Clear the memory
|
||||
memset(m_pMemory, 0, sizeof(FSA_ELEMENT) * m_MaxElements);
|
||||
|
||||
// Point at first element
|
||||
FSA_ELEMENT* pElement = m_pFirstFree;
|
||||
|
||||
// Set the double linked free list
|
||||
for (unsigned int i = 0; i < m_MaxElements; i++)
|
||||
{
|
||||
pElement->pPrev = pElement - 1;
|
||||
pElement->pNext = pElement + 1;
|
||||
|
||||
pElement++;
|
||||
}
|
||||
|
||||
// first element should have a null prev
|
||||
m_pFirstFree->pPrev = NULL;
|
||||
// last element should have a null next
|
||||
(pElement - 1)->pNext = NULL;
|
||||
|
||||
}
|
||||
|
||||
|
||||
~FixedSizeAllocator()
|
||||
{
|
||||
// Free up the memory
|
||||
delete[](char*) m_pMemory;
|
||||
}
|
||||
|
||||
// Allocate a new USER_TYPE and return a pointer to it
|
||||
USER_TYPE* alloc()
|
||||
{
|
||||
|
||||
FSA_ELEMENT* pNewNode = NULL;
|
||||
|
||||
if (!m_pFirstFree)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
pNewNode = m_pFirstFree;
|
||||
m_pFirstFree = pNewNode->pNext;
|
||||
|
||||
// if the new node points to another free node then
|
||||
// change that nodes prev free pointer...
|
||||
if (pNewNode->pNext)
|
||||
{
|
||||
pNewNode->pNext->pPrev = NULL;
|
||||
}
|
||||
|
||||
// node is now on the used list
|
||||
|
||||
pNewNode->pPrev = NULL; // the allocated node is always first in the list
|
||||
|
||||
if (m_pFirstUsed == NULL)
|
||||
{
|
||||
pNewNode->pNext = NULL; // no other nodes
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pFirstUsed->pPrev = pNewNode; // insert this at the head of the used list
|
||||
pNewNode->pNext = m_pFirstUsed;
|
||||
}
|
||||
|
||||
m_pFirstUsed = pNewNode;
|
||||
}
|
||||
|
||||
return reinterpret_cast<USER_TYPE*>(pNewNode);
|
||||
}
|
||||
|
||||
// Free the given user type
|
||||
// For efficiency I don't check whether the user_data is a valid
|
||||
// pointer that was allocated. I may add some debug only checking
|
||||
// (To add the debug check you'd need to make sure the pointer is in
|
||||
// the m_pMemory area and is pointing at the start of a node)
|
||||
void free(USER_TYPE* user_data)
|
||||
{
|
||||
FSA_ELEMENT* pNode = reinterpret_cast<FSA_ELEMENT*>(user_data);
|
||||
|
||||
// manage used list, remove this node from it
|
||||
if (pNode->pPrev)
|
||||
{
|
||||
pNode->pPrev->pNext = pNode->pNext;
|
||||
}
|
||||
else
|
||||
{
|
||||
// this handles the case that we delete the first node in the used list
|
||||
m_pFirstUsed = pNode->pNext;
|
||||
}
|
||||
|
||||
if (pNode->pNext)
|
||||
{
|
||||
pNode->pNext->pPrev = pNode->pPrev;
|
||||
}
|
||||
|
||||
// add to free list
|
||||
if (m_pFirstFree == NULL)
|
||||
{
|
||||
// free list was empty
|
||||
m_pFirstFree = pNode;
|
||||
pNode->pPrev = NULL;
|
||||
pNode->pNext = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add this node at the start of the free list
|
||||
m_pFirstFree->pPrev = pNode;
|
||||
pNode->pNext = m_pFirstFree;
|
||||
m_pFirstFree = pNode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// For debugging this displays both lists (using the prev/next list pointers)
|
||||
void Debug()
|
||||
{
|
||||
printf("free list ");
|
||||
|
||||
FSA_ELEMENT* p = m_pFirstFree;
|
||||
while (p)
|
||||
{
|
||||
printf("%x!%x ", p->pPrev, p->pNext);
|
||||
p = p->pNext;
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
printf("used list ");
|
||||
|
||||
p = m_pFirstUsed;
|
||||
while (p)
|
||||
{
|
||||
printf("%x!%x ", p->pPrev, p->pNext);
|
||||
p = p->pNext;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
// Iterators
|
||||
|
||||
USER_TYPE* GetFirst()
|
||||
{
|
||||
return reinterpret_cast<USER_TYPE*>(m_pFirstUsed);
|
||||
}
|
||||
|
||||
USER_TYPE* GetNext(USER_TYPE* node)
|
||||
{
|
||||
return reinterpret_cast<USER_TYPE*>
|
||||
(
|
||||
(reinterpret_cast<FSA_ELEMENT*>(node))->pNext
|
||||
);
|
||||
}
|
||||
|
||||
public: // data
|
||||
|
||||
private: // methods
|
||||
|
||||
private: // data
|
||||
|
||||
FSA_ELEMENT* m_pFirstFree;
|
||||
FSA_ELEMENT* m_pFirstUsed;
|
||||
unsigned int m_MaxElements;
|
||||
FSA_ELEMENT* m_pMemory;
|
||||
|
||||
};
|
||||
|
||||
#endif // defined FSA_H
|
||||
21
include/config.h
Normal file
21
include/config.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <limits>
|
||||
#include <cassert>
|
||||
|
||||
typedef uint16_t UnderlyingItemT;
|
||||
|
||||
constexpr uint32_t framesPerSecond = 32;
|
||||
|
||||
inline float FrameToSeconds(uint32_t frame)
|
||||
{
|
||||
return static_cast<float>(frame) / framesPerSecond;
|
||||
}
|
||||
|
||||
inline uint32_t SecondsToFrames(float time)
|
||||
{
|
||||
return static_cast<uint32_t>(time * framesPerSecond);
|
||||
}
|
||||
|
||||
#define DEV_ASSERT(x) assert(x)
|
||||
@@ -1 +0,0 @@
|
||||
add_subdirectory(core)
|
||||
13
src/Components/Config/WorldConfig.cpp
Normal file
13
src/Components/Config/WorldConfig.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "Components/Configs/WorldConfig.hpp"
|
||||
|
||||
void WorldConfig::RegisterItem(const std::string& name)
|
||||
{
|
||||
for (const auto& item : Items)
|
||||
{
|
||||
if (item.Name == name) return;
|
||||
}
|
||||
|
||||
ItemConfig cfg{};
|
||||
cfg.Name = name;
|
||||
Items.push_back(std::move(cfg));
|
||||
}
|
||||
247
src/Core/Chunk.cpp
Normal file
247
src/Core/Chunk.cpp
Normal file
@@ -0,0 +1,247 @@
|
||||
#include "Core/Chunk.h"
|
||||
#include "Chunk.h"
|
||||
|
||||
void ChunkData::MarkAsPersistant(entt::entity entity)
|
||||
{
|
||||
auto removedIt = std::remove_if(Entities.begin(), Entities.end(), [entity](EntityTile& tile)
|
||||
{
|
||||
return tile.Entity == entity;
|
||||
});
|
||||
|
||||
PersistantEntities.insert(PersistantEntities.end(), removedIt, Entities.end());
|
||||
Entities.erase(removedIt, Entities.end());
|
||||
}
|
||||
|
||||
void ChunkData::RemovePersistance(entt::entity entity)
|
||||
{
|
||||
auto removedIt = std::remove_if(PersistantEntities.begin(), PersistantEntities.end(), [entity](EntityTile& tile)
|
||||
{
|
||||
return tile.Entity == entity;
|
||||
});
|
||||
|
||||
Entities.insert(Entities.end(), removedIt, PersistantEntities.end());
|
||||
PersistantEntities.erase(removedIt, PersistantEntities.end());
|
||||
}
|
||||
|
||||
const Chunk& ChunkCollection::GetChunk(int x, int y)
|
||||
{
|
||||
return GetChunk(ChunkKey{x, y});
|
||||
}
|
||||
|
||||
const Chunk &ChunkCollection::GetChunk(ChunkKey key)
|
||||
{
|
||||
return GetChunkInternal(key);
|
||||
}
|
||||
|
||||
Chunk const* ChunkCollection::TryGetChunk(int x, int y) const
|
||||
{
|
||||
return TryGetChunk(ChunkKey{x, y});
|
||||
}
|
||||
|
||||
Chunk const *ChunkCollection::TryGetChunk(ChunkKey key) const
|
||||
{
|
||||
int chunkIndex = TryGetChunkIndex(key);
|
||||
if (chunkIndex == -1)
|
||||
return nullptr;
|
||||
|
||||
return ChunkDatas[chunkIndex].Chunk.get();
|
||||
}
|
||||
|
||||
Tile ChunkCollection::GetTile(int x, int y)
|
||||
{
|
||||
return GetChunk(x, y).GetTile(Chunk::WorldToLocal(x), Chunk::WorldToLocal(y));
|
||||
}
|
||||
|
||||
Tile const *ChunkCollection::TryGetTile(int x, int y) const
|
||||
{
|
||||
Chunk const* chunk = TryGetChunk(x, y);
|
||||
int chunkX = Chunk::WorldToLocal(x);
|
||||
int chunkY = Chunk::WorldToLocal(y);
|
||||
|
||||
return &chunk->GetTileRef(chunkX, chunkY);
|
||||
}
|
||||
|
||||
ChunkData& ChunkCollection::GetChunkData(int x, int y)
|
||||
{
|
||||
return GetChunkData(ChunkKey{x, y});
|
||||
}
|
||||
|
||||
ChunkData &ChunkCollection::GetChunkData(ChunkKey key)
|
||||
{
|
||||
int index = GetChunkIndex(key);
|
||||
return ChunkDatas[index];
|
||||
}
|
||||
|
||||
Tile &ChunkCollection::GetTileInternal(int x, int y)
|
||||
{
|
||||
return GetChunkInternal(x, y).GetTile(Chunk::WorldToLocal(x), Chunk::WorldToLocal(y));
|
||||
}
|
||||
|
||||
void ChunkCollection::SetTile(Tile tile, int x, int y)
|
||||
{
|
||||
GetTileInternal(x, y) = tile;
|
||||
}
|
||||
|
||||
void ChunkCollection::InvalidateCachedChunk()
|
||||
{
|
||||
CachedChunk = -1;
|
||||
}
|
||||
|
||||
entt::entity ChunkCollection::GetEntity(int x, int y) const
|
||||
{
|
||||
int chunkIndex = TryGetChunkIndex(x, y);
|
||||
int chunkX = Chunk::WorldToLocal(x);
|
||||
int chunkY = Chunk::WorldToLocal(y);
|
||||
|
||||
if (chunkIndex != -1 &&
|
||||
ChunkDatas[chunkIndex].Chunk->GetTile(chunkX, chunkY).HasEntity())
|
||||
{
|
||||
for (auto& entity : ChunkDatas[chunkIndex].Entities)
|
||||
{
|
||||
if (entity.ChunkX == chunkX && entity.ChunkY == chunkY)
|
||||
return entity.Entity;
|
||||
}
|
||||
DEV_ASSERT(false); // should be unreachable
|
||||
}
|
||||
|
||||
return entt::null;
|
||||
}
|
||||
|
||||
int ChunkCollection::GetChunkIndex(int x, int y)
|
||||
{
|
||||
return GetChunkIndex(ChunkKey{x, y});
|
||||
}
|
||||
|
||||
int ChunkCollection::GetChunkIndex(ChunkKey key)
|
||||
{
|
||||
if (key == CachedChunkKey && CachedChunk != -1)
|
||||
return CachedChunk;
|
||||
|
||||
auto chunkIt = ChunkMap.find(key);
|
||||
if (chunkIt != ChunkMap.end())
|
||||
{
|
||||
CachedChunkKey = key;
|
||||
CachedChunk = chunkIt->value;
|
||||
return chunkIt->value;
|
||||
}
|
||||
|
||||
ChunkDatas.push_back({});
|
||||
ChunkMap.insert(key, ChunkDatas.size() - 1);
|
||||
|
||||
CachedChunkKey = key;
|
||||
CachedChunk = ChunkDatas.size() - 1;
|
||||
|
||||
return ChunkDatas.size() - 1;
|
||||
}
|
||||
|
||||
int ChunkCollection::TryGetChunkIndex(int x, int y) const
|
||||
{
|
||||
return TryGetChunkIndex(ChunkKey{x, y});
|
||||
}
|
||||
|
||||
int ChunkCollection::TryGetChunkIndex(ChunkKey key) const
|
||||
{
|
||||
if (key == CachedChunkKey && CachedChunk != -1)
|
||||
return CachedChunk;
|
||||
|
||||
auto chunkIt = ChunkMap.find(key);
|
||||
if (chunkIt != ChunkMap.end())
|
||||
{
|
||||
const_cast<ChunkKey&>(CachedChunkKey) = key;
|
||||
const_cast<int&>(CachedChunk) = chunkIt->value;
|
||||
return chunkIt->value;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
Chunk& ChunkCollection::GetChunkInternal(int x, int y)
|
||||
{
|
||||
return GetChunkInternal(ChunkKey{x, y});
|
||||
}
|
||||
|
||||
Chunk &ChunkCollection::GetChunkInternal(ChunkKey key)
|
||||
{
|
||||
int index = GetChunkIndex(key);
|
||||
return *ChunkDatas[index].Chunk.get();
|
||||
}
|
||||
|
||||
void ChunkCollection::SetChunkTiles(int x, int y, std::unique_ptr<Chunk> &&chunk)
|
||||
{
|
||||
SetChunkTiles(ChunkKey{x, y}, std::move(chunk));
|
||||
}
|
||||
|
||||
void ChunkCollection::SetChunkTiles(ChunkKey key, std::unique_ptr<Chunk> &&chunk)
|
||||
{
|
||||
GetChunkData(key).Chunk = std::move(chunk);
|
||||
}
|
||||
|
||||
void ChunkCollection::AddEntity(entt::entity entity, const Vector<Vector2i> &claimedPositions)
|
||||
{
|
||||
for (auto& pos : claimedPositions)
|
||||
{
|
||||
int chunkX = Chunk::WorldToLocal(pos.x);
|
||||
int chunkY = Chunk::WorldToLocal(pos.y);
|
||||
|
||||
auto& chunkData = GetChunkData(pos.x, pos.y);
|
||||
chunkData.Entities.push_back(EntityTile{entity, chunkX, chunkY});
|
||||
}
|
||||
}
|
||||
|
||||
void ChunkCollection::AddPersistantEntity(entt::entity entity, const Vector<Vector2i> &claimedPositions)
|
||||
{
|
||||
for (auto& pos : claimedPositions)
|
||||
{
|
||||
int chunkX = Chunk::WorldToLocal(pos.x);
|
||||
int chunkY = Chunk::WorldToLocal(pos.y);
|
||||
|
||||
auto& chunkData = GetChunkData(pos.x, pos.y);
|
||||
chunkData.PersistantEntities.push_back(EntityTile{entity, chunkX, chunkY});
|
||||
}
|
||||
}
|
||||
|
||||
void ChunkCollection::MarkAsPersistant(entt::entity entity)
|
||||
{
|
||||
for (ChunkData& chunk : ChunkDatas)
|
||||
{
|
||||
chunk.MarkAsPersistant(entity);
|
||||
}
|
||||
}
|
||||
|
||||
void ChunkCollection::RemovePersistance(entt::entity entity)
|
||||
{
|
||||
for (ChunkData& chunk : ChunkDatas)
|
||||
{
|
||||
chunk.RemovePersistance(entity);
|
||||
}
|
||||
}
|
||||
|
||||
void ChunkCollection::RemoveEntity(entt::entity entity)
|
||||
{
|
||||
for (ChunkData& chunk : ChunkDatas)
|
||||
{
|
||||
chunk.Entities.erase(std::remove_if(chunk.Entities.begin(), chunk.Entities.end(), [entity](EntityTile& tile)
|
||||
{
|
||||
return tile.Entity == entity;
|
||||
}));
|
||||
chunk.PersistantEntities.erase(std::remove_if(chunk.PersistantEntities.begin(), chunk.PersistantEntities.end(), [entity](EntityTile& tile)
|
||||
{
|
||||
return tile.Entity == entity;
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
void ChunkCollection::RemoveChunk(int x, int y)
|
||||
{
|
||||
RemoveChunk(ChunkKey{x, y});
|
||||
}
|
||||
|
||||
void ChunkCollection::RemoveChunk(ChunkKey key)
|
||||
{
|
||||
auto it = ChunkMap.find(key);
|
||||
if (it != ChunkMap.end())
|
||||
{
|
||||
ChunkDatas[it->value].Clear();
|
||||
|
||||
InvalidateCachedChunk();
|
||||
}
|
||||
}
|
||||
16
src/Core/FactoryCommandQueue.cpp
Normal file
16
src/Core/FactoryCommandQueue.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#include "Core/FactoryCommandQueue.h"
|
||||
|
||||
void FactoryCommandQueue::ExecuteAll(FactoryWorld& world)
|
||||
{
|
||||
std::scoped_lock lock(Mutex);
|
||||
for (auto& command : Commands)
|
||||
command.Command(world, command.Entity, command.Data);
|
||||
ClearUnsafe();
|
||||
}
|
||||
|
||||
void FactoryCommandQueue::Clear()
|
||||
{
|
||||
std::scoped_lock lock(Mutex);
|
||||
ClearUnsafe();
|
||||
}
|
||||
|
||||
920
src/Core/FactoryWorld.cpp
Normal file
920
src/Core/FactoryWorld.cpp
Normal file
@@ -0,0 +1,920 @@
|
||||
#include "Core/FactoryWorld.h"
|
||||
|
||||
#include "modules/noise/fastnoise_lite.h"
|
||||
#include "core/config/engine.h"
|
||||
#include "core/templates/hash_set.h"
|
||||
#include "scene/main/scene_tree.h"
|
||||
#include "core/math/random_number_generator.h"
|
||||
|
||||
#include "Main/factory_server.h"
|
||||
|
||||
#include "Core/WorldGenerator.h"
|
||||
#include "Core/Serialization.h"
|
||||
|
||||
#include "Nodes/Entity/FactoryEntity.h"
|
||||
#include "Nodes/FactoryWorldInterface.h"
|
||||
|
||||
#include "Components/Sync.h"
|
||||
#include "Components/Minion.h"
|
||||
#include "Components/Position.h"
|
||||
#include "Components/Support.h"
|
||||
|
||||
#include "Util/AStar.h"
|
||||
|
||||
#include <queue>
|
||||
#include "FactoryWorld.h"
|
||||
|
||||
using namespace godot;
|
||||
|
||||
void FactoryWorld::Tick(int amount)
|
||||
{
|
||||
// dont tick sync and render systems when double processing a frame
|
||||
int32_t framesDrawn = Engine::get_singleton()->get_frames_drawn();
|
||||
int lastOrder = (framesDrawn == LastDrawnFrame) ? SystemBase::SYSTEM_EXECUTION_ORDER_SYNC : INT32_MAX;
|
||||
|
||||
WorldAccessMutex.lock();
|
||||
|
||||
CommandQueue->ExecuteAll(Registry);
|
||||
|
||||
auto& systems = Systems->GetSystems();
|
||||
for (auto it{systems.begin()}; it != systems.end() && (*it)->GetExecutionOrder() < lastOrder; ++it)
|
||||
{
|
||||
(*it)->Tick(Registry);
|
||||
}
|
||||
|
||||
WorldAccessMutex.unlock();
|
||||
|
||||
//Object::cast_to<SceneTree>(Engine::get_singleton()->get_main_loop());
|
||||
}
|
||||
|
||||
static constexpr auto WorldSavePath = "user://world";
|
||||
|
||||
void FactoryWorld::Initialize(FactoryWorldInterface* worldInterface)
|
||||
{
|
||||
Interface = worldInterface;
|
||||
|
||||
auto& systemGenerators = FactoryServer::get_singleton()->GetSystemGenerators();
|
||||
WorldSettings = FactoryServer::get_singleton()->GetSettings();
|
||||
|
||||
auto loader = WorldLoader{ *this };
|
||||
if (!loader.LoadFile(WorldSavePath))
|
||||
{
|
||||
WorldInventory = { WorldSettings->Items };
|
||||
auto random = Ref<RandomNumberGenerator>{};
|
||||
random.instantiate();
|
||||
Seed = random->randi();
|
||||
}
|
||||
|
||||
for (auto& generator : systemGenerators)
|
||||
{
|
||||
Systems->RegisterSystem(generator());
|
||||
}
|
||||
}
|
||||
|
||||
void FactoryWorld::Save()
|
||||
{
|
||||
auto saver = WorldSerializer{ *this };
|
||||
saver.SerializeFile(WorldSavePath);
|
||||
}
|
||||
|
||||
void FactoryWorld::SetInventory(const Vector<Ref<ItemConfig>>& items)
|
||||
{
|
||||
WorldInventory = { items };
|
||||
}
|
||||
|
||||
|
||||
|
||||
Inventory FactoryWorld::GetWorldInventory(Vector2 position) const
|
||||
{
|
||||
auto view = Registry.view<const TilePosition, const InventoryAreaOfEffect, const Inventory>();
|
||||
|
||||
int minDistance = INFINITY;
|
||||
Inventory closestInventory = WorldInventory;
|
||||
|
||||
for (auto [ent, pos, area, inv] : view.each())
|
||||
{
|
||||
if (position.x >= pos.Position.x - area.Size &&
|
||||
position.x <= pos.Position.x + area.Size &&
|
||||
position.y >= pos.Position.y - area.Size &&
|
||||
position.y <= pos.Position.y + area.Size)
|
||||
{
|
||||
int distanceSQ = pos.Position.distance_squared_to(position);
|
||||
if ((!area.IsCircle || distanceSQ <= area.Size * area.Size) &&
|
||||
distanceSQ < minDistance)
|
||||
{
|
||||
minDistance = distanceSQ;
|
||||
closestInventory = inv;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closestInventory;
|
||||
}
|
||||
|
||||
Inventory FactoryWorld::GetWorldInventory(entt::entity entity) const
|
||||
{
|
||||
auto position = Registry.get<TilePosition>(entity);
|
||||
return GetWorldInventory(position.Position);
|
||||
}
|
||||
|
||||
bool FactoryWorld::SupportCheckerHelper(entt::entity entity) const
|
||||
{
|
||||
auto support = Registry.try_get<Support>(entity);
|
||||
return support && support->SupportsAvailable > 0;
|
||||
}
|
||||
|
||||
uint8_t FactoryWorld::SupportValueHelper(entt::entity entity) const
|
||||
{
|
||||
auto support = Registry.try_get<Support>(entity);
|
||||
return support ? support->SupportsAvailable : 0;
|
||||
}
|
||||
|
||||
uint8_t FactoryWorld::GetSupportValue(int x, int y) const
|
||||
{
|
||||
auto entity = Chunks.GetEntity(x, y);
|
||||
if (entity != entt::null)
|
||||
return SupportValueHelper(entity);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool FactoryWorld::CheckIfSupportHelper(entt::entity entity, Support& support) const
|
||||
{
|
||||
auto pSupport = Registry.try_get<Support>(entity);
|
||||
if (pSupport)
|
||||
support = *pSupport;
|
||||
return pSupport;
|
||||
}
|
||||
|
||||
void FactoryWorld::ConnectSupports(Vector2i pos, Support& support, Vector2i direction)
|
||||
{
|
||||
}
|
||||
|
||||
// FactoryError FactoryWorld::CanPlaceSupport(int x, int y) const
|
||||
// {
|
||||
// {
|
||||
// auto bottomTile = TryGetTile(x, y - 1);
|
||||
// if (bottomTile && (bottomTile->IsFiller() || (bottomTile->HasEntity() && SupportCheckerHelper(GetEntity(x, y - 1)))))
|
||||
// return FACTORY_ERROR_NONE;
|
||||
// } {
|
||||
// auto leftTile = TryGetTile(x - 1, y);
|
||||
// if (leftTile && (leftTile->IsFiller() || (leftTile->HasEntity() && SupportCheckerHelper(GetEntity(x - 1, y)))))
|
||||
// return FACTORY_ERROR_NONE;
|
||||
// } {
|
||||
// auto rightTile = TryGetTile(x + 1, y);
|
||||
// if (rightTile && (rightTile->IsFiller() || (rightTile->HasEntity() && SupportCheckerHelper(GetEntity(x + 1, y)))))
|
||||
// return FACTORY_ERROR_NONE;
|
||||
// }
|
||||
// return FACTORY_ERROR_REQUIRES_SUPPORT;
|
||||
// }
|
||||
|
||||
// FactoryError FactoryWorld::CanRemoveSupport(int x, int y) const
|
||||
// {
|
||||
// auto supportTile = TryGetTile(x, y);
|
||||
// Support support{};
|
||||
// if (!supportTile || !supportTile->HasEntity() || CheckIfSupportHelper(GetEntity(x, y), support))
|
||||
// return FACTORY_ERROR_INVALID_POS;
|
||||
|
||||
// if (support.SupportsUp)
|
||||
// {
|
||||
// auto topTile = TryGetTile(x, y + 1);
|
||||
// auto entity = GetEntity(x, y + 1);
|
||||
// if (topTile &&
|
||||
// topTile->HasEntity() &&
|
||||
// !Registry.all_of<RequiresSupport>(entity) &&
|
||||
// support.SupportsAvailable > SupportValueHelper(entity) &&
|
||||
// GetSupportValue(x - 1, y + 1) == 0 &&
|
||||
// GetSupportValue(x + 1, y + 1) == 0)
|
||||
// return FACTORY_ERROR_REQUIRES_SUPPORT;
|
||||
// }
|
||||
// if (support.SupportsLeft)
|
||||
// {
|
||||
// auto leftTile = TryGetTile(x - 1, y);
|
||||
// auto entity = GetEntity(x - 1, y);
|
||||
// if (leftTile &&
|
||||
// leftTile->HasEntity() &&
|
||||
// support.SupportsAvailable > SupportValueHelper(entity) &&
|
||||
// GetSupportValue(x - 2, y) == 0 &&
|
||||
// GetSupportValue(x - 1, y - 1) == 0)
|
||||
// return FACTORY_ERROR_REQUIRES_SUPPORT;
|
||||
// }
|
||||
// if (support.SupportsRight)
|
||||
// {
|
||||
// auto rightTile = TryGetTile(x + 1, y);
|
||||
// auto entity = GetEntity(x + 1, y);
|
||||
// if (rightTile &&
|
||||
// rightTile->HasEntity() &&
|
||||
// support.SupportsAvailable > SupportValueHelper(entity) &&
|
||||
// GetSupportValue(x + 2, y) == 0 &&
|
||||
// GetSupportValue(x + 1, y - 1) == 0)
|
||||
// return FACTORY_ERROR_REQUIRES_SUPPORT;
|
||||
// }
|
||||
|
||||
// return FACTORY_ERROR_NONE;
|
||||
// }
|
||||
|
||||
// void FactoryWorld::RegisterSupport(int x, int y, Support& support)
|
||||
// {
|
||||
// DEV_ASSERT(support.MaxSupport != 0);
|
||||
// DEV_ASSERT(CanPlaceSupport(x, y) == FACTORY_ERROR_NONE);
|
||||
|
||||
// //auto& tile = GetTileInternal(x, y);
|
||||
|
||||
// {
|
||||
// auto bottomTile = GetTile(x, y - 1);
|
||||
// if (bottomTile.IsFiller())
|
||||
// {
|
||||
// support.SupportsAvailable = support.MaxSupport;
|
||||
// return;
|
||||
// }
|
||||
// if (bottomTile.HasEntity())
|
||||
// {
|
||||
// auto entity = GetEntity(x, y - 1);
|
||||
// auto bottomSupport = Registry.try_get<Support>(entity);
|
||||
// if (bottomSupport && bottomSupport->SupportsAvailable > 0)
|
||||
// {
|
||||
// support.SupportsAvailable = std::max<uint8_t>(support.SupportsAvailable, bottomSupport->SupportsAvailable - 1);
|
||||
// bottomSupport->SupportsUp = true;
|
||||
// support.SupportedByBottom = true;
|
||||
// }
|
||||
// }
|
||||
// } {
|
||||
// auto leftTile = GetTile(x - 1, y);
|
||||
// if (leftTile.IsFiller())
|
||||
// {
|
||||
// support.SupportsAvailable = support.MaxSupport;
|
||||
// return;
|
||||
// }
|
||||
// if (leftTile.HasEntity())
|
||||
// {
|
||||
// auto entity = GetEntity(x - 1, y);
|
||||
// auto leftSupport = Registry.try_get<Support>(entity);
|
||||
// if (leftSupport && leftSupport->SupportsAvailable > 0)
|
||||
// {
|
||||
// support.SupportsAvailable = std::max<uint8_t>(support.SupportsAvailable, leftSupport->SupportsAvailable - 1);
|
||||
// leftSupport->SupportsRight = true;
|
||||
// support.SupportedByLeft = true;
|
||||
// }
|
||||
// }
|
||||
// } {
|
||||
// auto rightTile = GetTile(x + 1, y);
|
||||
// if (rightTile.IsFiller())
|
||||
// {
|
||||
// support.SupportsAvailable = support.MaxSupport;
|
||||
// return;
|
||||
// }
|
||||
// if (rightTile.HasEntity())
|
||||
// {
|
||||
// auto entity = GetEntity(x + 1, y);
|
||||
// auto rightSupport = Registry.try_get<Support>(entity);
|
||||
// if (rightSupport && rightSupport->SupportsAvailable > 0)
|
||||
// {
|
||||
// support.SupportsAvailable = std::max<uint8_t>(support.SupportsAvailable, rightSupport->SupportsAvailable - 1);
|
||||
// rightSupport->SupportsLeft = true;
|
||||
// support.SupportedByRight = true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// support.SupportsAvailable = std::min(support.SupportsAvailable, support.MaxSupport);
|
||||
// }
|
||||
|
||||
// void FactoryWorld::RemoveSupport(int x, int y)
|
||||
// {
|
||||
// DEV_ASSERT(CanRemoveSupport(x, y) == FACTORY_ERROR_NONE);
|
||||
// DEV_ASSERT(Registry.all_of<Support>(GetEntity(x, y)));
|
||||
|
||||
// Support& support = Registry.get<Support>(GetEntity(x, y));
|
||||
|
||||
// }
|
||||
|
||||
FactoryError FactoryWorld::CanPlaceEntity(int x, int y, Ref<Archetype> archetype)
|
||||
{
|
||||
if (archetype.is_valid() && archetype->Scene.is_valid())
|
||||
{
|
||||
auto& chunk = Chunks.GetChunkData(x, y);
|
||||
|
||||
for (auto& condition : archetype->SpawnConditions)
|
||||
{
|
||||
if (condition->IsValid(Chunks, Vector2i{x, y}))
|
||||
return FACTORY_ERROR_NO_SPACE;
|
||||
}
|
||||
|
||||
Vector<Vector2i> claimedCoordinates{};
|
||||
for (auto& condition : archetype->SpawnConditions)
|
||||
{
|
||||
condition->ClaimTiles(claimedCoordinates, Chunks, Vector2i{x, y});
|
||||
}
|
||||
|
||||
for (auto coordinate : claimedCoordinates)
|
||||
{
|
||||
for (auto entity : chunk.Entities)
|
||||
{
|
||||
if (entity.ChunkX == coordinate.x && entity.ChunkY == coordinate.y)
|
||||
return FACTORY_ERROR_NO_SPACE;
|
||||
}
|
||||
}
|
||||
|
||||
return FACTORY_ERROR_NONE;
|
||||
}
|
||||
|
||||
return FACTORY_ERROR_NO_SPACE;
|
||||
}
|
||||
|
||||
FactoryError FactoryWorld::AddEntity(int x, int y, Ref<Archetype> archetype)
|
||||
{
|
||||
entt::entity createdEntity = CreateEntity();
|
||||
return AddEntity(x, y, archetype, createdEntity);
|
||||
}
|
||||
|
||||
void FactoryWorld::RemoveEntity(FactoryEntity *node)
|
||||
{
|
||||
RemoveEntity(node->GetEntity());
|
||||
}
|
||||
|
||||
void FactoryWorld::RemoveEntity(int x, int y)
|
||||
{
|
||||
RemoveEntity(Chunks.GetEntity(x, y));
|
||||
}
|
||||
|
||||
void FactoryWorld::RemoveEntity(entt::entity entity)
|
||||
{
|
||||
if (entity == entt::null || !Registry.all_of<NodePtr, ArchetypePtr, TilePosition>(entity))
|
||||
return;
|
||||
|
||||
auto&& [node, arch, pos] = Registry.get<NodePtr, ArchetypePtr, TilePosition>(entity);
|
||||
|
||||
node.Node->queue_free();
|
||||
Chunks.RemoveEntity(entity);
|
||||
Registry.destroy(entity);
|
||||
}
|
||||
|
||||
FactoryError FactoryWorld::AddEntity(int x, int y, Ref<Archetype> archetype, entt::entity entityID)
|
||||
{
|
||||
auto canPlace = CanPlaceEntity(x, y, archetype);
|
||||
if (canPlace != FACTORY_ERROR_NONE)
|
||||
return canPlace;
|
||||
|
||||
// instantiate node
|
||||
Node2D* node = Object::cast_to<Node2D>(archetype->Scene->instantiate());
|
||||
auto factoryNode = Object::cast_to<FactoryEntity>(node);
|
||||
DEV_ASSERT(node);
|
||||
|
||||
// set position in ecs
|
||||
auto tilePos = TilePosition();
|
||||
tilePos.Position = Vector2i(x, y);
|
||||
Registry.emplace<TilePosition>(entityID, tilePos);
|
||||
|
||||
// link archetype to the entity
|
||||
ArchetypePtr archPtr{};
|
||||
archPtr.Archetype = archetype;
|
||||
Registry.emplace<ArchetypePtr>(entityID, archPtr);
|
||||
|
||||
// Add Level component if archetype has levels
|
||||
if (!archetype->Upgrades.is_empty())
|
||||
{
|
||||
Registry.emplace<Level>(entityID);
|
||||
}
|
||||
|
||||
// register the entities in the chunks
|
||||
Vector<Vector2i> locations{};
|
||||
for (auto& condition : archetype->SpawnConditions)
|
||||
{
|
||||
condition->ClaimTiles(locations, Chunks, Vector2i{x, y});
|
||||
}
|
||||
|
||||
Chunks.AddEntity(entityID, locations);
|
||||
|
||||
// TODO add to chunk node instead of world
|
||||
Interface->add_child(node);
|
||||
node->set_position(Vector2(x + 0.5f, -y + 1));
|
||||
|
||||
if (factoryNode)
|
||||
{
|
||||
// set node ptr in ecs
|
||||
NodePtr nodePtr{};
|
||||
nodePtr.Node = factoryNode;
|
||||
Registry.emplace<NodePtr>(entityID, nodePtr);
|
||||
|
||||
factoryNode->SetCommandQueue(CommandQueue);
|
||||
factoryNode->SetEntity(entityID);
|
||||
|
||||
// initialize data in ecs
|
||||
factoryNode->Initialize(Registry, *this, entityID);
|
||||
}
|
||||
|
||||
return FACTORY_ERROR_NONE;
|
||||
}
|
||||
|
||||
entt::entity FactoryWorld::CreateEntity()
|
||||
{
|
||||
return Registry.create();
|
||||
}
|
||||
|
||||
bool FactoryWorld::IsValidCameraPos(Rect2i viewport) const
|
||||
{
|
||||
auto pos0 = viewport.position;
|
||||
auto pos3 = viewport.get_end();
|
||||
auto pos1 = Vector2i{ pos0.x, pos3.y };
|
||||
auto pos2 = Vector2i{ pos3.x, pos0.y };
|
||||
|
||||
bool valid0{};
|
||||
bool valid1{};
|
||||
bool valid2{};
|
||||
bool valid3{};
|
||||
|
||||
for (auto chunk : UnlockedChunks)
|
||||
{
|
||||
auto bounds = chunk.GetBounds();
|
||||
valid0 = valid0 || bounds.has_point(pos0);
|
||||
valid1 = valid1 || bounds.has_point(pos1);
|
||||
valid2 = valid2 || bounds.has_point(pos2);
|
||||
valid3 = valid3 || bounds.has_point(pos3);
|
||||
}
|
||||
|
||||
return valid0 && valid1 && valid2 && valid3;
|
||||
}
|
||||
|
||||
FactoryError FactoryWorld::TryUnlockChunk(ChunkKey chunk)
|
||||
{
|
||||
for (auto& unlockableChunk : UnlockableChunks)
|
||||
{
|
||||
if (unlockableChunk.ChunkID == chunk)
|
||||
{
|
||||
for (auto& requirement : unlockableChunk.Items)
|
||||
if (WorldInventory.GetItemsAmount(requirement.Item.Item) < requirement.Amount)
|
||||
return FACTORY_ERROR_NOT_ENOUGH_ITEMS;
|
||||
|
||||
for (auto& requirement : unlockableChunk.Items)
|
||||
WorldInventory.RemoveItems(requirement.Item);
|
||||
|
||||
UnlockedChunks.push_back(chunk);
|
||||
RefreshUnlockedChunks();
|
||||
return FACTORY_ERROR_NONE;
|
||||
}
|
||||
}
|
||||
return FACTORY_ERROR_INVALID_POS;
|
||||
}
|
||||
|
||||
void FactoryWorld::RefreshUnlockedChunks()
|
||||
{
|
||||
UnlockedChunks.clear();
|
||||
if (WorldSettings->LayerConfigs.is_empty()) return;
|
||||
|
||||
HashSet<ChunkKey> unlockableChunks{};
|
||||
int maxHeight = WorldSettings->LayerConfigs[0]->StartChunk;
|
||||
|
||||
for (auto& unlockedChunk : UnlockedChunks)
|
||||
{
|
||||
ChunkKey chunk0 = unlockedChunk;
|
||||
ChunkKey chunk1 = unlockedChunk;
|
||||
ChunkKey chunk2 = unlockedChunk;
|
||||
ChunkKey chunk3 = unlockedChunk;
|
||||
|
||||
chunk0.X -= 1; chunk1.X += 1;
|
||||
chunk2.Y -= 1; chunk3.Y += 1;
|
||||
|
||||
unlockableChunks.insert(chunk0);
|
||||
unlockableChunks.insert(chunk1);
|
||||
unlockableChunks.insert(chunk2);
|
||||
unlockableChunks.insert(chunk3);
|
||||
}
|
||||
|
||||
for (auto& unlockedChunk : UnlockedChunks)
|
||||
{
|
||||
unlockableChunks.erase(unlockedChunk);
|
||||
}
|
||||
|
||||
for (auto& unlockable : unlockableChunks)
|
||||
{
|
||||
UnlockedChunks.push_back(unlockable);
|
||||
}
|
||||
}
|
||||
|
||||
// void FactoryWorld::PlaceLight(LightValue light, Vector2i position)
|
||||
// {
|
||||
// PropogateLight(light, position);
|
||||
// Interface->call_deferred("update_lightmap");
|
||||
// }
|
||||
|
||||
// void FactoryWorld::RefreshLightMap()
|
||||
// {
|
||||
// auto lights = Registry.view<LightValue, TilePosition>();
|
||||
|
||||
// // Find World Bounds
|
||||
// Bounds = Bounds.merge(Rect2i(-256, -256, 512, 300));
|
||||
// lights.each([this](LightValue light, TilePosition position)
|
||||
// {
|
||||
// Rect2i lightRadius = Rect2i(position.Position - Vector2i(light.Val, light.Val), 2.f * light.Val * Vector2i(1, 1));
|
||||
// Bounds = Bounds.merge(lightRadius);
|
||||
// });
|
||||
|
||||
// // reset light chunks
|
||||
// for (int i{}; i < Chunks.size(); ++i)
|
||||
// for (int j{}; j < Chunks[i].LightChunk.Tiles.size(); ++j)
|
||||
// Chunks[i].LightChunk.Tiles[j].Val = 0;
|
||||
|
||||
// // skylight
|
||||
// ApplySkyLight(Bounds);
|
||||
|
||||
// // propogate all light sources
|
||||
// lights.each([this](LightValue light, TilePosition position)
|
||||
// {
|
||||
// PropogateLight(light, position.Position);
|
||||
// });
|
||||
|
||||
// // sync bounds with interface
|
||||
// Interface->call_deferred("set_bounds", Bounds);
|
||||
// }
|
||||
|
||||
// void FactoryWorld::ApplySkyLight(Rect2i bounds)
|
||||
// {
|
||||
// int lowestLightval = INT32_MAX;
|
||||
// int highestLightVal = INT32_MIN;
|
||||
|
||||
// // go down until block is found
|
||||
// for (int x{ bounds.position.x }; x <= bounds.get_end().x; ++x)
|
||||
// for (int y{ bounds.get_end().y }; y >= bounds.position.y; --y)
|
||||
// {
|
||||
// auto tileData = GetTileData(x, y);
|
||||
|
||||
// if (tileData.Tile.IsTile())
|
||||
// {
|
||||
// lowestLightval = std::min(lowestLightval, y + 1);
|
||||
// highestLightVal = std::max(highestLightVal, y + 1);
|
||||
// break;
|
||||
// }
|
||||
|
||||
// tileData.Light.Val = LightValue::MaxLightVal;
|
||||
|
||||
// }
|
||||
|
||||
// // spread each light block
|
||||
// for (int x{ bounds.position.x }; x <= bounds.get_end().x; ++x)
|
||||
// for (int y{ lowestLightval }; y <= highestLightVal; ++y)
|
||||
// {
|
||||
// LightValue& value = GetLightValueInternal(x, y);
|
||||
// if (value.Val == LightValue::MaxLightVal)
|
||||
// {
|
||||
// auto spreadValue = value;
|
||||
// value.Val = 0;
|
||||
// PropogateLight(spreadValue, Vector2i(x, y));
|
||||
// }
|
||||
// }
|
||||
|
||||
// Interface->call_deferred("update_lightmap");
|
||||
// }
|
||||
|
||||
// void FactoryWorld::PropogateLight(LightValue light, Vector2i position)
|
||||
// {
|
||||
// // light value of tile
|
||||
// LightValue& lightVal = GetLightValueInternal(position.x, position.y);
|
||||
|
||||
// int previousVal = lightVal.Val;
|
||||
// lightVal.Val = std::max(light.Val, lightVal.Val);
|
||||
|
||||
// // new light value decided by taking substracting the penetration
|
||||
// int newLightVal = light.Val - std::max(1, lightVal.Penetration / light.Penetration);
|
||||
|
||||
// if (light.Val > previousVal && newLightVal >= 0)
|
||||
// {
|
||||
// light.Val = newLightVal;
|
||||
|
||||
// // check neighboring tiles
|
||||
// PropogateLight(light, position + Vector2i(0, +1));
|
||||
// PropogateLight(light, position + Vector2i(0, -1));
|
||||
// PropogateLight(light, position + Vector2i(+1, 0));
|
||||
// PropogateLight(light, position + Vector2i(-1, 0));
|
||||
// }
|
||||
// }
|
||||
|
||||
FactoryError FactoryWorld::TryUpgradeEntity(FactoryEntity* entity)
|
||||
{
|
||||
return TryUpgradeEntity(entity->GetEntity());
|
||||
}
|
||||
|
||||
FactoryError FactoryWorld::TryUpgradeEntity(entt::entity entity)
|
||||
{
|
||||
auto upgradeError = CanUpgradeEntity(entity);
|
||||
if (upgradeError)
|
||||
return upgradeError;
|
||||
|
||||
Level const* level = Registry.try_get<Level>(entity);
|
||||
auto archetype = Registry.get<ArchetypePtr>(entity);
|
||||
|
||||
// remove ingredients
|
||||
auto newLevelIngredients = archetype.Archetype->Upgrades[level->Val];
|
||||
{
|
||||
auto inventory = FactoryServer::get_singleton()->AccessInventory();
|
||||
|
||||
for (auto ingredient : newLevelIngredients->UpgradeCost)
|
||||
{
|
||||
inventory->RemoveItems(ingredient->Item->Item.ItemID, ingredient->Amount);
|
||||
}
|
||||
}
|
||||
|
||||
UpgradeEntity(entity, archetype.Archetype);
|
||||
|
||||
return FACTORY_ERROR_NONE;
|
||||
}
|
||||
|
||||
FactoryError FactoryWorld::TryUpgradeEntity(const Vector2i& position)
|
||||
{
|
||||
auto entity = Chunks.GetEntity(position.x, position.y);
|
||||
if (entity != entt::null)
|
||||
return TryUpgradeEntity(entity);
|
||||
|
||||
return FACTORY_ERROR_NOT_ENTITY;
|
||||
}
|
||||
|
||||
FactoryError FactoryWorld::CanUpgradeEntity(entt::entity entity) const
|
||||
{
|
||||
Level const* level = Registry.try_get<Level>(entity);
|
||||
auto archetype = Registry.get<ArchetypePtr>(entity);
|
||||
if (!level || archetype.Archetype.is_null())
|
||||
return FACTORY_ERROR_CANT_UPGRADE;
|
||||
|
||||
if (level->Val >= archetype.Archetype->Upgrades.size())
|
||||
return FACTORY_ERROR_FULLY_UPGRADED;
|
||||
|
||||
auto newLevelIngredients = archetype.Archetype->Upgrades[level->Val];
|
||||
{
|
||||
auto inventory = FactoryServer::get_singleton()->AccessInventory();
|
||||
for (auto ingredient : newLevelIngredients->UpgradeCost)
|
||||
{
|
||||
if (inventory->GetItemsAmount(ingredient->Item->Item.ItemID) < ingredient->Amount)
|
||||
return FACTORY_ERROR_NOT_ENOUGH_ITEMS;
|
||||
}
|
||||
}
|
||||
|
||||
auto upgradeError = Registry.get<NodePtr>(entity).Node->CanUpgrade();
|
||||
if (upgradeError != FACTORY_ERROR_NONE)
|
||||
return upgradeError;
|
||||
|
||||
return FACTORY_ERROR_NONE;
|
||||
}
|
||||
|
||||
FactoryError FactoryWorld::CanUpgradeEntity(FactoryEntity* entity) const
|
||||
{
|
||||
return CanUpgradeEntity(entity->GetEntity());
|
||||
}
|
||||
|
||||
FactoryError FactoryWorld::CanUpgradeEntity(const Vector2i& position)
|
||||
{
|
||||
auto entity = Chunks.GetEntity(position.x, position.y);
|
||||
if (entity != entt::null)
|
||||
return CanUpgradeEntity(entity);
|
||||
|
||||
return FACTORY_ERROR_NOT_ENTITY;
|
||||
}
|
||||
|
||||
void FactoryWorld::UpgradeEntity(entt::entity entity, Ref<Archetype> archetype)
|
||||
{
|
||||
FactoryEntity* node = Registry.get<NodePtr>(entity).Node;
|
||||
|
||||
auto& level = Registry.get<Level>(entity);
|
||||
Registry.emplace<SaveLoad>(entity);
|
||||
|
||||
auto newLevelIngredients = archetype->Upgrades[level.Val];
|
||||
int newLevel = ++level.Val;
|
||||
|
||||
node->SetLevel(newLevel, newLevelIngredients);
|
||||
}
|
||||
|
||||
void FactoryWorld::SetEntityLevel(entt::entity entity, uint8_t level)
|
||||
{
|
||||
SetEntityLevel(entity, Registry.get<ArchetypePtr>(entity).Archetype, level);
|
||||
}
|
||||
|
||||
void FactoryWorld::SetEntityLevel(entt::entity entity, Ref<Archetype> archetype, uint8_t level)
|
||||
{
|
||||
FactoryEntity* node = Registry.get<NodePtr>(entity).Node;
|
||||
|
||||
auto& lvl = Registry.get<Level>(entity);
|
||||
|
||||
if (lvl.Val != level)
|
||||
{
|
||||
lvl = Level(level);
|
||||
|
||||
node->SetLevel(level, archetype->Upgrades[level]);
|
||||
}
|
||||
}
|
||||
|
||||
void FactoryWorld::HighlightUpgradableEntities(TileMapLayer* tilemap) const
|
||||
{
|
||||
tilemap->clear();
|
||||
|
||||
auto view = Registry.view<const Level, const ArchetypePtr, const TilePosition>();
|
||||
|
||||
view.each([tilemap](Level lvl, ArchetypePtr ptr, TilePosition pos) {
|
||||
if (!ptr.Archetype->OccupiedTiles.is_empty() &&
|
||||
lvl.Val < ptr.Archetype->Upgrades.size())
|
||||
{
|
||||
for (const auto& tile : ptr.Archetype->OccupiedTiles)
|
||||
{
|
||||
Vector2i position = pos.Position + tile->Offset;
|
||||
position.y = -position.y;
|
||||
tilemap->set_cell(position, 0, Vector2i(0, 0));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void FactoryWorld::UpgradeEntity(entt::entity entity)
|
||||
{
|
||||
UpgradeEntity(entity, Registry.get<ArchetypePtr>(entity).Archetype);
|
||||
}
|
||||
|
||||
struct WorldAStarNode
|
||||
{
|
||||
public:
|
||||
WorldAStarNode(const FactoryWorld& world, Vector2i pos) : World{ &world }, Position{ pos } {};
|
||||
WorldAStarNode() = default;
|
||||
WorldAStarNode(const WorldAStarNode&) = default;
|
||||
WorldAStarNode(WorldAStarNode&&) = default;
|
||||
|
||||
WorldAStarNode& operator=(const WorldAStarNode&) = default;
|
||||
WorldAStarNode& operator=(WorldAStarNode&&) = default;
|
||||
|
||||
public:
|
||||
|
||||
// Heuristic function which computes the estimated cost to the goal node
|
||||
float GoalDistanceEstimate(const WorldAStarNode& nodeGoal) const
|
||||
{
|
||||
return static_cast<float>(Position.distance_squared_to(nodeGoal.Position));
|
||||
}
|
||||
|
||||
// Returns true if this node is the goal node
|
||||
bool IsGoal(const WorldAStarNode& nodeGoal) const
|
||||
{
|
||||
return Position == nodeGoal.Position;
|
||||
}
|
||||
|
||||
// Retrieves all successors to this node and adds them via astarsearch.addSuccessor()
|
||||
bool GetSuccessors(AStarSearch<WorldAStarNode>* astarsearch, WorldAStarNode* parent_node) const
|
||||
{
|
||||
Vector2i parentPos{};
|
||||
if (parent_node)
|
||||
{
|
||||
parentPos = parent_node->Position;
|
||||
}
|
||||
|
||||
Vector2i bottomPos = Vector2i(Position.x, Position.y - 1);
|
||||
Vector2i leftPos = Vector2i(Position.x - 1, Position.y);
|
||||
Vector2i rightPos = Vector2i(Position.x + 1, Position.y);
|
||||
|
||||
auto bottomTile = World->TryGetTile(bottomPos.x, bottomPos.y);
|
||||
auto leftTile = World->TryGetTile(leftPos.x, leftPos.y);
|
||||
auto rightTile = World->TryGetTile(rightPos.x, rightPos.y);
|
||||
|
||||
if (bottomPos != parentPos && bottomTile && !bottomTile->IsFiller())
|
||||
astarsearch->AddSuccessor(WorldAStarNode(*World, bottomPos));
|
||||
|
||||
if (leftPos != parentPos && leftTile && !leftTile->IsFiller())
|
||||
astarsearch->AddSuccessor(WorldAStarNode(*World, leftPos));
|
||||
|
||||
if (rightPos != parentPos && rightTile && !rightTile->IsFiller())
|
||||
astarsearch->AddSuccessor(WorldAStarNode(*World, rightPos));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Computes the cost of travelling from this node to the successor node
|
||||
float GetCost(const WorldAStarNode& successor) const
|
||||
{
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
// Returns true if this node is the same as the rhs node
|
||||
bool IsSameState(const WorldAStarNode& rhs) const
|
||||
{
|
||||
return rhs.Position == Position;
|
||||
}
|
||||
|
||||
// Returns a hash for the state
|
||||
size_t Hash() const
|
||||
{
|
||||
static_assert(sizeof(size_t) == sizeof(Vector2i));
|
||||
return reinterpret_cast<const size_t&>(Position);
|
||||
}
|
||||
public:
|
||||
FactoryWorld const* World;
|
||||
Vector2i Position{};
|
||||
};
|
||||
|
||||
FactoryError FactoryWorld::FindChutePath(Vector<Vector2i>& path, Vector2i startPos, Vector2i endPos) const
|
||||
{
|
||||
constexpr int MaxLength = 128;
|
||||
if (startPos.distance_squared_to(endPos) > MaxLength * MaxLength)
|
||||
return FACTORY_ERROR_PATH_TOO_LONG;
|
||||
|
||||
auto tile = TryGetTile(startPos.x, startPos.y);
|
||||
if (!tile || !tile->IsAir())
|
||||
return FACTORY_ERROR_INVALID_POS;
|
||||
|
||||
tile = TryGetTile(endPos.x, endPos.y);
|
||||
if (!tile || !tile->IsAir())
|
||||
return FACTORY_ERROR_INVALID_POS;
|
||||
|
||||
if (endPos.y > startPos.y)
|
||||
std::swap(endPos, startPos);
|
||||
|
||||
AStarSearch<WorldAStarNode> aStarSearch{MaxLength * MaxLength * 2};
|
||||
aStarSearch.SetStartAndGoalStates(WorldAStarNode{ *this, startPos }, WorldAStarNode{ *this, endPos });
|
||||
|
||||
unsigned int searchState = -1;
|
||||
do
|
||||
{
|
||||
searchState = aStarSearch.SearchStep();
|
||||
|
||||
} while (searchState == AStarSearch<WorldAStarNode>::SEARCH_STATE_SEARCHING);
|
||||
|
||||
switch (searchState)
|
||||
{
|
||||
case AStarSearch<WorldAStarNode>::SEARCH_STATE_FAILED:
|
||||
case AStarSearch<WorldAStarNode>::SEARCH_STATE_INVALID:
|
||||
case AStarSearch<WorldAStarNode>::SEARCH_STATE_NOT_INITIALISED:
|
||||
case AStarSearch<WorldAStarNode>::SEARCH_STATE_OUT_OF_MEMORY:
|
||||
default:
|
||||
return FACTORY_ERROR_INVALID_PATH;
|
||||
|
||||
case AStarSearch<WorldAStarNode>::SEARCH_STATE_SUCCEEDED:
|
||||
path.clear();
|
||||
path.push_back(aStarSearch.GetSolutionStart()->Position);
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto newCell = aStarSearch.GetSolutionNext();
|
||||
if (unlikely(!newCell))
|
||||
{
|
||||
path.push_back(endPos);
|
||||
break;
|
||||
}
|
||||
|
||||
if (Raycast(path[path.size() - 1], newCell->Position))
|
||||
path.push_back(newCell->Position);
|
||||
}
|
||||
|
||||
aStarSearch.FreeSolutionNodes();
|
||||
break;
|
||||
}
|
||||
|
||||
aStarSearch.EnsureMemoryFreed();
|
||||
return FACTORY_ERROR_NONE;
|
||||
}
|
||||
|
||||
Tile const* FactoryWorld::Raycast(Vector2i startPos, Vector2i endPos) const
|
||||
{
|
||||
Vector2 direction = Vector2(endPos - startPos).normalized();
|
||||
Vector2 deltaDist{ std::abs(1 / direction.x), std::abs(1 / direction.y) };
|
||||
Vector2 step{ 1, 1 };
|
||||
Vector2 sideDist{ 0.5f, 0.5f };
|
||||
|
||||
int mapX = startPos.x;
|
||||
int mapY = startPos.y;
|
||||
|
||||
if (direction.x < 0)
|
||||
{
|
||||
step.x = -1;
|
||||
}
|
||||
|
||||
if (direction.y < 0)
|
||||
{
|
||||
step.y = -1;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (sideDist.x < sideDist.y)
|
||||
{
|
||||
sideDist.x += deltaDist.x;
|
||||
mapX += step.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
sideDist.y += deltaDist.y;
|
||||
mapY += step.y;
|
||||
}
|
||||
|
||||
Tile const* cell = TryGetTile(mapX, mapY);
|
||||
|
||||
if (cell && !cell->IsAir())
|
||||
{
|
||||
return cell;
|
||||
}
|
||||
|
||||
if (mapX < 0 || mapX >= endPos.x || mapY < 0 || mapY >= endPos.y)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FactoryWorld::IsSupport(int x, int y) const
|
||||
{
|
||||
auto entity = GetEntity(x, y);
|
||||
return entity != entt::null && Registry.try_get<Support>(entity);
|
||||
}
|
||||
|
||||
bool FactoryWorld::IsSupport(entt::entity entity) const
|
||||
{
|
||||
return Registry.try_get<Support>(entity);
|
||||
}
|
||||
618
src/Core/WorldGenerator.cpp
Normal file
618
src/Core/WorldGenerator.cpp
Normal file
@@ -0,0 +1,618 @@
|
||||
#include "Core/WorldGenerator.h"
|
||||
#include "Core/FactoryWorld.h"
|
||||
#include "WorldGenerator.h"
|
||||
#include <bitset>
|
||||
|
||||
using namespace godot;
|
||||
|
||||
template <typename T>
|
||||
void SetNoiseSeed(Vector<Ref<T>>& layers, int32_t seed)
|
||||
{
|
||||
for (auto& layer : layers)
|
||||
{
|
||||
if (layer->NoiseGenerator.is_null())
|
||||
layer->NoiseGenerator.instantiate();
|
||||
layer->NoiseGenerator->set_seed(seed);
|
||||
}
|
||||
}
|
||||
|
||||
// WorldGenerator::WorldGenerator(Ref<FactoryWorldSettings> settings, int32_t seed)
|
||||
// : Settings{ settings }
|
||||
// , Seed{ seed }
|
||||
// {
|
||||
// if (Settings.is_valid())
|
||||
// {
|
||||
// Graph = Settings->WorldGenerator;
|
||||
// }
|
||||
// }
|
||||
|
||||
// static Vector<TileGeneratorFunction> GetGraphFunctions(Ref<LayerConfig> layer, const WorldGraph& graph);
|
||||
// static std::array<tcb::span<const TileGeneratorFunction>, TILE_TYPE::TILE_MAX> DivideTileTypes(const Vector<TileGeneratorFunction>& tiles);
|
||||
|
||||
struct TileGeneratorFunction final
|
||||
{
|
||||
WorldNodeBase* Function{};
|
||||
Tile ReturnedTile{};
|
||||
};
|
||||
|
||||
Vector<TileGeneratorFunction> GetGraphFunctions(Ref<LayerConfig> layer, const WorldGraph& graph)
|
||||
{
|
||||
if (layer.is_null()) return {};
|
||||
|
||||
Vector<TileGeneratorFunction> Generators{};
|
||||
for (auto tileGen : layer->Tiles)
|
||||
{
|
||||
TileGeneratorFunction generator{};
|
||||
generator.Function = graph.GetNode(tileGen->TileGenerator);
|
||||
generator.ReturnedTile = tileGen->Tile->TileData;
|
||||
Generators.push_back(generator);
|
||||
}
|
||||
return Generators;
|
||||
}
|
||||
|
||||
std::array<tcb::span<const TileGeneratorFunction>, TILE_TYPE::TILE_MAX> DivideTileTypes(const Vector<TileGeneratorFunction>& tiles)
|
||||
{
|
||||
std::array<tcb::span<const TileGeneratorFunction>, TILE_TYPE::TILE_MAX> values{};
|
||||
for (int i{}, j{}; i < TILE_TYPE::TILE_MAX; ++i)
|
||||
{
|
||||
values[i] = tcb::span<const TileGeneratorFunction>{tiles.ptr() + j, std::size_t{0}};
|
||||
for (; j < tiles.size() && tiles[j].ReturnedTile.GetType() == static_cast<TILE_TYPE>(i); ++j)
|
||||
{
|
||||
values[i] = tcb::span<const TileGeneratorFunction>(values[i].data(), tiles.ptr() + j + 1);
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
void ApplyGeneratorFunctions(WorldNodeParameters& parameters, tcb::span<const TileGeneratorFunction> generators, tcb::span<const TileGeneratorFunction> nextGenerators = {})
|
||||
{
|
||||
if (generators.empty() && nextGenerators.empty()) return;
|
||||
|
||||
auto bounds = parameters.GetGenerationBounds();
|
||||
auto chunkBounds = parameters.ChunkInfo.GetBounds();
|
||||
|
||||
auto buffer = WorldNodeParameters::TileArray{ *parameters.GeneratedTiles };
|
||||
|
||||
for (int y = bounds.position.y; y < bounds.get_end().y; ++y)
|
||||
{
|
||||
float topLayerSubtract = !nextGenerators.empty() ? std::clamp(static_cast<float>(y - chunkBounds.position.y) / Chunk::ChunkSize, 0.f, 1.f) : 0;
|
||||
float bottomLayerSubstract = 1.f - topLayerSubtract;
|
||||
|
||||
for (int x = bounds.position.x; x < bounds.get_end().x; ++x)
|
||||
{
|
||||
parameters.X = x;
|
||||
parameters.Y = y;
|
||||
|
||||
float maxVal{};
|
||||
|
||||
for (auto& gen : generators)
|
||||
{
|
||||
auto val = gen.Function->Evaluate(parameters).get_unsafe_float() - topLayerSubtract;
|
||||
if (val > maxVal)
|
||||
{
|
||||
maxVal = val;
|
||||
buffer[parameters.GetArrayIndex(x, y)] = gen.ReturnedTile;
|
||||
}
|
||||
}
|
||||
for (auto& gen : nextGenerators)
|
||||
{
|
||||
auto val = gen.Function->Evaluate(parameters).get_unsafe_float() - bottomLayerSubstract;
|
||||
if (val > maxVal)
|
||||
{
|
||||
maxVal = val;
|
||||
buffer[parameters.GetArrayIndex(x, y)] = gen.ReturnedTile;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*parameters.GeneratedTiles = buffer;
|
||||
}
|
||||
|
||||
bool IsPocket(const WorldNodeParameters::TileArray& tiles, Vector2i pos, Tile tile)
|
||||
{
|
||||
for (int x{pos.x}; x >= 0 && tiles[WorldNodeParameters::GetArrayIndex(x - 1, pos.y)].IsAir(); --x)
|
||||
{
|
||||
auto underneathTile = tiles[WorldNodeParameters::GetArrayIndex(x, pos.y - 1)];
|
||||
if ((!underneathTile.IsFiller() && underneathTile != tile) || x - 1 < 0) return false;
|
||||
}
|
||||
for (int x{pos.x + 1}; x < Chunk::ChunkSize && tiles[WorldNodeParameters::GetArrayIndex(x + 1, pos.y)].IsAir(); ++x)
|
||||
{
|
||||
auto underneathTile = tiles[WorldNodeParameters::GetArrayIndex(x, pos.y - 1)];
|
||||
if ((!underneathTile.IsFiller() && underneathTile != tile) || x + 1 >= Chunk::ChunkSize) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FillPocket(WorldNodeParameters::TileArray& tiles, Vector2i pos, Tile tile)
|
||||
{
|
||||
for (int x{pos.x}; x >= 0 && tiles[WorldNodeParameters::GetArrayIndex(x, pos.y)].IsAir(); --x)
|
||||
{
|
||||
tiles[WorldNodeParameters::GetArrayIndex(x, pos.y)] = tile;
|
||||
}
|
||||
for (int x{pos.x + 1}; x < Chunk::ChunkSize && tiles[WorldNodeParameters::GetArrayIndex(x, pos.y)].IsAir(); ++x)
|
||||
{
|
||||
tiles[WorldNodeParameters::GetArrayIndex(x, pos.y)] = tile;
|
||||
}
|
||||
}
|
||||
|
||||
void FlowLiquid(WorldNodeParameters& parameters, Vector2i pos, Tile liquidTile)
|
||||
{
|
||||
auto& tiles = *parameters.GeneratedTiles;
|
||||
|
||||
if (tiles[parameters.GetArrayIndex(pos.x, pos.y)].IsFiller()) return;
|
||||
|
||||
for (int y{pos.y}; y >= 0; --y)
|
||||
{
|
||||
if (tiles[parameters.GetArrayIndex(pos.x, y - 1)].IsFiller())
|
||||
{
|
||||
for (int upperBounds{y + 2}; y <= pos.y && y < upperBounds && IsPocket(tiles, Vector2i{pos.x, y}, liquidTile); ++y)
|
||||
{
|
||||
FillPocket(tiles, Vector2i{pos.x, y}, liquidTile);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyLiquids(WorldNodeParameters& parameters, tcb::span<const TileGeneratorFunction> generators, tcb::span<const TileGeneratorFunction> nextGenerators = {})
|
||||
{
|
||||
auto chunkCenter = parameters.ChunkInfo.GetBounds().get_center();
|
||||
parameters.X = chunkCenter.x;
|
||||
parameters.Y = chunkCenter.y;
|
||||
|
||||
for (auto& generator : generators)
|
||||
{
|
||||
int frequency = std::clamp(static_cast<int>(Chunk::ChunkSize * static_cast<float>(generator.Function->Evaluate(parameters))), 1, Chunk::ChunkSize);
|
||||
|
||||
for (int y{ Chunk::ChunkSize - 1 }; y >= (nextGenerators.empty() ? 0 : (Chunk::ChunkSize / 2)); y -= frequency)
|
||||
{
|
||||
for (int x{frequency / 2}; x < Chunk::ChunkSize; x += frequency)
|
||||
{
|
||||
FlowLiquid(parameters, Vector2i(x, y), generator.ReturnedTile);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto& generator : nextGenerators)
|
||||
{
|
||||
int frequency = std::clamp(static_cast<int>(Chunk::ChunkSize * static_cast<float>(generator.Function->Evaluate(parameters))), 1, Chunk::ChunkSize);
|
||||
|
||||
for (int y{ Chunk::ChunkSize / 2 }; y >= 0; y -= frequency)
|
||||
{
|
||||
for (int x{frequency / 2}; x < Chunk::ChunkSize; x += frequency)
|
||||
{
|
||||
FlowLiquid(parameters, Vector2i(x, y), generator.ReturnedTile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// bool WorldGenerator::GenerateChunk(ChunkKey chunkKey, Chunk& chunk) const
|
||||
// {
|
||||
// auto [currentLayer, nextLayer] = GetLayers(chunkKey);
|
||||
|
||||
// if (currentLayer.is_null()) return false;
|
||||
|
||||
// return GenerateChunk(chunkKey, chunk, currentLayer, nextLayer);
|
||||
// }
|
||||
|
||||
// bool WorldGenerator::GenerateChunk(ChunkKey chunkKey, Chunk &chunk, Ref<LayerConfig> layer, Ref<LayerConfig> nextLayer) const
|
||||
// {
|
||||
// Vector<TileGeneratorFunction> Generators{GetGraphFunctions(layer, Graph)};
|
||||
// Vector<TileGeneratorFunction> NextGenerators{GetGraphFunctions(nextLayer, Graph)};
|
||||
|
||||
// auto generatorLayers = DivideTileTypes(Generators);
|
||||
// auto nextGeneratorLayers = DivideTileTypes(NextGenerators);
|
||||
|
||||
// WorldNodeParameters Parameters{};
|
||||
// Parameters.ChunkInfo = chunkKey;
|
||||
// Parameters.FinalValueSubstract = 0;
|
||||
// Parameters.Seed = Seed;
|
||||
// auto bounds = Parameters.GetGenerationBounds();
|
||||
|
||||
// WorldNodeParameters::TileArray buffer{};
|
||||
|
||||
// int chunkStart = chunkKey.GetBounds().position.y;
|
||||
|
||||
// ApplyGeneratorFunctions(Parameters, generatorLayers[TILE_AIR], nextGeneratorLayers[TILE_AIR]);
|
||||
// ApplyGeneratorFunctions(Parameters, generatorLayers[TILE_FILLER], nextGeneratorLayers[TILE_FILLER]);
|
||||
// ApplyLiquids(Parameters, generatorLayers[TILE_LIQUID], nextGeneratorLayers[TILE_LIQUID]);
|
||||
// ApplyGeneratorFunctions(Parameters, generatorLayers[TILE_ORE], nextGeneratorLayers[TILE_ORE]);
|
||||
// ApplyGeneratorFunctions(Parameters, generatorLayers[TILE_NPC], nextGeneratorLayers[TILE_NPC]);
|
||||
// ApplyGeneratorFunctions(Parameters, generatorLayers[TILE_PLANT], nextGeneratorLayers[TILE_PLANT]);
|
||||
|
||||
// for (int y{}; y < Chunk::ChunkSize; ++y)
|
||||
// {
|
||||
// for (int x{}; x < Chunk::ChunkSize; ++x)
|
||||
// {
|
||||
// chunk.Tiles[y * Chunk::ChunkSize + x] = Parameters.GeneratedTiles[(y + WorldNodeParameters::MaxQueryOffset) * WorldNodeParameters::PaddedChunkSide + (x + WorldNodeParameters::MaxQueryOffset)];
|
||||
// }
|
||||
// }
|
||||
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// Vector<WorldGenerator::SpawnedEntities> WorldGenerator::SpawnEntities(ChunkKey chunkKey, Chunk &chunk, const std::vector<EntityTile>& persistantEntities) const
|
||||
// {
|
||||
// auto [currentLayer, nextLayer] = GetLayers(chunkKey);
|
||||
|
||||
// if (currentLayer.is_null()) return {};
|
||||
|
||||
// return SpawnEntities(chunkKey, chunk, currentLayer, nextLayer, persistantEntities);
|
||||
// }
|
||||
|
||||
// Vector<WorldGenerator::SpawnedEntities> WorldGenerator::SpawnEntities(ChunkKey chunkKey, Chunk &chunk, Ref<LayerConfig> layer, Ref<LayerConfig> nextLayer, const std::vector<EntityTile>& persistantEntities) const
|
||||
// {
|
||||
|
||||
// }
|
||||
|
||||
// struct ThreadParameters
|
||||
// {
|
||||
// WorldGenerator Generator{};
|
||||
// ChunkKey ChunkKey{};
|
||||
// std::function<void(ChunkData&&)> Callback{};
|
||||
// std::vector<EntityTile> PersistantEntities{};
|
||||
// };
|
||||
|
||||
// void ThreadedGenerateChunk_Internal(void* pData)
|
||||
// {
|
||||
// auto parameters = static_cast<ThreadParameters*>(pData);
|
||||
|
||||
// ChunkData data{};
|
||||
// data.Chunk = std::make_unique<Chunk>();
|
||||
|
||||
// parameters->Generator.GenerateChunk(parameters->ChunkKey, *data.Chunk);
|
||||
// auto entities = parameters->Generator.SpawnEntities(parameters->ChunkKey, *data.Chunk, parameters->PersistantEntities);
|
||||
|
||||
// for (int i{}; i < entities.size(); ++i)
|
||||
// {
|
||||
// auto entity = entities[i];
|
||||
// for (auto pos : entity.ClaimedPositions)
|
||||
// {
|
||||
// data.Entities.push_back(EntityTile {
|
||||
// entt::entity{i}, ChunkKey::WorldToChunk(pos.x), ChunkKey::WorldToChunk(pos.y)
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// data.PersistantEntities = std::move(std::move(parameters->PersistantEntities));
|
||||
|
||||
// parameters->Callback(std::move(data));
|
||||
|
||||
// memdelete(parameters);
|
||||
// }
|
||||
|
||||
// void WorldGenerator::ThreadedGenerateChunk(ChunkKey chunkKey, std::function<void(ChunkData &&)> callback, const std::vector<EntityTile>& persistantEntities)
|
||||
// {
|
||||
// DEV_ASSERT(callback);
|
||||
|
||||
// ChunkData Chunk{};
|
||||
|
||||
// auto pParameters = memnew(ThreadParameters);
|
||||
// pParameters->Callback = callback;
|
||||
// pParameters->ChunkKey = chunkKey;
|
||||
// pParameters->Generator = *this;
|
||||
// pParameters->PersistantEntities = persistantEntities;
|
||||
|
||||
// WorkerThreadPool::TaskID tid = WorkerThreadPool::get_singleton()->add_native_task(&ThreadedGenerateChunk_Internal, pParameters, false);
|
||||
// }
|
||||
|
||||
Pair<Ref<LayerConfig>, Ref<LayerConfig>> ChunkGenerator::GetLayers() const
|
||||
{
|
||||
Ref<LayerConfig> currentLayer{};
|
||||
Ref<LayerConfig> nextLayer{};
|
||||
for (auto& layer : Settings->LayerConfigs)
|
||||
{
|
||||
if (layer->StartChunk > ChunkInfo.Y)
|
||||
{
|
||||
currentLayer = layer;
|
||||
}
|
||||
else if (layer->StartChunk == ChunkInfo.Y)
|
||||
{
|
||||
nextLayer = layer;
|
||||
}
|
||||
else if (layer->StartChunk < ChunkInfo.Y)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {currentLayer, nextLayer};
|
||||
}
|
||||
|
||||
void ChunkGenerator::FillChunkCollection(int relativeX, int relativeY, ChunkCollection &collection) const
|
||||
{
|
||||
ChunkKey relativeKey{};
|
||||
relativeKey.X = relativeX;
|
||||
relativeKey.Y = relativeY;
|
||||
|
||||
auto bounds = relativeKey.GetBounds();
|
||||
|
||||
auto chunk = std::make_unique<Chunk>();
|
||||
|
||||
for (int y{ bounds.position.y }; y < bounds.get_end().y; ++y)
|
||||
{
|
||||
for (int x{ bounds.position.x }; x < bounds.get_end().x; ++x)
|
||||
{
|
||||
if (InBounds(x, y))
|
||||
{
|
||||
(*chunk).GetTile(Chunk::WorldToLocal(x), Chunk::WorldToLocal(y)) = GetTile(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ChunkKey key{ ChunkInfo };
|
||||
key.X += relativeX;
|
||||
key.Y += relativeY;
|
||||
|
||||
collection.SetChunkTiles(key, std::move(chunk));
|
||||
}
|
||||
|
||||
Tile ChunkGenerator::GetTile(int x, int y) const
|
||||
{
|
||||
DEV_ASSERT(InBounds(x, y));
|
||||
return (*TileArray)[(y + WorldNodeParameters::MaxQueryOffset) * WorldNodeParameters::PaddedChunkSide + (x + WorldNodeParameters::MaxQueryOffset)];
|
||||
}
|
||||
|
||||
bool ChunkGenerator::InBounds(int x, int y) const
|
||||
{
|
||||
return x > -WorldNodeParameters::MaxQueryOffset && x < WorldNodeParameters::MaxQueryOffset + Chunk::ChunkSize &&
|
||||
y > -WorldNodeParameters::MaxQueryOffset && y < WorldNodeParameters::MaxQueryOffset + Chunk::ChunkSize;
|
||||
}
|
||||
|
||||
struct ThreadParameters
|
||||
{
|
||||
ChunkGenerator Generator;
|
||||
ChunkGenerator::CreatedChunkCallback ChunkCallback;
|
||||
ChunkGenerator::SpawnedEntitiesCallback EntitiesCallback;
|
||||
ChunkGenerator::VisualizedChunkCallback VisualsCallback;
|
||||
ChunkGenerator::ShadowsCallback ShadowsCallback;
|
||||
};
|
||||
|
||||
void ChunkGenerator::GenerateChunk(Ref<FactoryWorldSettings> settings, ChunkKey chunkInfo, int seed, CreatedChunkCallback chunkCallback, SpawnedEntitiesCallback entitiesCallback, VisualizedChunkCallback visualsCallback, ShadowsCallback shadowsCallback)
|
||||
{
|
||||
auto parameters = memnew(ThreadParameters);
|
||||
parameters->Generator = ChunkGenerator(settings, chunkInfo, seed);
|
||||
parameters->ChunkCallback = chunkCallback;
|
||||
parameters->EntitiesCallback = entitiesCallback;
|
||||
parameters->VisualsCallback = visualsCallback;
|
||||
parameters->ShadowsCallback = shadowsCallback;
|
||||
|
||||
WorkerThreadPool::get_singleton()->add_native_task(&ChunkGenerator::GenerateChunk, parameters, false, "Generating Chunk");
|
||||
}
|
||||
|
||||
std::unique_ptr<Chunk> ChunkGenerator::GenerateChunkTilesNonThreaded()
|
||||
{
|
||||
std::unique_ptr<Chunk> returnVal;
|
||||
|
||||
GenerateChunkInternal([&returnVal](std::unique_ptr<Chunk>&& chunk)
|
||||
{
|
||||
returnVal = std::move(chunk);
|
||||
}, {}, {}, {});
|
||||
|
||||
return std::move(returnVal);
|
||||
}
|
||||
|
||||
void ChunkGenerator::GenerateChunk(void *pData)
|
||||
{
|
||||
ThreadParameters* parameters = static_cast<ThreadParameters*>(pData);
|
||||
|
||||
parameters->Generator.GenerateChunkInternal(parameters->ChunkCallback, parameters->EntitiesCallback, parameters->VisualsCallback, parameters->ShadowsCallback);
|
||||
|
||||
memdelete(parameters);
|
||||
}
|
||||
|
||||
void ChunkGenerator::GenerateChunkInternal(CreatedChunkCallback chunkCallback, SpawnedEntitiesCallback entitiesCallback, VisualizedChunkCallback visualsCallback, ShadowsCallback shadowsCallback)
|
||||
{
|
||||
TileArray = std::make_unique<WorldNodeParameters::TileArray>();
|
||||
|
||||
GenerateChunkTiles();
|
||||
|
||||
if (chunkCallback)
|
||||
{
|
||||
auto chunk = std::make_unique<Chunk>();
|
||||
|
||||
for (int y{}; y < Chunk::ChunkSize; ++y)
|
||||
{
|
||||
for (int x{}; x < Chunk::ChunkSize; ++x)
|
||||
{
|
||||
chunk->Tiles[y * Chunk::ChunkSize + x] = (*TileArray)[(y + WorldNodeParameters::MaxQueryOffset) * WorldNodeParameters::PaddedChunkSide + (x + WorldNodeParameters::MaxQueryOffset)];
|
||||
}
|
||||
}
|
||||
|
||||
chunkCallback(std::move(chunk));
|
||||
}
|
||||
|
||||
if (entitiesCallback)
|
||||
{
|
||||
auto entities = SpawnEntities();
|
||||
|
||||
entitiesCallback(entities);
|
||||
}
|
||||
|
||||
if (visualsCallback)
|
||||
{
|
||||
auto visuals = CreateVisuals();
|
||||
|
||||
visualsCallback(visuals.get());
|
||||
}
|
||||
|
||||
if (shadowsCallback)
|
||||
{
|
||||
auto shadowMap = CascadeShadows();
|
||||
|
||||
shadowsCallback(shadowMap.get());
|
||||
}
|
||||
}
|
||||
|
||||
void ChunkGenerator::GenerateChunkTiles() const
|
||||
{
|
||||
auto [currentLayer, nextLayer] = GetLayers();
|
||||
|
||||
Vector<TileGeneratorFunction> Generators{GetGraphFunctions(currentLayer, Settings->WorldGenerator)};
|
||||
Vector<TileGeneratorFunction> NextGenerators{GetGraphFunctions(nextLayer, Settings->WorldGenerator)};
|
||||
|
||||
auto generatorLayers = DivideTileTypes(Generators);
|
||||
auto nextGeneratorLayers = DivideTileTypes(NextGenerators);
|
||||
|
||||
WorldNodeParameters Parameters{};
|
||||
Parameters.ChunkInfo = ChunkInfo;
|
||||
Parameters.FinalValueSubstract = 0;
|
||||
Parameters.Seed = Seed;
|
||||
Parameters.GeneratedTiles = TileArray.get();
|
||||
auto bounds = Parameters.GetGenerationBounds();
|
||||
|
||||
WorldNodeParameters::TileArray buffer{};
|
||||
|
||||
int chunkStart = ChunkInfo.GetBounds().position.y;
|
||||
|
||||
ApplyGeneratorFunctions(Parameters, generatorLayers[TILE_AIR], nextGeneratorLayers[TILE_AIR]);
|
||||
ApplyGeneratorFunctions(Parameters, generatorLayers[TILE_FILLER], nextGeneratorLayers[TILE_FILLER]);
|
||||
ApplyLiquids(Parameters, generatorLayers[TILE_LIQUID], nextGeneratorLayers[TILE_LIQUID]);
|
||||
ApplyGeneratorFunctions(Parameters, generatorLayers[TILE_ORE], nextGeneratorLayers[TILE_ORE]);
|
||||
ApplyGeneratorFunctions(Parameters, generatorLayers[TILE_NPC], nextGeneratorLayers[TILE_NPC]);
|
||||
ApplyGeneratorFunctions(Parameters, generatorLayers[TILE_PLANT], nextGeneratorLayers[TILE_PLANT]);
|
||||
}
|
||||
|
||||
Vector<ChunkGenerator::SpawnedEntities> ChunkGenerator::SpawnEntities(const std::vector<EntityTile> &persistantEntities) const
|
||||
{
|
||||
Vector<SpawnedEntities> spawnedEntities{};
|
||||
Vector<Vector2i> claimedTilesBuffer{};
|
||||
std::bitset<Chunk::ChunkSize * Chunk::ChunkSize> claimedTiles{};
|
||||
auto chunkBounds = ChunkInfo.GetBounds();
|
||||
|
||||
ChunkCollection collection{};
|
||||
|
||||
FillChunkCollection(-1, +1, collection); FillChunkCollection(+0, +1, collection); FillChunkCollection(+1, +1, collection);
|
||||
FillChunkCollection(-1, +0, collection); FillChunkCollection(+0, +0, collection); FillChunkCollection(+1, +0, collection);
|
||||
FillChunkCollection(-1, -1, collection); FillChunkCollection(+0, -1, collection); FillChunkCollection(+1, -1, collection);
|
||||
|
||||
for (auto& entity : persistantEntities)
|
||||
{
|
||||
claimedTiles.set(entity.ChunkY * Chunk::ChunkSize + entity.ChunkX);
|
||||
}
|
||||
|
||||
auto [layer, nextLayer] = GetLayers();
|
||||
|
||||
auto currentLayer = layer;
|
||||
for (int y{chunkBounds.get_end().y}; y >= chunkBounds.get_position().y; --y)
|
||||
{
|
||||
if (y < chunkBounds.get_center().y && nextLayer.is_valid()) currentLayer = nextLayer;
|
||||
|
||||
for (int x{chunkBounds.get_position().x}; x < chunkBounds.get_end().x; ++x)
|
||||
{
|
||||
for (auto& arch : currentLayer->SpawningArchetypes)
|
||||
{
|
||||
for (auto& condition : arch->SpawnConditions)
|
||||
{
|
||||
if (!condition->IsValid(collection, Vector2i{x, y}))
|
||||
{
|
||||
goto NextArch;
|
||||
}
|
||||
}
|
||||
for (auto& condition : arch->SpawnConditions)
|
||||
{
|
||||
claimedTilesBuffer.clear();
|
||||
condition->ClaimTiles(claimedTilesBuffer, collection, Vector2i{x, y});
|
||||
|
||||
for (auto tile : claimedTilesBuffer)
|
||||
{
|
||||
if (!chunkBounds.has_point(Vector2i{x, y}) ||
|
||||
claimedTiles[(chunkBounds.position.y - tile.y) * Chunk::ChunkSize + (chunkBounds.position.x - tile.x)])
|
||||
{
|
||||
goto NextArch;
|
||||
}
|
||||
}
|
||||
for (auto tile : claimedTilesBuffer)
|
||||
{
|
||||
claimedTiles.set((chunkBounds.position.y - tile.y) * Chunk::ChunkSize + (chunkBounds.position.x - tile.x));
|
||||
}
|
||||
|
||||
ChunkGenerator::SpawnedEntities spawnInfo{};
|
||||
spawnInfo.Archetype = arch;
|
||||
spawnInfo.SpawnPosition = Vector2i{x, y};
|
||||
spawnInfo.ClaimedPositions = claimedTilesBuffer;
|
||||
|
||||
spawnedEntities.push_back(spawnInfo);
|
||||
}
|
||||
NextArch:;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<ChunkGenerator::CreatedVisualsChunk> ChunkGenerator::CreateVisuals()
|
||||
{
|
||||
auto visuals = std::make_unique<ChunkGenerator::CreatedVisualsChunk>();
|
||||
|
||||
for (int y{}; y < Chunk::ChunkSize; ++y)
|
||||
{
|
||||
for (int x{}; x < Chunk::ChunkSize; ++x)
|
||||
{
|
||||
auto tile = GetTile(x, y);
|
||||
|
||||
auto& tileConfig = Settings->TileConfigs[tile.GetID()];
|
||||
auto& possibleTextures = tileConfig->PossibleTextures;
|
||||
auto& possibleNeighbors = tileConfig->NeighborTransitions;
|
||||
|
||||
if (possibleTextures.size() == 1)
|
||||
{
|
||||
(*visuals)[y * Chunk::ChunkSize + x] = CreatedVisualsTile{ possibleTextures[0]->AtlasX, possibleTextures[0]->AtlasY, possibleTextures[0]->AtlasIndex };
|
||||
}
|
||||
else if (possibleTextures.size() > 1)
|
||||
{
|
||||
TextureWeight::GetTexture(possibleTextures, fastnoiselitestatic::SingleValue(Seed, x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return visuals;
|
||||
}
|
||||
|
||||
void ChunkGenerator::CascadeShadows_Recursive(std::array<int8_t, WorldNodeParameters::PaddedChunkSize>& values, int posX, int posY, int value)
|
||||
{
|
||||
if (InBounds(posX, posY))
|
||||
{
|
||||
int index = posY * WorldNodeParameters::PaddedChunkSide + posX;
|
||||
value += Settings->TileConfigs[(*TileArray)[index].GetID()]->LightResistance;
|
||||
|
||||
if (value < values[index])
|
||||
{
|
||||
values[index] = value;
|
||||
|
||||
CascadeShadows_Recursive(values, posX + 1, posY, value);
|
||||
CascadeShadows_Recursive(values, posX - 1, posY, value);
|
||||
CascadeShadows_Recursive(values, posX, posY + 1, value);
|
||||
CascadeShadows_Recursive(values, posX, posY - 1, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<ChunkGenerator::ChunkShadowValues> ChunkGenerator::CascadeShadows()
|
||||
{
|
||||
auto lightValues = std::make_unique<std::array<int8_t, WorldNodeParameters::PaddedChunkSize>>();
|
||||
auto lightValuesChunk = std::make_unique<ChunkShadowValues>();
|
||||
|
||||
for (int i{}; i < WorldNodeParameters::PaddedChunkSize; ++i)
|
||||
{
|
||||
auto tile = (*TileArray)[i];
|
||||
auto tileConfig = Settings->TileConfigs[tile.GetID()];
|
||||
(*lightValues)[i] = tileConfig->LightResistance <= 0 ? tileConfig->LightResistance : std::numeric_limits<int8_t>::max();
|
||||
}
|
||||
|
||||
for (int y{}; y < Chunk::ChunkSize; ++y)
|
||||
{
|
||||
for (int x{}; x < Chunk::ChunkSize; ++x)
|
||||
{
|
||||
CascadeShadows_Recursive(*lightValues, x, y, );
|
||||
}
|
||||
}
|
||||
|
||||
for (int y{}; y < Chunk::ChunkSize; ++y)
|
||||
{
|
||||
for (int x{}; x < Chunk::ChunkSize; ++x)
|
||||
{
|
||||
(*lightValuesChunk)[y * Chunk::ChunkSize + x] = lightValues[(y + WorldNodeParameters::MaxQueryOffset) * WorldNodeParameters::PaddedChunkSide + (x + WorldNodeParameters::MaxQueryOffset)];
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(lightValuesChunk);
|
||||
}
|
||||
22
src/Core/WorldInstance.cpp
Normal file
22
src/Core/WorldInstance.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include "Core/WorldInstance.h"
|
||||
|
||||
#include "Types/Item.hpp"
|
||||
#include "Components/Misc.hpp"
|
||||
#include "Components/Resource.hpp"
|
||||
#include "Components/Tick.hpp"
|
||||
|
||||
WorldInstance::WorldInstance(const WorldConfig& worldConfig)
|
||||
{
|
||||
RegisterTypes(EcsWorld);
|
||||
|
||||
EcsWorld.set<WorldConfig>(worldConfig);
|
||||
}
|
||||
|
||||
void WorldInstance::RegisterTypes(flecs::world &world)
|
||||
{
|
||||
Flecs_Misc(world);
|
||||
Flecs_Item(world);
|
||||
Flecs_Configs(world);
|
||||
Flecs_Tick(world);
|
||||
Flecs_Resource(world);
|
||||
}
|
||||
177
src/Data/Archetype.cpp
Normal file
177
src/Data/Archetype.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
#include "Data/Archetype.h"
|
||||
#include "Core/Chunk.h"
|
||||
|
||||
// using namespace godot;
|
||||
|
||||
// #define MAKE_RESOURCE_TYPE_HINT(m_type) vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, m_type)
|
||||
|
||||
|
||||
// void Archetype::_bind_methods()
|
||||
// {
|
||||
// ClassDB::bind_method(D_METHOD("get_scene"), &Archetype::GetScene);
|
||||
// ClassDB::bind_method(D_METHOD("get_preview_scene"), &Archetype::GetPreviewScene);
|
||||
// ClassDB::bind_method(D_METHOD("get_preview_texture"), &Archetype::GetPreviewTexture);
|
||||
// ClassDB::bind_method(D_METHOD("get_occupied_tiles"), &Archetype::GetOccupiedTiles);
|
||||
// ClassDB::bind_method(D_METHOD("get_upgrades"), &Archetype::GetUpgrades);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("set_scene", "scene"), &Archetype::SetScene);
|
||||
// ClassDB::bind_method(D_METHOD("set_preview_scene", "scene"), &Archetype::SetPreviewScene);
|
||||
// ClassDB::bind_method(D_METHOD("set_preview_texture", "texture"), &Archetype::SetPreviewTetxture);
|
||||
// ClassDB::bind_method(D_METHOD("set_occupied_tiles", "tiles"), &Archetype::SetOccupiedTiles);
|
||||
// ClassDB::bind_method(D_METHOD("set_upgrades", "tiles"), &Archetype::SetUpgrades);
|
||||
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "scene", PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"), "set_scene", "get_scene");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "preview_scene", PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"), "set_preview_scene", "get_preview_scene");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "preview_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_preview_texture", "get_preview_texture");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "occupied_tiles", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("OccupiedTile")), "set_occupied_tiles", "get_occupied_tiles");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "upgrades", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("UpgradeLevelConfig")), "set_upgrades", "get_upgrades");
|
||||
// }
|
||||
|
||||
// void SpawnDescription::_bind_methods()
|
||||
// {
|
||||
// ClassDB::bind_method(D_METHOD("get_filter"), &SpawnDescription::GetFilter);
|
||||
// ClassDB::bind_method(D_METHOD("get_tile"), &SpawnDescription::GetTile);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("set_filter", "offset"), &SpawnDescription::SetFilter);
|
||||
// ClassDB::bind_method(D_METHOD("set_tile", "tile"), &SpawnDescription::SetTile);
|
||||
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::INT, "filter", PROPERTY_HINT_ENUM, TileTypeEnumString), "set_offset", "get_offset");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "required_tile", PROPERTY_HINT_RESOURCE_TYPE, "TileConfig"), "set_tile", "get_tile");
|
||||
// }
|
||||
|
||||
bool SpawnDescription::TryFilterTile(const ChunkCollection &chunks, int x, int y)
|
||||
{
|
||||
auto tile = chunks.TryGetTile(x, y);
|
||||
if (tile) return FilterTile(*tile);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SpawnDescription::TryFilterTile(const ChunkCollection &chunks, Vector2i pos)
|
||||
{
|
||||
return TryFilterTile(chunks, pos.x, pos.y);
|
||||
}
|
||||
|
||||
// void SpawnNearby::_bind_methods()
|
||||
// {
|
||||
// ClassDB::bind_method(D_METHOD("get_range"), &SpawnNearby::GetRange);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("set_range", "range"), &SpawnNearby::SetRange);
|
||||
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::INT, "range"), "set_range", "get_range");
|
||||
// }
|
||||
|
||||
bool SpawnNearby::IsValid(const ChunkCollection &chunk, Vector2i pos)
|
||||
{
|
||||
for (int y{pos.y - Range}; y < pos.y + Range; ++y)
|
||||
{
|
||||
for (int x{pos.x - Range}; x < pos.x + Range; ++x)
|
||||
{
|
||||
if (TryFilterTile(chunk, x, y))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// void OccupiedTiles::_bind_methods()
|
||||
// {
|
||||
// ClassDB::bind_method(D_METHOD("get_offsets"), &OccupiedTiles::GetOffset);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("set_offsets", "offsets"), &OccupiedTiles::SetOffset);
|
||||
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "offsets", PROPERTY_HINT_ARRAY_TYPE, "Vector2i"), "set_offsets", "get_offsets");
|
||||
// }
|
||||
|
||||
bool InBounds(Vector2i pos)
|
||||
{
|
||||
return pos.x >= 0 && pos.x < Chunk::ChunkSize && pos.y >= 0 && pos.y < Chunk::ChunkSize;
|
||||
}
|
||||
|
||||
bool OccupiedTiles::IsValid(const ChunkCollection &chunk, Vector2i pos)
|
||||
{
|
||||
for (auto offset : Offsets)
|
||||
{
|
||||
if (!InBounds(pos + offset) || !TryFilterTile(chunk, pos + offset))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void OccupiedTiles::ClaimTiles(Vector<Vector2i> &tiles, const ChunkCollection &chunk, Vector2i pos)
|
||||
{
|
||||
for (auto offset : Offsets)
|
||||
{
|
||||
if (!tiles.has(pos + offset))
|
||||
tiles.push_back(pos + offset);
|
||||
}
|
||||
}
|
||||
|
||||
void RequiredTiles::_bind_methods()
|
||||
{
|
||||
ClassDB::bind_method(D_METHOD("get_offsets"), &RequiredTiles::GetOffset);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_offsets", "offsets"), &RequiredTiles::SetOffset);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "offsets", PROPERTY_HINT_ARRAY_TYPE, "Vector2i"), "set_offsets", "get_offsets");
|
||||
}
|
||||
|
||||
bool RequiredTiles::IsValid(const ChunkCollection &chunk, Vector2i pos)
|
||||
{
|
||||
for (auto offset : Offsets)
|
||||
{
|
||||
if (InBounds(pos + offset) && TryFilterTile(chunk, pos + offset))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LinkedTiles::_bind_methods()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool LinkedTiles::IsValid(const ChunkCollection &chunk, Vector2i pos)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void LinkedTiles::ClaimTiles_Recursive(Vector<Vector2i>& tiles, const ChunkCollection& chunk, Vector2i pos)
|
||||
{
|
||||
if (InBounds(pos) && TryFilterTile(chunk, pos) && !tiles.has(pos))
|
||||
{
|
||||
tiles.push_back(pos);
|
||||
|
||||
ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(+1, -1));
|
||||
ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(+1, +0));
|
||||
ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(+1, +1));
|
||||
ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(-1, -1));
|
||||
ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(-1, +0));
|
||||
ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(-1, +1));
|
||||
ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(+0, +1));
|
||||
ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(+0, -1));
|
||||
}
|
||||
}
|
||||
|
||||
void LinkedTiles::ClaimTiles(Vector<Vector2i> &tiles, const ChunkCollection &chunk, Vector2i pos)
|
||||
{
|
||||
ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(+1, -1));
|
||||
ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(+1, +0));
|
||||
ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(+1, +1));
|
||||
ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(-1, -1));
|
||||
ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(-1, +0));
|
||||
ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(-1, +1));
|
||||
ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(+0, +1));
|
||||
ClaimTiles_Recursive(tiles, chunk, pos + Vector2i(+0, -1));
|
||||
}
|
||||
|
||||
void PlaceableArchetype::_bind_methods()
|
||||
{
|
||||
ClassDB::bind_method(D_METHOD("get_archetype"), &PlaceableArchetype::GetArchetype);
|
||||
ClassDB::bind_method(D_METHOD("get_place_costs"), &PlaceableArchetype::GetPlaceCosts);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_archetypes", "archetypes"), &PlaceableArchetype::SetArchetype);
|
||||
ClassDB::bind_method(D_METHOD("set_place_costs", "costs"), &PlaceableArchetype::SetPlaceCosts);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "archetype", PROPERTY_HINT_RESOURCE_TYPE, "PackedScene"), "set_archetype", "get_archetype");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "costs", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("ItemAmountConfig")), "set_place_costs", "get_place_costs");
|
||||
}
|
||||
49
src/Data/Item.cpp
Normal file
49
src/Data/Item.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// #include "Data/Item.h"
|
||||
// #include "Main/factory_server.h"
|
||||
|
||||
// #define MAKE_RESOURCE_TYPE_HINT(m_type) vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, m_type)
|
||||
|
||||
// using namespace godot;
|
||||
|
||||
// void ItemConfig::_bind_methods()
|
||||
// {
|
||||
// ClassDB::bind_method(D_METHOD("get_id"), &ItemConfig::GetID);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("get_visual_name"), &ItemConfig::GetName);
|
||||
// // ClassDB::bind_method(D_METHOD("is_fuel"), &ItemConfig::IsFuel);
|
||||
// // ClassDB::bind_method(D_METHOD("is_fluid"), &ItemConfig::IsFluid);
|
||||
// // ClassDB::bind_method(D_METHOD("is_ore"), &ItemConfig::IsOre);
|
||||
// // ClassDB::bind_method(D_METHOD("is_plant"), &ItemConfig::IsPlant);
|
||||
// // ClassDB::bind_method(D_METHOD("is_mob_drop"), &ItemConfig::IsMobDrop);
|
||||
// // ClassDB::bind_method(D_METHOD("is_processed"), &ItemConfig::IsProcessed);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("set_visual_name", "name"), &ItemConfig::SetName);
|
||||
// // ClassDB::bind_method(D_METHOD("set_fuel", "val"), &ItemConfig::SetFuel);
|
||||
// // ClassDB::bind_method(D_METHOD("set_fluid", "val"), &ItemConfig::SetFluid);
|
||||
// // ClassDB::bind_method(D_METHOD("set_ore", "val"), &ItemConfig::SetOre);
|
||||
// // ClassDB::bind_method(D_METHOD("set_plant", "val"), &ItemConfig::SetPlant);
|
||||
// // ClassDB::bind_method(D_METHOD("set_mob_drop", "val"), &ItemConfig::SetMobDrop);
|
||||
// // ClassDB::bind_method(D_METHOD("set_processed", "val"), &ItemConfig::SetProcessed);
|
||||
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "visual_name"), "set_visual_name", "get_visual_name");
|
||||
// // ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_fuel"), "set_fuel", "is_fuel");
|
||||
// // ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_fluid"), "set_fluid", "is_fluid");
|
||||
// // ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_ore"), "set_ore", "is_ore");
|
||||
// // ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_plant"), "set_plant", "is_plant");
|
||||
// // ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_mob_drop"), "set_mob_drop", "is_mob_drop");
|
||||
// // ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_processed"), "set_processed", "is_processed");
|
||||
|
||||
// }
|
||||
|
||||
// void ItemAmountConfig::_bind_methods()
|
||||
// {
|
||||
// ClassDB::bind_method(D_METHOD("get_item"), &ItemAmountConfig::GetItem);
|
||||
// ClassDB::bind_method(D_METHOD("get_amount"), &ItemAmountConfig::GetAmount);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("set_item", "item"), &ItemAmountConfig::SetItem);
|
||||
// ClassDB::bind_method(D_METHOD("set_amount", "amount"), &ItemAmountConfig::SetAmount);
|
||||
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "ItemConfig"), "set_item", "get_item");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "amount"), "set_amount", "get_amount");
|
||||
|
||||
// }
|
||||
51
src/Data/Recipe.cpp
Normal file
51
src/Data/Recipe.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#include "Data/Recipe.h"
|
||||
#include "Main/factory_server.h"
|
||||
|
||||
// #define MAKE_RESOURCE_TYPE_HINT(m_type) vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, m_type)
|
||||
|
||||
// using namespace godot;
|
||||
|
||||
// void RecipeEntryConfig::_bind_methods()
|
||||
// {
|
||||
// ClassDB::bind_method(D_METHOD("get_item"), &RecipeEntryConfig::GetItem);
|
||||
// ClassDB::bind_method(D_METHOD("get_amount"), &RecipeEntryConfig::GetAmount);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("set_item", "item"), &RecipeEntryConfig::SetItem);
|
||||
// ClassDB::bind_method(D_METHOD("set_amount", "amount"), &RecipeEntryConfig::SetAmount);
|
||||
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "ItemConfig"), "set_item", "get_item");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::INT, "amount"), "set_amount", "get_amount");
|
||||
// }
|
||||
|
||||
// void RecipeConfig::_bind_methods()
|
||||
// {
|
||||
// ClassDB::bind_method(D_METHOD("get_ingredients"), &RecipeConfig::GetIngredients);
|
||||
// ClassDB::bind_method(D_METHOD("get_results"), &RecipeConfig::GetResults);
|
||||
// ClassDB::bind_method(D_METHOD("get_processing_time"), &RecipeConfig::GetProcessingTime);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("set_ingredients", "item"), &RecipeConfig::SetIngredients);
|
||||
// ClassDB::bind_method(D_METHOD("set_results", "amount"), &RecipeConfig::SetResults);
|
||||
// ClassDB::bind_method(D_METHOD("set_processing_time", "time"), &RecipeConfig::SetProcessingTime);
|
||||
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ingredients", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("RecipeEntryConfig")), "set_ingredients", "get_ingredients");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "results", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("RecipeEntryConfig")), "set_results", "get_results");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "processing_time"), "set_processing_time", "get_processing_time");
|
||||
// }
|
||||
|
||||
// Recipe::Recipe(const Ref<RecipeConfig>& recipe)
|
||||
// {
|
||||
// if (recipe.is_null()) return;
|
||||
|
||||
// RecipeMeta meta{};
|
||||
// meta.IngredientsAmount = recipe->Ingredients.size();
|
||||
// meta.ResultsAmount = recipe->Results.size();
|
||||
// meta.OriginalRecipe = recipe;
|
||||
// meta.ProcessingTime = FactoryServer::SecondToTicks(recipe->ProcessingTime);
|
||||
|
||||
// Data = SharedBuffer<ItemAmount, RecipeMeta>(meta.IngredientsAmount + meta.ResultsAmount, meta);
|
||||
|
||||
// for (uint8_t i{}; i < meta.IngredientsAmount; ++i)
|
||||
// Data[i] = recipe->Ingredients[i]->GetItemAmount();
|
||||
// for (uint8_t i{}; i < meta.ResultsAmount; ++i)
|
||||
// Data[i + meta.IngredientsAmount] = recipe->Results[i]->GetItemAmount();
|
||||
// }
|
||||
50
src/Data/Tile.cpp
Normal file
50
src/Data/Tile.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
// #include "Data/Tile.h"
|
||||
|
||||
// #define MAKE_RESOURCE_TYPE_HINT(m_type) vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, m_type)
|
||||
// using namespace godot;
|
||||
|
||||
// void TileConfig::_bind_methods()
|
||||
// {
|
||||
// BIND_ENUM_CONSTANT(TILE_AIR);
|
||||
// BIND_ENUM_CONSTANT(TILE_FILLER);
|
||||
// BIND_ENUM_CONSTANT(TILE_LIQUID);
|
||||
// BIND_ENUM_CONSTANT(TILE_ORE);
|
||||
// BIND_ENUM_CONSTANT(TILE_PLANT);
|
||||
// BIND_ENUM_CONSTANT(TILE_NPC);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("get_textures"), &TileConfig::GetTextures);
|
||||
// ClassDB::bind_method(D_METHOD("get_transitions"), &TileConfig::GetTransitions);
|
||||
// ClassDB::bind_method(D_METHOD("get_type"), &TileConfig::GetType);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("set_textures", "textures"), &TileConfig::SetTextures);
|
||||
// ClassDB::bind_method(D_METHOD("set_transitions", "transitions"), &TileConfig::SetTransitions);
|
||||
// ClassDB::bind_method(D_METHOD("set_type", "type"), &TileConfig::SetType);
|
||||
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "textures", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("TextureWeight")), "set_textures", "get_textures");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "transitions", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("TileTransitionConfig")), "set_transitions", "get_transitions");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, "Air,Filler,Liquid,Ore,Npc,Plant"), "set_type", "get_type");
|
||||
// }
|
||||
|
||||
// void TileTransitionConfig::_bind_methods()
|
||||
// {
|
||||
// ClassDB::bind_method(D_METHOD("get_neighbor"), &TileTransitionConfig::GetNeighbor);
|
||||
// ClassDB::bind_method(D_METHOD("get_possible_textures"), &TileTransitionConfig::GetPossibleTextures);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("set_neighbor", "neighbor"), &TileTransitionConfig::SetNeighbor);
|
||||
// ClassDB::bind_method(D_METHOD("set_possible_textures", "textures"), &TileTransitionConfig::SetPossibleTextures);
|
||||
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "neighbor", PROPERTY_HINT_RESOURCE_TYPE, "TileConfig"), "set_neighbor", "get_neighbor");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "possible_textures", PROPERTY_HINT_ARRAY_TYPE, MAKE_RESOURCE_TYPE_HINT("TextureWeight")), "set_possible_textures", "get_possible_textures");
|
||||
// }
|
||||
|
||||
// void TextureWeight::_bind_methods()
|
||||
// {
|
||||
// ClassDB::bind_method(D_METHOD("get_texture"), &TextureWeight::GetTexture);
|
||||
// ClassDB::bind_method(D_METHOD("get_weight"), &TextureWeight::GetWeight);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("set_texture", "texture"), &TextureWeight::SetTexture);
|
||||
// ClassDB::bind_method(D_METHOD("set_weight", "weight"), &TextureWeight::SetWeight);
|
||||
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::INT, "weight"), "set_weight", "get_weight");
|
||||
// }
|
||||
17
src/Data/UpgradeLevel.cpp
Normal file
17
src/Data/UpgradeLevel.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
// #include "Data/UpgradeLevel.h"
|
||||
|
||||
// #define MAKE_RESOURCE_TYPE_HINT(m_type) vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, m_type)
|
||||
|
||||
// using namespace godot;
|
||||
|
||||
// void UpgradeLevelConfig::_bind_methods()
|
||||
// {
|
||||
// ClassDB::bind_method(D_METHOD("get_costs"), &UpgradeLevelConfig::GetUpgradeCosts);
|
||||
// ClassDB::bind_method(D_METHOD("get_results"), &UpgradeLevelConfig::GetUpgradeResults);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("set_costs", "costs"), &UpgradeLevelConfig::SetUpgradeCosts);
|
||||
// ClassDB::bind_method(D_METHOD("set_results", "results"), &UpgradeLevelConfig::SetUpgradeResults);
|
||||
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "recipe_cost", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("ItemAmountConfig")), "set_costs", "get_costs");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "results"), "set_results", "get_results");
|
||||
// }
|
||||
139
src/Data/WorldGraph/WorldGraph.cpp
Normal file
139
src/Data/WorldGraph/WorldGraph.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "Data/WorldGraph/WorldGraph.h"
|
||||
|
||||
WorldGraph::WorldGraph(const Vector<Ref<WorldGraphVisualNodeBase>>& nodes)
|
||||
{
|
||||
Compile(nodes);
|
||||
}
|
||||
|
||||
WorldGraph::WorldGraph(const WorldGraph &other)
|
||||
{
|
||||
MemorySize = other.MemorySize;
|
||||
CompiledData = other.CopyMemory(NodeMap);
|
||||
}
|
||||
|
||||
WorldGraph &WorldGraph::operator=(const WorldGraph &other)
|
||||
{
|
||||
MemorySize = other.MemorySize;
|
||||
CompiledData = other.CopyMemory(NodeMap);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Variant WorldGraph::Execute(Ref<WorldGraphVisualNodeBase> node, const WorldNodeParameters ¶ms) const
|
||||
{
|
||||
auto nodePtr = GetNode(node);
|
||||
if (nodePtr)
|
||||
{
|
||||
return nodePtr->Evaluate(params);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
WorldNodeBase *WorldGraph::GetNode(Ref<WorldGraphVisualNodeBase> node) const
|
||||
{
|
||||
auto it = NodeMap.find(node);
|
||||
if (it != NodeMap.end())
|
||||
{
|
||||
return it->value;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AddAllNodes(HashSet<Ref<WorldGraphVisualNodeBase>>& allNodes, Ref<WorldGraphVisualNodeBase> node)
|
||||
{
|
||||
allNodes.insert(node);
|
||||
for (auto& input : node->InputNodes)
|
||||
{
|
||||
if (input.is_valid())
|
||||
{
|
||||
AddAllNodes(allNodes, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldGraph::Compile(const Vector<Ref<WorldGraphVisualNodeBase>>& nodes)
|
||||
{
|
||||
NodeMap.clear();
|
||||
|
||||
HashSet<Ref<WorldGraphVisualNodeBase>> allNodes{};
|
||||
HashMap<Ref<WorldGraphVisualNodeBase>, int> nodeMap{};
|
||||
HashMap<WorldNodeBase*, WorldNodeBase*> linker{};
|
||||
|
||||
// collect all nodes
|
||||
for (auto& node : nodes)
|
||||
AddAllNodes(allNodes, node);
|
||||
|
||||
WorldGraphSizeMeasurer sizeMeasurer{};
|
||||
for (auto& node : allNodes)
|
||||
{
|
||||
if (node.is_null() || !node->GetInternalNode())
|
||||
throw std::runtime_error("graph is invalid");
|
||||
|
||||
// refresh node values
|
||||
node->RefreshInputs();
|
||||
node->RefreshValues();
|
||||
|
||||
// check if nodes are valid
|
||||
if (!node->IsValid())
|
||||
throw std::runtime_error("graph is invalid");
|
||||
|
||||
// find the size of the total compiled program
|
||||
node->GetInternalNode()->Allocate(&sizeMeasurer);
|
||||
}
|
||||
DEV_ASSERT(sizeMeasurer.TotalSize % 8 == 0);
|
||||
|
||||
// allocate the nodes
|
||||
WorldGraphAllocator allocator{ sizeMeasurer.TotalSize };
|
||||
for (auto& node : allNodes)
|
||||
{
|
||||
void* allocatedNodeAddress = allocator.GetCurrentAddress();
|
||||
|
||||
node->GetInternalNode()->Allocate(&allocator);
|
||||
|
||||
NodeMap.insert(node, static_cast<WorldNodeBase*>(allocatedNodeAddress));
|
||||
linker.insert(node->GetInternalNode(), static_cast<WorldNodeBase*>(allocatedNodeAddress));
|
||||
}
|
||||
|
||||
// get the compiled memory
|
||||
std::unique_ptr<WorldNodeBase*[]> compiledMemory = std::move(reinterpret_cast<std::unique_ptr<WorldNodeBase*[]>&>(allocator.Data));
|
||||
|
||||
// link the nodes
|
||||
MemorySize = sizeMeasurer.TotalSize / 8;
|
||||
for (int i{}; i < MemorySize; ++i)
|
||||
{
|
||||
auto it = linker.find(compiledMemory[i]);
|
||||
if (it != linker.end())
|
||||
{
|
||||
compiledMemory[i] = it->value;
|
||||
}
|
||||
}
|
||||
|
||||
CompiledData = std::move(compiledMemory);
|
||||
}
|
||||
|
||||
std::unique_ptr<WorldNodeBase *[]> WorldGraph::CopyMemory(HashMap<Ref<WorldGraphVisualNodeBase>, WorldNodeBase *> &nodeMap) const
|
||||
{
|
||||
auto memory = std::make_unique<WorldNodeBase*[]>(MemorySize);
|
||||
|
||||
int64_t memoryDifference = memory.get() - CompiledData.get();
|
||||
|
||||
nodeMap = NodeMap;
|
||||
for (auto& node : nodeMap)
|
||||
{
|
||||
node.value += memoryDifference;
|
||||
}
|
||||
|
||||
WorldNodeBase* memoryStart = reinterpret_cast<WorldNodeBase*>(CompiledData.get());
|
||||
WorldNodeBase* memoryEnd = reinterpret_cast<WorldNodeBase*>(CompiledData.get() + MemorySize);
|
||||
|
||||
for (int i{}; i < MemorySize; ++i)
|
||||
{
|
||||
auto& address = memory[i];
|
||||
// if the address points somewhere that is in the old memory address, move it to the new memory address
|
||||
if (address >= memoryStart && address < memoryEnd)
|
||||
{
|
||||
address += memoryDifference;
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(memory);
|
||||
}
|
||||
338
src/Data/WorldGraph/WorldGraphVisualNode.cpp
Normal file
338
src/Data/WorldGraph/WorldGraphVisualNode.cpp
Normal file
@@ -0,0 +1,338 @@
|
||||
#include "Data/WorldGraph/WorldGraphVisualNode.h"
|
||||
|
||||
void WorldGraphVisualNodeBase::_bind_methods()
|
||||
{
|
||||
ClassDB::bind_method(D_METHOD("get_position"), &WorldGraphVisualNodeBase::GetPosition);
|
||||
ClassDB::bind_method(D_METHOD("get_inputs"), &WorldGraphVisualNodeBase::GetInputs);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_position", "position"), &WorldGraphVisualNodeBase::SetPosition);
|
||||
ClassDB::bind_method(D_METHOD("set_inputs", "input"), &WorldGraphVisualNodeBase::SetInputNodes);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("is_valid"), &WorldGraphVisualNodeBase::NodeIsValid);
|
||||
ClassDB::bind_method(D_METHOD("get_input_types"), &WorldGraphVisualNodeBase::NodeGetInputTypes);
|
||||
ClassDB::bind_method(D_METHOD("get_output_type"), &WorldGraphVisualNodeBase::NodeGetOutputType);
|
||||
//ClassDB::bind_method(D_METHOD("set_default"), &WorldGraphVisualNodeBase::NodeSetDefault);
|
||||
ClassDB::bind_method(D_METHOD("has_internal_node"), &WorldGraphVisualNodeBase::HasInternalNode);
|
||||
ClassDB::bind_method(D_METHOD("set_input", "index", "node"), &WorldGraphVisualNodeBase::NodeSetInput);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "position"), "set_position", "get_position");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "inputs", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("WorldGraphVisualNodeBase")), "set_inputs", "get_inputs");
|
||||
}
|
||||
|
||||
bool CanExecuteNode_Recursive(Ref<WorldGraphVisualNodeBase> node)
|
||||
{
|
||||
for (auto& node : node->InputNodes)
|
||||
{
|
||||
for (auto input : node->InputNodes)
|
||||
{
|
||||
if (input.is_null()) return false;
|
||||
CanExecuteNode_Recursive(input);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WorldGraphVisualNodeBase::CanExecuteNode()
|
||||
{
|
||||
return CanExecuteNode_Recursive(Ref(this));
|
||||
}
|
||||
|
||||
void WorldGraphVisualNodeBase::SetInternalNode(std::unique_ptr<WorldNodeBase> &&node)
|
||||
{
|
||||
InternalNode = std::move(node);
|
||||
|
||||
int inputAmounts = InternalNode->GetInputTypes().size();
|
||||
if (inputAmounts > InputNodes.size())
|
||||
{
|
||||
InputNodes.resize(inputAmounts);
|
||||
}
|
||||
|
||||
RefreshInputs();
|
||||
RefreshValues();
|
||||
}
|
||||
|
||||
void WorldGraphVisualNodeBase::RefreshInputs()
|
||||
{
|
||||
if (InternalNode)
|
||||
{
|
||||
auto internalNodeInputs = InternalNode->GetInputTypes().size();
|
||||
for (int i{}; i < InputNodes.size() && i < internalNodeInputs; ++i)
|
||||
SetInput(i, InputNodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
struct NodeGenerator
|
||||
{
|
||||
NodeGenerator() = default;
|
||||
NodeGenerator(const char* name, std::function<std::unique_ptr<WorldNodeBase>()>&& generator) : Name{ name }, Generator{ std::move(generator) } {};
|
||||
|
||||
const char* Name{};
|
||||
std::function<std::unique_ptr<WorldNodeBase>()> Generator{};
|
||||
};
|
||||
|
||||
static const NodeGenerator MathGenerators[] =
|
||||
{
|
||||
{ "Abs", [](){ return std::make_unique<WorldNode_Abs>(); }},
|
||||
{ "Add", [](){ return std::make_unique<WorldNode_Add>(); }},
|
||||
{ "And", [](){ return std::make_unique<WorldNode_And>(); }},
|
||||
{ "Ceil", [](){ return std::make_unique<WorldNode_Ceil>(); }},
|
||||
{ "Clamp", [](){ return std::make_unique<WorldNode_Clamp>(); }},
|
||||
{ "Cos", [](){ return std::make_unique<WorldNode_Cos>(); }},
|
||||
{ "Divide", [](){ return std::make_unique<WorldNode_Divide>(); }},
|
||||
{ "Equal", [](){ return std::make_unique<WorldNode_Equal>(); }},
|
||||
{ "Exp", [](){ return std::make_unique<WorldNode_Exp>(); }},
|
||||
{ "Floor", [](){ return std::make_unique<WorldNode_Floor>(); }},
|
||||
{ "Greater", [](){ return std::make_unique<WorldNode_Greater>(); }},
|
||||
{ "Greater or Equal", [](){ return std::make_unique<WorldNode_GreaterEqual>(); }},
|
||||
{ "Lerp", [](){ return std::make_unique<WorldNode_Lerp>(); }},
|
||||
{ "Log", [](){ return std::make_unique<WorldNode_Log>(); }},
|
||||
{ "Max", [](){ return std::make_unique<WorldNode_Max>(); }},
|
||||
{ "Min", [](){ return std::make_unique<WorldNode_Min>(); }},
|
||||
{ "Modulo", [](){ return std::make_unique<WorldNode_Modulo>(); }},
|
||||
{ "Multiply", [](){ return std::make_unique<WorldNode_Multiply>(); }},
|
||||
{ "Negate", [](){ return std::make_unique<WorldNode_Negate>(); }},
|
||||
{ "One Minus", [](){ return std::make_unique<WorldNode_OneMinus>(); }},
|
||||
{ "Or", [](){ return std::make_unique<WorldNode_Or>(); }},
|
||||
{ "Pow", [](){ return std::make_unique<WorldNode_Pow>(); }},
|
||||
{ "Round", [](){ return std::make_unique<WorldNode_Round>(); }},
|
||||
{ "Sin", [](){ return std::make_unique<WorldNode_Sin>(); }},
|
||||
{ "Smaller", [](){ return std::make_unique<WorldNode_Smaller>(); }},
|
||||
{ "Smaller or Equal", [](){ return std::make_unique<WorldNode_SmallerEqual>(); }},
|
||||
{ "Square", [](){ return std::make_unique<WorldNode_Square>(); }},
|
||||
{ "Substract", [](){ return std::make_unique<WorldNode_Subtract>(); }},
|
||||
{ "Tan", [](){ return std::make_unique<WorldNode_Tan>(); }},
|
||||
};
|
||||
static constexpr int MathArraySize = sizeof(MathGenerators) / sizeof(NodeGenerator);
|
||||
|
||||
void WorldGraphVisualNode_Math::_bind_methods()
|
||||
{
|
||||
ClassDB::bind_method(D_METHOD("get_node"), &WorldGraphVisualNode_Math::GetNode);
|
||||
ClassDB::bind_method(D_METHOD("get_node_names"), &WorldGraphVisualNode_Math::GetNodeNames);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_node", "name"), &WorldGraphVisualNode_Math::SetNode);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "node_id"), "set_node", "get_node");
|
||||
}
|
||||
|
||||
WorldGraphVisualNode_Math::WorldGraphVisualNode_Math()
|
||||
{
|
||||
NodeID = "Add";
|
||||
SetInternalNode(std::make_unique<WorldNode_Add>());
|
||||
}
|
||||
|
||||
TypedArray<String> WorldGraphVisualNode_Math::GetNodeNames() const
|
||||
{
|
||||
TypedArray<String> Values{};
|
||||
Values.resize(MathArraySize);
|
||||
|
||||
for (int i{}; i < MathArraySize; ++i)
|
||||
{
|
||||
Values[i] = String(MathGenerators[i].Name);
|
||||
}
|
||||
|
||||
return Values;
|
||||
}
|
||||
|
||||
void WorldGraphVisualNode_Math::SetNode(String nodeName)
|
||||
{
|
||||
if (nodeName == NodeID) return;
|
||||
|
||||
for (int i{}; i < MathArraySize; ++i)
|
||||
{
|
||||
if (MathGenerators[i].Name == nodeName)
|
||||
{
|
||||
SetInternalNode(MathGenerators[i].Generator());
|
||||
NodeID = nodeName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorldGraphVisualNode_Constant::_bind_methods()
|
||||
{
|
||||
ClassDB::bind_method(D_METHOD("get_value"), &WorldGraphVisualNode_Constant::GetValue);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_value", "val"), &WorldGraphVisualNode_Constant::SetValue);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "value"), "set_value", "get_value");
|
||||
}
|
||||
|
||||
WorldGraphVisualNode_Constant::WorldGraphVisualNode_Constant()
|
||||
{
|
||||
SetInternalNode(std::make_unique<WorldNode_Constant>());
|
||||
}
|
||||
|
||||
void WorldGraphVisualNode_Constant::SetValue(float val)
|
||||
{
|
||||
static_cast<WorldNode_Constant*>(GetInternalNode())->Value = val;
|
||||
}
|
||||
|
||||
float WorldGraphVisualNode_Constant::GetValue() const
|
||||
{
|
||||
return static_cast<WorldNode_Constant*>(GetInternalNode())->Value;
|
||||
}
|
||||
|
||||
static const NodeGenerator NoiseGenerators[] =
|
||||
{
|
||||
{ "Simplex", [](){ return std::make_unique<WorldNode_Simplex>(); }},
|
||||
{ "Open Simplex", [](){ return std::make_unique<WorldNode_OpenSimplex>(); }},
|
||||
{ "Perlin", [](){ return std::make_unique<WorldNode_Perlin>(); }},
|
||||
{ "Value", [](){ return std::make_unique<WorldNode_Value>(); }},
|
||||
{ "Value Cubic", [](){ return std::make_unique<WorldNode_ValueCubic>(); }},
|
||||
};
|
||||
static constexpr int NoiseArraySize = sizeof(NoiseGenerators) / sizeof(NodeGenerator);
|
||||
|
||||
WorldGraphVisualNode_Noise::WorldGraphVisualNode_Noise()
|
||||
{
|
||||
NoiseType = "Simplex";
|
||||
SetInternalNode(std::make_unique<WorldNode_Simplex>());
|
||||
}
|
||||
|
||||
TypedArray<String> WorldGraphVisualNode_Noise::GetNoiseTypes() const
|
||||
{
|
||||
TypedArray<String> Values{};
|
||||
Values.resize(NoiseArraySize);
|
||||
|
||||
for (int i{}; i < NoiseArraySize; ++i)
|
||||
{
|
||||
Values[i] = String(NoiseGenerators[i].Name);
|
||||
}
|
||||
|
||||
return Values;
|
||||
}
|
||||
|
||||
void WorldGraphVisualNode_Noise::SetFrequency(float val)
|
||||
{
|
||||
Frequency = val;
|
||||
if (val != 0)
|
||||
{
|
||||
static_cast<WorldNode_NoiseBase*>(GetInternalNode())->Frequency = val;
|
||||
}
|
||||
}
|
||||
|
||||
void WorldGraphVisualNode_Noise::RefreshValues()
|
||||
{
|
||||
static_cast<WorldNode_NoiseBase*>(GetInternalNode())->Frequency = Frequency;
|
||||
}
|
||||
|
||||
void WorldGraphVisualNode_Noise::_bind_methods()
|
||||
{
|
||||
ClassDB::bind_method(D_METHOD("get_noise_type"), &WorldGraphVisualNode_Noise::GetNoiseType);
|
||||
ClassDB::bind_method(D_METHOD("get_all_noise_types"), &WorldGraphVisualNode_Noise::GetNoiseTypes);
|
||||
ClassDB::bind_method(D_METHOD("get_frequency"), &WorldGraphVisualNode_Noise::GetFrequency);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_noise_type", "type"), &WorldGraphVisualNode_Noise::SetNoiseType);
|
||||
ClassDB::bind_method(D_METHOD("set_frequency", "frequency"), &WorldGraphVisualNode_Noise::SetFrequency);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "noise_type"), "set_noise_type", "get_noise_type");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "frequency"), "set_frequency", "get_frequency");
|
||||
}
|
||||
|
||||
void WorldGraphVisualNode_Noise::SetNoiseType(String noiseType) {
|
||||
if (noiseType == NoiseType) return;
|
||||
|
||||
for (int i{}; i < NoiseArraySize; ++i)
|
||||
{
|
||||
if (NoiseGenerators[i].Name == noiseType)
|
||||
{
|
||||
SetInternalNode(NoiseGenerators[i].Generator());
|
||||
NoiseType = noiseType;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// float WorldGraphVisualNode_Noise::GetFrequency() const
|
||||
// {
|
||||
// return static_cast<WorldNode_NoiseBase*>(GetInternalNode())->Frequency;
|
||||
// }
|
||||
|
||||
WorldGraphVisualNode_If::WorldGraphVisualNode_If()
|
||||
{
|
||||
SetInternalNode(std::make_unique<WorldNode_Branch>());
|
||||
}
|
||||
|
||||
void WorldGraphVisualNode_Tile::_bind_methods()
|
||||
{
|
||||
ClassDB::bind_method(D_METHOD("get_type"), &WorldGraphVisualNode_Tile::GetType);
|
||||
ClassDB::bind_method(D_METHOD("get_relative_x"), &WorldGraphVisualNode_Tile::GetRelativeX);
|
||||
ClassDB::bind_method(D_METHOD("get_relative_y"), &WorldGraphVisualNode_Tile::GetRelativeY);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_type", "type"), &WorldGraphVisualNode_Tile::SetType);
|
||||
ClassDB::bind_method(D_METHOD("set_relative_x", "offset"), &WorldGraphVisualNode_Tile::SetRelativeX);
|
||||
ClassDB::bind_method(D_METHOD("set_relative_y", "offset"), &WorldGraphVisualNode_Tile::SetRelativeY);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, "Air,Filler,Liquid,Ore,Npc,Plant"), "set_type", "get_type");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "relative_x"), "set_relative_x", "get_relative_x");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "relative_y"), "set_relative_y", "get_relative_y");
|
||||
}
|
||||
|
||||
WorldGraphVisualNode_Tile::WorldGraphVisualNode_Tile()
|
||||
{
|
||||
SetInternalNode(std::make_unique<WorldNode_IsTile>());
|
||||
}
|
||||
|
||||
void WorldGraphVisualNode_Tile::SetType(int type)
|
||||
{
|
||||
static_cast<WorldNode_IsTile*>(GetInternalNode())->TileType = static_cast<TILE_TYPE>(std::clamp<int>(type, 0, TILE_TYPE::TILE_MAX));
|
||||
}
|
||||
|
||||
void WorldGraphVisualNode_Tile::SetRelativeX(int offset)
|
||||
{
|
||||
static_cast<WorldNode_IsTile*>(GetInternalNode())->RelativeX = static_cast<int8_t>(std::clamp<int>(offset, -WorldNodeParameters::MaxQueryOffset, WorldNodeParameters::MaxQueryOffset));
|
||||
}
|
||||
|
||||
void WorldGraphVisualNode_Tile::SetRelativeY(int offset)
|
||||
{
|
||||
static_cast<WorldNode_IsTile*>(GetInternalNode())->RelativeY = static_cast<int8_t>(std::clamp<int>(offset, -WorldNodeParameters::MaxQueryOffset, WorldNodeParameters::MaxQueryOffset));
|
||||
}
|
||||
|
||||
int WorldGraphVisualNode_Tile::GetType() const
|
||||
{
|
||||
return static_cast<WorldNode_IsTile*>(GetInternalNode())->TileType;
|
||||
}
|
||||
|
||||
int WorldGraphVisualNode_Tile::GetRelativeX() const
|
||||
{
|
||||
return static_cast<WorldNode_IsTile*>(GetInternalNode())->RelativeX;
|
||||
}
|
||||
|
||||
int WorldGraphVisualNode_Tile::GetRelativeY() const
|
||||
{
|
||||
return static_cast<WorldNode_IsTile*>(GetInternalNode())->RelativeY;
|
||||
}
|
||||
|
||||
void WorldGraphVisualNode_TileDistance::_bind_methods()
|
||||
{
|
||||
ClassDB::bind_method(D_METHOD("get_type"), &WorldGraphVisualNode_TileDistance::GetType);
|
||||
ClassDB::bind_method(D_METHOD("get_range"), &WorldGraphVisualNode_TileDistance::GetRange);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_type", "type"), &WorldGraphVisualNode_TileDistance::SetType);
|
||||
ClassDB::bind_method(D_METHOD("set_range", "range"), &WorldGraphVisualNode_TileDistance::SetRange);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "type", PROPERTY_HINT_ENUM, "Air,Filler,Liquid,Ore,Npc,Plant"), "set_type", "get_type");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "range", PROPERTY_HINT_RANGE, "1,3"), "set_range", "get_range");
|
||||
}
|
||||
|
||||
WorldGraphVisualNode_TileDistance::WorldGraphVisualNode_TileDistance()
|
||||
{
|
||||
SetInternalNode(std::make_unique<WorldNode_TileDistance>());
|
||||
}
|
||||
|
||||
void WorldGraphVisualNode_TileDistance::SetType(int type)
|
||||
{
|
||||
static_cast<WorldNode_TileDistance*>(GetInternalNode())->TileType = static_cast<TILE_TYPE>(type);
|
||||
}
|
||||
|
||||
void WorldGraphVisualNode_TileDistance::SetRange(int range)
|
||||
{
|
||||
static_cast<WorldNode_TileDistance*>(GetInternalNode())->Range = range;
|
||||
}
|
||||
|
||||
int WorldGraphVisualNode_TileDistance::GetType() const
|
||||
{
|
||||
return static_cast<WorldNode_TileDistance*>(GetInternalNode())->TileType;
|
||||
}
|
||||
|
||||
int WorldGraphVisualNode_TileDistance::GetRange() const
|
||||
{
|
||||
return static_cast<WorldNode_TileDistance*>(GetInternalNode())->Range;
|
||||
}
|
||||
652
src/Data/WorldSettings.cpp
Normal file
652
src/Data/WorldSettings.cpp
Normal file
@@ -0,0 +1,652 @@
|
||||
#include "Data/WorldSettings.h"
|
||||
#include "Util/Helpers.h"
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "core/config/project_settings.h"
|
||||
#include "servers/rendering_server.h"
|
||||
#include <functional>
|
||||
#include "Core/Chunk.h"
|
||||
#include "Data/LayerConfigs.h"
|
||||
#include "Data/Item.h"
|
||||
#include "Core/WorldGenerator.h"
|
||||
#include "Util/RandomPicker.h"
|
||||
|
||||
// #define MAKE_RESOURCE_TYPE_HINT(m_type) vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, m_type)
|
||||
|
||||
// using namespace godot;
|
||||
|
||||
// void FactoryWorldSettings::_bind_methods()
|
||||
// {
|
||||
// std::function test{ std::divides<float>() };
|
||||
// ClassDB::bind_method(D_METHOD("get_recipes"), &FactoryWorldSettings::GetRecipes);
|
||||
// ClassDB::bind_method(D_METHOD("get_archetypes"), &FactoryWorldSettings::GetPlaceableArchetypes);
|
||||
// ClassDB::bind_method(D_METHOD("get_tiles"), &FactoryWorldSettings::GetTileConfigs);
|
||||
// ClassDB::bind_method(D_METHOD("get_layers"), &FactoryWorldSettings::GetLayerConfigs);
|
||||
|
||||
// ClassDB::bind_method(D_METHOD("set_recipes", "recipes"), &FactoryWorldSettings::SetRecipes);
|
||||
// ClassDB::bind_method(D_METHOD("set_archetypes", "archetypes"), &FactoryWorldSettings::SetPlaceableArchetypes);
|
||||
// ClassDB::bind_method(D_METHOD("set_tiles", "tiles"), &FactoryWorldSettings::SetTileConfigs);
|
||||
// ClassDB::bind_method(D_METHOD("set_layers", "tiles"), &FactoryWorldSettings::SetLayerConfigs);
|
||||
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "recipes", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("RecipeConfig")), "set_recipes", "get_recipes");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "archetypes", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("PlaceableArchetype")), "set_archetypes", "get_archetypes");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "tiles", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("TileConfig")), "set_tiles", "get_tiles");
|
||||
// ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "layers", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("LayerConfig")), "set_layers", "get_layers");
|
||||
// }
|
||||
|
||||
template <typename T>
|
||||
void AddUnique(Vector<T>& target, const Vector<T>& source)
|
||||
{
|
||||
for (auto& src : source)
|
||||
{
|
||||
for (auto& tar : target)
|
||||
{
|
||||
if (src == tar || src.is_null())
|
||||
continue;
|
||||
}
|
||||
target.push_back(src);
|
||||
}
|
||||
}
|
||||
|
||||
void FactoryWorldSettings::Merge(Ref<FactoryWorldSettings> settings)
|
||||
{
|
||||
// merge layers
|
||||
AddUnique(LayerConfigs, settings->LayerConfigs);
|
||||
|
||||
AddUnique(TileConfigs, settings->TileConfigs);
|
||||
|
||||
AddUnique(Recipes, settings->Recipes);
|
||||
AddUnique(Archetypes, settings->Archetypes);
|
||||
//AddUnique(Placeables, settings->Placeables);
|
||||
AddUnique(Items, settings->Items);
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
void GetSubResources(Resource* resource, List<PropertyInfo>& buffer, const std::function<void(Resource*)>& callback);
|
||||
|
||||
bool CheckVariant(Variant var, List<PropertyInfo>& buffer, const std::function<void(Resource*)>& callback)
|
||||
{
|
||||
if (var.get_type() == Variant::Type::OBJECT)
|
||||
{
|
||||
auto resourceVar = Object::cast_to<Resource>(var);
|
||||
if (resourceVar)
|
||||
{
|
||||
callback(resourceVar);
|
||||
|
||||
GetSubResources(resourceVar, buffer, callback);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void GetSubResources(Resource* resource, List<PropertyInfo>& buffer, const std::function<void(Resource*)>& callback)
|
||||
{
|
||||
resource->get_property_list(&buffer);
|
||||
|
||||
while (!buffer.is_empty())
|
||||
{
|
||||
auto property = buffer.front();
|
||||
auto className = property->get().class_name;
|
||||
StringName propertyName = property->get().name;
|
||||
auto getterName = ClassDB::get_property_getter(className, propertyName);
|
||||
auto propertyVal = resource->call(getterName);
|
||||
|
||||
if (CheckVariant(propertyVal, buffer, callback))
|
||||
{
|
||||
auto packedScene = Object::cast_to<PackedScene>(propertyVal);
|
||||
if (packedScene)
|
||||
{
|
||||
auto state = packedScene->get_state();
|
||||
for (int i{}; i < state->get_node_count(); ++i)
|
||||
for (int j{}; j < state->get_node_property_count(i); ++j)
|
||||
{
|
||||
CheckVariant(state->get_node_property_value(i, j), buffer, callback);
|
||||
}
|
||||
}
|
||||
|
||||
if (propertyVal.is_array())
|
||||
{
|
||||
Array propertyArray = propertyVal;
|
||||
for (auto entry : propertyArray)
|
||||
{
|
||||
CheckVariant(entry, buffer, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void AddTextureToSheets(HashMap<int,Vector<Ref<TextureWeight>>> sheets, Ref<Texture2D> texture)
|
||||
{
|
||||
if (texture.is_null()) return;
|
||||
|
||||
DEV_ASSERT(texture->get_width() == texture->get_height());
|
||||
if (!sheets.has(texture->get_width()))
|
||||
{
|
||||
// Ref<TileSetAtlasSource> tileSet{};
|
||||
// tileSet.instantiate();
|
||||
// tileSet->set_texture_region_size(texture->get_size());
|
||||
// sheets[texture->get_width()] = tileSet;
|
||||
|
||||
sheets.insert(texture->get_width(), {});
|
||||
}
|
||||
|
||||
auto& sheet = sheets[texture->get_width()];
|
||||
|
||||
sheet.push_back(texture);
|
||||
}
|
||||
|
||||
void FactoryWorldSettings::Initialize()
|
||||
{
|
||||
InitializeResources();
|
||||
InitializeLayers();
|
||||
InitializeWorldGenerators();
|
||||
InitializeTexturesSheets();
|
||||
InitializeItemGraph();
|
||||
}
|
||||
|
||||
int FactoryWorldSettings::GetStartHeight() const
|
||||
{
|
||||
return Chunk::ChunkSize * LayerConfigs[0]->StartChunk;
|
||||
}
|
||||
|
||||
Vector<ItemAmount> FactoryWorldSettings::GetChunkUnlockCosts(int x, int y) const
|
||||
{
|
||||
return GetChunkUnlockCosts(ChunkKey{x, y});
|
||||
}
|
||||
|
||||
constexpr double Square(double x)
|
||||
{
|
||||
return x * x;
|
||||
}
|
||||
|
||||
constexpr double BellCurve(double x, double mean = 0, double deviation = 0.4)
|
||||
{
|
||||
constexpr double Sqrt2Pi = 2.506628274631000502415765284811;
|
||||
return (1/(deviation * Sqrt2Pi)) * exp(-0.5 * Square((x - mean) / deviation));
|
||||
}
|
||||
|
||||
double GetRandomDouble(uint64_t& val)
|
||||
{
|
||||
double randomVal{ reinterpret_cast<const double&>(val) };
|
||||
while (isnan(randomVal) || std::isnormal(randomVal))
|
||||
{
|
||||
val = std::hash<uint64_t>{}(val);
|
||||
randomVal = reinterpret_cast<const double&>(val);
|
||||
}
|
||||
return randomVal;
|
||||
}
|
||||
|
||||
Vector<ItemAmount> FactoryWorldSettings::GetChunkUnlockCosts(ChunkKey chunk) const
|
||||
{
|
||||
HashMap<Ref<LayerConfig>,int> LayerCounts{};
|
||||
|
||||
for (int y{ LayerConfigs[0]->StartChunk }; y > chunk.Y; --y)
|
||||
{
|
||||
auto key = chunk;
|
||||
key.Y = y;
|
||||
++LayerCounts[GetLayer(key)];
|
||||
}
|
||||
|
||||
for (int x{ }; x < std::abs(chunk.X); ++x)
|
||||
{
|
||||
auto key = chunk;
|
||||
key.X = chunk.X < 0 ? -x : x;
|
||||
++LayerCounts[GetLayer(key)];
|
||||
}
|
||||
|
||||
std::vector<RandomPickerD<ItemConfig*>::Entry> possibleItems{};
|
||||
|
||||
possibleItems.push_back(RandomPickerD<ItemConfig*>::Entry{ nullptr, BellCurve(0, chunk.X + chunk.Y) });
|
||||
possibleItems.push_back(RandomPickerD<ItemConfig*>::Entry{ nullptr, BellCurve(0, chunk.X + chunk.Y) });
|
||||
for (auto [layer, count] : LayerCounts)
|
||||
{
|
||||
int maxComplexity{};
|
||||
for (auto item : layer->UnlockedItems)
|
||||
{
|
||||
maxComplexity = std::max(maxComplexity, ItemComplexity[item]);
|
||||
}
|
||||
|
||||
for (auto item : layer->UnlockedItems)
|
||||
{
|
||||
auto complexity = ItemComplexity[item];
|
||||
possibleItems.push_back(RandomPickerD<ItemConfig*>::Entry{ item.ptr(), BellCurve(complexity, std::min(count / 3, maxComplexity)) * count });
|
||||
}
|
||||
}
|
||||
|
||||
RandomPickerD<ItemConfig*> itemPicker{ possibleItems };
|
||||
|
||||
uint64_t chunkHash = chunk.hash64();
|
||||
|
||||
return Vector<ItemAmount>
|
||||
{
|
||||
itemPicker.GetAndRemoveRandom(GetRandomDouble(chunkHash)),
|
||||
itemPicker.GetAndRemoveRandom(GetRandomDouble(chunkHash)),
|
||||
itemPicker.GetAndRemoveRandom(GetRandomDouble(chunkHash))
|
||||
};
|
||||
}
|
||||
|
||||
Ref<LayerConfig> FactoryWorldSettings::GetLayer(ChunkKey chunk) const
|
||||
{
|
||||
for (auto layer : LayerConfigs)
|
||||
{
|
||||
if (layer->StartChunk <= chunk.Y)
|
||||
return layer;
|
||||
}
|
||||
}
|
||||
|
||||
void FactoryWorldSettings::InitializeResources()
|
||||
{
|
||||
List<PropertyInfo> buffer{};
|
||||
|
||||
struct MetaData
|
||||
{
|
||||
RecipeConfig* CurrentRecipe{};
|
||||
Archetype* CurrentArchetype{};
|
||||
LayerConfig* CurrentLayer{};
|
||||
};
|
||||
MetaData data{};
|
||||
|
||||
std::function<void(Resource*)> resourceChecker = [this, &data](Resource* res)
|
||||
{
|
||||
{
|
||||
auto item = Object::cast_to<ItemConfig>(res);
|
||||
if (item && item->GetID() == -1)
|
||||
{
|
||||
Items.push_back(item);
|
||||
item->Item.ItemID = static_cast<uint16_t>(Items.size() - 1);
|
||||
}
|
||||
if (data.CurrentLayer)
|
||||
{
|
||||
data.CurrentLayer->UnlockedItems.push_back_unique(item);
|
||||
}
|
||||
}
|
||||
{
|
||||
auto tile = Object::cast_to<TileConfig>(res);
|
||||
if (tile && !TileConfigs.has(tile))
|
||||
{
|
||||
TileConfigs.push_back(tile);
|
||||
tile->TileData.SetID(static_cast<uint16_t>(TileConfigs.size() - 1));
|
||||
}
|
||||
}
|
||||
{
|
||||
auto recipe = Object::cast_to<RecipeConfig>(res);
|
||||
if (recipe)
|
||||
{
|
||||
data.CurrentRecipe = recipe;
|
||||
if (!Recipes.has(recipe))
|
||||
{
|
||||
Recipes.push_back(recipe);
|
||||
recipe->SetID(Recipes.size() - 1);
|
||||
}
|
||||
if (data.CurrentArchetype)
|
||||
{
|
||||
recipe->RecipeSources.push_back_unique(data.CurrentArchetype);
|
||||
}
|
||||
if (data.CurrentLayer)
|
||||
{
|
||||
data.CurrentLayer->UnlockedRecipes.push_back_unique(recipe);
|
||||
for (auto result : recipe->Results)
|
||||
data.CurrentLayer->UnlockedItems.push_back_unique(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
auto archetype = Object::cast_to<Archetype>(res);
|
||||
if (archetype && !Archetypes.has(archetype))
|
||||
{
|
||||
data.CurrentArchetype = archetype;
|
||||
data.CurrentRecipe = nullptr;
|
||||
Archetypes.push_back(archetype);
|
||||
archetype->SetID(Archetypes.size() - 1);
|
||||
}
|
||||
if (data.CurrentLayer)
|
||||
{
|
||||
data.CurrentLayer->UnlockedBuildings.push_back_unique(archetype);
|
||||
}
|
||||
}
|
||||
{
|
||||
auto tile = Object::cast_to<TileConfig>(res);
|
||||
if (tile)
|
||||
{
|
||||
TileConfigs.push_back_unique(tile);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (int i{}; i < LayerConfigs.size(); ++i)
|
||||
{
|
||||
data.CurrentLayer = LayerConfigs[i].ptr();
|
||||
data.CurrentArchetype = nullptr;
|
||||
data.CurrentRecipe = nullptr;
|
||||
GetSubResources(LayerConfigs[i].ptr(), buffer, resourceChecker);
|
||||
}
|
||||
data.CurrentLayer = nullptr;
|
||||
data.CurrentArchetype = nullptr;
|
||||
data.CurrentRecipe = nullptr;
|
||||
|
||||
for (int i{}; i < PlaceableArchetypes.size(); ++i)
|
||||
{
|
||||
data.CurrentArchetype = PlaceableArchetypes[i]->Archetype.ptr();
|
||||
GetSubResources(PlaceableArchetypes[i].ptr(), buffer, resourceChecker);
|
||||
}
|
||||
}
|
||||
|
||||
void FactoryWorldSettings::InitializeLayers()
|
||||
{
|
||||
// sort Layers
|
||||
struct LayerSorter
|
||||
{
|
||||
bool operator()(const Ref<LayerConfig>& lhs, const Ref<LayerConfig>& rhs) const
|
||||
{
|
||||
return lhs->StartChunk > rhs->StartChunk;
|
||||
}
|
||||
};
|
||||
LayerConfigs.sort_custom<LayerSorter>();
|
||||
|
||||
struct TileSorter
|
||||
{
|
||||
bool operator()(const Ref<LayerTileConfig>& lhs, const Ref<LayerTileConfig>& rhs) const
|
||||
{
|
||||
return lhs->Tile->GetType() < rhs->Tile->GetType();
|
||||
}
|
||||
};
|
||||
for (auto& layer : LayerConfigs)
|
||||
{
|
||||
layer->Tiles.sort_custom<TileSorter>();
|
||||
}
|
||||
}
|
||||
|
||||
void FactoryWorldSettings::InitializeWorldGenerators()
|
||||
{
|
||||
// Compile World Generator
|
||||
Vector<Ref<WorldGraphVisualNodeBase>> graphs;
|
||||
for (auto& layer : LayerConfigs)
|
||||
for (auto& tileGenerator : layer->Tiles)
|
||||
if (!LayerConfigs.has(tileGenerator->TileGenerator))
|
||||
LayerConfigs.push_back(tileGenerator->TileGenerator);
|
||||
|
||||
WorldGenerator = WorldGraph{ graphs };
|
||||
}
|
||||
|
||||
void FactoryWorldSettings::InitializeTexturesSheets()
|
||||
{
|
||||
// Gather all textures
|
||||
TileSet = {};
|
||||
HashMap<int,Vector<Ref<TextureWeight>>> Sheets{};
|
||||
|
||||
for (auto tile : TileConfigs)
|
||||
{
|
||||
for (auto texture : tile->PossibleTextures)
|
||||
AddTextureToSheets(Sheets, texture);
|
||||
|
||||
for (auto transition : tile->NeighborTransitions)
|
||||
for (auto texture : transition->PossibleTextures)
|
||||
AddTextureToSheets(Sheets, texture);
|
||||
}
|
||||
|
||||
// Make sheets
|
||||
for (auto& [size, textures] : Sheets)
|
||||
{
|
||||
constexpr int32_t MaxTextureSizeBits = 12; // 4096
|
||||
const int32_t individualTextureSizeBits = (int32_t)std::ceil(std::log2(size));
|
||||
const int32_t textureAmountBits = ((int32_t)std::ceil(std::log2(textures.size())) + 1) / 2;
|
||||
|
||||
const int32_t SheetsAmountBits = std::max(0, individualTextureSizeBits + textureAmountBits - MaxTextureSizeBits);
|
||||
const int32_t SheetDimensionBits = std::max(0, individualTextureSizeBits + textureAmountBits);
|
||||
|
||||
const int32_t SheetsAmount = 1 << SheetsAmountBits;
|
||||
const int32_t SheetDimensions = 1 << SheetDimensionBits;
|
||||
const int32_t TexturesPerSheet = 1 << (textureAmountBits * 2);
|
||||
const int32_t TexturesPerRow = 1 << textureAmountBits;
|
||||
|
||||
for (int sheetIndex{}; sheetIndex < SheetsAmount; ++sheetIndex)
|
||||
{
|
||||
Ref<Image> sheet{};
|
||||
sheet.instantiate();
|
||||
sheet->initialize_data(SheetDimensions, SheetDimensions, true, Image::FORMAT_RGBA8);
|
||||
for (int i{sheetIndex * TexturesPerSheet}; i < textures.size() && i < (sheetIndex + 1) * TexturesPerSheet; ++i)
|
||||
{
|
||||
int x = textures[i]->AtlasX = i % TexturesPerRow;
|
||||
int y = textures[i]->AtlasY = (i / TexturesPerRow) % TexturesPerRow;
|
||||
textures[i]->AtlasIndex = TileSet->get_source_count();
|
||||
|
||||
auto texture = textures[i]->Texture;
|
||||
Image sourceImage = Image{};
|
||||
sheet->blit_rect(texture, Rect2i{Vector2i{}, texture->get_size()}, Vector2i{x, y});
|
||||
}
|
||||
|
||||
Ref<TileSetAtlasSource> tileSetSource{};
|
||||
tileSetSource.instantiate();
|
||||
tileSetSource->set_texture_region_size(Vector2i{size, size});
|
||||
tileSetSource->set_texture(ImageTexture::create_from_image(sheet));
|
||||
|
||||
TileSet->add_source(tileSetSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ref<RecipeConfig> IsItemFromRecipe(Ref<LayerConfig> layer, Ref<ItemConfig> item)
|
||||
{
|
||||
for (auto recipe : layer->UnlockedRecipes)
|
||||
{
|
||||
for (auto result : recipe->Results)
|
||||
{
|
||||
if (result->Item == item)
|
||||
{
|
||||
return recipe;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void FactoryWorldSettings::InitializeItemGraph()
|
||||
{
|
||||
for (auto layer : LayerConfigs)
|
||||
{
|
||||
for (bool repeat{}; repeat;)
|
||||
{
|
||||
for (auto item : layer->UnlockedItems)
|
||||
{
|
||||
auto recipe = IsItemFromRecipe(layer, item);
|
||||
if (recipe.is_valid())
|
||||
{
|
||||
int maxComplexity{};
|
||||
for (auto ingredient : recipe->Ingredients)
|
||||
{
|
||||
if (ItemComplexity.has(ingredient))
|
||||
{
|
||||
maxComplexity = std::max(maxComplexity, ItemComplexity[ingredient]);
|
||||
}
|
||||
else
|
||||
{
|
||||
repeat = true;
|
||||
}
|
||||
}
|
||||
ItemComplexity[item] = maxComplexity;
|
||||
}
|
||||
else
|
||||
{
|
||||
ItemComplexity[item] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#define MAKE_RESOURCE_TYPE_HINT(m_type) vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, m_type)
|
||||
|
||||
void LayerConfig::_bind_methods()
|
||||
{
|
||||
ClassDB::bind_method(D_METHOD("get_start_chunk"), &LayerConfig::GetStartChunk);
|
||||
ClassDB::bind_method(D_METHOD("get_tiles"), &LayerConfig::GetTiles);
|
||||
ClassDB::bind_method(D_METHOD("get_registered_nodes"), &LayerConfig::GetRegisteredNodes);
|
||||
ClassDB::bind_method(D_METHOD("get_archetypes"), &LayerConfig::GetArchetypes);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_start_chunk", "height"), &LayerConfig::SetStartChunk);
|
||||
ClassDB::bind_method(D_METHOD("set_tiles", "tiles"), &LayerConfig::SetTiles);
|
||||
ClassDB::bind_method(D_METHOD("set_registered_nodes", "nodes"), &LayerConfig::SetRegisteredNodes);
|
||||
ClassDB::bind_method(D_METHOD("set_archetypes", "archetypes"), &LayerConfig::SetArchetypes);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("register_visual_node", "node"), &LayerConfig::RegisterVisualNode);
|
||||
ClassDB::bind_method(D_METHOD("remove_visual_node", "node"), &LayerConfig::RemoveVisualNode);
|
||||
ClassDB::bind_method(D_METHOD("has_visual_node", "node"), &LayerConfig::HasVisualNode);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("is_valid"), &LayerConfig::IsValid);
|
||||
ClassDB::bind_method(D_METHOD("can_connect", "connect"), &LayerConfig::CanConnect);
|
||||
ClassDB::bind_method(D_METHOD("create_texture", "chunk", "seed"), &LayerConfig::CreateTexture);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "start_chunk"), "set_start_chunk", "get_start_chunk");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "tile", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("LayerTileConfig")), "set_tiles", "get_tiles");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "registered_nodes", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("WorldGraphVisualNodeBase")), "set_registered_nodes", "get_registered_nodes");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "spawning_archetypes", PROPERTY_HINT_TYPE_STRING, MAKE_RESOURCE_TYPE_HINT("Archetype")), "set_archetypes", "get_archetypes");
|
||||
}
|
||||
|
||||
void LayerConfig::SetTiles(TypedArray<LayerTileConfig> tiles)
|
||||
{
|
||||
Tiles = TypedArrayToVector(tiles);
|
||||
|
||||
struct TileSorter
|
||||
{
|
||||
bool operator()(const Ref<LayerTileConfig>& lhs, const Ref<LayerTileConfig>& rhs) const
|
||||
{
|
||||
return lhs->Tile->TileData.GetType() < rhs->Tile->TileData.GetType();
|
||||
}
|
||||
};
|
||||
|
||||
for (auto& tile : Tiles)
|
||||
{
|
||||
if (tile.is_null() || tile->Tile.is_null()) return;
|
||||
}
|
||||
Tiles.sort_custom<TileSorter>();
|
||||
}
|
||||
|
||||
void LayerTileConfig::_bind_methods()
|
||||
{
|
||||
ClassDB::bind_method(D_METHOD("get_generator"), &LayerTileConfig::GetTileGenerator);
|
||||
ClassDB::bind_method(D_METHOD("get_tile"), &LayerTileConfig::GetTile);
|
||||
ClassDB::bind_method(D_METHOD("get_preview_color"), &LayerTileConfig::GetPreviewColor);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_generator", "generator"), &LayerTileConfig::SetTileGenerator);
|
||||
ClassDB::bind_method(D_METHOD("set_tile", "tile"), &LayerTileConfig::SetTile);
|
||||
ClassDB::bind_method(D_METHOD("set_preview_color", "color"), &LayerTileConfig::SetPreviewColor);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "generator", PROPERTY_HINT_RESOURCE_TYPE, "WorldGraphVisualNodeBase"), "set_generator", "get_generator");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile", PROPERTY_HINT_RESOURCE_TYPE, "TileConfig"), "set_tile", "get_tile");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "preview_color"), "set_preview_color", "get_preview_color");
|
||||
}
|
||||
|
||||
bool IsValid_Recursive(Vector<Ref<WorldGraphVisualNodeBase>>& stack, Ref<WorldGraphVisualNodeBase> node)
|
||||
{
|
||||
if (node.is_null()) return false;
|
||||
|
||||
for (auto& stackNode : stack)
|
||||
{
|
||||
if (stackNode == node)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stack.size() > 256)
|
||||
return false;
|
||||
|
||||
stack.push_back(node);
|
||||
|
||||
for (auto& input : node->InputNodes)
|
||||
{
|
||||
if (input.is_valid() &&
|
||||
input->IsValid() &&
|
||||
input->GetInternalNode()->GetInputTypes().size() == input->InputNodes.size())
|
||||
{
|
||||
if (!IsValid_Recursive(stack, input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (stack[stack.size() - 1] != node) return false;
|
||||
stack.remove_at(stack.size() - 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LayerConfig::IsValid() const
|
||||
{
|
||||
Vector<Ref<WorldGraphVisualNodeBase>> nodeStack{};
|
||||
|
||||
for (auto tile : Tiles)
|
||||
{
|
||||
if (tile.is_null() || tile->Tile.is_null() || tile->TileGenerator.is_null() || !tile->TileGenerator->IsValid() || !IsValid_Recursive(nodeStack, tile->TileGenerator)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LayerConfig::CanConnect(Ref<WorldGraphVisualNodeBase> from, Ref<WorldGraphVisualNodeBase> to) const
|
||||
{
|
||||
if (from == to) return false;
|
||||
|
||||
for (auto input : to->InputNodes)
|
||||
{
|
||||
if (input.is_valid() && !CanConnect(from, input)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Ref<ImageTexture> LayerConfig::CreateTexture(Vector2i chunk, int seed) const
|
||||
{
|
||||
if (!IsValid()) return {};
|
||||
|
||||
Vector<Ref<WorldGraphVisualNodeBase>> inputs{};
|
||||
Vector<Color> PreviewColors{};
|
||||
|
||||
for (int i{}; i < Tiles.size(); ++i)
|
||||
{
|
||||
auto& input = Tiles[i];
|
||||
inputs.push_back(input->TileGenerator);
|
||||
input->Tile->TileData.SetID(i);
|
||||
PreviewColors.push_back(input->PreviewColor);
|
||||
}
|
||||
|
||||
Ref<FactoryWorldSettings> settings{};
|
||||
auto duplicated = duplicate();
|
||||
auto layerCopy = Ref(Object::cast_to<LayerConfig>(duplicated.ptr()));
|
||||
layerCopy->StartChunk = -5;
|
||||
|
||||
settings->LayerConfigs = {};
|
||||
settings->LayerConfigs.push_back(layerCopy);
|
||||
settings->Initialize();
|
||||
|
||||
Ref<ImageTexture> texture{};
|
||||
texture.instantiate();
|
||||
|
||||
ChunkGenerator::GenerateChunk(settings, ChunkKey{0, 0}, seed, [PreviewColors, texture] (std::unique_ptr<Chunk>&& chunk)
|
||||
{
|
||||
Ref<Image> image;
|
||||
image.instantiate(Chunk::ChunkSize, Chunk::ChunkSize, false, Image::FORMAT_RGB8);
|
||||
|
||||
for (int y{}; y < Chunk::ChunkSize; ++y)
|
||||
{
|
||||
for (int x{}; x < Chunk::ChunkSize; ++x)
|
||||
{
|
||||
auto tileID = chunk->Tiles[y * Chunk::ChunkSize + x].GetID();
|
||||
if (tileID < PreviewColors.size()) image->set_pixel(x, Chunk::ChunkSize - y - 1, PreviewColors[tileID]);
|
||||
else image->set_pixel(x, Chunk::ChunkSize - y - 1, Color{1, 0, 1, 1});
|
||||
}
|
||||
}
|
||||
|
||||
texture->set_image(image);
|
||||
});
|
||||
|
||||
return texture;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
# Factory Core library
|
||||
add_library(factory_core STATIC
|
||||
factory_core.cpp
|
||||
)
|
||||
|
||||
# Add alias for consistent usage
|
||||
add_library(factory::core ALIAS factory_core)
|
||||
|
||||
target_include_directories(factory_core
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
)
|
||||
|
||||
target_link_libraries(factory_core
|
||||
PUBLIC
|
||||
flecs::flecs_static
|
||||
)
|
||||
|
||||
# Set compile features
|
||||
target_compile_features(factory_core PUBLIC cxx_std_17)
|
||||
@@ -1,5 +0,0 @@
|
||||
#include "factory_core.hpp"
|
||||
|
||||
// Implementation file for factory_core
|
||||
// Currently empty as Core is header-only, but needed for static library target
|
||||
// Future implementations will go here
|
||||
@@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <flecs.h>
|
||||
|
||||
namespace factory {
|
||||
|
||||
// Core engine class wrapping flecs world
|
||||
class Core {
|
||||
public:
|
||||
Core() : world_() {}
|
||||
|
||||
// Access the underlying flecs world
|
||||
flecs::world& world() { return world_; }
|
||||
const flecs::world& world() const { return world_; }
|
||||
|
||||
// Progress the simulation by one tick
|
||||
bool progress(float delta_time = 0.0f) {
|
||||
return world_.progress(delta_time);
|
||||
}
|
||||
|
||||
private:
|
||||
flecs::world world_;
|
||||
};
|
||||
|
||||
} // namespace factory
|
||||
24
src/main.cpp
Normal file
24
src/main.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "Components/Configs/WorldConfig.hpp"
|
||||
#include "Core/WorldInstance.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
|
||||
WorldConfig config{};
|
||||
|
||||
config.RegisterItem("Stone");
|
||||
config.RegisterItem("Wood");
|
||||
config.RegisterItem("Stick");
|
||||
config.RegisterItem("Copper Ore");
|
||||
|
||||
|
||||
WorldInstance worldInstance{ config };
|
||||
|
||||
|
||||
std::cout << "test\n";
|
||||
|
||||
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
# Test executable
|
||||
add_executable(factory_core_tests
|
||||
test_main.cpp
|
||||
test_example.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(factory_core_tests
|
||||
PRIVATE
|
||||
factory_core
|
||||
doctest::doctest
|
||||
)
|
||||
|
||||
# Register with CTest
|
||||
include(${doctest_SOURCE_DIR}/scripts/cmake/doctest.cmake)
|
||||
doctest_discover_tests(factory_core_tests)
|
||||
431
tests/test_SharedBuffer.cpp
Normal file
431
tests/test_SharedBuffer.cpp
Normal file
@@ -0,0 +1,431 @@
|
||||
#include <doctest/doctest.h>
|
||||
#include <atomic>
|
||||
#include "Util/SharedBuffer.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
|
||||
struct SimpleMetaData
|
||||
{
|
||||
int Id{};
|
||||
float Value{};
|
||||
};
|
||||
|
||||
TEST_SUITE("SharedBuffer")
|
||||
{
|
||||
TEST_CASE("default construction")
|
||||
{
|
||||
SharedBuffer<int, SimpleMetaData> buf;
|
||||
CHECK_FALSE(buf);
|
||||
}
|
||||
|
||||
TEST_CASE("sized construction")
|
||||
{
|
||||
SimpleMetaData meta{ 42, 3.14f };
|
||||
SharedBuffer<int, SimpleMetaData> buf(5, meta);
|
||||
|
||||
CHECK(buf);
|
||||
CHECK(buf.GetSize() == 5);
|
||||
CHECK(buf.GetMetaData()->Id == 42);
|
||||
CHECK(buf.GetMetaData()->Value == doctest::Approx(3.14f));
|
||||
|
||||
for (uint32_t i = 0; i < buf.GetSize(); ++i)
|
||||
CHECK(buf[i] == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("element access and mutation")
|
||||
{
|
||||
SimpleMetaData meta{};
|
||||
SharedBuffer<int, SimpleMetaData> buf(3, meta);
|
||||
|
||||
buf[0] = 10;
|
||||
buf[1] = 20;
|
||||
buf[2] = 30;
|
||||
|
||||
CHECK(buf[0] == 10);
|
||||
CHECK(buf[1] == 20);
|
||||
CHECK(buf[2] == 30);
|
||||
}
|
||||
|
||||
TEST_CASE("Ptr() access")
|
||||
{
|
||||
SimpleMetaData meta{};
|
||||
SharedBuffer<int, SimpleMetaData> buf(3, meta);
|
||||
buf[0] = 100;
|
||||
|
||||
int* ptr = buf.Ptr();
|
||||
CHECK(ptr[0] == 100);
|
||||
|
||||
ptr[1] = 200;
|
||||
CHECK(buf[1] == 200);
|
||||
}
|
||||
|
||||
TEST_CASE("GetData() returns valid span")
|
||||
{
|
||||
SimpleMetaData meta{};
|
||||
SharedBuffer<int, SimpleMetaData> buf(4, meta);
|
||||
buf[0] = 1;
|
||||
buf[1] = 2;
|
||||
buf[2] = 3;
|
||||
buf[3] = 4;
|
||||
|
||||
auto span = buf.GetData();
|
||||
CHECK(span.size() == 4);
|
||||
CHECK(span[0] == 1);
|
||||
CHECK(span[3] == 4);
|
||||
}
|
||||
|
||||
TEST_CASE("const access")
|
||||
{
|
||||
SimpleMetaData meta{ 7, 1.0f };
|
||||
SharedBuffer<int, SimpleMetaData> buf(2, meta);
|
||||
buf[0] = 99;
|
||||
|
||||
const auto& cbuf = buf;
|
||||
CHECK(cbuf[0] == 99);
|
||||
CHECK(cbuf.GetSize() == 2);
|
||||
CHECK(cbuf.Ptr()[0] == 99);
|
||||
CHECK(cbuf.GetMetaData()->Id == 7);
|
||||
CHECK(cbuf.GetData().size() == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("copy constructor shares data")
|
||||
{
|
||||
SimpleMetaData meta{};
|
||||
SharedBuffer<int, SimpleMetaData> a(3, meta);
|
||||
a[0] = 42;
|
||||
|
||||
SharedBuffer<int, SimpleMetaData> b(a);
|
||||
|
||||
CHECK(b);
|
||||
CHECK(b.GetSize() == 3);
|
||||
CHECK(b[0] == 42);
|
||||
|
||||
// They share the same underlying data
|
||||
CHECK(a.Ptr() == b.Ptr());
|
||||
|
||||
// Mutation through one is visible in the other
|
||||
a[1] = 77;
|
||||
CHECK(b[1] == 77);
|
||||
}
|
||||
|
||||
TEST_CASE("copy assignment shares data")
|
||||
{
|
||||
SimpleMetaData meta{};
|
||||
SharedBuffer<int, SimpleMetaData> a(2, meta);
|
||||
a[0] = 10;
|
||||
|
||||
SharedBuffer<int, SimpleMetaData> b;
|
||||
b = a;
|
||||
|
||||
CHECK(b);
|
||||
CHECK(b[0] == 10);
|
||||
CHECK(a.Ptr() == b.Ptr());
|
||||
}
|
||||
|
||||
TEST_CASE("copy assignment from non-empty to non-empty")
|
||||
{
|
||||
SimpleMetaData meta1{};
|
||||
SimpleMetaData meta2{};
|
||||
SharedBuffer<int, SimpleMetaData> a(2, meta1);
|
||||
SharedBuffer<int, SimpleMetaData> b(3, meta2);
|
||||
a[0] = 1;
|
||||
b[0] = 2;
|
||||
|
||||
int* oldBPtr = b.Ptr();
|
||||
b = a;
|
||||
|
||||
CHECK(b.GetSize() == 2);
|
||||
CHECK(b[0] == 1);
|
||||
CHECK(b.Ptr() == a.Ptr());
|
||||
CHECK(b.Ptr() != oldBPtr);
|
||||
}
|
||||
|
||||
TEST_CASE("self copy assignment")
|
||||
{
|
||||
SimpleMetaData meta{};
|
||||
SharedBuffer<int, SimpleMetaData> a(2, meta);
|
||||
a[0] = 55;
|
||||
|
||||
a = a;
|
||||
|
||||
CHECK(a);
|
||||
CHECK(a[0] == 55);
|
||||
CHECK(a.GetSize() == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("move constructor transfers ownership")
|
||||
{
|
||||
SimpleMetaData meta{};
|
||||
SharedBuffer<int, SimpleMetaData> a(3, meta);
|
||||
a[0] = 42;
|
||||
int* origPtr = a.Ptr();
|
||||
|
||||
SharedBuffer<int, SimpleMetaData> b(std::move(a));
|
||||
|
||||
CHECK(b);
|
||||
CHECK(b[0] == 42);
|
||||
CHECK(b.Ptr() == origPtr);
|
||||
CHECK_FALSE(a); // source is empty
|
||||
}
|
||||
|
||||
TEST_CASE("move assignment transfers ownership")
|
||||
{
|
||||
SimpleMetaData meta{};
|
||||
SharedBuffer<int, SimpleMetaData> a(3, meta);
|
||||
a[0] = 42;
|
||||
int* origPtr = a.Ptr();
|
||||
|
||||
SharedBuffer<int, SimpleMetaData> b;
|
||||
b = std::move(a);
|
||||
|
||||
CHECK(b);
|
||||
CHECK(b[0] == 42);
|
||||
CHECK(b.Ptr() == origPtr);
|
||||
CHECK_FALSE(a);
|
||||
}
|
||||
|
||||
TEST_CASE("self move assignment")
|
||||
{
|
||||
SimpleMetaData meta{};
|
||||
SharedBuffer<int, SimpleMetaData> a(2, meta);
|
||||
a[0] = 55;
|
||||
|
||||
a = std::move(a);
|
||||
|
||||
CHECK(a);
|
||||
CHECK(a[0] == 55);
|
||||
}
|
||||
|
||||
TEST_CASE("reference counting - last copy cleans up")
|
||||
{
|
||||
SimpleMetaData meta{};
|
||||
int* ptr;
|
||||
|
||||
{
|
||||
SharedBuffer<int, SimpleMetaData> a(3, meta);
|
||||
a[0] = 1;
|
||||
ptr = a.Ptr();
|
||||
|
||||
{
|
||||
SharedBuffer<int, SimpleMetaData> b(a);
|
||||
CHECK(b.Ptr() == ptr);
|
||||
// b goes out of scope - should NOT free since a still alive
|
||||
}
|
||||
|
||||
// a should still be valid
|
||||
CHECK(a);
|
||||
CHECK(a[0] == 1);
|
||||
}
|
||||
// a goes out of scope here - memory freed (no way to check, but no crash)
|
||||
}
|
||||
|
||||
TEST_CASE("multiple copies and sequential destruction")
|
||||
{
|
||||
SimpleMetaData meta{};
|
||||
SharedBuffer<int, SimpleMetaData> a(2, meta);
|
||||
a[0] = 100;
|
||||
|
||||
SharedBuffer<int, SimpleMetaData> b(a);
|
||||
SharedBuffer<int, SimpleMetaData> c(b);
|
||||
SharedBuffer<int, SimpleMetaData> d(c);
|
||||
|
||||
CHECK(a.Ptr() == d.Ptr());
|
||||
|
||||
// Destroy in various orders
|
||||
b = SharedBuffer<int, SimpleMetaData>(); // release b's ref
|
||||
CHECK_FALSE(b);
|
||||
CHECK(a[0] == 100); // a still valid
|
||||
|
||||
d = SharedBuffer<int, SimpleMetaData>(); // release d's ref
|
||||
CHECK(a[0] == 100); // a still valid
|
||||
CHECK(c[0] == 100); // c still valid
|
||||
}
|
||||
|
||||
TEST_CASE("reassignment releases old buffer")
|
||||
{
|
||||
SimpleMetaData meta{};
|
||||
SharedBuffer<int, SimpleMetaData> a(2, meta);
|
||||
SharedBuffer<int, SimpleMetaData> b(3, meta);
|
||||
|
||||
a[0] = 1;
|
||||
b[0] = 2;
|
||||
|
||||
// a had sole ownership of its buffer; assigning b should free old buffer
|
||||
a = b;
|
||||
CHECK(a[0] == 2);
|
||||
CHECK(a.GetSize() == 3);
|
||||
}
|
||||
|
||||
TEST_CASE("works with non-trivial element types")
|
||||
{
|
||||
struct Meta { int x{}; };
|
||||
|
||||
SharedBuffer<std::string, Meta> buf(3, Meta{ 1 });
|
||||
buf[0] = "hello";
|
||||
buf[1] = "world";
|
||||
buf[2] = "test";
|
||||
|
||||
CHECK(buf[0] == "hello");
|
||||
CHECK(buf[1] == "world");
|
||||
CHECK(buf[2] == "test");
|
||||
|
||||
// Copy and verify strings are shared
|
||||
SharedBuffer<std::string, Meta> copy(buf);
|
||||
CHECK(copy[0] == "hello");
|
||||
|
||||
buf[0] = "modified";
|
||||
CHECK(copy[0] == "modified"); // shared data
|
||||
}
|
||||
|
||||
TEST_CASE("copy of non-trivial types cleans up properly")
|
||||
{
|
||||
struct Meta { int x{}; };
|
||||
|
||||
{
|
||||
SharedBuffer<std::string, Meta> a(2, Meta{});
|
||||
a[0] = "aaa";
|
||||
a[1] = "bbb";
|
||||
|
||||
{
|
||||
SharedBuffer<std::string, Meta> b(a);
|
||||
CHECK(b[0] == "aaa");
|
||||
}
|
||||
// b destroyed, a still valid
|
||||
CHECK(a[0] == "aaa");
|
||||
}
|
||||
// a destroyed, strings cleaned up (no leak/crash)
|
||||
}
|
||||
|
||||
TEST_CASE("zero-size buffer")
|
||||
{
|
||||
SimpleMetaData meta{ 1, 2.0f };
|
||||
SharedBuffer<int, SimpleMetaData> buf(0, meta);
|
||||
|
||||
CHECK(buf);
|
||||
CHECK(buf.GetSize() == 0);
|
||||
CHECK(buf.GetMetaData()->Id == 1);
|
||||
CHECK(buf.GetData().empty());
|
||||
}
|
||||
|
||||
TEST_CASE("metadata mutation")
|
||||
{
|
||||
SimpleMetaData meta{ 1, 0.0f };
|
||||
SharedBuffer<int, SimpleMetaData> buf(1, meta);
|
||||
|
||||
buf.GetMetaData()->Id = 99;
|
||||
buf.GetMetaData()->Value = 1.5f;
|
||||
|
||||
CHECK(buf.GetMetaData()->Id == 99);
|
||||
CHECK(buf.GetMetaData()->Value == doctest::Approx(1.5f));
|
||||
}
|
||||
|
||||
TEST_CASE("metadata shared between copies")
|
||||
{
|
||||
SimpleMetaData meta{ 10, 0.0f };
|
||||
SharedBuffer<int, SimpleMetaData> a(1, meta);
|
||||
SharedBuffer<int, SimpleMetaData> b(a);
|
||||
|
||||
a.GetMetaData()->Id = 50;
|
||||
CHECK(b.GetMetaData()->Id == 50);
|
||||
}
|
||||
|
||||
TEST_CASE("bool conversion")
|
||||
{
|
||||
SharedBuffer<int, SimpleMetaData> empty;
|
||||
CHECK_FALSE(empty);
|
||||
|
||||
SimpleMetaData meta{};
|
||||
SharedBuffer<int, SimpleMetaData> valid(1, meta);
|
||||
CHECK(valid);
|
||||
|
||||
SharedBuffer<int, SimpleMetaData> moved(std::move(valid));
|
||||
CHECK_FALSE(valid);
|
||||
CHECK(moved);
|
||||
}
|
||||
|
||||
TEST_CASE("copy from default-constructed buffer")
|
||||
{
|
||||
SharedBuffer<int, SimpleMetaData> empty;
|
||||
SharedBuffer<int, SimpleMetaData> copy(empty);
|
||||
CHECK_FALSE(copy);
|
||||
}
|
||||
|
||||
TEST_CASE("assign default-constructed buffer")
|
||||
{
|
||||
SimpleMetaData meta{};
|
||||
SharedBuffer<int, SimpleMetaData> a(2, meta);
|
||||
SharedBuffer<int, SimpleMetaData> empty;
|
||||
|
||||
a = empty;
|
||||
CHECK_FALSE(a);
|
||||
}
|
||||
|
||||
TEST_CASE("move from default-constructed buffer")
|
||||
{
|
||||
SharedBuffer<int, SimpleMetaData> empty;
|
||||
SharedBuffer<int, SimpleMetaData> moved(std::move(empty));
|
||||
CHECK_FALSE(moved);
|
||||
}
|
||||
|
||||
TEST_CASE("large buffer")
|
||||
{
|
||||
SimpleMetaData meta{};
|
||||
constexpr int N = 10000;
|
||||
SharedBuffer<int, SimpleMetaData> buf(N, meta);
|
||||
|
||||
for (int i = 0; i < N; ++i)
|
||||
buf[i] = i * 2;
|
||||
|
||||
for (int i = 0; i < N; ++i)
|
||||
CHECK(buf[i] == i * 2);
|
||||
}
|
||||
|
||||
TEST_CASE("chain of assignments")
|
||||
{
|
||||
SimpleMetaData meta{};
|
||||
SharedBuffer<int, SimpleMetaData> a(1, meta);
|
||||
SharedBuffer<int, SimpleMetaData> b(1, meta);
|
||||
SharedBuffer<int, SimpleMetaData> c(1, meta);
|
||||
|
||||
a[0] = 1;
|
||||
b[0] = 2;
|
||||
c[0] = 3;
|
||||
|
||||
a = b;
|
||||
b = c;
|
||||
|
||||
CHECK(a[0] == 2);
|
||||
CHECK(b[0] == 3);
|
||||
CHECK(c[0] == 3);
|
||||
CHECK(b.Ptr() == c.Ptr());
|
||||
}
|
||||
|
||||
TEST_CASE("concurrent read access from copies")
|
||||
{
|
||||
SimpleMetaData meta{};
|
||||
SharedBuffer<int, SimpleMetaData> buf(100, meta);
|
||||
for (uint32_t i = 0; i < 100; ++i)
|
||||
buf[i] = static_cast<int>(i);
|
||||
|
||||
auto copy1 = buf;
|
||||
auto copy2 = buf;
|
||||
|
||||
std::thread t1([©1]() {
|
||||
int sum = 0;
|
||||
for (uint32_t i = 0; i < copy1.GetSize(); ++i)
|
||||
sum += copy1[i];
|
||||
CHECK(sum == 4950);
|
||||
});
|
||||
|
||||
std::thread t2([©2]() {
|
||||
int sum = 0;
|
||||
for (uint32_t i = 0; i < copy2.GetSize(); ++i)
|
||||
sum += copy2[i];
|
||||
CHECK(sum == 4950);
|
||||
});
|
||||
|
||||
t1.join();
|
||||
t2.join();
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
#include <doctest/doctest.h>
|
||||
#include "factory_core.hpp"
|
||||
|
||||
TEST_CASE("Core initialization") {
|
||||
factory::Core core;
|
||||
|
||||
SUBCASE("World is valid after construction") {
|
||||
CHECK(core.world().id() != 0);
|
||||
}
|
||||
|
||||
SUBCASE("Progress returns true") {
|
||||
CHECK(core.progress() == true);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Basic ECS operations") {
|
||||
factory::Core core;
|
||||
auto& world = core.world();
|
||||
|
||||
SUBCASE("Can create entity") {
|
||||
auto entity = world.entity();
|
||||
CHECK(entity.is_valid());
|
||||
}
|
||||
|
||||
SUBCASE("Can create entity with name") {
|
||||
auto entity = world.entity("test_entity");
|
||||
CHECK(entity.is_valid());
|
||||
CHECK(entity.name() == std::string("test_entity"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user