CaveGame Edits -> Filling out TileID
This commit is contained in:
@@ -20,7 +20,7 @@ include(cmake/CPM.cmake)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME J3ML
|
||||
URL https://git.redacted.cc/josh/j3ml/archive/Release-3.4.zip
|
||||
URL https://git.redacted.cc/josh/j3ml/archive/3.4.3.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
@@ -30,7 +30,7 @@ CPMAddPackage(
|
||||
|
||||
CPMAddPackage(
|
||||
NAME JGL
|
||||
URL https://git.redacted.cc/josh/JGL/archive/Prerelease-38.zip
|
||||
URL https://git.redacted.cc/josh/JGL/archive/Prerelease-40.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
|
@@ -26,7 +26,7 @@ namespace CaveGame::Client {
|
||||
class Container
|
||||
{
|
||||
public:
|
||||
Container(int rows, int columns, );
|
||||
Container(int rows, int columns);
|
||||
|
||||
protected:
|
||||
private:
|
||||
|
@@ -13,6 +13,7 @@
|
||||
#include <steam_api.h>
|
||||
#include <ClientApp/CaveGameWindow.hpp>
|
||||
#include <rewindow/logger/logger.h>
|
||||
#include <Core/Tile.hpp>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
@@ -23,6 +24,13 @@ int main(int argc, char** argv) {
|
||||
|
||||
//srand(0);
|
||||
|
||||
using namespace factory;
|
||||
|
||||
auto result = Registry<CaveGame::Core::Tile>::GetKeys();
|
||||
|
||||
for (auto& tile : result)
|
||||
std:: cout << tile << std::endl;
|
||||
|
||||
auto* window = new CaveGame::ClientApp::CaveGameWindow("Re-CaveGame", 800, 600);
|
||||
//window->SetResizable(true);
|
||||
window->Run();
|
||||
|
@@ -1,27 +1,140 @@
|
||||
#include <cstdint>
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
// TODO: Consider implementing traits.
|
||||
|
||||
enum class TileID : std::uint16_t
|
||||
{
|
||||
VOID = 65535,
|
||||
AIR = 0,
|
||||
|
||||
STONE,
|
||||
DIRT,
|
||||
GRASS,
|
||||
MUD,
|
||||
CLAY,
|
||||
SAND,
|
||||
TALL_GRASS,
|
||||
VINE,
|
||||
TRUNK,
|
||||
BRANCH,
|
||||
LEAVES,
|
||||
PLANK,
|
||||
GRASS,
|
||||
MOSSY_STONE, LIMESTONE, BASALT, COBBLESTONE,
|
||||
|
||||
WET_CEMENT,
|
||||
CEMENT,
|
||||
CONCRETE,
|
||||
MUD_BRICK,
|
||||
BRICK,
|
||||
CLAY_BRICK,
|
||||
STONE_BRICK,
|
||||
SANDSTONE,
|
||||
SAND_BRICK,
|
||||
GRANITE,
|
||||
SMOOTH_GRANTITE,
|
||||
SLATE,
|
||||
ANDESITE,
|
||||
TUFF,
|
||||
CHALK,
|
||||
GYPSUM,
|
||||
PUMICE,
|
||||
QUARTZ,
|
||||
|
||||
BRIMSTONE,
|
||||
OBSIDIAN,
|
||||
HARDENED_CLAY, CLAY,
|
||||
SILT, LOAM, QUAGMIRE, ASH,
|
||||
GLOWY_GRASS, DRY_GRASS, DEAD_GRASS, GMO_GRASS, FAKE_GRASS,
|
||||
MOSS,
|
||||
SNOW, PACKED_SNOW,
|
||||
ICE, PACKED_ICE, ICE_BRICK, THIN_ICE,
|
||||
SAND, WET_SAND, WHITE_SAND, BLACK_SAND, GRAVEL, DUST,
|
||||
|
||||
GLASS, CRACKED_GLASS,
|
||||
RED_STAINED_GLASS,
|
||||
ORANGE_STAINED_GLASS,
|
||||
YELLOW_STAINED_GLASS,
|
||||
LIME_STAINED_GLASS,
|
||||
GREEN_STAINED_GLASS,
|
||||
CYAN_STAINED_GLASS,
|
||||
BLUE_STAINED_GLASS,
|
||||
MAGENTA_STAINED_GLASS,
|
||||
BLACK_STAINED_GLASS,
|
||||
WHITE_STAINED_GLASS,
|
||||
|
||||
RED_TERRACOTTA,
|
||||
ORANGE_TERRACOTTA,
|
||||
YELLOW_TERRACOTTA,
|
||||
LIME_TERRACOTTA,
|
||||
GREEN_TERRACOTTA,
|
||||
CYAN_TERRACOTTA,
|
||||
BLUE_TERRACOTTA,
|
||||
MAGENTA_TERRACOTTA,
|
||||
BLACK_TERRACOTTA,
|
||||
WHITE_TERRACOTTA,
|
||||
|
||||
RED_PAINT,
|
||||
ORANGE_PAINT,
|
||||
YELLOW_PAINT,
|
||||
LIME_PAINT,
|
||||
GREEN_PAINT,
|
||||
CYAN_PAINT,
|
||||
BLUE_PAINT,
|
||||
MAGENTA_PAINT,
|
||||
BLACK_PAINT,
|
||||
WHITE_PAINT,
|
||||
|
||||
|
||||
CACTUS,
|
||||
BLOOMING_CACTUS,
|
||||
|
||||
TALL_GRASS,
|
||||
VINE,
|
||||
BAMBOO,
|
||||
FLOWER_STEM,
|
||||
|
||||
RED_FLOWER_PETAL,
|
||||
ORANGE_FLOWER_PETAL,
|
||||
YELLOW_FLOWER_PETAL,
|
||||
LIME_FLOWER_PETAL,
|
||||
GREEN_FLOWER_PETAL,
|
||||
CYAN_FLOWER_PETAL,
|
||||
BLUE_FLOWER_PETAL,
|
||||
MAGENTA_FLOWER_PETAL,
|
||||
BLACK_FLOWER_PETAL,
|
||||
WHITE_FLOWER_PETAL,
|
||||
|
||||
LILYPAD,
|
||||
ALGAE,
|
||||
|
||||
OAK_LOG, OAK_LEAF, OAK_PLANK,
|
||||
REDWOOD_LOG, REDWOOD_LEAF, REDWOOD_PLANK,
|
||||
EBONY_LOG, EBONY_LEAF, EBONY_PLANK,
|
||||
|
||||
C4, TNT,
|
||||
|
||||
TIN_ORE,
|
||||
COPPER_ORE,
|
||||
IRON_ORE,
|
||||
COAL_ORE,
|
||||
NICKEL_ORE,
|
||||
CHROMIUM_ORE,
|
||||
TITANIUM_ORE,
|
||||
ARSENIC_ORE,
|
||||
GALLIUM_ORE,
|
||||
URANIUM_ORE,
|
||||
MAGNETITE_ORE,
|
||||
METEORITE_ORE,
|
||||
|
||||
SOVITE, PYRITE, CINNABAR,
|
||||
|
||||
|
||||
WATER, LAVA, SLUDGE, OIL,
|
||||
BLOOD, PISS, HONEY, ECTOPLASM,
|
||||
|
||||
COPPER_WIRE, GOLD_WIRE,
|
||||
AND_GATE, NOR_GATE, XOR_GATE,
|
||||
LED,
|
||||
|
||||
TORCH_BASE,
|
||||
TORCH_EMBER,
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
33
Core/include/Core/Interfaces.hpp
Normal file
33
Core/include/Core/Interfaces.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Core/Data.hpp>
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
using TileState = uint16_t;
|
||||
|
||||
class ITileMap
|
||||
{
|
||||
public:
|
||||
virtual TileID GetTile(int x, int y) const = 0;
|
||||
virtual void SetTile(int x, int y, TileID tile) = 0;
|
||||
|
||||
virtual TileState GetTileState(int x, int y) const = 0;
|
||||
virtual void SetTileState(int x, int y, TileState state) = 0;
|
||||
|
||||
bool GrassSpreadable(int x, int y) const;
|
||||
|
||||
bool IsNonSolidTile(int x, int y) const;
|
||||
|
||||
bool GrassShouldSuffocate(int x, int y) const;
|
||||
|
||||
void GrassRandomTicc(int wx, int wy);
|
||||
|
||||
bool IsSolidTile(int x, int y) const;
|
||||
|
||||
bool HasAdjacentOrDiagonalAirBlock(int x, int y) const;
|
||||
|
||||
bool HasAdjacentAirBlock(int x, int y) const;
|
||||
};
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
#include <set>
|
||||
#include <Core/Registry.hpp>
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -52,6 +53,11 @@ namespace CaveGame::Core
|
||||
private:
|
||||
};
|
||||
|
||||
class EmptyBottle : public Item
|
||||
{
|
||||
REGISTER("EmptyBottle", Item);
|
||||
};
|
||||
|
||||
class ItemFilter
|
||||
{
|
||||
|
||||
@@ -61,5 +67,7 @@ namespace CaveGame::Core
|
||||
std::set<std::string> AllowedItems;
|
||||
std::set<std::string> BannedItems;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
//
|
||||
// Created by dawsh on 12/4/24.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#ifndef RECAVEGAME_MACROS_HPP
|
||||
#define RECAVEGAME_MACROS_HPP
|
||||
|
||||
#endif //RECAVEGAME_MACROS_HPP
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
|
||||
|
||||
}
|
@@ -1,56 +1,428 @@
|
||||
/// Framework for performing registration of object factories.
|
||||
//
|
||||
// The major points of the framework are:
|
||||
// - header-only library
|
||||
// - no need to declare registration ahead on the base class,
|
||||
// which means header need to be included only in files
|
||||
// declaring subclasses that register themselves.
|
||||
// - registration is done via a macro called inside the declaration
|
||||
// of the class, which reduces boilerplate code, and incidentally makes
|
||||
// the registration name available to the class.
|
||||
// - ingle macro supports constructors with arbitrary number of arguments.
|
||||
// - class can be registered for different constructors, making
|
||||
// it easy to implement logic that try to instantiate an object
|
||||
// from different parameters.
|
||||
// - builtin mechanism to override registered classes, making dependency
|
||||
// injection e.g. for tests very easy.
|
||||
//
|
||||
// Basic usage
|
||||
// -----------
|
||||
// Given an interface:
|
||||
//
|
||||
// class Shape {
|
||||
// public:
|
||||
// virtual ~Shape() {}
|
||||
// virtual void Draw() const = 0;
|
||||
// };
|
||||
//
|
||||
// The framework allows to annotate with REGISTER macro any subclass:
|
||||
//
|
||||
// class Circle : public Shape {
|
||||
// REGISTER("Circle", Shape);
|
||||
// public:
|
||||
// void Draw() const override { ... }
|
||||
// };
|
||||
//
|
||||
// The first parameter of the macro can be any string and does not have to
|
||||
// match the name of the class:
|
||||
//
|
||||
// class Rect : public Shape {
|
||||
// REGISTER("Rectangle", Shape);
|
||||
// public:
|
||||
// void Draw() const override { ... }
|
||||
// };
|
||||
//
|
||||
// Note that the annotation is done only inside the derived classes.
|
||||
// Nothing needs to be added to the Shape class, and no declaration
|
||||
// other than the Shape class needs to be done. The annotation can be
|
||||
// put inside the private, protected or public section of the class.
|
||||
// The annotation adds not bytes to the class, whose size is equal to
|
||||
// the class would have without the annotation.
|
||||
//
|
||||
// With the annotation, a Shape instance can be created from a string:
|
||||
//
|
||||
// std::unique_ptr<Shape> shape = Registry<Shape>::New("Rect");
|
||||
// shape->Draw(); // will draw a rectangle!
|
||||
//
|
||||
// The function returns a unique_ptr<> which makes ownership clear and simple.
|
||||
// If no class is registered, it will return a null unique_ptr<>.
|
||||
// The CanNew() predicate can be used to check if New() would succeed without
|
||||
// actually creating an instance.
|
||||
//
|
||||
// Advanced usage
|
||||
// --------------
|
||||
//
|
||||
// The basic usage considered classes with parameter-less constructor.
|
||||
// The REGISTER macro can also be used with classes taking arbitrary
|
||||
// parameters. For example, shapes with injectable parameters
|
||||
// can be registered using REGISTER() macro with extra parameters matching
|
||||
// the constructor signature:
|
||||
//
|
||||
// class Ellipsis : public Shape {
|
||||
// REGISTER("Ellipsis", Shape, const std::string&);
|
||||
// public:
|
||||
// explicit Ellipsis(const std::string& params) { ... }
|
||||
// void Draw() const override { ... }
|
||||
// };
|
||||
//
|
||||
// The class can be instantiated using Registry<> with extra parameters
|
||||
// matching the constructor signature:
|
||||
//
|
||||
// auto shape = Registry<Shape, const string&>::New("Ellipsis");
|
||||
// shape->Draw(); // will draw a circle!
|
||||
//
|
||||
// One very interesting benefit of this approach is that client code can
|
||||
// extend the supported types without editing the base class or
|
||||
// any of the existing registered classes. The code below simply test
|
||||
// if a class can be instantiated with a parameter, and otherwise
|
||||
// tries to instantiate without parameters:
|
||||
//
|
||||
// int main(int argc, char **argv) {
|
||||
// for (int i = 1; i + 1 < argc; i += 2) {
|
||||
// const std::string key = argv[i];
|
||||
// const std::string params = argv[i + 1];
|
||||
// if (Registry<Shape, const std::string &>::CanNew(key, params)) {
|
||||
// Registry<Shape, const std::string &>::New(key, params)->Draw();
|
||||
// } else if (Registry<Shape>::CanNew(key)) {
|
||||
// Registry<Shape>::New(key)->Draw();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Additionally, it is possible to register a class with different constructors
|
||||
// so it works with old client code that only uses Registry<Shape> and
|
||||
// some client code like the one above:
|
||||
//
|
||||
// class Ellipsis : public Shape {
|
||||
// REGISTER("Ellipsis", Shape);
|
||||
// REGISTER("Ellipsis", Shape, const std::string&);
|
||||
// public:
|
||||
// explicit Ellipsis(const std::string& params = "") { ... }
|
||||
// void Draw() const override { ... }
|
||||
// };
|
||||
//
|
||||
// Testing
|
||||
// -------
|
||||
//
|
||||
// When testing client code using registered classes, it may be not desired to
|
||||
// actually instantiate real classes. If the Draw() implementations for example
|
||||
// require a graphic context to be active, calling those functions in an
|
||||
// offscreen automated test will fail. Injectors can be used to temporarily
|
||||
// replace registered classes by arbitrary factories:
|
||||
//
|
||||
// class FakeShape : public Shape {
|
||||
// public:
|
||||
// void Draw() override {}
|
||||
// };
|
||||
//
|
||||
// TEST(Draw, WorkingCase) {
|
||||
// const Registry<Shape>::Injector injectors[] =
|
||||
// Registry<Shape>::Injector("Circle", []{ return new FakeShape});
|
||||
// Registry<Shape>::Injector("Rectangle", []{ return new FakeShape});
|
||||
// Registry<Shape>::Injector("Circle", []{ return new FakeShape});
|
||||
// EXPECT_TRUE(FunctionUsingRegistryForShape());
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// Note that it may be necessary to specify the return type of lambda function
|
||||
// for code to compile as in [] -> Shape* { return .... }
|
||||
//
|
||||
// Injectors can also be used as static global variables to perform
|
||||
// registration of a class *outside* of the class, in replacement for
|
||||
// the REGISTER macro. This is useful to register classes whose code
|
||||
// cannot be edited.
|
||||
//
|
||||
// Injectors can also be used to define global or local name alias, as
|
||||
// illustrated by the example REGISTER_ALIAS macro in this file.
|
||||
//
|
||||
// REGISTER_ALIAS(Shape, "Rectangle", "Rect");
|
||||
//
|
||||
// Goodies
|
||||
// -------
|
||||
//
|
||||
// Classes registered with the REGISTER macro can know the key under
|
||||
// which they are registered. The following code:
|
||||
//
|
||||
// Registry<Vehicle>::GetKeyFor<Circle>()
|
||||
//
|
||||
// will return the string "Circle". This works on object classes,
|
||||
// passed as template parameter to GetKeyFor(), and not on object
|
||||
// instances. So there is no such functionality as:
|
||||
//
|
||||
// std::unique_ptr<Shape> shape = new Circle();
|
||||
// std::cout << Registry<Vehicle>::GetKeyFor(*shape); // Not implemented
|
||||
//
|
||||
// as it would require runtime type identification. It is possible to
|
||||
// extend the framework to support it though, by storing `type_info`
|
||||
// objects in the registry.
|
||||
//
|
||||
// The Registry<> class can also be used to list all keys that are
|
||||
// registered, along with the filename and line number at which the
|
||||
// registration is defined. It does not list though the type associated
|
||||
// to the key. Here again, it is pretty easy to extend the framework
|
||||
// to provide such functionality using type_info objects.
|
||||
//
|
||||
// Even though not necessary, one can define intermediate macros to
|
||||
// reduce boilerplate code even more. For the Shape example above,
|
||||
// one could define:
|
||||
//
|
||||
// #define REGISTER_SHAPE(KEY, ARGS...) REGISTER(#KEY, Shape, ##ARGS)
|
||||
//
|
||||
// with which the Ellipsis class above would simply be written:
|
||||
//
|
||||
// class Ellipsis : public Shape {
|
||||
// REGISTER_SHAPE(Ellipsis);
|
||||
// REGISTER_SHAPE(Ellipsis, const std::string &);
|
||||
//
|
||||
// public:
|
||||
// };
|
||||
//
|
||||
// Limitations
|
||||
// -----------
|
||||
// The code requires a C++11 compliant compiler.
|
||||
//
|
||||
// The code relies on the a GNU preprocessor feature for variadic macros:
|
||||
//
|
||||
// #define MACRO(x, opt...) call(x, ##opt)
|
||||
//
|
||||
// which extends to call(x) when opt is empty and call(x, ...) otherwise.
|
||||
//
|
||||
// None of the static methods of Registry<> can be called from
|
||||
// a global static, as it would result in an initialization order fiasco.
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <typeinfo>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CaveGame
|
||||
{
|
||||
/// Dead Simple Registry system, however, it relies on implementation-defined behavior:
|
||||
/** @note 'It is implementation-defined whether the dynamic initialization of a non-local variable
|
||||
with static storage duration is done before the first statement of main.
|
||||
If the initialization is deferred to some point in time after the first statement of main,
|
||||
it shall occur before the first odr-use (3.2) of any function or variable defined in the same
|
||||
translation unit as the variable to be initialized.' */
|
||||
template <class T>
|
||||
struct Registry
|
||||
{
|
||||
struct proxy { inline proxy(); };
|
||||
static proxy p;
|
||||
};
|
||||
|
||||
/// What The Fuck Bro?
|
||||
template <class T> typename Registry<T>::proxy Registry<T>::p;
|
||||
#define REGISTER(KEY, TYPE, ARGS...) REGISTER_AT(__LINE__, KEY, TYPE ##ARGS)
|
||||
|
||||
#define REGISTER_ALIAS(TYPE, NAME, ALIAS) \
|
||||
REGISTER_ALIAS_AT(__LINE__, TYPE, NAME, ALIAS)
|
||||
|
||||
#define REGISTER_ALIAS_AT(LINE, TYPE, NAME, ALIAS) \
|
||||
Registry<TYPE>::Injector CONCAT_TOKENS(_xd_injector, LINE)(ALIAS, []() { \
|
||||
return Registry<TYPE>::New(NAME).release(); \
|
||||
}, __FILE__, STRINGIFY(LINE)); \
|
||||
static_assert(true, "") // enforce ; at EOL
|
||||
|
||||
|
||||
struct factory
|
||||
{
|
||||
template <typename T> static T* create()
|
||||
{
|
||||
Registry<T>::p;
|
||||
return new T();
|
||||
namespace factory {
|
||||
template<typename T, class... Args>
|
||||
class Registry {
|
||||
public:
|
||||
// Return 'true' if there is a class registered for `key` for
|
||||
// a constructor with signature (Args... args).
|
||||
//
|
||||
// This function can not be called from any static initializer
|
||||
// or it creates initializer order fiasco.
|
||||
static bool CanNew(const std::string &key, Args... args) {
|
||||
return GetEntry(key, args...).first;
|
||||
}
|
||||
|
||||
// If there is a class registered for `key` for a constructor
|
||||
// with signature (Args... args), instantiate an object of that class
|
||||
// passing args to the constructor. Returns a null pointer otherwise.
|
||||
//
|
||||
// This function can not be called from any static initializer
|
||||
// or it creates initializer order fiasco.
|
||||
static std::unique_ptr<T> New(const std::string &key, Args... args) {
|
||||
std::unique_ptr<T> result;
|
||||
auto entry = GetEntry(key, args...);
|
||||
if (entry.first) {
|
||||
result.reset(entry.second->function(args...));
|
||||
}
|
||||
registry_mutex_.unlock();
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
// Return the key under which class `C` is registered. The header
|
||||
// defining that class must be included by code calling this
|
||||
// function. If class is not registered, there will be a compile-
|
||||
// time failure.
|
||||
template<typename C>
|
||||
static const char *GetKeyFor() {
|
||||
return C::_xd_key(static_cast<const T *>(nullptr),
|
||||
std::function<void(Args...)>());
|
||||
}
|
||||
|
||||
// Returns the list of keys registered for the registry.
|
||||
// Keys corresponding to injectors (see below) are suffixed with
|
||||
// a star.
|
||||
//
|
||||
// This function can not be called from any static initializer
|
||||
// or it creates initializer order fiasco.
|
||||
static std::vector<std::string> GetKeys() {
|
||||
std::vector<std::string> keys;
|
||||
registry_mutex_.lock();
|
||||
for (const auto &iter: *GetRegistry()) {
|
||||
keys.emplace_back(iter.first);
|
||||
}
|
||||
for (const auto &iter: *GetInjectors()) {
|
||||
keys.emplace_back(iter.first + "*");
|
||||
}
|
||||
registry_mutex_.unlock();
|
||||
return keys;
|
||||
}
|
||||
|
||||
// Like GetKeys() function, but also returns the filename
|
||||
// and line number of the corresponding REGISTER() macros.
|
||||
// For injectors, the filename and line number are those
|
||||
// passed to the injector constructor.
|
||||
static std::vector<std::string> GetKeysWithLocations() {
|
||||
std::vector<std::string> keys;
|
||||
registry_mutex_.lock();
|
||||
for (const auto &iter: *GetRegistry()) {
|
||||
keys.emplace_back(std::string(iter.second.file) + ":" +
|
||||
std::string(iter.second.line) + ": " + iter.first);
|
||||
}
|
||||
for (const auto &iter: *GetInjectors()) {
|
||||
keys.emplace_back(std::string(iter.second.file) + ":" +
|
||||
std::string(iter.second.line) + ": " + iter.first +
|
||||
"*");
|
||||
}
|
||||
registry_mutex_.unlock();
|
||||
return keys;
|
||||
}
|
||||
|
||||
// Helper class which uses RAII to inject a factory which will be used
|
||||
// instead of any class registered with the same key, for any call
|
||||
// within the scope of the variable.
|
||||
// If there are two injectors in the same scope for the same key,
|
||||
// the last one takes precedence and cancels the first one, which will
|
||||
// never be active, even if the second one gets out of scope.
|
||||
struct Injector {
|
||||
const std::string &key;
|
||||
|
||||
Injector(const std::string &key,
|
||||
const std::function<T *(Args...)> &function,
|
||||
const char *file = "undefined", const char *line = "undefined")
|
||||
: key(key) {
|
||||
registry_mutex_.lock();
|
||||
const Entry entry = {file, line, function};
|
||||
GetInjectors()->insert(std::make_pair(key, entry));
|
||||
registry_mutex_.unlock();
|
||||
}
|
||||
|
||||
~Injector() {
|
||||
registry_mutex_.lock();
|
||||
GetInjectors()->erase(key);
|
||||
registry_mutex_.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
// Implementation details that can't be made private because used in macros
|
||||
//***************************************************************************
|
||||
typedef std::function<T *(Args...)> function_t;
|
||||
|
||||
struct Registerer {
|
||||
Registerer(function_t function, const std::string &key, const char *file,
|
||||
const char *line) {
|
||||
const Entry entry = {file, line, function};
|
||||
registry_mutex_.lock();
|
||||
GetRegistry()->insert(std::make_pair(key, entry));
|
||||
registry_mutex_.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
struct Entry {
|
||||
const char *const file;
|
||||
const char *const line;
|
||||
const function_t function;
|
||||
};
|
||||
typedef std::map<std::string, Entry> EntryMap;
|
||||
|
||||
// The registry and injectors are created on demand using static variables
|
||||
// inside a static method so that there is no order initialization fiasco.
|
||||
static EntryMap *GetRegistry() {
|
||||
static EntryMap registry;
|
||||
return ®istry;
|
||||
}
|
||||
|
||||
static EntryMap *GetInjectors() {
|
||||
static EntryMap injectors;
|
||||
return &injectors;
|
||||
};
|
||||
static std::mutex registry_mutex_;
|
||||
|
||||
static std::pair<bool, const Entry *> GetEntry(const std::string &key,
|
||||
Args... args) {
|
||||
registry_mutex_.lock();
|
||||
auto it = GetInjectors()->find(key);
|
||||
bool found = (it != GetInjectors()->end());
|
||||
if (!found) {
|
||||
it = GetRegistry()->find(key);
|
||||
found = (it != GetRegistry()->end());
|
||||
}
|
||||
registry_mutex_.unlock();
|
||||
return std::make_pair(found, &(it->second));
|
||||
}
|
||||
};
|
||||
|
||||
std::set<std::string> & types();
|
||||
template<typename T, class... Args>
|
||||
std::mutex Registry<T, Args...>::registry_mutex_;
|
||||
|
||||
template <typename T>
|
||||
Registry<T>::proxy::proxy() { types().insert(typeid(T).name());}
|
||||
|
||||
|
||||
struct Registry2
|
||||
{
|
||||
static std::vector<std::string> entries;
|
||||
Registry2(std::string name) { entries.push_back(name); }
|
||||
//*****************************************************************************
|
||||
// Implementation details of REGISTER() macro.
|
||||
//
|
||||
// Creates uniquely named traits class and functions which forces the
|
||||
// instantiation of a TypeRegisterer class with a static member doing the
|
||||
// actual registration in the registry. Note that TypeRegisterer being a
|
||||
// template, there is no violation of the One Definition Rule. The use of
|
||||
// Trait class is way to pass back information from the class where the
|
||||
// macro is called, to the definition of TypeRegisterer static member.
|
||||
// This works only because the Trait functions do not reference any other
|
||||
// static variable, or it would create an initialization order fiasco.
|
||||
//*****************************************************************************
|
||||
template<typename Trait, typename base_type, typename derived_type,
|
||||
typename... Args>
|
||||
struct TypeRegisterer {
|
||||
static const typename Registry<base_type, Args...>::Registerer instance;
|
||||
};
|
||||
|
||||
#define TILE(cls) temp_##cls; static Registry2 tile_##cls(cls); class cls
|
||||
template<typename Trait, typename base_type, typename derived_type,
|
||||
typename... Args>
|
||||
const typename Registry<base_type, Args...>::Registerer
|
||||
TypeRegisterer<Trait, base_type, derived_type, Args...>::instance(
|
||||
[](Args... args) { return new derived_type(args...); },
|
||||
Trait::key(), Trait::file(), Trait::line());
|
||||
|
||||
#define CONCAT_TOKENS(x, y) x##y
|
||||
#define STRINGIFY(x) #x
|
||||
|
||||
#define REGISTER_AT(LINE, KEY, TYPE, ARGS...) \
|
||||
friend class ::factory::Registry<TYPE, ##ARGS>; \
|
||||
struct CONCAT_TOKENS(_xd_Trait, LINE) { \
|
||||
static const char *key() { return KEY; } \
|
||||
static const char *file() { return __FILE__; } \
|
||||
static const char *line() { return STRINGIFY(LINE); } \
|
||||
}; \
|
||||
const void *CONCAT_TOKENS(_xd_unused, LINE)() const { \
|
||||
return &::factory::TypeRegisterer<CONCAT_TOKENS(_xd_Trait, LINE), TYPE, \
|
||||
std::decay<decltype(*this)>::type, \
|
||||
##ARGS>::instance; \
|
||||
} \
|
||||
static const char *_xd_key(const TYPE *, std::function<void(ARGS)>) { \
|
||||
return KEY; \
|
||||
} \
|
||||
static_assert(true, "") // enforce ; at EOL
|
||||
|
||||
class TILE(Stone)
|
||||
{
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -1,10 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "Color3.hpp"
|
||||
#include <Color4.hpp>
|
||||
#include <Colors.hpp>
|
||||
#include <Core/Data.hpp>
|
||||
#include <Core/Registry.hpp>
|
||||
#include <functional>
|
||||
#include <Core/Interfaces.hpp>
|
||||
#include <J3ML/LinearAlgebra/Vector2i.hpp>
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
using J3ML::LinearAlgebra::Vector2i;
|
||||
|
||||
enum TileFlags : uint16_t
|
||||
{
|
||||
SOLID,
|
||||
NONSOLID = !SOLID,
|
||||
|
||||
};
|
||||
|
||||
|
||||
// Use this for space-efficiency and convert to Color3 for doing color manipulations.
|
||||
@@ -16,4 +30,114 @@ namespace CaveGame::Core
|
||||
uint8_t g;
|
||||
};
|
||||
|
||||
|
||||
class TileGroup {};
|
||||
|
||||
class Tile
|
||||
{
|
||||
public:
|
||||
TileID numeric_id;
|
||||
std::string mnemonic_id;
|
||||
Color4 base_color;
|
||||
std::vector<Color4> color_pallet;
|
||||
bool collides;
|
||||
bool does_random_ticc;
|
||||
public:
|
||||
|
||||
Tile(TileID numeric, const std::string& name, Color4 color)
|
||||
: numeric_id(numeric), mnemonic_id(name), base_color(color)
|
||||
{}
|
||||
virtual TileID NumericID() const { return numeric_id; };
|
||||
virtual std::string MnemonicID() const { return mnemonic_id; };
|
||||
bool DoesRandomTicc() const { return does_random_ticc; }
|
||||
|
||||
|
||||
virtual void Draw(TileState state, int x, int y) {}
|
||||
virtual void ForcedTicc(ITileMap* world, TileState state, int x, int y) {}
|
||||
virtual void RandomTicc(ITileMap* world, TileState state, int x, int y) {}
|
||||
|
||||
|
||||
template <TileID TDecaysTo>
|
||||
void DecayTo(ITileMap *world, TileState state, int x, int y)
|
||||
{
|
||||
world->SetTile(x, y, TDecaysTo);
|
||||
}
|
||||
|
||||
void DecayTo(ITileMap *world, TileState state, int x, int y, TileID TDecaysTo)
|
||||
{
|
||||
world->SetTile(x, y, TDecaysTo);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
class SoilTile : public Tile
|
||||
{
|
||||
public:
|
||||
TileID decays_to;
|
||||
SoilTile(TileID numeric, const std::string& name, Color4 color, TileID decays_target)
|
||||
: Tile(numeric, name, color)
|
||||
{
|
||||
decays_to = decays_target;
|
||||
does_random_ticc = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <TileID TDecaysTo>
|
||||
void CheckSuffocate(ITileMap *world, TileState state, int x, int y)
|
||||
{
|
||||
if (!world->HasAdjacentOrDiagonalAirBlock(x, y))
|
||||
DecayTo(world, state, x, y, TDecaysTo);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void RandomTicc(ITileMap *world, TileState state, int x, int y) override
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//void CanGrowGrass
|
||||
};
|
||||
|
||||
class DirtTile : public SoilTile
|
||||
{
|
||||
void RandomTicc(CaveGame::Core::ITileMap *world, CaveGame::Core::TileState state, int x, int y) override
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
class GrassyTile : public SoilTile
|
||||
{
|
||||
public:
|
||||
GrassyTile(TileID numeric, const std::string& name, Color4 color)
|
||||
: SoilTile(numeric, name, color, TileID::DIRT)
|
||||
{
|
||||
does_random_ticc = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class GasTile : public Tile
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
|
||||
// TODO: Constexpr-qualify Color4
|
||||
|
||||
|
||||
static std::vector<Tile*> registered_tiles;
|
||||
|
||||
static void RegisterTile(Tile* data);
|
||||
|
||||
static Tile* GetByNumeric(TileID id);
|
||||
|
||||
static Tile* GetByName(const std::string& name);
|
||||
|
||||
void Test();
|
||||
|
||||
}
|
7
Core/include/Core/TileList.hpp
Normal file
7
Core/include/Core/TileList.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
|
||||
}
|
@@ -2,6 +2,7 @@
|
||||
#include "Chunk.hpp"
|
||||
#include "Generator.hpp"
|
||||
#include "J3ML/LinearAlgebra/Vector2i.hpp"
|
||||
#include "Interfaces.hpp"
|
||||
#include <unordered_map>
|
||||
#include <filesystem>
|
||||
|
||||
@@ -22,7 +23,7 @@ struct std::hash<Vector2>
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
|
||||
class World {
|
||||
class World : public ITileMap {
|
||||
public:
|
||||
constexpr static uint RandomTileTickCoefficient = 100;
|
||||
World() = default;
|
||||
@@ -31,9 +32,13 @@ namespace CaveGame::Core
|
||||
explicit World(const std::string &worldName, int seed = 0, bool overwrite = false);
|
||||
|
||||
|
||||
TileID GetTile(int x, int y) const;
|
||||
TileID GetTile(int x, int y) const override;
|
||||
|
||||
void SetTile(int x, int y, TileID t);
|
||||
void SetTile(int x, int y, TileID t) override;
|
||||
|
||||
TileState GetTileState(int x, int y) const override { return 0; /* TODO: Implement tile state field */ }
|
||||
|
||||
void SetTileState(int x, int y, TileState state) { /* TODO: Implement tile state field */ }
|
||||
|
||||
// TODO: Doesn't really belong here.
|
||||
Vector2 ToUnitDirection(float rotation);
|
||||
@@ -75,18 +80,6 @@ namespace CaveGame::Core
|
||||
private:
|
||||
|
||||
|
||||
bool GrassSpreadable(int x, int y) const;
|
||||
|
||||
bool IsNonSolidTile(int x, int y) const;
|
||||
|
||||
bool GrassShouldSuffocate(int x, int y) const;
|
||||
|
||||
void GrassRandomTicc(int wx, int wy);
|
||||
|
||||
bool IsSolidTile(int x, int y) const;
|
||||
|
||||
bool HasAdjacentOrDiagonalAirBlock(int x, int y) const;
|
||||
|
||||
bool HasAdjacentAirBlock(int x, int y) const;
|
||||
};
|
||||
}
|
116
Core/src/Core/Interfaces.cpp
Normal file
116
Core/src/Core/Interfaces.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
#include <Core/Interfaces.hpp>
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
bool ITileMap::IsNonSolidTile(int x, int y) const
|
||||
{
|
||||
return GetTile(x, y) == TileID::AIR;
|
||||
}
|
||||
|
||||
bool ITileMap::IsSolidTile(int x, int y) const
|
||||
{
|
||||
return !IsNonSolidTile(x, y);
|
||||
}
|
||||
|
||||
bool ITileMap::HasAdjacentAirBlock(int x, int y) const
|
||||
{
|
||||
if (IsNonSolidTile(x+1, y) || IsNonSolidTile(x-1, y) || IsNonSolidTile(x, y+1) || IsNonSolidTile(x, y-1))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ITileMap::HasAdjacentOrDiagonalAirBlock(int x, int y) const
|
||||
{
|
||||
if (IsNonSolidTile(x+1, y) || IsNonSolidTile(x-1, y) || IsNonSolidTile(x, y+1) || IsNonSolidTile(x, y-1) ||
|
||||
IsNonSolidTile(x+1, y-1) || IsNonSolidTile(x-1, y+1) || IsNonSolidTile(x+1, y+1) || IsNonSolidTile(x-1, y-1))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ITileMap::GrassSpreadable(int x, int y) const
|
||||
{
|
||||
if (GetTile(x, y) == TileID::DIRT)
|
||||
{
|
||||
if (HasAdjacentOrDiagonalAirBlock(x, y))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
bool ITileMap::GrassShouldSuffocate(int x, int y) const
|
||||
{
|
||||
//if (IsSolidTile(x+1, y) && IsSolidTile(x-1, y) && IsSolidTile(x, y+1) && IsSolidTile(x, y-1) &&
|
||||
//IsSolidTile(x+1, y+1) && IsSolidTile(x-1, y+1) && IsSolidTile(x+1, y-1) && IsSolidTile(x-1, y-1))
|
||||
if (!HasAdjacentOrDiagonalAirBlock(x, y))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void ITileMap::GrassRandomTicc(int wx, int wy)
|
||||
{
|
||||
if (GrassShouldSuffocate(wx, wy))
|
||||
{
|
||||
SetTile(wx, wy, TileID::DIRT);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check adjacent blocks
|
||||
if (GrassSpreadable(wx, wy+1))
|
||||
{
|
||||
SetTile(wx, wy+1, TileID::GRASS);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (GrassSpreadable(wx, wy-1))
|
||||
{
|
||||
SetTile(wx, wy-1, TileID::GRASS);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (GrassSpreadable(wx+1, wy))
|
||||
{
|
||||
SetTile(wx+1, wy, TileID::GRASS);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (GrassSpreadable(wx-1, wy))
|
||||
{
|
||||
SetTile(wx-1, wy, TileID::GRASS);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Check diagonal blocks.
|
||||
if (GrassSpreadable(wx+1, wy-1))
|
||||
{
|
||||
SetTile(wx+1, wy-1, TileID::GRASS);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (GrassSpreadable(wx-1, wy-1))
|
||||
{
|
||||
SetTile(wx-1, wy-1, TileID::GRASS);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (GrassSpreadable(wx+1, wy+1))
|
||||
{
|
||||
SetTile(wx+1, wy+1, TileID::GRASS);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (GrassSpreadable(wx-1, wy+1))
|
||||
{
|
||||
SetTile(wx-1, wy+1, TileID::GRASS);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -1 +1,35 @@
|
||||
#include <Core/Tile.hpp>
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
|
||||
void Test() {
|
||||
RegisterTile(new Tile(TileID::AIR, "air", {0,0,0,0}));
|
||||
RegisterTile(new SoilTile(TileID::DIRT, "dirt", Colors::Green));
|
||||
RegisterTile(new SoilTile(TileID::MUD, "mud", Colors::Green));
|
||||
RegisterTile(new GrassyTile(TileID::STONE, "stone", Colors::Green));
|
||||
RegisterTile(new GrassyTile(TileID::GRASS, "grass", Colors::Green));
|
||||
|
||||
}
|
||||
|
||||
Tile *GetByName(const std::string &name) {
|
||||
// TODO: Optimize with additional mapping!!
|
||||
for(auto& tile : registered_tiles)
|
||||
if (tile->mnemonic_id == name)
|
||||
return tile;
|
||||
|
||||
throw std::runtime_error("Invalid mnemonic ID!");
|
||||
}
|
||||
|
||||
Tile *GetByNumeric(TileID id) {
|
||||
// TODO: Optimize with additional mapping!!
|
||||
for(auto& tile : registered_tiles)
|
||||
if (tile->numeric_id == id)
|
||||
return tile;
|
||||
throw std::runtime_error("Invalid numeric ID!");
|
||||
}
|
||||
|
||||
void RegisterTile(Tile *data) {
|
||||
registered_tiles.push_back(data);
|
||||
}
|
||||
}
|
@@ -137,117 +137,6 @@ namespace CaveGame::Core
|
||||
}
|
||||
|
||||
|
||||
bool World::IsNonSolidTile(int x, int y) const
|
||||
{
|
||||
return GetTile(x, y) == TileID::AIR;
|
||||
}
|
||||
|
||||
bool World::IsSolidTile(int x, int y) const
|
||||
{
|
||||
return !IsNonSolidTile(x, y);
|
||||
}
|
||||
|
||||
bool World::HasAdjacentAirBlock(int x, int y) const
|
||||
{
|
||||
if (IsNonSolidTile(x+1, y) || IsNonSolidTile(x-1, y) || IsNonSolidTile(x, y+1) || IsNonSolidTile(x, y-1))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool World::HasAdjacentOrDiagonalAirBlock(int x, int y) const
|
||||
{
|
||||
if (IsNonSolidTile(x+1, y) || IsNonSolidTile(x-1, y) || IsNonSolidTile(x, y+1) || IsNonSolidTile(x, y-1) ||
|
||||
IsNonSolidTile(x+1, y-1) || IsNonSolidTile(x-1, y+1) || IsNonSolidTile(x+1, y+1) || IsNonSolidTile(x-1, y-1))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool World::GrassSpreadable(int x, int y) const
|
||||
{
|
||||
if (GetTile(x, y) == TileID::DIRT)
|
||||
{
|
||||
if (HasAdjacentOrDiagonalAirBlock(x, y))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
bool World::GrassShouldSuffocate(int x, int y) const
|
||||
{
|
||||
//if (IsSolidTile(x+1, y) && IsSolidTile(x-1, y) && IsSolidTile(x, y+1) && IsSolidTile(x, y-1) &&
|
||||
//IsSolidTile(x+1, y+1) && IsSolidTile(x-1, y+1) && IsSolidTile(x+1, y-1) && IsSolidTile(x-1, y-1))
|
||||
if (!HasAdjacentOrDiagonalAirBlock(x, y))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void World::GrassRandomTicc(int wx, int wy)
|
||||
{
|
||||
if (GrassShouldSuffocate(wx, wy))
|
||||
{
|
||||
SetTile(wx, wy, TileID::DIRT);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check adjacent blocks
|
||||
if (GrassSpreadable(wx, wy+1))
|
||||
{
|
||||
SetTile(wx, wy+1, TileID::GRASS);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (GrassSpreadable(wx, wy-1))
|
||||
{
|
||||
SetTile(wx, wy-1, TileID::GRASS);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (GrassSpreadable(wx+1, wy))
|
||||
{
|
||||
SetTile(wx+1, wy, TileID::GRASS);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (GrassSpreadable(wx-1, wy))
|
||||
{
|
||||
SetTile(wx-1, wy, TileID::GRASS);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Check diagonal blocks.
|
||||
if (GrassSpreadable(wx+1, wy-1))
|
||||
{
|
||||
SetTile(wx+1, wy-1, TileID::GRASS);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (GrassSpreadable(wx-1, wy-1))
|
||||
{
|
||||
SetTile(wx-1, wy-1, TileID::GRASS);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (GrassSpreadable(wx+1, wy+1))
|
||||
{
|
||||
SetTile(wx+1, wy+1, TileID::GRASS);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (GrassSpreadable(wx-1, wy+1))
|
||||
{
|
||||
SetTile(wx-1, wy+1, TileID::GRASS);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void World::DoRandomTileTicks() {
|
||||
// Every frame, In each loaded chunk, perform a RandomTileTick on N tiles.
|
||||
@@ -265,11 +154,10 @@ namespace CaveGame::Core
|
||||
|
||||
TileID at = chunk.GetTile(x, y);
|
||||
|
||||
if (at == TileID::GRASS)
|
||||
GrassRandomTicc(wx, wy);
|
||||
|
||||
//if (at == TileID::SAND)
|
||||
|
||||
Tile* base = GetByNumeric(at);
|
||||
if (base->DoesRandomTicc())
|
||||
base->RandomTicc(this, 0, wx, wy);
|
||||
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user