Base refactor

This commit is contained in:
2023-11-14 09:16:48 -06:00
parent 214082d00f
commit 1817dcaf3a
410 changed files with 749 additions and 20823 deletions

View File

@ -9,13 +9,13 @@ set(
CACHE INTERNAL ${DAWN_CACHE_TARGET}
)
# Change what we are building. Pulled from the cmake/targets dir.
if(DEFINED DAWN_BUILDING)
add_subdirectory(${DAWN_BUILDING})
endif()
# Build Project
add_executable(${DAWN_TARGET_NAME})
# Definitions
add_subdirectory(dawndefs)
# Change what we are building. Pulled from the cmake/targets dir.
# if(DEFINED DAWN_BUILDING)
# add_subdirectory(${DAWN_BUILDING})
# endif()
# Validate game project includes the target name
if(DEFINED DAWN_TARGET_NAME)
@ -25,8 +25,8 @@ if(DEFINED DAWN_TARGET_NAME)
# Compile entry targets
if(DAWN_TARGET_WIN32)
add_subdirectory(dawnwin32)
elseif(DAWN_TARGET_LINUX64)
add_subdirectory(dawnlinux64)
elseif(DAWN_TARGET_LINUX)
add_subdirectory(dawnlinux)
elseif(DAWN_TARGET_OSX)
add_subdirectory(dawnosx)
elseif(DAWN_TARGET_VITA)
@ -55,7 +55,7 @@ if(DEFINED DAWN_TARGET_NAME)
endif()
if(DAWN_TARGET_ARCHIVE)
add_subdirectory(dawnarchiveasset)
# add_subdirectory(dawnarchiveasset)
else()
add_subdirectory(dawnfileasset)
endif()

View File

@ -18,26 +18,26 @@ target_include_directories(${DAWN_TARGET_NAME}
# Subdirs
add_subdirectory(assert)
add_subdirectory(asset)
add_subdirectory(audio)
add_subdirectory(display)
# add_subdirectory(asset)
# add_subdirectory(audio)
# add_subdirectory(display)
add_subdirectory(game)
add_subdirectory(games)
add_subdirectory(input)
add_subdirectory(locale)
add_subdirectory(prefab)
add_subdirectory(physics)
add_subdirectory(save)
add_subdirectory(scene)
add_subdirectory(state)
add_subdirectory(time)
add_subdirectory(util)
# add_subdirectory(games)
# add_subdirectory(input)
# add_subdirectory(locale)
# add_subdirectory(prefab)
# add_subdirectory(physics)
# add_subdirectory(save)
# add_subdirectory(scene)
# add_subdirectory(state)
# add_subdirectory(time)
# add_subdirectory(util)
# Definitions
target_compile_definitions(${DAWN_TARGET_NAME}
PUBLIC
DAWN_DEBUG_BUILD=${DAWN_DEBUG_BUILD}
)
# target_compile_definitions(${DAWN_TARGET_NAME}
# PUBLIC
# DAWN_DEBUG_BUILD=${DAWN_DEBUG_BUILD}
# )
# Tests
target_link_libraries(dawntests

View File

@ -1,16 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "AssetManager.hpp"
using namespace Dawn;
Asset::Asset(const std::string name) : name(name) {
assertTrue(name.size() > 0, "Asset::Asset: Name cannot be empty");
}
Asset::~Asset() {
this->loaded = false;
}

View File

@ -1,44 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "state/StateEvent.hpp"
namespace Dawn {
class AssetManager;
class Asset {
public:
const std::string name;
bool_t loaded = false;
StateEvent<> eventLoaded;
/**
* Create an abstract Asset object.
*
* @param name Name of the asset.
*/
Asset(const std::string name);
/**
* Virtual function that will be called by the asset manager on a
* synchronous basis. This will only trigger if the blocks are false and
* the loaded is also false.
*/
virtual void updateSync() = 0;
/**
* Virtual function called by the asset manager asynchronously every tick.
* This will only trigger if blocks are false and the loaded state is also
* false.
*/
virtual void updateAsync() = 0;
/**
* Dispose the asset item.
*/
virtual ~Asset();
};
}

View File

@ -1,92 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "AssetManager.hpp"
using namespace Dawn;
void AssetManager::init() {
}
void AssetManager::update() {
this->syncTick();
}
void AssetManager::queueLoad(const std::vector<std::shared_ptr<Asset>> assets) {
assetsToLoad.insert(this->assetsToLoad.end(), assets.begin(), assets.end());
}
void AssetManager::queueLoad(const std::shared_ptr<Asset> &asset) {
this->assetsToLoad.push_back(asset);
}
void AssetManager::queueUnload(
const std::vector<std::shared_ptr<Asset>> &assets
) {
std::cout <<
"Asset list was queued to unload, but is not yet implemented" <<
std::endl;
assetsToUnload.insert(assetsToUnload.end(), assets.begin(), assets.end());
}
void AssetManager::queueUnload(const std::shared_ptr<Asset> &asset) {
std::cout <<
"Asset was queued to unload, but is not yet implemented" <<
std::endl;
this->assetsToUnload.push_back(asset);
}
void AssetManager::queueSwap(
const std::vector<std::shared_ptr<Asset>> &newAssets,
const std::vector<std::shared_ptr<Asset>> &oldAssets
) {
std::vector<std::shared_ptr<Asset>> unload;
std::vector<std::shared_ptr<Asset>> load;
// Determine assets to unload.
std::for_each(oldAssets.begin(), oldAssets.end(), [&](const auto &asset){
auto it = std::find(newAssets.begin(), newAssets.end(), asset);
if(it == newAssets.end()) unload.push_back(asset);
});
// Determine assets to load
std::for_each(newAssets.begin(), newAssets.end(), [&](const auto &asset){
auto it = std::find(oldAssets.begin(), oldAssets.end(), asset);
if(it == oldAssets.end()) load.push_back(asset);
});
this->queueUnload(unload);
this->queueLoad(load);
}
void AssetManager::syncTick() {
std::erase_if(assetsToLoad, [&](auto &asset){
if(asset->loaded) {
asset->eventLoaded.invoke();
return true;
}
asset->updateSync();
asset->updateAsync();//TODO: Make Async
if(asset->loaded) {
asset->eventLoaded.invoke();
return true;
}
return asset->loaded;
});
}
void AssetManager::syncLoad() {
while(this->assetsToLoad.size() > 0) {
this->syncTick();
}
}
AssetManager::~AssetManager() {
this->assets.clear();
}

View File

@ -1,127 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "Asset.hpp"
namespace Dawn {
class AssetManager {
private:
/** List of pointers to assets, mapped by their asset key. */
std::map<std::string, std::shared_ptr<Asset>> assets;
std::vector<std::shared_ptr<Asset>> assetsToLoad;
std::vector<std::shared_ptr<Asset>> assetsToUnload;
public:
/**
* Initializes this asset manager so it can begin accepting assets.
*/
void init();
/**
* Updates the asset manager.
*/
void update();
/**
* Queue a loading of a list of assets. Does not actually begin loading.
*
* @param assets Assets to load.
*/
void queueLoad(const std::vector<std::shared_ptr<Asset>> assets);
/**
* Queue a loading of a single asset. Does not actually begin loading.
*
* @param asset Asset to load.
*/
void queueLoad(const std::shared_ptr<Asset> &asset);
/**
* Takes a list of lists to queue to unload. Does not immediately unload.
*
* @param assets Assets to unload.
*/
void queueUnload(const std::vector<std::shared_ptr<Asset>> &assets);
/**
* Takes a single asset to queue to unload. Does not immediately unload.
*
* @param assets Assets to unload.
*/
void queueUnload(const std::shared_ptr<Asset> &assets);
/**
* Queues load and unload based on the difference between two sets of
* assets. This is mostly used to perform a scene change.
*
* @param newAssets New list of assets to maintain.
* @param oldAssets Old list of assets to no longer maintain.
*/
void queueSwap(
const std::vector<std::shared_ptr<Asset>> &newAssets,
const std::vector<std::shared_ptr<Asset>> &oldAssets
);
/**
* Ticks the asset manager, synchronously.
*/
void syncTick();
/**
* Loads everything that isn't loaded, blocks the current thread until
* that has finished.
*/
void syncLoad();
/**
* Creates and queue an asset to load.
*
* @param name Name of the asset to load.
* @return The asset element to be loaded.
*/
template<class T>
std::shared_ptr<T> get(std::string name) {
assertTrue(
name.size() > 0, "AssetManager::get: name must be greater than 0"
);
auto existing = this->assets.find(name);
if(existing != this->assets.end()) {
return dynamic_pointer_cast<T>(existing->second);
}
auto asset = std::make_shared<T>(name);
this->assets[name] = asset;
return asset;
}
/**
* Both gets an asset, and puts it into the load queue.
*
* @param name Name of the asset to load.
* @return The asset element to be loaded.
*/
template<class T>
std::shared_ptr<T> getAndLoad(std::string name) {
auto asset = this->get<T>(name);
this->queueLoad(asset);
return asset;
}
template<class T>
void unload(std::shared_ptr<T> asset) {
assertUnreachable("AssetManager::unload: NOT IMPLEMENTED");
}
void unload(std::string name) {
assertUnreachable("AssetManager::unload: NOT IMPLEMENTED");
}
/**
* Dispose the asset manager, and all attached assets.
*/
~AssetManager();
};
}

View File

@ -1,15 +0,0 @@
# Copyright (c) 2022 Dominic Msters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
Asset.cpp
IAssetLoader.cpp
AssetManager.cpp
)
# Subdirs
add_subdirectory(assets)

View File

@ -1,27 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "IAssetLoader.hpp"
using namespace Dawn;
IAssetLoader::IAssetLoader(const std::string fileName) : fileName(fileName) {
assertTrue(
fileName.size() > 0,
"IAssetLoader::IAssetLoader: fileName must be greater than 0"
);
}
size_t IAssetLoader::setPosition(const size_t position) {
assertTrue(
position >= 0,
"IAssetLoader::setPosition: position must be greater than or equal to 0"
);
this->rewind();
return this->skip(position);
}
IAssetLoader::~IAssetLoader() {
}

View File

@ -1,132 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "assert/assert.hpp"
#define ASSET_LOADER_BUFFER_SIZE 32768
#if !defined(DAWN_ASSET_LOCATION)
#error Asset Archive Location has not been defined.
#endif
namespace Dawn {
class IAssetLoader {
protected:
std::string fileName;
public:
/**
* Create a new asset loader. Asset Loaders can be used to load data from
* a file in a myriad of ways.
*
* @param fileName File name of the asset that is to be loaded.
*/
IAssetLoader(const std::string fileName);
/**
* Platform-centric method to open a file buffer to an asset.
*/
virtual void open() = 0;
/**
* Closes the previously ppened asset.
* @return 0 if successful, otherwise false.
*/
virtual int32_t close() = 0;
/**
* Read bytes from buffer.
* @param buffer Pointer to a ubyte array to buffer data into.
* @param size Length of the data buffer (How many bytes to read).
* @return The count of bytes read.
*/
virtual size_t read(uint8_t *buffer, const size_t size) = 0;
/**
* Get the size of the asset.
* @return The size of the asset in bytes.
*/
virtual size_t getSize() = 0;
/**
* Skips the read head forward to a given position.
*
* @param n Count of bytes to progress the read head by.
* @return Count of bytes progressed.
*/
virtual size_t skip(const size_t n) = 0;
/**
* Rewind the read head to the beginning of the file.
*/
virtual void rewind() = 0;
/**
* Sets the absolute position of the read head within the buffer of the
* file.
*
* @param absolutePosition Absolute position to set the read head to.
*/
size_t setPosition(const size_t absolutePosition);
/**
* Returns the current position of the read head.
*
* @return The current read head position.
*/
virtual size_t getPosition() = 0;
/**
* Run a callback for each byte within the asset. The callback will
* receive each byte individually.
*
* @tparam T Type of instance to run callback against.
* @param instance Instance of the object to run the callback against.
* @param callback Callback method on the class to run the callback for.
* @return The count of bytes read.
*/
template<class T>
size_t loadBufferedCallback(
std::function<bool_t(uint8_t n)> callback
) {
uint8_t buffer[1024];
size_t read, length;
int32_t i;
bool_t result;
assertNotNull(callback, "Callback cannot be null.");
// Open the buffer.
this->open();
// Reset length size
length = 0;
// Buffer from input
while((read = this->read(buffer, 1024)) != 0) {
for(i = 0; i < read; i++) {
result = callback(buffer[i]);
if(!result) {
length += i;
break;
}
}
if(!result) break;
length += read;
}
// Close the buffer
this->close();
return length;
}
/**
* Cleanup the asset loader.
*/
virtual ~IAssetLoader();
};
}

View File

@ -1,79 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "AudioAsset.hpp"
using namespace Dawn;
AudioAsset::AudioAsset(const std::string name) :
Asset(name),
loader(name + ".audio")
{
}
void AudioAsset::updateSync() {
if(this->state != AUDIO_ASSET_LOAD_STATE_DATA_READ) return;
// THIS WILL DEFINITELY CHANGE
this->state = AUDIO_ASSET_LOAD_STATE_COMPLETE;
this->loaded = true;
}
void AudioAsset::updateAsync() {
if(this->state != AUDIO_ASSET_LOAD_STATE_INITIAL) return;
char *start, *end;
char buffer[1024];
// Open asset for reading
this->state = AUDIO_ASSET_LOAD_STATE_OPENING;
this->loader.open();
// Parse header data.
this->state = AUDIO_ASSET_LOAD_STATE_READING_HEADER;
this->loader.read((uint8_t *)buffer, 1024);
// Channel count
this->state = AUDIO_ASSET_LOAD_STATE_READING_CHANNEL_COUNT;
start = buffer;
end = strchr(start, '|');
*end = '\0';
this->channelCount = atoi(start);
assertTrue(this->channelCount > 0, "Channel count must be greater than 0");
// Sample Rate
this->state = AUDIO_ASSET_LOAD_STATE_READING_SAMPLE_RATE;
start = end + 1;
end = strchr(start, '|');
*end = '\0';
this->sampleRate = (uint32_t)strtoul(start, NULL, 10);
assertTrue(this->sampleRate > 0, "Sample rate must be greater than 0");
// Number of samples per channel
this->state = AUDIO_ASSET_LOAD_STATE_READING_SAMPLES_PER_CHANNEL;
start = end + 1;
end = strchr(start, '|');
*end = '\0';
this->samplesPerChannel = atoi(start);
assertTrue(
this->samplesPerChannel > 0,
"Samples per channel must be greater than 0"
);
// Total Data Length
this->state = AUDIO_ASSET_LOAD_STATE_READING_DATA_LENGTH;
start = end + 1;
end = strchr(start, '|');
*end = '\0';
this->bufferSize = (size_t)atoll(start);
assertTrue(this->bufferSize > 0, "Buffer size must be greater than 0");
// Determine frame size
this->frameSize = sizeof(int16_t) * this->channelCount;
// Indicated start of data
this->state = AUDIO_ASSET_LOAD_STATE_DATA_READ;
this->bufferStart = end - buffer;
}

View File

@ -1,45 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "../Asset.hpp"
#include "asset/AssetLoader.hpp"
namespace Dawn {
class AudioSource;
enum AudioAssetLoadState {
AUDIO_ASSET_LOAD_STATE_INITIAL,
AUDIO_ASSET_LOAD_STATE_OPENING,
AUDIO_ASSET_LOAD_STATE_READING_HEADER,
AUDIO_ASSET_LOAD_STATE_READING_CHANNEL_COUNT,
AUDIO_ASSET_LOAD_STATE_READING_SAMPLE_RATE,
AUDIO_ASSET_LOAD_STATE_READING_SAMPLES_PER_CHANNEL,
AUDIO_ASSET_LOAD_STATE_READING_DATA_LENGTH,
AUDIO_ASSET_LOAD_STATE_DATA_READ,
AUDIO_ASSET_LOAD_STATE_COMPLETE
};
class AudioAsset : public Asset {
protected:
AssetLoader loader;
enum AudioAssetLoadState state = AUDIO_ASSET_LOAD_STATE_INITIAL;
int32_t channelCount;
uint32_t sampleRate;
int32_t samplesPerChannel;
size_t bufferSize;
size_t bufferStart;
size_t frameSize;
public:
AudioAsset(const std::string name);
void updateSync() override;
void updateAsync() override;
friend class AudioSource;
};
}

View File

@ -1,13 +0,0 @@
# Copyright (c) 2022 Dominic Msters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
AudioAsset.cpp
LanguageAsset.cpp
TextureAsset.cpp
TrueTypeAsset.cpp
)

View File

@ -1,86 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "LanguageAsset.hpp"
using namespace Dawn;
LanguageAsset::LanguageAsset(const std::string name) :
Asset(name),
loader(name + ".language")
{
}
void LanguageAsset::updateSync() {
}
void LanguageAsset::updateAsync() {
assertTrue(
this->state == LANGUAGE_ASSET_LOAD_STATE_INITIAL,
"State must be initial"
);
// Open Asset
this->state = LANGUAGE_ASSET_LOAD_STATE_OPENING;
this->loader.open();
// Get Length
size_t len = this->loader.getSize();
// Create buffer
this->state = LANGUAGE_ASSET_LOAD_STATE_CREATING_BUFFER;
size_t position = 0;
// Loop over CSV
while(position < len) {
this->loader.read(buffer, 1024);
// Get strings
uint8_t *keyStart = buffer;
uint8_t *keyEnd = (uint8_t*)strchr((char*)keyStart, '|');
*keyEnd = '\0';
uint8_t *valueStart = keyEnd + 1;
uint8_t *valueEnd = (uint8_t*)strchr((char*)valueStart, '|');
// Load value positions
struct AssetLanguageValue value;
value.begin = position + (size_t)(valueStart - buffer);
value.length = (size_t)(valueEnd - valueStart);
// Prepare for next string.
position = position + (size_t)(valueEnd - buffer + 1);
this->loader.setPosition(position);
// Store strings.
std::string key((char *)keyStart);
this->values[key] = value;
}
this->state = LANGUAGE_ASSET_LOAD_STATE_READY_TO_READ;
this->loaded = true;
}
std::string LanguageAsset::getValue(const std::string key) {
assertTrue(
this->state == LANGUAGE_ASSET_LOAD_STATE_READY_TO_READ,
"State must be LANGUAGE_ASSET_LOAD_STATE_READY_TO_READ"
);
assertMapHasKey(this->values, key, "Key does not exist");
assertTrue(
this->values[key].begin >= 0 && this->values[key].length > 0,
"Value is invalid"
);
// Get the positions
struct AssetLanguageValue value = this->values[key];
this->loader.setPosition(value.begin);
this->loader.read(buffer, value.length);
buffer[value.length] = '\0';
return std::string((char *)buffer, value.length + 1);
}

View File

@ -1,36 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "../Asset.hpp"
#include "asset/AssetLoader.hpp"
namespace Dawn {
struct AssetLanguageValue {
size_t begin;
size_t length;
};
enum LangageAssetLoadState {
LANGUAGE_ASSET_LOAD_STATE_INITIAL,
LANGUAGE_ASSET_LOAD_STATE_OPENING,
LANGUAGE_ASSET_LOAD_STATE_CREATING_BUFFER,
LANGUAGE_ASSET_LOAD_STATE_READY_TO_READ
};
class LanguageAsset : public Asset {
protected:
AssetLoader loader;
enum LangageAssetLoadState state = LANGUAGE_ASSET_LOAD_STATE_INITIAL;
std::map<std::string, struct AssetLanguageValue> values;
uint8_t buffer[1024];
public:
LanguageAsset(const std::string name);
void updateSync() override;
void updateAsync() override;
std::string getValue(const std::string key);
};
}

View File

@ -1,136 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "TextureAsset.hpp"
#include "util/memory.hpp"
using namespace Dawn;
TextureAsset::TextureAsset(const std::string name) :
Asset(name),
loader(name + ".texture"),
texture()
{
}
void TextureAsset::updateSync() {
if(this->state != TEXTURE_ASSET_LOAD_STATE_SYNC_HANDOFF) return;
this->state = TEXTURE_ASSET_LOAD_STATE_BUFFERING_TEXTURE;
this->texture.setSize(
this->width, this->height, this->format, TEXTURE_DATA_FORMAT_UNSIGNED_BYTE
);
this->texture.buffer(this->colors);
this->texture.wrapModeX = this->wrapModeX;
this->texture.wrapModeY = this->wrapModeY;
this->texture.filterModeMin = this->filterModeMin;
this->texture.filterModeMag = this->filterModeMag;
this->state = TEXTURE_ASSET_LOAD_STATE_COMPLETE;
this->loaded = true;
}
void TextureAsset::updateAsync() {
if(this->state != TEXTURE_ASSET_LOAD_STATE_INITIAL) return;
this->state = TEXTURE_ASSET_LOAD_STATE_OPENING;
this->loader.open();
this->buffer = memoryAllocate<uint8_t>(this->loader.getSize());
this->loader.read(this->buffer, this->loader.getSize());
this->loader.close();
this->state = TEXTURE_ASSET_LOAD_STATE_PARSING_HEADER;
// Parse header data.
char integer[256];
size_t j = 0, i = 0;
enum TextureAssetHeaderParseState parseState =
TEXTURE_ASSET_HEADER_PARSE_STATE_VERSION
;
while(true) {
if(parseState == TEXTURE_ASSET_HEADER_PARSE_STATE_END) break;
auto c = this->buffer[i++];
if(c != '|') {
integer[j++] = c;
continue;
}
integer[j] = '\0';
switch(parseState) {
case TEXTURE_ASSET_HEADER_PARSE_STATE_VERSION: {
auto compared = strcmp(integer, "DT_2.00");
assertTrue(compared == 0, "Invalid version");
j = 0;
parseState = TEXTURE_ASSET_HEADER_PARSE_STATE_WIDTH;
break;
}
case TEXTURE_ASSET_HEADER_PARSE_STATE_WIDTH: {
this->width = atoi(integer);
assertTrue(this->width > 0, "Invalid width");
j = 0;
parseState = TEXTURE_ASSET_HEADER_PARSE_STATE_HEIGHT;
break;
}
case TEXTURE_ASSET_HEADER_PARSE_STATE_HEIGHT: {
this->height = atoi(integer);
assertTrue(this->height > 0, "Invalid height");
j = 0;
parseState = TEXTURE_ASSET_HEADER_PARSE_STATE_FORMAT;
break;
}
case TEXTURE_ASSET_HEADER_PARSE_STATE_FORMAT: {
this->format = (enum TextureFormat)atoi(integer);
j = 0;
parseState = TEXTURE_ASSET_HEADER_PARSE_STATE_WRAP_MODE_X;
break;
}
case TEXTURE_ASSET_HEADER_PARSE_STATE_WRAP_MODE_X: {
this->wrapModeX = (enum TextureWrapMode)atoi(integer);
j = 0;
parseState = TEXTURE_ASSET_HEADER_PARSE_STATE_WRAP_MODE_Y;
break;
}
case TEXTURE_ASSET_HEADER_PARSE_STATE_WRAP_MODE_Y: {
this->wrapModeY = (enum TextureWrapMode)atoi(integer);
j = 0;
parseState = TEXTURE_ASSET_HEADER_PARSE_STATE_FILTER_MODE_MIN;
break;
}
case TEXTURE_ASSET_HEADER_PARSE_STATE_FILTER_MODE_MIN: {
this->filterModeMin = (enum TextureFilterMode)atoi(integer);
j = 0;
parseState = TEXTURE_ASSET_HEADER_PARSE_STATE_FILTER_MODE_MAG;
break;
}
case TEXTURE_ASSET_HEADER_PARSE_STATE_FILTER_MODE_MAG: {
this->filterModeMag = (enum TextureFilterMode)atoi(integer);
j = 0;
parseState = TEXTURE_ASSET_HEADER_PARSE_STATE_END;
break;
}
default:
assertUnreachable("Invalid parse state");
}
}
this->colors = (uint8_t*)((void *)(this->buffer + i));
this->state = TEXTURE_ASSET_LOAD_STATE_SYNC_HANDOFF;
}
TextureAsset::~TextureAsset() {
if(this->buffer != nullptr) {
memoryFree(this->buffer);
this->buffer = nullptr;
}
}

View File

@ -1,66 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "../Asset.hpp"
#include "asset/AssetLoader.hpp"
#include "display/Texture.hpp"
namespace Dawn {
enum TextureAssetHeaderParseState {
TEXTURE_ASSET_HEADER_PARSE_STATE_VERSION,
TEXTURE_ASSET_HEADER_PARSE_STATE_WIDTH,
TEXTURE_ASSET_HEADER_PARSE_STATE_HEIGHT,
TEXTURE_ASSET_HEADER_PARSE_STATE_FORMAT,
TEXTURE_ASSET_HEADER_PARSE_STATE_WRAP_MODE_X,
TEXTURE_ASSET_HEADER_PARSE_STATE_WRAP_MODE_Y,
TEXTURE_ASSET_HEADER_PARSE_STATE_FILTER_MODE_MIN,
TEXTURE_ASSET_HEADER_PARSE_STATE_FILTER_MODE_MAG,
TEXTURE_ASSET_HEADER_PARSE_STATE_END
};
enum TextureAssetLoadState {
TEXTURE_ASSET_LOAD_STATE_INITIAL,
TEXTURE_ASSET_LOAD_STATE_OPENING,
TEXTURE_ASSET_LOAD_STATE_PARSING_HEADER,
TEXTURE_ASSET_LOAD_STATE_SYNC_HANDOFF,
TEXTURE_ASSET_LOAD_STATE_BUFFERING_TEXTURE,
TEXTURE_ASSET_LOAD_STATE_COMPLETE,
};
class TextureAsset : public Asset {
protected:
AssetLoader loader;
uint8_t *buffer = nullptr;
int32_t width = -1, height = -1;
uint8_t *colors;
enum TextureAssetLoadState state = TEXTURE_ASSET_LOAD_STATE_INITIAL;
enum TextureFormat format;
enum TextureWrapMode wrapModeX;
enum TextureWrapMode wrapModeY;
enum TextureFilterMode filterModeMin;
enum TextureFilterMode filterModeMag;
public:
Texture texture;
/**
* Constructs a texture asset loader. You should instead use the parent
* asset managers' abstracted load method
*
* @param name File name asset to load, omitting the extension.
*/
TextureAsset(const std::string name);
void updateSync() override;
void updateAsync() override;
/**
* Dispose / Cleanup the texture asset. Will also dispose the underlying
* texture itself.
*/
~TextureAsset();
};
}

View File

@ -1,239 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "TrueTypeAsset.hpp"
#include "asset/AssetManager.hpp"
#include "util/memory.hpp"
using namespace Dawn;
TrueTypeAsset::TrueTypeAsset(const std::string name) :
Asset(name),
loader(name + ".truetype")
{
this->locks.onLockRemoved = [&](usagelockid_t id){
auto texture = this->textureByLock[id];
assertNotNull(texture, "Texture cannot be null");
std::vector<usagelockid_t> &lbt = this->locksByTexture[texture];
auto it0 = std::find(lbt.begin(), lbt.end(), id);
assertTrue(it0 != lbt.end(), "Could not remove locksByTexture[texture]");
lbt.erase(it0);
auto it1 = this->textureByLock.find(id);
assertTrue(
it1 != this->textureByLock.end(), "Could not remove textureByLock"
);
this->textureByLock.erase(it1);
if(lbt.empty()) {
auto it2 = locksByTexture.find(texture);
assertTrue(
it2 != locksByTexture.end(), "Could not remove locksByTexture"
);
locksByTexture.erase(it2);
std::erase_if(textureByStyle, [&](const auto &item){
auto const& [key, value] = item;
return value == texture;
});
std::erase(textures, texture);
delete texture;
}
};
}
void TrueTypeAsset::updateSync() {
}
void TrueTypeAsset::updateAsync() {
if(this->state != TRUE_TYPE_ASSET_STATE_INITIAL) return;
this->state = TRUE_TYPE_ASSET_STATE_OPEN;
this->loader.open();
this->state = TRUE_TYPE_ASSET_STATE_VALIDATE_HEADER;
uint8_t buffer[64];
this->loader.rewind();
size_t read = this->loader.read(buffer, sizeof(char) * 6);
assertTrue(read == (6 * sizeof(char)), "Could not read header");
buffer[6] = '\0';
// Confirm "DE_TTF"
assertTrue(
std::string((char *)buffer) == "DE_TTF",
"Header is invalid (Missing DE_TTF)"
);
// Vertical bar
this->loader.read(buffer, 1);
assertTrue(
buffer[0] == '|',
"Header is invalid (Missing first vertical bar)"
);
// Read version
this->state = TRUE_TYPE_ASSET_STATE_VALIDATE_VERSION;
read = this->loader.read(buffer, sizeof(char) * 5);
assertTrue(buffer[0] == '3', "Version is invalid 3");
assertTrue(buffer[1] == '.', "Version is invalid .");
assertTrue(buffer[2] == '0', "Version is invalid 0(1)");
assertTrue(buffer[3] == '0', "Version is invalid 0(2)");
assertTrue(
buffer[4] == '|',
"Version is invalid (Missing second vertical bar)"
);
// Read the count of font styles / variants.
size_t styleListBegin = this->loader.getPosition();
this->state = TRUE_TYPE_ASSET_STATE_READ_VARIANT_COUNT;
read = this->loader.read(buffer, 64);
assertTrue(read > 0, "Could not read variant count");
// Get position of vertical bar.
size_t i = 0;
while(buffer[i] != '|' && i < 64) i++;
assertTrue(buffer[i] == '|', "Could not find vertical bar");
styleListBegin += i + 1;
buffer[i] = '\0';
int32_t count = atoi((char *)buffer);
assertTrue(count > 0, "Invalid variant count");
// Now begin parsing each font style.
this->state = TRUE_TYPE_ASSET_STATE_READ_VARIANT;
assetStyles.clear();
while(assetStyles.size() != count) {
struct TrueTypeAssetStyle style;
// Buffer
this->loader.setPosition(styleListBegin);
read = this->loader.read(buffer, 32);
assertTrue(read == 32, "Could not read variant");
// Read style
i = 0;
while(buffer[i] != ':' && i < 64) i++;
buffer[i] = '\0';
style.style = atoi((char *)buffer);
styleListBegin += i + 1;
// Buffer
this->loader.setPosition(styleListBegin);
read = this->loader.read(buffer, 32);
assertTrue(read == 32, "Could not read variant style");
// Read length
i = 0;
while(buffer[i] != '|' && i < 64) i++;
buffer[i] = '\0';
styleListBegin += i + 1;
style.dataSize = atol((char *)buffer);
// Push
assetStyles.push_back(style);
}
// Now we are at the first byte of the first style.
this->state = TRUE_TYPE_ASSET_STATE_ADJUSTING_OFFSETS;
std::for_each(
assetStyles.begin(),
assetStyles.end(),
[&](struct TrueTypeAssetStyle &style){
style.dataOffset = styleListBegin;
styleListBegin += style.dataSize;
styleListBegin += 1;
}
);
// Init FreeType
this->state = TRUE_TYPE_ASSET_STATE_INIT_FREETYPE;
int32_t ret = FT_Init_FreeType(&fontLibrary);
assertTrue(ret == 0, "Could not init FreeType");
// Done parsing!
this->state = TRUE_TYPE_ASSET_STATE_READY;
this->loaded = true;
}
usagelockid_t TrueTypeAsset::lock(const struct TrueTypeFaceTextureStyle style) {
assertTrue(this->state == TRUE_TYPE_ASSET_STATE_READY, "Asset is not ready");
// Try and find an existing texture that matches this style
auto it = this->textureByStyle.find(style);
TrueTypeFaceTexture *texture = nullptr;
if(it == this->textureByStyle.end()) {
// Does not exist, Find asset style
auto itAssetStyle = std::find_if(
assetStyles.begin(),
assetStyles.end(),
[&](const struct TrueTypeAssetStyle &assetStyle) {
return assetStyle.style == style.style;
}
);
assertTrue(
itAssetStyle != this->assetStyles.end(),
"Could not find asset style"
);
// Create and read buffer.
uint8_t *dataBuffer = memoryAllocate<uint8_t>(itAssetStyle->dataSize);
this->loader.rewind();
this->loader.setPosition(itAssetStyle->dataOffset);
auto read = this->loader.read(dataBuffer, itAssetStyle->dataSize);
assertTrue(read == itAssetStyle->dataSize, "Could not read data");
// Create the face
FT_Face face;
auto ret = FT_New_Memory_Face(
this->fontLibrary,
(FT_Byte*)dataBuffer,
itAssetStyle->dataSize,
0,
&face
);
assertTrue(ret == 0, "Could not create face");
texture = new TrueTypeFaceTexture(face, style);
memoryFree(dataBuffer);
this->textures.push_back(texture);
this->textureByStyle[style] = texture;
} else {
// Exists
texture = it->second;
}
auto lock = this->locks.createLock();
this->textureByLock[lock] = texture;
this->locksByTexture[texture].push_back(lock);
return lock;
}
TrueTypeFaceTexture * TrueTypeAsset::getTexture(const usagelockid_t id) {
auto it = this->textureByLock.find(id);
assertTrue(it != this->textureByLock.end(), "Could not find texture");
return it->second;
}
void TrueTypeAsset::unlock(const usagelockid_t id) {
this->locks.removeLock(id);
}
TrueTypeAsset::~TrueTypeAsset() {
std::for_each(
this->textures.begin(),
this->textures.end(),
[](TrueTypeFaceTexture *texture){
delete texture;
}
);
FT_Done_FreeType(this->fontLibrary);
}

View File

@ -1,77 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "../Asset.hpp"
#include "asset/AssetLoader.hpp"
#include "util/flag.hpp"
#include "display/font/truetype/TrueTypeFaceTexture.hpp"
#include "util/UsageLock.hpp"
namespace Dawn {
enum TrueTypeAssetState {
TRUE_TYPE_ASSET_STATE_INITIAL,
TRUE_TYPE_ASSET_STATE_OPEN,
TRUE_TYPE_ASSET_STATE_VALIDATE_HEADER,
TRUE_TYPE_ASSET_STATE_VALIDATE_VERSION,
TRUE_TYPE_ASSET_STATE_READ_VARIANT_COUNT,
TRUE_TYPE_ASSET_STATE_READ_VARIANT,
TRUE_TYPE_ASSET_STATE_ADJUSTING_OFFSETS,
TRUE_TYPE_ASSET_STATE_INIT_FREETYPE,
TRUE_TYPE_ASSET_STATE_READY
};
struct TrueTypeAssetStyle {
flag_t style;
size_t dataSize;
size_t dataOffset;
};
class TrueTypeAsset : public Asset {
protected:
UsageLock locks;
AssetLoader loader;
FT_Library fontLibrary;
enum TrueTypeAssetState state = TRUE_TYPE_ASSET_STATE_INITIAL;
std::vector<struct TrueTypeAssetStyle> assetStyles;
std::vector<TrueTypeFaceTexture*> textures;
std::map<usagelockid_t, TrueTypeFaceTexture*> textureByLock;
std::map<struct TrueTypeFaceTextureStyle, TrueTypeFaceTexture*>
textureByStyle
;
std::map<TrueTypeFaceTexture*, std::vector<usagelockid_t>> locksByTexture;
public:
TrueTypeAsset(const std::string name);
void updateSync() override;
void updateAsync() override;
/**
* Create a lock for a specific style. Locks will ensure that the font is
* held loaded until it is no longer required.
*
* @param style Style to lock.
* @return A unique lock ID for this style.
*/
usagelockid_t lock(const struct TrueTypeFaceTextureStyle style);
/**
* Get a texture by a previous lock ID.
*
* @param lock Lock to get the texture of.
* @return Matching texture by this ID.
*/
TrueTypeFaceTexture * getTexture(const usagelockid_t lock);
/**
* Releases a previously held font lock.
*
* @param lock Lock to release/unlock.
*/
void unlock(const usagelockid_t lock);
~TrueTypeAsset();
};
}

View File

@ -1,10 +0,0 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
IAudioManager.cpp
)

View File

@ -1,14 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "IAudioManager.hpp"
using namespace Dawn;
IAudioManager::IAudioManager() {
}
IAudioManager::~IAudioManager() {
}

View File

@ -1,34 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "assert/assert.hpp"
namespace Dawn {
class DawnGame;
class IAudioManager {
public:
/**
* Construct a new IAudioManager.
*/
IAudioManager();
/**
* Initializes the audio manager system.
*/
virtual void init() = 0;
/**
* Ticks/Update the audio manager system.
*/
virtual void update() = 0;
/**
* Deinitializes the audio manager system.
*/
virtual ~IAudioManager();
};
}

View File

@ -1,17 +0,0 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
RenderPipeline.cpp
Tileset.cpp
Color.cpp
)
# Subdirs
add_subdirectory(font)
add_subdirectory(mesh)
add_subdirectory(shader)

View File

@ -1,83 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "Color.hpp"
#include "util/parser/TypeParsers.hpp"
using namespace Dawn;
struct Color Color::fromString(const std::string str) {
// Convert to lowercase
auto lower = stringToLowercase(str);
if(stringIncludes(lower, "cornflower")) {
return COLOR_CORNFLOWER_BLUE;
} else if(stringIncludes(lower, "magenta")) {
return COLOR_MAGENTA;
} else if(stringIncludes(lower, "white")) {
return COLOR_WHITE;
} else if(stringIncludes(lower, "black")) {
return COLOR_BLACK;
} else if(stringIncludes(lower, "red")) {
return COLOR_RED;
} else if(stringIncludes(lower, "green")) {
return COLOR_GREEN;
} else if(stringIncludes(lower, "blue")) {
return COLOR_BLUE;
} else if(stringIncludes(lower, "transparent")) {
return COLOR_TRANSPARENT;
}
// Hex code?
if(lower[0] == '#') {
// Remove the hash
lower = lower.substr(1);
// Convert to RGB
if(lower.length() == 3) {
// Convert to 6 digit hex
lower = lower[0] + lower[0] + lower[1] + lower[1] + lower[2] + lower[2];
}
// Convert to RGB
return {
(float_t)std::stoi(lower.substr(0, 2), nullptr, 16) / 255.0f,
(float_t)std::stoi(lower.substr(2, 2), nullptr, 16) / 255.0f,
(float_t)std::stoi(lower.substr(4, 2), nullptr, 16) / 255.0f,
1.0f
};
}
// Split by comma
auto splitByComma = stringSplit(str, ",");
if(splitByComma.size() == 3) {
// RGB
return {
(float_t)std::stof(splitByComma[0]),
(float_t)std::stof(splitByComma[1]),
(float_t)std::stof(splitByComma[2]),
1.0f
};
} else if(splitByComma.size() == 4) {
// RGBA
return {
(float_t)std::stof(splitByComma[0]),
(float_t)std::stof(splitByComma[1]),
(float_t)std::stof(splitByComma[2]),
(float_t)std::stof(splitByComma[3])
};
}
// TODO: Parse other kinds of colors
assertUnreachable("Failed to find a color match for %s", str);
return {};
}

View File

@ -1,90 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
#include "util/string.hpp"
namespace Dawn {
struct ColorU8 {
uint8_t r, g, b, a;
};
#pragma pack(push, 4)
struct Color {
/**
* Returns a color from a string.
*
* @param str String to parse.
* @return Color parsed.
*/
static struct Color fromString(const std::string str);
float_t r, g, b, a;
const struct Color& operator = (const struct Color &val) {
this->r = val.r;
this->g = val.g;
this->b = val.b;
this->a = val.a;
return *this;
}
struct Color operator * (const float_t &x) {
return {
r * x,
g * x,
b * x,
a * x
};
}
struct Color operator - (const struct Color &color) {
return {
r - color.r,
g - color.g,
b - color.b,
a - color.a
};
}
struct Color operator + (const struct Color &color) {
return {
r + color.r,
g + color.g,
b + color.b,
a + color.a
};
}
const bool_t operator == (const struct Color &other) {
return r == other.r && g == other.g && b == other.b && a == other.a;
}
operator struct ColorU8() const {
return {
(uint8_t)(r * 255),
(uint8_t)(g * 255),
(uint8_t)(b * 255),
(uint8_t)(a * 255)
};
}
};
#pragma pack(pop)
#define COLOR_DEF(r,g,b,a) { r, g, b, a }
#define COLOR_WHITE COLOR_DEF(1.0f, 1.0f, 1.0f, 1.0f)
#define COLOR_RED COLOR_DEF(1.0f, 0, 0, 1.0f)
#define COLOR_GREEN COLOR_DEF(0, 1.0f, 0, 1.0f)
#define COLOR_BLUE COLOR_DEF(0, 0, 1.0f, 1.0f)
#define COLOR_BLACK COLOR_DEF(0, 0, 0, 1.0f)
#define COLOR_MAGENTA COLOR_DEF(1.0f, 0, 1.0f, 1.0f)
#define COLOR_DARK_GREY COLOR_DEF(0.2f, 0.2f, 0.2f, 1.0f)
#define COLOR_LIGHT_GREY COLOR_DEF(0.8f, 0.8f, 0.8f, 1.0f)
#define COLOR_CORNFLOWER_BLUE COLOR_DEF(0.4f, 0.6f, 0.9f, 1.0f)
#define COLOR_WHITE_TRANSPARENT COLOR_DEF(1.0f, 1.0f, 1.0f, 0.0f)
#define COLOR_BLACK_TRANSPARENT COLOR_DEF(0.0f, 0.0f, 0.0f, 0.0f)
#define COLOR_TRANSPARENT COLOR_WHITE_TRANSPARENT
}

View File

@ -1,81 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/RenderTarget.hpp"
#define RENDER_MANAGER_RENDER_FLAG_DEPTH_TEST FLAG_DEFINE(0)
#define RENDER_MANAGER_RENDER_FLAG_BLEND FLAG_DEFINE(1)
namespace Dawn {
class DawnGame;
class RenderPipeline;
class ShaderManager;
class IRenderManager : public std::enable_shared_from_this<IRenderManager> {
protected:
flag_t renderFlags = 0;
public:
std::weak_ptr<DawnGame> game;
/**
* Default constructor for a render manager instance.
*
* @param game Game that this render manager belongs to.
*/
IRenderManager() {
}
/**
* Returns the primary render target (the backbuffer) that draws directly
* to the screen.
*
* @return Shared pointer to the backbuffer render target.
*/
virtual std::shared_ptr<RenderTarget> getBackBuffer() = 0;
/**
* Returns the current render pipeline intended to be used for rendering
* the currently active scene on the game instance.
*
* @return Reference to the currently active main scene render pipeline.
*/
virtual std::shared_ptr<RenderPipeline> getRenderPipeline() = 0;
/**
* Returns the shader manager that this render manager uses.
*
* @return Reference to the shader manager.
*/
virtual std::shared_ptr<ShaderManager> getShaderManager() = 0;
/**
* Sets the render flags for the render manager to use.
*
* @param renderFlags Render flags to use.
*/
virtual void setRenderFlags(const flag_t renderFlags) = 0;
/**
* Initialize / Start the Render Manager.
*
* @param game Game instance this render manager belongs to.
*/
virtual void init(const std::weak_ptr<DawnGame> game) = 0;
/**
* Perform a synchronous frame update on the render manager.
*/
virtual void update() = 0;
/**
* Clean up the render manager.
*/
virtual ~IRenderManager() {
}
};
}

View File

@ -1,115 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/Color.hpp"
#include "state/StateOwner.hpp"
namespace Dawn {
enum TextureFormat {
TEXTURE_FORMAT_R = 1,
TEXTURE_FORMAT_RG = 2,
TEXTURE_FORMAT_RGB = 3,
TEXTURE_FORMAT_RGBA = 4
};
enum TextureWrapMode {
TEXTURE_WRAP_MODE_REPEAT = 0,
TEXTURE_WRAP_MODE_MIRRORED_REPEAT = 1,
TEXTURE_WRAP_MODE_CLAMP_TO_EDGE = 2,
TEXTURE_WRAP_MODE_CLAMP_TO_BORDER = 3
};
enum TextureFilterMode {
TEXTURE_FILTER_MODE_NEAREST = 0,
TEXTURE_FILTER_MODE_LINEAR = 1
};
enum TextureDataFormat {
TEXTURE_DATA_FORMAT_UNSIGNED_BYTE = 0,
TEXTURE_DATA_FORMAT_FLOAT = 1
};
class ITexture : public StateOwner {
protected:
bool_t texturePropertiesNeedUpdating = true;
public:
StateProperty<enum TextureWrapMode> wrapModeX;
StateProperty<enum TextureWrapMode> wrapModeY;
StateProperty<enum TextureFilterMode> filterModeMin;
StateProperty<enum TextureFilterMode> filterModeMag;
StateProperty<enum TextureFilterMode> mipmapFilterModeMin;
StateProperty<enum TextureFilterMode> mipmapFilterModeMag;
ITexture() :
wrapModeX(TEXTURE_WRAP_MODE_CLAMP_TO_EDGE),
wrapModeY(TEXTURE_WRAP_MODE_CLAMP_TO_EDGE),
filterModeMin(TEXTURE_FILTER_MODE_LINEAR),
filterModeMag(TEXTURE_FILTER_MODE_LINEAR),
mipmapFilterModeMin(TEXTURE_FILTER_MODE_NEAREST),
mipmapFilterModeMag(TEXTURE_FILTER_MODE_NEAREST)
{
}
/**
* Returns the width of the texture.
*
* @return Width of the texture.
*/
virtual int32_t getWidth() = 0;
/**
* Returns the height of the texture.
*
* @return Height of the texture.
*/
virtual int32_t getHeight() = 0;
/**
* Initializes a texture.
*
* @param width Width of the texture (in pixels).
* @param height Height of the texture (in pixels).
* @param format Data format of the texture to use.
* @param dataFormat Data format of the texture to use.
*/
virtual void setSize(
const int32_t width,
const int32_t height,
const enum TextureFormat format,
const enum TextureDataFormat dataFormat
) = 0;
/**
* Fill a texture with a single color. This is stupidly costly.
*
* @param color Color to fill.
*/
virtual void fill(const struct Color) = 0;
virtual void fill(const uint8_t) = 0;
/**
* Returns true only when the texture has been loaded, sized and put on
* the gpu for rendering.
*
* @return True if ready, otherwise false.
*/
virtual bool_t isReady() = 0;
/**
* Buffer pixel data onto the GPU. Pixel buffering is rather costly so
* avoid doing this too often.
*
* @param pixels Array of pixels you're trying to buffer.
* @return The amount of bytes buffered to the texture.
*/
virtual void buffer(const struct ColorU8 pixels[]) = 0;
virtual void buffer(const uint8_t pixels[]) = 0;
virtual ~ITexture() {
}
};
}

View File

@ -1,262 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "RenderPipeline.hpp"
#include "game/DawnGame.hpp"
#include "scene/components/scene/SubSceneController.hpp"
#if DAWN_DEBUG_BUILD
#include "scene/debug/SceneDebugLine.hpp"
#endif
using namespace Dawn;
RenderPipeline::RenderPipeline() {
}
void RenderPipeline::init(const std::weak_ptr<RenderManager> renderManager) {
this->renderManager = renderManager;
shaderBuffer.init();
}
void RenderPipeline::render() {
auto rm = renderManager.lock();
assertNotNull(rm, "RenderManager cannot be null");
auto game = rm->game.lock();
assertNotNull(game, "Game cannot be null");
if(game->scene != nullptr) {
renderScene(game->scene);
}
}
void RenderPipeline::renderScene(const std::shared_ptr<Scene> scene) {
assertNotNull(scene, "Scene cannot be null");
auto rm = renderManager.lock();
assertNotNull(rm, "RenderManager cannot be null");
// Render subscenes first.
auto subSceneControllers = scene->findComponents<SubSceneController>();
auto itSubScene = subSceneControllers.begin();
while(itSubScene != subSceneControllers.end()) {
auto subScene = (*itSubScene)->subScene;
if(subScene == nullptr) {
++itSubScene;
continue;
}
if(
(*itSubScene)->onlyUpdateUnpaused &&
scene->game.lock()->timeManager.isPaused
) {
++itSubScene;
continue;
}
renderScene(subScene);
++itSubScene;
}
// Now render backbuffers.
auto backBuffer = rm->getBackBuffer();
auto cameras = scene->findComponents<Camera>();
std::shared_ptr<Camera> backBufferCamera;
// First, render all non-backbuffer cameras.
auto it = cameras.begin();
while(it != cameras.end()) {
auto cameraTarget = (*it)->getRenderTarget();
// Leave the backbuffer camera(s) to last, so we skip them. we do this so
// that children framebuffers contain the CURRENT frame, not LAST frame.
if(cameraTarget == nullptr) {
++it;
continue;
} else if(cameraTarget == backBuffer) {
backBufferCamera = *it;
} else {
renderSceneCamera(scene, *it);
}
++it;
}
// Now render the backbuffer camera.
if(backBufferCamera == nullptr) return;
renderSceneCamera(scene, backBufferCamera);
}
void RenderPipeline::renderSceneCamera(
const std::shared_ptr<Scene> scene,
const std::shared_ptr<Camera> camera
) {
auto rm = renderManager.lock();
assertNotNull(rm, "RenderManager cannot be null");
std::vector<struct ShaderPassItem>::iterator itPassItem;
assertNotNull(scene, "Scene cannot be null");
assertNotNull(camera, "Camera cannot be null");
// Create a new render ID. Long story short this is a really dirty way of
// not sending parameters to shaders more than we need.
renderId--;
// Get the render target.
auto renderTarget = camera->getRenderTarget();
assertNotNull(renderTarget, "Camera must have a render target");
// Update shader parameter buffers with current knowledge
struct RenderPipelineShaderBufferData shaderBufferData;
shaderBufferData.projection = camera->getProjection();
shaderBufferData.view = camera->item.lock()->getWorldTransform();
shaderBuffer.buffer(&shaderBufferData);
// Prepare a render context. This is just a nice way of letting renderables
// know a bit about what is happening, they can choose to use this data to
// render themselves differently.
struct IRenderableContext context = {
scene,
camera,
shared_from_this()
};
// Get the list of things to render first.
std::vector<struct ShaderPassItem> shaderPassItems;
// Renderables
auto renderables = scene->findComponents<IRenderable>();
auto itRenderables = renderables.begin();
while(itRenderables != renderables.end()) {
vectorAppend(shaderPassItems, (*itRenderables)->getRenderPasses(context));
++itRenderables;
}
// Inject index into each item
itPassItem = shaderPassItems.begin();
while(itPassItem != shaderPassItems.end()) {
itPassItem->index = itPassItem;
++itPassItem;
}
// Now we've queued everything, let's sort the rendering queue by the priority
std::sort(
shaderPassItems.begin(),
shaderPassItems.end(),
[](struct ShaderPassItem &a, struct ShaderPassItem &b) {
if(a.priority == b.priority) {
// Compare indexes if w is same.
if(a.w == b.w) return a.index < b.index;
return a.w < b.w;
}
return a.priority < b.priority;
}
);
// Now we've sorted everything! Let's actually start rendering.
std::shared_ptr<Shader> boundShader;
std::map<textureslot_t, Texture*> boundTextures;
std::map<shaderbufferlocation_t, shaderbufferslot_t> boundBuffers;
shaderbufferslot_t slot;
// TODO: This will be editable!
renderTarget->bind();
renderTarget->clear(
RENDER_TARGET_CLEAR_FLAG_DEPTH |
RENDER_TARGET_CLEAR_FLAG_COLOR
);
// Shader items
itPassItem = shaderPassItems.begin();
while(itPassItem != shaderPassItems.end()) {
auto item = *itPassItem;
// Bind the program.
if(boundShader != item.shader) {
boundShader = item.shader;
boundShader->bind();
}
// Bind the textures to the slots
auto itTextureSlot = item.textureSlots.begin();
while(itTextureSlot != item.textureSlots.end()) {
// Assert texture isn't null, just don't include it.
assertNotNull(itTextureSlot->second, "Texture cannot be null (omit)");
if(boundTextures[itTextureSlot->first] != itTextureSlot->second) {
itTextureSlot->second->bind(itTextureSlot->first);
boundTextures[itTextureSlot->first] = itTextureSlot->second;
}
++itTextureSlot;
}
// Bind the buffers to their slots
slot = 0;
auto itBufferSlot = item.parameterBuffers.begin();
while(itBufferSlot != item.parameterBuffers.end()) {
auto location = itBufferSlot->first;
auto buff = itBufferSlot->second;
boundBuffers[itBufferSlot->first] = slot;
buff->bind(slot);
slot++;
++itBufferSlot;
}
// Now set each of the parameters. Nothing exciting here.
auto itColors = item.colorValues.begin();
while(itColors != item.colorValues.end()) {
item.shader->setColor(itColors->first, itColors->second);
++itColors;
}
auto itBool = item.boolValues.begin();
while(itBool != item.boolValues.end()) {
item.shader->setBoolean(itBool->first, itBool->second);
++itBool;
}
auto itMat = item.matrixValues.begin();
while(itMat != item.matrixValues.end()) {
item.shader->setMatrix(itMat->first, itMat->second);
++itMat;
}
auto itVec3 = item.vec3Values.begin();
while(itVec3 != item.vec3Values.end()) {
item.shader->setVector3(itVec3->first, itVec3->second);
++itVec3;
}
auto itText = item.textureValues.begin();
while(itText != item.textureValues.end()) {
item.shader->setTexture(itText->first, itText->second);
++itText;
}
auto itBuffer = item.parameterBuffers.begin();
while(itBuffer != item.parameterBuffers.end()) {
item.shader->setParameterBuffer(
itBuffer->first, boundBuffers[itBuffer->first]
);
++itBuffer;
}
auto itFloat = item.floatValues.begin();
while(itFloat != item.floatValues.end()) {
item.shader->setFloat(itFloat->first, itFloat->second);
++itFloat;
}
// Set Render flags
rm->setRenderFlags(item.renderFlags);
// Thank god that's done, now just draw the damn mesh.
item.mesh->draw(item.drawMode, item.start, item.count);
++itPassItem;
}
}
RenderPipeline::~RenderPipeline() {
}

View File

@ -1,67 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/IRenderManager.hpp"
#include "display/shader/ShaderPass.hpp"
#include "scene/components/display/IRenderable.hpp"
#include "display/shader/buffers/RenderPipelineShaderBuffer.hpp"
#include "scene/components/display/Camera.hpp"
namespace Dawn {
class RenderManager;
class RenderPipeline : public std::enable_shared_from_this<RenderPipeline> {
private:
int_fast16_t renderId = -1;
public:
std::weak_ptr<RenderManager> renderManager;
RenderPipelineShaderBuffer shaderBuffer;
/**
* Constructs a new RenderPipeline. Render Pipelines are my attempt to
* create both a flexible, but standard way to allow the individual games
* to decide how they want to render the common scene-item models.
*/
RenderPipeline();
/**
* Initialize the render pipeline.
*
* @param renderManager Parent render manager this pipeline belongs to.
*/
virtual void init(const std::weak_ptr<RenderManager> renderManager);
/**
* Renders the games' currently active scene, and all of its' cameras.
*/
virtual void render();
/**
* Render a specific scene, usually just called for the currently active
* scene, but in future this could include sub-scenes.
*
* @param scene Scene to render.
*/
virtual void renderScene(const std::shared_ptr<Scene> scene);
/**
* Render a specific camera on a specific scene.
*
* @param scene Scene to render.
* @param camera Camera within the scene to render.
*/
virtual void renderSceneCamera(
const std::shared_ptr<Scene> scene,
const std::shared_ptr<Camera> camera
);
/**
* Cleanup a render pipeline that has been initialized.
*/
virtual ~RenderPipeline();
};
}

View File

@ -1,70 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "util/flag.hpp"
#include "display/Color.hpp"
#include "state/StateEvent.hpp"
#define RENDER_TARGET_CLEAR_FLAG_COLOR FLAG_DEFINE(0)
#define RENDER_TARGET_CLEAR_FLAG_DEPTH FLAG_DEFINE(1)
namespace Dawn {
class RenderTarget {
public:
StateEvent<
RenderTarget&,
const float_t,
const float_t
> eventRenderTargetResized;
/**
* Return the width of the render target.
*
* @return The width of the render target.
*/
virtual float_t getWidth() = 0;
/**
* Return the height of the render target.
*
* @return The height of the render target.
*/
virtual float_t getHeight() = 0;
/**
* Returns the scale (as in pixel density) of the render target. This is
* typically 1.0f, but on high DPI displays this may be 2.0f or higher.
*
* @return The scale of the render target.
*/
virtual float_t getScale() = 0;
/**
* Sets the clear color of the render target when the clear method for
* the color buffer is requested.
*
* @param color Color to use for the clear operation.
*/
virtual void setClearColor(const struct Color color) = 0;
/**
* Request the existing data in the render target to be cleared out. We
* typically assume the render target can support multiple buffer types,
* so you can opt to only clear certain buffer types.
*
* @param clearFlags Flags to request what is going to be cleared.
*/
virtual void clear(const flag8_t clearFlags) = 0;
/**
* Bind the render target for rendering to. The proceeding render requests
* will want to render to this render target directly. In future I may
* see if we can have multiple render targets bound at once to make this
* operation perform faster.
*/
virtual void bind() = 0;
};
}

View File

@ -1,94 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "Tileset.hpp"
#include "assert/assert.hpp"
using namespace Dawn;
struct Tile Tileset::getTile(const int32_t tile) {
assertTrue(tile >= 0, "Tile must be greater than or equal to 0");
assertTrue(tile < this->tiles.size(), "Tile is out of bounds");
return this->tiles[tile];
}
TilesetGrid::TilesetGrid() {
}
TilesetGrid::TilesetGrid(
Texture &texture,
const int32_t columns,
const int32_t rows
) : TilesetGrid(
columns, rows,
texture.getWidth(), texture.getHeight(),
0, 0,
0, 0
) {
}
TilesetGrid::TilesetGrid(
const int32_t columns,
const int32_t rows,
const int32_t w,
const int32_t h,
const int32_t gapX,
const int32_t gapY,
const int32_t borderX,
const int32_t borderY
) {
assertTrue(columns >= 1, "Columns must be greater than or equal to 1");
assertTrue(rows >= 1, "Rows must be greater than or equal to 1");
assertTrue(w >= 1, "Width must be greater than or equal to 1");
assertTrue(h >= 1, "Height must be greater than or equal to 1");
assertTrue(gapX >= 0, "GapX must be greater than or equal to 0");
assertTrue(gapY >= 0, "GapY must be greater than or equal to 0");
assertTrue(borderX >= 0, "BorderX must be greater than or equal to 0");
assertTrue(borderY >= 0, "BorderY must be greater than or equal to 0");
assertTrue(w >= (columns + (gapX * columns) + borderX + borderX), "Width is too small");
assertTrue(h >= (rows + (gapY * rows) + borderY + borderY), "Height is too small");
this->rows = rows;
this->columns = columns;
// Calculate division sizes (pixels)
this->divX = (w - (borderX * 2) - (gapX * (columns - 1))) / columns;
this->divY = (h - (borderY * 2) - (gapY * (rows - 1))) / rows;
// Calculate the division sizes (units)
const float_t tdivX = (float_t)this->divX / (float_t)w;
const float_t tdivY = (float_t)this->divY / (float_t)h;
struct Tile tile;
for(int32_t y = 0; y < rows; y++) {
for(int32_t x = 0; x < columns; x++) {
tile.uv0.x = (borderX + ((float_t)this->divX * x) + (gapX * x)) / w;
tile.uv1.x = tile.uv0.x + tdivX;
tile.uv0.y = (borderY + ((float_t)this->divY * y) + (gapY * y)) / h;
tile.uv1.y = tile.uv0.y + tdivY;
this->tiles.push_back(tile);
}
}
}
float_t TilesetGrid::getTileWidth(const int32_t tile) {
return this->divX;
}
float_t TilesetGrid::getTileHeight(const int32_t tile) {
return this->divY;
}
struct Tile TilesetGrid::getTileFromGrid(
const int32_t column,
const int32_t row
) {
assertTrue(row > 0 && row < this->rows, "Row is out of bounds");
assertTrue(column > 0 && column < this->columns, "Column is out of bounds");
return this->getTile(row + (column * this->rows));
}

View File

@ -1,105 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
#include "display/Texture.hpp"
namespace Dawn {
struct Tile {
glm::vec2 uv0;
glm::vec2 uv1;
};
struct Tileset {
public:
std::vector<struct Tile> tiles;
/**
* Returns the tile at the given tile index.
*
* @param tile Tile index to get.
* @return Tile at that index.
*/
struct Tile getTile(const int32_t tile);
/**
* Returns the width of an individual tile.
*
* @param tile The tile to get the width of.
* @return The tile width.
*/
virtual float_t getTileWidth(const int32_t tile) = 0;
/**
* Returns the height of an individual tile.
*
* @param tile The tile to get the height of.
* @return The tile height.
*/
virtual float_t getTileHeight(const int32_t tile) = 0;
};
struct TilesetGrid : public Tileset{
public:
int32_t rows;
int32_t columns;
int32_t divX;
int32_t divY;
/**
* Constructs a new Tileset Grid.
*/
TilesetGrid();
/**
* Constructs a new Tileset Grid from a texture.
*
* @param texture Texture to use.
* @param columns Columns in the grid.
* @param rows Rows in the grid.
*/
TilesetGrid(
Texture &texture,
const int32_t columns,
const int32_t rows
);
/**
* Constructs a new Tileset Grid.
*
* @param columns How many columns in the grid of tiles.
* @param rows How many rows in the grid of tiles.
* @param w Width of the grid.
* @param h Height of te grid.
* @param gapX Gap / Gutter between tiles.
* @param gapY Gap / Gutter between tiles.
* @param borderX Border at the edge of the grid before the first tiles.
* @param borderY Border at the edge of the grid before the first tiles.
*/
TilesetGrid(
const int32_t columns,
const int32_t rows,
const int32_t w,
const int32_t h,
const int32_t gapX,
const int32_t gapY,
const int32_t borderX,
const int32_t borderY
);
float_t getTileWidth(const int32_t tile) override;
float_t getTileHeight(const int32_t tile) override;
/**
* Returns the tile at a given grid position.
*
* @param column Column (0 indexed) to get the tile of.
* @param row Row (0 indexed) to get the tile of.
* @return Tile at this grid position.
*/
struct Tile getTileFromGrid(const int32_t column, const int32_t row);
};
}

View File

@ -1,6 +0,0 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
add_subdirectory(truetype)

View File

@ -1,10 +0,0 @@
# Copyright (c) 2023 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
TrueTypeFaceTexture.cpp
)

View File

@ -1,109 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "TrueTypeFaceTexture.hpp"
#include "util/memory.hpp"
#include "util/mathutils.hpp"
using namespace Dawn;
TrueTypeFaceTexture::TrueTypeFaceTexture(
const FT_Face face,
const struct TrueTypeFaceTextureStyle style
) :
face(face),
style(style)
{
assertTrue(style.fontSize < 256, "Font size cannot be greater than 256");
// Set freetype font size prior to baking.
if(FT_Set_Pixel_Sizes(face, 0, style.fontSize)) {
assertUnreachable("Failed to set font size");
}
size_t w = 0, h = 0;
FT_ULong c;
// First pass, determine the textures' dimensions.
for(c = TRUE_TYPE_CHAR_BEGIN; c < TRUE_TYPE_CHAR_END; c++) {
// Load the character
auto ret = FT_Load_Char(face, c, ~FT_LOAD_RENDER);
if(ret) {
assertUnreachable("Failed to load character (0)");
}
if(face->glyph->bitmap.width == 0 || face->glyph->bitmap.rows == 0) {
continue;
}
// Update the width and height
w = mathMax<size_t>(w, face->glyph->bitmap.width);
h += face->glyph->bitmap.rows;
}
assertTrue(w > 0, "Width cannot be less than or equal to 0");
assertTrue(h > 0, "Height cannot be less than or equal to 0");
// Now buffer pixels to the texture
float_t y = 0;
// I'd love to just buffer straight to the GPU, but it seems that is a bit
// unstable right now.
uint8_t *buffer = (uint8_t *)memoryAllocateEmpty(w * h, sizeof(uint8_t));
size_t offset = 0;
for(c = TRUE_TYPE_CHAR_BEGIN; c < TRUE_TYPE_CHAR_END; c++) {
// Load the character
if(FT_Load_Char(face, c, FT_LOAD_RENDER)) {
assertUnreachable("Failed to load character (1)");
}
// Store the character information
const struct TrueTypeCharacter info = {
.advanceX = (float_t)(face->glyph->advance.x >> 6),
.advanceY = (float_t)(face->glyph->advance.y >> 6),
.bitmapSize = glm::vec2(
face->glyph->bitmap.width,
face->glyph->bitmap.rows
),
.bitmapPosition = glm::vec2(
face->glyph->bitmap_left,
-face->glyph->bitmap_top
),
.textureY = y
};
this->characterData[c] = info;
// Buffer the pixels, oh dear GOD there has to be a more efficient way.
for(int32_t i = 0; i < face->glyph->bitmap.rows; i++) {
memoryCopy(
(void *)(face->glyph->bitmap.buffer + (i * face->glyph->bitmap.width)),
(void *)(buffer + offset),
face->glyph->bitmap.width * sizeof(uint8_t)
);
offset += w * sizeof(uint8_t);
assertTrue(offset <= (w * h * sizeof(uint8_t)), "Buffer overflow");
}
y += face->glyph->bitmap.rows;
}
this->texture.setSize(
w, h,
TEXTURE_FORMAT_R,
TEXTURE_DATA_FORMAT_UNSIGNED_BYTE
);
this->texture.buffer(buffer);
memoryFree(buffer);
}
struct TrueTypeCharacter TrueTypeFaceTexture::getCharacterData(
const FT_ULong c
) {
return this->characterData[c];
}
TrueTypeFaceTexture::~TrueTypeFaceTexture() {
FT_Done_Face(this->face);
}

View File

@ -1,80 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include <ft2build.h>
#include FT_FREETYPE_H
#include "util/flag.hpp"
#include "display/Texture.hpp"
#define TRUE_TYPE_CHAR_BEGIN 0x00
#define TRUE_TYPE_CHAR_END 0xFF
#define TRUE_TYPE_VARIANT_BOLD FLAG_DEFINE(0)
#define TRUE_TYPE_VARIANT_ITALICS FLAG_DEFINE(1)
#define TRUE_TYPE_DECORATION_STRIKETHROUGH FLAG_DEFINE(0)
#define TRUE_TYPE_DECORATION_UNDERLINE FLAG_DEFINE(1)
namespace Dawn {
class TrueTypeAsset;
struct TrueTypeCharacter {
float_t advanceX;
float_t advanceY;
glm::vec2 bitmapSize;
glm::vec2 bitmapPosition;
float_t textureY;
};
struct TrueTypeFaceTextureStyle {
uint32_t fontSize;
flag_t style;
/**
* Overload for the less than operator.
*
* @param r Right hand side of the operator.
* @return True if the left hand side is less than the right hand side.
*/
bool operator < (const struct TrueTypeFaceTextureStyle& r) const {
return std::tie(fontSize, style) < std::tie(r.fontSize, r.style);
}
};
class TrueTypeFaceTexture {
public:
const FT_Face face;
const struct TrueTypeFaceTextureStyle style;
std::map<FT_ULong, struct TrueTypeCharacter> characterData;
Texture texture;
/**
* Construct a new New True Type Face Texture object
*
* @param face The freetype face object.
* @param style Style that this font has, used for locking.
*/
TrueTypeFaceTexture(
const FT_Face face,
const struct TrueTypeFaceTextureStyle style
);
/**
* Returns the character data for the given character.
*
* @param c Character to get data for.
* @return The Character data for the given character.
*/
struct TrueTypeCharacter getCharacterData(const FT_ULong c);
/**
* Destroys this true type face texture.
*/
virtual ~TrueTypeFaceTexture();
friend class TrueTypeAsset;
};
}

View File

@ -1,14 +0,0 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
CapsuleMesh.cpp
CubeMesh.cpp
TriangleMesh.cpp
QuadMesh.cpp
SphereMesh.cpp
)

View File

@ -1,97 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "CapsuleMesh.hpp"
#include "util/mathutils.hpp"
using namespace Dawn;
void CapsuleMesh::calculateRing(
const int32_t segments,
const float_t height,
const float_t radius,
const float_t dr,
const float_t y,
const float_t dy,
std::vector<glm::vec3> &positions
) {
float_t segIncr = 1.0f / (float_t)(segments - 1);
for(int32_t s = 0; s < segments; s++ ) {
float_t x = cosf(MATH_PI * 2 * s * segIncr) * dr;
float_t z = sinf(MATH_PI * 2 * s * segIncr) * dr;
positions.emplace_back(glm::vec3(radius * x, radius * y + height * dy, radius * z ));
}
}
void CapsuleMesh::create(
Mesh &mesh,
const float_t radius,
const float_t height
) {
std::vector<glm::vec3> positions;
std::vector<meshindice_t> indices;
const int32_t slices = 12;
const int32_t segments = 12;
const int32_t ringsBody = slices + 1;
const int32_t ringsTotal = slices + ringsBody;
positions.reserve(segments * ringsTotal);
indices.reserve((segments - 1) * (ringsTotal - 1) * 6);
const float_t bodyIncr = 1.0f / (float_t)(ringsBody - 1);
const float_t ringIncr = 1.0f / (float_t)(slices - 1);
for(int32_t r = 0; r < slices / 2; r++) {
calculateRing(
segments,
height,
radius,
sinf(MATH_PI * r * ringIncr),
sinf(MATH_PI * (r * ringIncr - 0.5f)),
-0.5f,
positions
);
}
for(int32_t r = 0; r < ringsBody; r++ ) {
calculateRing(
segments,
height,
radius,
1.0f,
0.0f,
r * bodyIncr - 0.5f,
positions
);
}
for(int32_t r = slices / 2; r < slices; r++) {
calculateRing(
segments,
height,
radius,
sinf(MATH_PI * r * ringIncr),
sinf(MATH_PI * (r * ringIncr - 0.5f)),
0.5f,
positions
);
}
for(int32_t r = 0; r < ringsTotal - 1; r++ ) {
for(int32_t s = 0; s < segments - 1; s++ ) {
indices.push_back( (uint32_t)(r * segments + ( s + 1 )) );
indices.push_back( (uint32_t)(r * segments + ( s + 0 )) );
indices.push_back( (uint32_t)(( r + 1 ) * segments + ( s + 1 )) );
indices.push_back( (uint32_t)(( r + 1 ) * segments + ( s + 0 )) );
indices.push_back( (uint32_t)(( r + 1 ) * segments + ( s + 1 )) );
indices.push_back( (uint32_t)(r * segments + s) );
}
}
mesh.createBuffers(positions.size(), indices.size());
mesh.bufferPositions(0, positions.data(), positions.size());
mesh.bufferIndices(0, indices.data(), indices.size());
}

View File

@ -1,47 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/mesh/Mesh.hpp"
namespace Dawn {
class CapsuleMesh {
protected:
/**
* Calculates a ring of vertices within a capsule.
*
* @param segments Count of segments in the ring.
* @param height Height of the ring.
* @param radius Radius of the ring.
* @param dr The delta radius of the ring.
* @param y The y position of the ring.
* @param dy The delta y position of the ring.
* @param positions The positions vector to push the positions to.
*/
static void calculateRing(
const int32_t segments,
const float_t height,
const float_t radius,
const float_t dr,
const float_t y,
const float_t dy,
std::vector<glm::vec3> &positions
);
public:
/**
* Creates a capsule mesh.
*
* @param mesh Mesh to instanciate.
* @param radius Radius of the capsule.
* @param height Height of the capsule.
*/
static void create(
Mesh &mesh,
const float_t radius,
const float_t height
);
};
}

View File

@ -1,70 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "CubeMesh.hpp"
using namespace Dawn;
void CubeMesh::buffer(
Mesh &mesh,
const glm::vec3 pos,
const glm::vec3 size,
const int32_t verticeStart,
const int32_t indiceStart
) {
glm::vec3 positions[CUBE_VERTICE_COUNT] = {
pos,
glm::vec3(pos.x+size.x, pos.y, pos.z),
glm::vec3(pos.x, pos.y+size.y, pos.z),
glm::vec3(pos.x+size.x, pos.y+size.y, pos.z),
glm::vec3(pos.x, pos.y, pos.z+size.z),
glm::vec3(pos.x+size.x, pos.y, pos.z+size.z),
glm::vec3(pos.x, pos.y+size.y, pos.z+size.z),
pos + size
};
glm::vec2 coordinates[CUBE_VERTICE_COUNT] = {
glm::vec2(0, 0),
glm::vec2(1, 0),
glm::vec2(0, 1),
glm::vec2(1, 1),
glm::vec2(0, 0),
glm::vec2(1, 0),
glm::vec2(0, 1),
glm::vec2(1, 1)
};
meshindice_t indices[CUBE_INDICE_COUNT] = {
// Back
verticeStart, verticeStart + 1, verticeStart + 3,
verticeStart, verticeStart + 2, verticeStart + 3,
// Right
verticeStart + 1, verticeStart + 5, verticeStart + 7,
verticeStart + 1, verticeStart + 3, verticeStart + 7,
// Left
verticeStart + 4, verticeStart, verticeStart + 2,
verticeStart + 4, verticeStart + 6, verticeStart + 2,
// Front
verticeStart + 5, verticeStart + 4, verticeStart + 6,
verticeStart + 5, verticeStart + 7, verticeStart + 6,
// Top
verticeStart + 7, verticeStart + 2, verticeStart + 6,
verticeStart + 7, verticeStart + 3, verticeStart + 2,
// Bottom
verticeStart + 1, verticeStart, verticeStart + 4,
verticeStart + 1, verticeStart + 4, verticeStart + 5
};
mesh.bufferPositions(verticeStart, positions, CUBE_VERTICE_COUNT);
mesh.bufferCoordinates(verticeStart, coordinates, CUBE_VERTICE_COUNT);
mesh.bufferIndices(indiceStart, indices, CUBE_INDICE_COUNT);
}

View File

@ -1,32 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/mesh/Mesh.hpp"
#define CUBE_VERTICE_COUNT 8
#define CUBE_INDICE_COUNT 36
namespace Dawn {
class CubeMesh {
public:
/**
* Buffers cube mesh vertices onto a mesh.
*
* @param mesh Mesh to buffer onto.
* @param pos Position of the cube.
* @param size Size of the cube.
* @param verticeStart Starting vertice index.
* @param indiceStart Starting indice index.
*/
static void buffer(
Mesh &mesh,
const glm::vec3 pos,
const glm::vec3 size,
const int32_t verticeStart,
const int32_t indiceStart
);
};
}

View File

@ -1,92 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "QuadMesh.hpp"
using namespace Dawn;
void QuadMesh::bufferQuadMeshWithZ(
Mesh &mesh,
const glm::vec2 xy0,
const glm::vec2 uv0,
const glm::vec2 xy1,
const glm::vec2 uv1,
const float_t z,
const int32_t verticeStart,
const int32_t indiceStart
) {
glm::vec3 positions[QUAD_VERTICE_COUNT] = {
glm::vec3(xy0, z),
glm::vec3(xy1.x, xy0.y, z),
glm::vec3(xy0.x, xy1.y, z),
glm::vec3(xy1, z)
};
glm::vec2 coordinates[QUAD_VERTICE_COUNT] = {
uv0, glm::vec2(uv1.x, uv0.y),
glm::vec2(uv0.x, uv1.y), uv1
};
meshindice_t indices[QUAD_INDICE_COUNT] = {
verticeStart, verticeStart + 1, verticeStart + 2,
verticeStart + 1, verticeStart + 2, verticeStart + 3
};
mesh.bufferPositions(verticeStart, positions, QUAD_VERTICE_COUNT);
mesh.bufferCoordinates(verticeStart, coordinates, QUAD_VERTICE_COUNT);
mesh.bufferIndices(indiceStart, indices, QUAD_INDICE_COUNT);
}
void QuadMesh::bufferQuadMesh(
Mesh &mesh,
const glm::vec2 xy0,
const glm::vec2 uv0,
const glm::vec2 xy1,
const glm::vec2 uv1,
const int32_t verticeStart,
const int32_t indiceStart
) {
QuadMesh::bufferQuadMeshWithZ(
mesh, xy0, uv0, xy1, uv1, 0, verticeStart, indiceStart
);
}
void QuadMesh::bufferCoordinates(
Mesh &mesh,
const glm::vec2 uv0,
const glm::vec2 uv1,
const int32_t verticeStart
) {
glm::vec2 coordinates[QUAD_VERTICE_COUNT] = {
uv0, glm::vec2(uv1.x, uv0.y),
glm::vec2(uv0.x, uv1.y), uv1
};
mesh.bufferCoordinates(verticeStart, coordinates, QUAD_VERTICE_COUNT);
}
void QuadMesh::bufferPositions(
Mesh &mesh,
const glm::vec2 xy0,
const glm::vec2 xy1,
const int32_t verticeStart
) {
glm::vec3 positions[QUAD_VERTICE_COUNT] = {
glm::vec3(xy0, 0),
glm::vec3(xy1.x, xy0.y, 0),
glm::vec3(xy0.x, xy1.y, 0),
glm::vec3(xy1, 0)
};
mesh.bufferPositions(verticeStart, positions, QUAD_VERTICE_COUNT);
}
void QuadMesh::initQuadMesh(
Mesh &mesh,
const glm::vec2 xy0,
const glm::vec2 uv0,
const glm::vec2 xy1,
const glm::vec2 uv1,
const float_t z
) {
mesh.createBuffers(QUAD_VERTICE_COUNT, QUAD_INDICE_COUNT);
QuadMesh::bufferQuadMeshWithZ(mesh, xy0, uv0, xy1, uv1, z, 0, 0);
}

View File

@ -1,109 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/mesh/Mesh.hpp"
#define QUAD_VERTICE_COUNT 4
#define QUAD_INDICE_COUNT 6
#define QUAD_INDICE_PER_QUAD 2
namespace Dawn {
class QuadMesh {
public:
/**
* Buffers the vertices of a quad onto a primitive.
*
* @param mesh The primitive to buffer to.
* @param xy0 The lower X and Y coordinate.
* @param uv0 The lower Xand Y texture coordinate.
* @param xy1 The higher X and Y coordinate.
* @param uv1 The higher X and Y texture coordinate.
* @param z The Z position of the coordinates.
* @param verticeStart Start vertice to buffer to.
* @param indiceStart Start indice to buffer to.
*/
static void bufferQuadMeshWithZ(
Mesh &mesh,
const glm::vec2 xy0,
const glm::vec2 uv0,
const glm::vec2 xy1,
const glm::vec2 uv1,
const float_t z,
const int32_t verticeStart,
const int32_t indiceStart
);
/**
* Buffers the vertices of a quad onto a primitive.
*
* @param mesh The primitive to buffer to.
* @param xy0 The lower X and Y coordinate.
* @param uv0 The lower Xand Y texture coordinate.
* @param xy1 The higher X and Y coordinate.
* @param uv1 The higher X and Y texture coordinate.
* @param verticeStart Start vertice to buffer to.
* @param indiceStart Start indice to buffer to.
*/
static void bufferQuadMesh(
Mesh &mesh,
const glm::vec2 xy0,
const glm::vec2 uv0,
const glm::vec2 xy1,
const glm::vec2 uv1,
const int32_t verticeStart,
const int32_t indiceStart
);
/**
* Buffers texture coordinates on to an already initialized quad mesh.
*
* @param mesh Mesh to buffer the texture coordinates on to.
* @param uv0 Lower X and Y coordinates.
* @param uv1 Upper X and Y coordinates.
* @param verticeStart Start vertice to buffer in to.
*/
static void bufferCoordinates(
Mesh &mesh,
const glm::vec2 uv0,
const glm::vec2 uv1,
const int32_t verticeStart
);
/**
* Buffers the positions of a quad onto a primitive.
*
* @param mesh The primitive to buffer to.
* @param xy0 The lower X and Y coordinate.
* @param xy1 The higher X and Y coordinate.
* @param verticeStart Start vertice to buffer to.
*/
static void bufferPositions(
Mesh &mesh,
const glm::vec2 xy0,
const glm::vec2 xy1,
const int32_t verticeStart
);
/**
* Initializes a mesh to be a single quad.
*
* @param mesh The primitive to buffer to.
* @param xy0 The lower X and Y coordinate.
* @param uv0 The lower Xand Y texture coordinate.
* @param xy1 The higher X and Y coordinate.
* @param uv1 The higher X and Y texture coordinate.
* @param z The Z position of the coordinates.
*/
static void initQuadMesh(
Mesh &mesh,
const glm::vec2 xy0,
const glm::vec2 uv0,
const glm::vec2 xy1,
const glm::vec2 uv1,
const float_t z
);
};
}

View File

@ -1,60 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "SphereMesh.hpp"
#include "util/mathutils.hpp"
using namespace Dawn;
void SphereMesh::createSphere(
Mesh &mesh,
const float_t radius,
const int32_t slices,
const int32_t stacks
) {
std::vector<glm::vec3> positions;
// Create vertices
int32_t c = 0;
for (int32_t i = 0; i <= stacks; i++) {
float_t phi = MATH_PI * i / stacks;
float_t cosPhi = cos(phi);
float_t sinPhi = sin(phi);
for (int32_t j = 0; j <= slices; j++) {
float_t theta = 2 * MATH_PI * j / slices;
float_t cosTheta = cos(theta);
float_t sinTheta = sin(theta);
glm::vec3 v;
v.x = radius * sinPhi * cosTheta;
v.y = radius * sinPhi * sinTheta;
v.z = radius * cosPhi;
positions.push_back(v);
}
}
// Create indices
std::vector<meshindice_t> indices;
for (int32_t i = 0; i < stacks; i++) {
for (int32_t j = 0; j < slices; j++) {
meshindice_t p1 = i * (slices + 1) + j;
meshindice_t p2 = p1 + slices + 1;
indices.push_back(p1);
indices.push_back(p2);
indices.push_back(p1 + 1);
indices.push_back(p1 + 1);
indices.push_back(p2);
indices.push_back(p2 + 1);
}
}
mesh.createBuffers(positions.size(), indices.size());
mesh.bufferPositions(0, positions.data(), positions.size());
mesh.bufferIndices(0, indices.data(), indices.size());
}

View File

@ -1,27 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/mesh/Mesh.hpp"
namespace Dawn {
class SphereMesh {
public:
/**
* Creates a sphere mesh.
*
* @param mesh Mesh to instanciate.
* @param radius Radius of the sphere.
* @param slices How many horizontal slices to make.
* @param stacks How many vertical stacks to make.
*/
static void createSphere(
Mesh &mesh,
const float_t radius,
const int32_t slices,
const int32_t stacks
);
};
}

View File

@ -1,31 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "display/mesh/TriangleMesh.hpp"
using namespace Dawn;
void TriangleMesh::createTriangleMesh(Mesh &mesh) {
glm::vec3 positions[3] = {
glm::vec3(-0.5f, -0.5f, 0),
glm::vec3(0.5f, -0.5f, 0),
glm::vec3(0, 0.5f, 0)
};
glm::vec2 coordinates[3] = {
glm::vec2(0, 0),
glm::vec2(0, 1),
glm::vec2(1, 0)
};
meshindice_t indices[3] = {
0, 1, 2
};
mesh.createBuffers(3, 3);
mesh.bufferPositions(0, positions, 3);
mesh.bufferCoordinates(0, coordinates, 3);
mesh.bufferIndices(0, indices, 3);
}

View File

@ -1,19 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/mesh/Mesh.hpp"
namespace Dawn {
class TriangleMesh {
public:
/**
* Initializes a mesh to hold a single triangle.
*
* @param mesh Mesh to initialize as a triangle.
*/
static void createTriangleMesh(Mesh &mesh);
};
}

View File

@ -1,10 +0,0 @@
# Copyright (c) 2023 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
ShaderManager.cpp
)

View File

@ -1,112 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/Texture.hpp"
#include "display/shader/ShaderParameterBuffer.hpp"
namespace Dawn {
template<typename T>
class IShader {
public:
int32_t shaderId = -1;
int_fast16_t renderId = 0;
/**
* Compile all programs for this shader.
*/
virtual void compile() = 0;
/**
* Attaches the supplied shader as the current shader.
*/
virtual void bind() = 0;
/**
* Binds a shader buffer to a specific slot.
*
* @param slot Slot to bind the buffer to.
* @param buffer Buffer to bind.
*/
template<typename J>
void setParameterBuffer(
const shaderbufferslot_t slot,
const ShaderParameterBuffer<J> &buffer
);
/**
* Set's a specific shader parameter to a matrix.
*
* @param parameter parameter on the shader to set.
* @param matrix Matrix to apply.
*/
virtual void setMatrix(
const T parameter,
const glm::mat4 matrix
) = 0;
/**
* Attaches a boolean to a shader.
*
* @param parameter parameter to set.
* @param value Value to set.
*/
virtual void setBoolean(
const T parameter,
const bool_t value
) = 0;
/**
* Set a color on to the shader.
*
* @param parameter parameter to set the color to.
* @param color Color to set.
*/
virtual void setColor(
const T parameter,
const struct Color color
) = 0;
/**
* Set a 3D vector on to the shader.
*
* @param parameter parameter to set the vector to.
* @param vector Vector to set.
*/
virtual void setVector3(
const T parameter,
const glm::vec3 vector
) = 0;
/**
* Attaches a texture to the currently bound shader.
*
* @param parameter parameter to set the texture on to.
* @param texture Texture slot to bind to the parameter.
*/
virtual void setTexture(
const T parameter,
const textureslot_t texture
) = 0;
/**
* Sets a floating point value to the shader.
*
* @param parameter Paramater to set the float ont o.
* @param Float to bind.
*/
virtual void setFloat(
const T parameter,
const float_t value
) = 0;
/**
* Destroys/Cleans up the shader.
*/
virtual ~IShader() {
}
};
}

View File

@ -1,25 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
namespace Dawn {
template<typename L>
class IShaderParameterBuffer {
public:
/**
* Initializes this shader parameter buffer.
*/
virtual void init() = 0;
/**
* Bind this shader buffer to the supplied location.
*
* @param location Location to bind this buffer to.
*/
virtual void bind(const L location) = 0;
};
}

View File

@ -1,16 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "ShaderManager.hpp"
using namespace Dawn;
ShaderManager::ShaderManager() {
this->nextId = 0;
this->nextLock = 0;
}
ShaderManager::~ShaderManager() {
}

View File

@ -1,106 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/shader/Shader.hpp"
namespace Dawn {
typedef int64_t shaderlock_t;
typedef int16_t shaderid_t;
class ShaderManager {
private:
int32_t nextId;
shaderlock_t nextLock;
std::map<shaderid_t, std::shared_ptr<Shader>> shaders;
std::map<shaderlock_t, shaderid_t> shaderLocks;
std::map<shaderid_t, std::vector<shaderlock_t>> shaderLocksByShader;
/**
* Returns the shader id for the given shader type, or -1 if it is not
* loaded.
*
* @return The shader id for the shader, or -1 if it is not loaded.
*/
template<class T>
shaderid_t getShaderId() {
auto it = shaders.begin();
while(it != shaders.end()) {
auto asT = std::dynamic_pointer_cast<T>(it->second);
if(asT != nullptr) return asT->shaderId;
++it;
}
return -1;
}
public:
/**
* Creates a new shader manager.
*/
ShaderManager();
/**
* Locks a shader of the given type. If the shader is not already loaded,
* it will be loaded. If the shader is already loaded, it will be
* returned.
*
* @return The shader lock for the shader of the given type.
*/
template<class T>
shaderlock_t lockShader() {
auto shaderId = this->getShaderId<T>();
if(shaderId == -1) {
auto shader = std::make_shared<T>();
shader->compile();
shader->shaderId = this->nextId++;
this->shaders[shader->shaderId] = shader;
shaderId = shader->shaderId;
}
shaderlock_t lock = this->nextLock++;
this->shaderLocks[lock] = shaderId;
this->shaderLocksByShader[shaderId].push_back(lock);
return lock;
}
/**
* Returns the shader for the given lock.
*
* @param lock The shader lock.
* @return The shader for the given lock.
*/
template<class T>
std::shared_ptr<T> getShader(shaderlock_t lock) {
auto shaderId = this->shaderLocks[lock];
return std::static_pointer_cast<T>(this->shaders[shaderId]);
}
/**
* Releases the shader for the given lock. This will unload any shader
* that is no longer in use.
*
* @param lock Lock to release.
*/
template<class T>
void releaseShader(shaderlock_t lock) {
auto shaderId = this->shaderLocks[lock];
this->shaderLocks.erase(lock);
auto& locks = this->shaderLocksByShader[shaderId];
auto it = std::find(locks.begin(), locks.end(), lock);
if(it != locks.end()) locks.erase(it);
if(locks.size() == 0) {
this->shaderLocksByShader.erase(shaderId);
this->shaders.erase(shaderId);
}
}
/**
* Destroys the shader manager.
*/
~ShaderManager();
};
}

View File

@ -1,37 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "display/shader/Shader.hpp"
#include "display/mesh/Mesh.hpp"
namespace Dawn {
struct ShaderPassItem;
struct ShaderPassItem {
std::shared_ptr<Shader> shader;
int32_t priority = 0;
std::vector<struct ShaderPassItem>::iterator index;
Mesh *mesh;
int32_t start = 0;
int32_t count = -1;
float_t w = 0;
flag_t renderFlags = RENDER_MANAGER_RENDER_FLAG_DEPTH_TEST;
enum MeshDrawMode drawMode = MESH_DRAW_MODE_TRIANGLES;
// Parameters
std::map<shaderparameter_t, struct Color> colorValues;
std::map<shaderparameter_t, bool_t> boolValues;
std::map<shaderparameter_t, glm::mat4> matrixValues;
std::map<shaderparameter_t, glm::vec3> vec3Values;
std::map<shaderparameter_t, textureslot_t> textureValues;
std::map<shaderparameter_t, float_t> floatValues;
std::map<shaderbufferlocation_t, IShaderParameterBuffer<shaderbufferslot_t>*> parameterBuffers;
// Textures
std::map<textureslot_t, Texture*> textureSlots;
};
}

View File

@ -1,32 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
namespace Dawn {
template<typename ...T>
class Event {
private:
std::hashmap<int32_t, std::function<void(T...)>> callback;
int32_t nextEventId = 0;
public:
int32_t listen(const std::function <void(T...)> &callback) {
this->callback.insert(std::make_pair(this->nextEventId, callback));
return this->nextEventId++;
}
void unlisten(int32_t id) {
this->callback.erase(id);
}
void trigger(T... args) {
for(auto &it : this->callback) {
it.second(args...);
}
}
};
}

View File

@ -1,10 +1,9 @@
# Copyright (c) 2023 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
DawnGame.cpp
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
target_sources(${DAWN_TARGET_NAME}
PRIVATE
Game.cpp
)

View File

@ -1,54 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "DawnGame.hpp"
using namespace Dawn;
DawnGame::DawnGame(const std::weak_ptr<DawnHost> host) :
host(host),
inputManager(),
saveManager(this)
{
renderManager = std::make_shared<RenderManager>();
}
int32_t DawnGame::init() {
this->assetManager.init();
this->renderManager->init(weak_from_this());
this->scene = dawnGameGetInitialScene(weak_from_this());
return DAWN_GAME_INIT_RESULT_SUCCESS;
}
int32_t DawnGame::update(float_t delta) {
this->assetManager.update();
this->inputManager.update();
this->timeManager.update(delta);
if(this->scene != nullptr) this->scene->update();
this->renderManager->update();
if(this->sceneToCutTo != nullptr) {
this->scene = nullptr;
this->scene = this->sceneToCutTo;
this->sceneToCutTo = nullptr;
}
if(this->closeRequested) {
return DAWN_GAME_UPDATE_RESULT_EXIT;
}
return DAWN_GAME_UPDATE_RESULT_SUCCESS;
}
void DawnGame::sceneCutover(std::shared_ptr<Scene> scene) {
if(scene == nullptr) scene = this->scene;
this->sceneToCutTo = scene;
}
void DawnGame::close() {
this->closeRequested = true;
}

View File

@ -1,92 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
#include "scene/Scene.hpp"
#include "display/RenderManager.hpp"
#include "asset/AssetManager.hpp"
#include "input/InputManager.hpp"
#include "time/TimeManager.hpp"
#include "input/InputBinds.hpp"
#include "save/SaveManager.hpp"
#define DAWN_GAME_INIT_RESULT_SUCCESS 0
#define DAWN_GAME_UPDATE_RESULT_SUCCESS 0
#define DAWN_GAME_UPDATE_RESULT_EXIT 1
namespace Dawn {
class DawnHost;
class DawnGame : public std::enable_shared_from_this<DawnGame> {
private:
std::shared_ptr<Scene> sceneToCutTo;
bool_t closeRequested = false;
public:
const std::weak_ptr<DawnHost> host;
std::shared_ptr<Scene> scene;
AssetManager assetManager;
TimeManager timeManager;
std::shared_ptr<RenderManager> renderManager;
InputManager inputManager;
SaveManager saveManager;
/**
* Construct a new game instance.
*
* @param host Host that executed this game instantiation.
*/
DawnGame(const std::weak_ptr<DawnHost> host);
/**
* Initialize the game. This is performed by the host at a time that is
* deemed to have the host ready for the game's initialization. This will
* return an initialize result, where DAWN_GAME_INIT_RESULT_SUCCESS is
* the only "successful" result, anything else is deemed a failure state.
*
* @param host Pointer to the host that is running this game.
* @return The game initialize result.
*/
int32_t init();
/**
* Performs a game update operation. This operation should occur exactly
* once per frame, synchronously on the main thread. Updates can only
* have two valid exit results, either DAWN_GAME_UPDATE_RESULT_SUCCESS for
* a successful update, and request that we continue to update, or
* DAWN_GAME_UPDATE_RESULT_EXIT for a successful update but to request
* that no more update operations occur. Any other result is considered a
* failure state.
*
* @param delta Time delta to tick the game by.
* @return The game update result.
*/
int32_t update(float_t delta);
/**
* Changes to a new scene, will dispose the currently active scene as part
* of that process. This assumes the other scene has already been loaded
* and staged.
*
* @param scene Scene to cut over to.
*/
void sceneCutover(std::shared_ptr<Scene> scene);
/**
* Gracefully requests that the game should be closed as soon as possible.
*/
void close();
};
/**
* Unimplemented by default, required by the game as the basic entry point
* for which scene should be used by default.
*
* @param game Game that is requesting this scene.
* @return Pointer to a scene that you wish to have as the default scene.
*/
std::shared_ptr<Scene> dawnGameGetInitialScene(std::weak_ptr<DawnGame> game);
}

31
src/dawn/game/Game.cpp Normal file
View File

@ -0,0 +1,31 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "Game.hpp"
using namespace Dawn;
Game::Game() {
}
void Game::init() {
renderHost.init();
inputManager.init(shared_from_this());
}
void Game::update() {
renderHost.update();
}
bool_t Game::isCloseRequested() {
return (
renderHost.isCloseRequested()
);
}
Game::~Game() {
}

46
src/dawn/game/Game.hpp Normal file
View File

@ -0,0 +1,46 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
#include "display/RenderHost.hpp"
#include "input/InputManager.hpp"
namespace Dawn {
class Game : public std::enable_shared_from_this<Game> {
public:
RenderHost renderHost;
InputManager inputManager;
/**
* Constructs the game instance, does not initialize anything.
*/
Game();
/**
* Initialize the game and all of its components.
*/
void init();
/**
* Performs a single update tick on the game engine, and in turn all of
* the game's sub systems.
*/
void update();
/**
* Returns whether the game has been requested to gracefully close at the
* next available opportunity.
*
* @return True if the game should close.
*/
bool_t isCloseRequested();
/**
* Deconstructs the game instance, does not deinitialize anything.
*/
virtual ~Game();
};
}

View File

@ -1,12 +0,0 @@
# Copyright (c) 2023 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Subdirs
# add_subdirectory(poker)
add_subdirectory(tictactoe)
if(DAWN_VISUAL_NOVEL)
add_subdirectory(vn)
endif()

View File

@ -1,20 +0,0 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
Card.cpp
PokerPot.cpp
PokerPlayer.cpp
PokerGame.cpp
PokerWinning.cpp
PokerTurn.cpp
)
# Subdirs
if(DAWN_VISUAL_NOVEL)
add_subdirectory(visualnovel)
endif()

View File

@ -1,70 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "Card.hpp"
using namespace Dawn;
void Card::fillDeck(std::vector<struct Card> *deck) {
assertNotNull(deck);
for(uint8_t i = 0; i < CARD_DECK_SIZE; i++) {
deck->push_back(Card(i));
}
}
int32_t Card::contains(std::vector<struct Card> *deck, struct Card c) {
assertNotNull(deck);
auto it = deck->begin();
while(it != deck->end()) {
if(it->cardValue == c.cardValue) return (int32_t)(it - deck->begin());
++it;
}
return -1;
}
int32_t Card::containsNumber(
std::vector<struct Card> *deck,
enum CardValue number
) {
assertNotNull(deck);
assertTrue(number < CARD_COUNT_PER_SUIT);
auto it = deck->begin();
while(it != deck->end()) {
if(it->getValue() == number) return (int32_t)(it - deck->begin());
++it;
}
return -1;
}
std::vector<struct Card> Card::countPairs(
std::vector<struct Card> *deck,
enum CardValue val
) {
std::vector<struct Card> pairs;
assertNotNull(deck);
assertTrue(deck->size() > 0);
auto it = deck->begin();
while(it != deck->end()) {
if(it->getValue() == val) pairs.push_back(*it);
++it;
}
return pairs;
}
bool_t Card::cardSorter(struct Card left, struct Card right) {
return left.cardValue < right.cardValue;
}
void Card::sort(std::vector<struct Card> *deck) {
assertNotNull(deck);
assertTrue(deck->size() > 1);
std::sort(deck->begin(), deck->end(), &Card::cardSorter);
}

View File

@ -1,139 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
#include "assert/assert.hpp"
namespace Dawn {
enum CardSuit {
CARD_CLUBS = 0,
CARD_DIAMONDS = 1,
CARD_HEARTS = 2,
CARD_SPADES = 3,
CARD_SUIT_INVALUD = 0xFF
};
enum CardValue {
CARD_TWO = 0,
CARD_THREE = 1,
CARD_FOUR = 2,
CARD_FIVE = 3,
CARD_SIX = 4,
CARD_SEVEN = 5,
CARD_EIGHT = 6,
CARD_NINE = 7,
CARD_TEN = 8,
CARD_JACK = 9,
CARD_QUEEN = 10,
CARD_KING = 11,
CARD_ACE = 12,
CARD_VALUE_INVALD = 0xFF
};
/** Count of cards in each suit */
#define CARD_COUNT_PER_SUIT 13
/** Count of suits */
#define CARD_SUIT_COUNT 4
/** Standard Card Deck Size */
#define CARD_DECK_SIZE CARD_COUNT_PER_SUIT*CARD_SUIT_COUNT
struct Card {
public:
uint8_t cardValue;
/**
* Shuffles a hand / deck
*
* @param deck Array of cards to shuffle.
*/
static void shuffle(std::vector<struct Card> *deck);
/**
* Fills a vector with all of the cards in a deck, in order.
*
* @param deck Deck to fill.
*/
static void fillDeck(std::vector<struct Card> *deck);
/**
* Check if an array of cards contains a specific card.
*
* @param deck Deck/Hand/Array of cards to check.
* @param card Card to look for
* @returns The index within the array that the card is. -1 if not found.
*/
static int32_t contains(std::vector<struct Card> *deck, struct Card card);
/**
* Check if the array of cards contains a specific number.
*
* @param deck Array of cards to check
* @param number The number to look for.
* @returns The index that the first card is. -1 if not found.
*/
static int32_t containsNumber(
std::vector<struct Card> *deck,
enum CardValue number
);
/**
* Counts the amount of times a card's number appears within the given
* hand.
*
* @param deck The hand to check
* @param val Value of pairs to find.
* @return Card pairs in the deck.
*/
static std::vector<struct Card> countPairs(
std::vector<struct Card> *deck,
enum CardValue val
);
static bool_t cardSorter(struct Card left, struct Card right);
/**
* Sort a hand of cards. Cards are ordered in descending weight, aces are
* high. Cards will be grouped by their suits, e.g. CARD_CLUBS_TWO will
* appear before CARD_DIAMONDS_KING.
*
* @param deck Hand of cards to sort.
*/
static void sort(std::vector<struct Card> *deck);
Card(CardSuit suit, CardValue num) :
cardValue((suit * CARD_COUNT_PER_SUIT) + num)
{
if(suit == CARD_SUIT_INVALUD || num == CARD_VALUE_INVALD) {
this->cardValue = 0xFF;
}
}
Card(uint8_t cv) : cardValue(cv) {
// assertTrue(cv < CARD_DECK_SIZE);
}
/**
* Returns the number of a given card.
* @returns The card number.
*/
CardValue getValue() {
return (CardValue)(cardValue % CARD_COUNT_PER_SUIT);
}
/**
* Returns the suit of a given card.
* @returns The suit.
*/
CardSuit getSuit() {
return (CardSuit)(cardValue / CARD_COUNT_PER_SUIT);
}
};
}

View File

@ -1,197 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "PokerGame.hpp"
using namespace Dawn;
PokerGame::PokerGame(std::weak_ptr<SceneItem> item) : SceneItemComponent(item) {
}
void PokerGame::onStart() {
SceneItemComponent::onStart();
this->players = this->getScene()->findComponents<PokerPlayer>();
assertTrue(this->players.size() > 0);
this->newGame();
}
void PokerGame::newGame() {
this->newRound();
auto it = this->players.begin();
while(it != this->players.end()) {
auto player = *it;
player->setChips(POKER_PLAYER_CHIPS_DEFAULT);
player->isOut = false;
++it;
}
this->setDealer(0x00);
}
void PokerGame::newRound() {
this->deck.clear();
Card::fillDeck(&this->deck);
this->smallBlind = POKER_BLIND_SMALL_DEFAULT;
this->bigBlind = POKER_BLIND_BIG_DEFAULT;
this->grave.clear();
this->community.clear();
this->pots.clear();
this->pots.push_back(PokerPot());
auto it = this->players.begin();
while(it != this->players.end()) {
auto player = *it;
player->hand.clear();
player->currentBet = 0;
player->isFolded = false;
player->isShowingHand = false;
player->hasBetThisRound = false;
player->timesRaised = 0;
player->currentBet = 0;
++it;
}
this->setDealer(this->dealerIndex + 0x01);
}
void PokerGame::newBettingRound() {
auto it = this->players.begin();
while(it != this->players.end()) {
auto player = *it;
player->hasBetThisRound = false;
player->timesRaised = 0;
++it;
}
this->betterIndex = this->bigBlindIndex;
this->betterIndex = this->getNextBetterIndex();
}
uint8_t PokerGame::getNextBetterIndex() {
uint8_t j, i;
for(i = 0; i < this->players.size(); i++) {
j = (i + this->betterIndex) % this->players.size();
auto player = this->players[j];
if(player->needsToBetThisRound()) return j;
}
return 0xFF;
}
void PokerGame::takeBlinds() {
auto playerSmallBlind = this->players[this->smallBlindIndex];
auto playerBigBlind = this->players[this->bigBlindIndex];
playerSmallBlind->bet(this->smallBlind);
playerBigBlind->bet(this->bigBlind);
playerSmallBlind->hasBetThisRound = false;
playerBigBlind->hasBetThisRound = false;
}
void PokerGame::setDealer(uint8_t dealer) {
uint8_t i, k;
PokerPlayer *player;
bool_t foundDealer;
bool_t foundSmall;
foundDealer = false;
foundSmall = false;
this->dealerIndex = dealer;
for(i = 0; i < this->players.size(); i++) {
k = (dealer + i) % this->players.size();
player = this->players[k];
if(player->isOut) continue;
if(!foundDealer) {
this->dealerIndex = k;
foundDealer = true;
} else if(!foundSmall) {
this->smallBlindIndex = k;
foundSmall = true;
} else {
this->bigBlindIndex = k;
break;
}
}
}
uint8_t PokerGame::getRemainingBettersCount() {
uint8_t count = 0;
auto it = this->players.begin();
while(it != this->players.end()) {
if((*it)->needsToBetThisRound()) count++;
++it;
}
return count;
}
uint8_t PokerGame::getRemainingPlayersCount() {
uint8_t count = 0;
auto it = this->players.begin();
while(it != this->players.end()) {
auto player = *it;
if(!player->isFolded && !player->isOut) count++;
++it;
}
return count;
}
int32_t PokerGame::getCurrentCallValue() {
assertTrue(this->pots.size() > 0);
return this->pots.back().call;
}
void PokerGame::burnCard() {
assertTrue(this->deck.size() > 0);
auto card = this->deck.back();
this->deck.pop_back();
this->grave.push_back(card);
}
void PokerGame::dealCard(PokerPlayer *player) {
assertTrue(this->deck.size() > 0);
auto card = this->deck.back();
this->deck.pop_back();
player->hand.push_back(card);
}
void PokerGame::dealToEveryone(uint8_t count) {
for(uint8_t i = 0; i < count; i++) {
auto it = this->players.begin();
while(it != this->players.end()) {
this->dealCard(*it);
++it;
}
}
}
void PokerGame::turn(uint8_t count) {
assertTrue(this->deck.size() >= count);
for(uint8_t i = 0; i < count; i++) {
auto card = this->deck.back();
this->deck.pop_back();
this->community.push_back(card);
}
}
uint8_t PokerGame::getCountOfCardsToTurn() {
switch(this->community.size()) {
case 0x00:
return POKER_FLOP_CARD_COUNT;
case 0x03:
return POKER_TURN_CARD_COUNT;
case 0x04:
return POKER_RIVER_CARD_COUNT;
default:
return 0xFF;
}
}

View File

@ -1,59 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "PokerPlayer.hpp"
/** The default blind cost for the big blind. */
#define POKER_BLIND_BIG_DEFAULT 600
/** The default blind cost for the small blind. (Defaults half big blind) */
#define POKER_BLIND_SMALL_DEFAULT (POKER_BLIND_BIG_DEFAULT/2)
/** How many cards are dealt for the flop, turn and river */
#define POKER_FLOP_CARD_COUNT 3
#define POKER_TURN_CARD_COUNT 1
#define POKER_RIVER_CARD_COUNT 1
namespace Dawn {
class PokerGame : public SceneItemComponent {
protected:
std::vector<struct Card> deck;
std::vector<struct Card> grave;
std::vector<struct Card> community;
uint8_t dealerIndex;
uint8_t smallBlindIndex;
uint8_t bigBlindIndex;
int32_t smallBlind = POKER_BLIND_SMALL_DEFAULT;
int32_t bigBlind = POKER_BLIND_BIG_DEFAULT;
public:
std::vector<PokerPlayer*> players;
std::vector<struct PokerPot> pots;
uint8_t betterIndex;
PokerGame(std::weak_ptr<SceneItem> item);
void onStart() override;
void newGame();
void newRound();
void newBettingRound();
void takeBlinds();
void setBlinds(int32_t small, int32_t big);
uint8_t getRemainingBettersCount();
int32_t getCurrentCallValue();
uint8_t getNextBetterIndex();
void setDealer(uint8_t dealer);
void newDealer();
void burnCard();
void dealCard(PokerPlayer *player);
void dealToEveryone(uint8_t count);
void turn(uint8_t count);
uint8_t getCountOfCardsToTurn();
uint8_t getRemainingPlayersCount();
friend class PokerPlayer;
};
}

View File

@ -1,450 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "PokerPlayer.hpp"
#include "PokerGame.hpp"
using namespace Dawn;
PokerPlayer::PokerPlayer(std::weak_ptr<SceneItem> item) : SceneItemComponent(item) {
}
void PokerPlayer::onStart() {
SceneItemComponent::onStart();
this->pokerGame = this->getScene()->findComponent<PokerGame>();
}
void PokerPlayer::addChips(int32_t chips) {
assertTrue(chips > 0);
this->chips += chips;
if(this->chips > 0) this->isOut = false;
eventChipsChanged.invoke();
}
void PokerPlayer::setChips(int32_t chips) {
this->chips = 0;
this->addChips(chips);
}
bool_t PokerPlayer::needsToBetThisRound() {
if(this->isFolded) return false;
if(this->chips <= 0) return false;
if(!this->hasBetThisRound) return true;
if(this->currentBet < this->pokerGame->getCurrentCallValue()) return true;
return false;
}
void PokerPlayer::bet(struct PokerPot *pot, int32_t chips) {
assertNotNull(pot);
assertTrue(chips >= 0);
assertTrue(!this->isFolded);
assertTrue(!this->isOut);
this->setChips(this->chips - chips);
this->currentBet += chips;
this->hasBetThisRound = true;
if(chips > 0) {
this->timesRaised++;
} else {
this->timesRaised = 0;
}
pot->chips += chips;
pot->call = mathMax<int32_t>(pot->call, this ->currentBet);
auto existing = std::find(pot->players.begin(), pot->players.end(), this);
if(existing == pot->players.end()) pot->players.push_back(this);
}
void PokerPlayer::bet(int32_t chips) {
assertTrue(this->pokerGame->pots.size() > 0);
this->bet(&this->pokerGame->pots.back(), chips);
}
void PokerPlayer::fold() {
this->isFolded = true;
this->hasBetThisRound = true;
this->timesRaised = 0;
}
bool_t PokerPlayer::canCheck() {
return this->pokerGame->getCurrentCallValue() <= this->currentBet;
}
struct PokerTurn PokerPlayer::getAITurn() {
struct PokerTurn turn;
float_t confidence;
int32_t callBet;
float_t potOdds;
// Can the player do anything?
if(this->isFolded || this->isOut) {
turn.type = POKER_TURN_TYPE_OUT;
return turn;
}
// The following logic is heavily inspired by;
// https://github.com/gorel/C-Poker-AI/blob/master/src/common/pokerai.c
// But with some changes and smarts added by me. The original source code will
// essentially just run a crap tun of simulated games and get the times that
// they are expected to win from those games, but I'm just going to use the
// odds of the winning hand.
// Is this preflop?
if(this->pokerGame->community.size() == 0) {
assertTrue(this->hand.size() == POKER_PLAYER_HAND_SIZE_MAX);
// Get the hand weight
auto cardNumber0 = this->hand[0].getValue();
auto suitNumber0 = this->hand[0].getSuit();
auto cardNumber1 = this->hand[1].getValue();
auto suitNumber1 = this->hand[1].getSuit();
// Get delta between cards
auto i = (uint8_t)mathAbs<int8_t>(
(int8_t)cardNumber0 - (int8_t)cardNumber1
);
// Get card weight
confidence = (float_t)cardNumber0 + (float_t)cardNumber1;
if(cardNumber0 == cardNumber1) {// Pairs
confidence += 6;
} else if(suitNumber0 == suitNumber1) {// Same suit
confidence += 4;
}
// Get difference from cards for guessing flush
if(i > 4) {
confidence -= 4;
} else if(i > 2) {
confidence -= i;
}
// Get the confidence delta 0-1
confidence = confidence / 30.0f;
// This may change in future, but I was finding the AI did not want to bet
// during the preflop enough, this curves the AI to want to preflop call
// often.
confidence = easeOutCubic(confidence);
} else {
// Simulate my hand being the winning hand, use that as the confidence
auto winning = this->getWinning();
confidence = PokerWinning::getWinningTypeConfidence(winning.type);
}
// Now we know how confident the AI is, let's put a chip value to that weight
// How many chips to call?
callBet = this->getCallBet();
// Do they need chips to call, or is it possible to check?
if(callBet > 0) {
potOdds = (float_t)callBet / (
(float_t)callBet +
(float_t)this->getSumOfChips()
);
} else {
potOdds = 1.0f / (float_t)this->pokerGame->getRemainingBettersCount();
}
// Now determine the expected ROI
auto expectedGain = confidence / potOdds;
// Now get a random 0-100
auto random = randomGenerate<int32_t>() % 100;
// Determine the max bet that the AI is willing to make
auto maxBet = (int32_t)((float_t)this->chips / 1.75f) - (random / 2);
maxBet -= callBet;
// Determine what's a good bluff bet.
auto bluffBet = random * maxBet / 100 / 2;
// Now prep the output
auto isBluff = false;
auto amount = 0;
// Now the actual AI can happen. This is basically a weight to confidence
// ratio. The higher the gains and the confidence then the more likely the AI
// is to betting. There are also bluff chances within here.
if(expectedGain < 0.8f && confidence < 0.8f) {
if(random < 85) {
amount = 0;
} else {
amount = bluffBet;
isBluff = true;
}
} else if((expectedGain < 1.0f && confidence < 0.85f) || confidence < 0.1f) {
if(random < 80) {
amount = 0;
} else if(random < 5) {
amount = callBet;
isBluff = true;
} else {
amount = bluffBet;
isBluff = true;
}
} else if((expectedGain < 1.3f && confidence < 0.9f) || confidence < 0.5f) {
if(random < 60 || confidence < 0.5f) {
amount = callBet;
} else {
amount = maxBet;
}
} else if(confidence < 0.95f || this->pokerGame->community.size() < 4) {
if(random < 20) {
amount = callBet;
} else {
amount = maxBet;
}
} else {
amount = (this->chips - callBet) * 9 / 10;
}
// TODO: We can nicely round the amounts here to get us to a more "human"
// number.
// If this is the first round... make it a lot less likely I'll bet
if(this->pokerGame->community.size() == 0 && amount > callBet) {
if(random > 5) amount = callBet;
}
// Did we actually bet?
if(amount > 0) {
std::cout << "AI is betting " << amount << " chips, bluff:" << isBluff << std::endl;
// Let's not get caught in a raising loop with AI.
if(this->timesRaised >= POKER_PLAYER_MAX_RAISES) {
amount = callBet;
}
amount = mathMax<int32_t>(amount, callBet);
turn = PokerTurn::bet(this, amount);
turn.confidence = confidence;
} else if(this->canCheck()) {
turn = PokerTurn::bet(this, 0);
turn.confidence = 1;
} else {
turn = PokerTurn::fold(this);
turn.confidence = 1 - confidence;
}
return turn;
}
int32_t PokerPlayer::getCallBet() {
return this->pokerGame->getCurrentCallValue() - this->currentBet;
}
int32_t PokerPlayer::getSumOfChips() {
int32_t count = 0;
auto it = this->pokerGame->pots.begin();
while(it != this->pokerGame->pots.end()) {
if(std::find(it->players.begin(), it->players.end(), this) != it->players.end()) {
count += it->chips;
}
++it;
}
return count;
}
struct PokerWinning PokerPlayer::getWinning() {
struct PokerWinning winning;
struct Card card(0x00);
uint8_t i, j;
int32_t index;
enum CardValue number, look;
enum CardSuit suit;
std::vector<struct Card> pairs;
winning.player = this;
// Get the full poker hand (should be a 7 card hand, but MAY not be)
for(i = 0; i < this->pokerGame->community.size(); i++) {
winning.full.push_back(this->pokerGame->community[i]);
}
for(i = 0; i < this->hand.size(); i++) {
winning.full.push_back(this->hand[i]);
}
Card::sort(&winning.full);
//////////////////////// Now look for the winning set ////////////////////////
// Royal / Straight Flush
for(i = 0; i < winning.full.size(); i++) {
card = winning.full[i];
number = card.getValue();
if(number < CARD_FIVE) continue;
suit = card.getSuit();
winning.set.clear();
winning.set.push_back(card);
// Now look for the matching cards (Reverse order to order from A to 10)
for(j = 1; j <= 4; j++) {
// Ace low.
look = (
number == CARD_FIVE && j == 4 ?
(enum CardValue)CARD_ACE :
(enum CardValue)(number - j)
);
index = Card::contains(&winning.full, Card(suit, look));
if(index == -1) break;
winning.set.push_back(winning.full[index]);
}
// Check if has all necessary cards.
if(winning.set.size() < POKER_WINNING_SET_SIZE) continue;
// Add self to array
winning.type = (
number == CARD_ACE ? POKER_WINNING_TYPE_ROYAL_FLUSH :
POKER_WINNING_TYPE_STRAIGHT_FLUSH
);
winning.fillRemaining();
return winning;
}
// Four of a kind.
for(i = 0; i < winning.full.size(); i++) {
card = winning.full[i];
number = card.getValue();
pairs = Card::countPairs(&winning.full, number);
if(pairs.size() < CARD_SUIT_COUNT) continue;
winning.set = pairs;
winning.type = POKER_WINNING_TYPE_FOUR_OF_A_KIND;
winning.fillRemaining();
return winning;
}
// Full House
winning.set.clear();
for(i = 0; i < winning.full.size(); i++) {
// Check we haven't already added this card.
card = winning.full[i];
if(Card::contains(&winning.set, card) != -1) {
continue;
}
number = card.getValue();
pairs = Card::countPairs(&winning.full, number);
// Did we find either two pair or three pair?
if(pairs.size() != 2 && pairs.size() != 3) continue;
if(winning.set.size() == 3) {//Clamp to 5 max.
pairs.pop_back();
}
// Copy found pairs.
for(j = 0; j < pairs.size(); j++) {
winning.set.push_back(pairs[j]);
}
// Winned?
if(winning.set.size() != POKER_WINNING_SET_SIZE) continue;
winning.type = POKER_WINNING_TYPE_FULL_HOUSE;
winning.fillRemaining();
return winning;
}
// Flush (5 same suit)
for(i = 0; i < winning.full.size(); i++) {
card = winning.full[i];
suit = card.getSuit();
winning.set.clear();
winning.set.push_back(card);
for(j = i+1; j < winning.full.size(); j++) {
if(winning.full[j].getSuit() != suit) continue;
winning.set.push_back(winning.full[j]);
if(winning.set.size() == POKER_WINNING_SET_SIZE) break;
}
if(winning.set.size() < POKER_WINNING_SET_SIZE) continue;
winning.type = POKER_WINNING_TYPE_FLUSH;
winning.fillRemaining();
return winning;
}
// Straight (sequence any suit)
for(i = 0; i < winning.full.size(); i++) {
card = winning.full[i];
number = card.getValue();
if(number < CARD_FIVE) continue;
winning.set.clear();
winning.set.push_back(card);
for(j = 1; j <= 4; j++) {
// Ace low.
look = (
number == CARD_FIVE && j == 4 ?
(enum CardValue)CARD_ACE :
(enum CardValue)(number - j)
);
index = Card::containsNumber(&winning.full, look);
if(index == -1) break;
winning.set.push_back(winning.full[index]);
}
// Check if has all necessary cards.
if(winning.set.size() < POKER_WINNING_SET_SIZE) continue;
winning.type = POKER_WINNING_TYPE_STRAIGHT;
winning.fillRemaining();
return winning;
}
// Three of a kind
for(i = 0; i < winning.full.size(); i++) {
card = winning.full[i];
number = card.getValue();
pairs = Card::countPairs(&winning.full, number);
if(pairs.size() != 3) continue;
winning.set = pairs;
winning.type = POKER_WINNING_TYPE_THREE_OF_A_KIND;
winning.fillRemaining();
return winning;
}
// Two Pair
winning.set.clear();
for(i = 0; i < winning.full.size(); i++) {
card = winning.full[i];// Check we haven't already added this card.
if(
winning.set.size() > 0 &&
Card::contains(&winning.set, card) != -1
) {
continue;
}
number = card.getValue();
pairs = Card::countPairs(&winning.full, number);
if(pairs.size() != 2) continue;
for(j = 0; j < pairs.size(); j++) {
winning.set.push_back(pairs[j]);
}
if(winning.set.size() != 4) continue;
winning.type = POKER_WINNING_TYPE_TWO_PAIR;
winning.fillRemaining();
return winning;
}
// Pair
if(winning.set.size() == 2) {
winning.type = POKER_WINNING_TYPE_PAIR;
winning.fillRemaining();
return winning;
}
// High card
winning.set.clear();
winning.fillRemaining();
winning.type = POKER_WINNING_TYPE_HIGH_CARD;
return winning;
}

View File

@ -1,132 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "scene/SceneItemComponent.hpp"
#include "Card.hpp"
#include "PokerPot.hpp"
#include "util/mathutils.hpp"
#include "display/animation/Easing.hpp"
#include "PokerWinning.hpp"
#include "PokerTurn.hpp"
#include "util/random.hpp"
#define POKER_PLAYER_CHIPS_DEFAULT 10000
/** Maximum cards a players' hand can hold */
#define POKER_PLAYER_HAND_SIZE_MAX 2
#define POKER_PLAYER_MAX_RAISES 0x02
namespace Dawn {
class PokerGame;
class PokerPlayer : public SceneItemComponent {
public:
PokerGame *pokerGame;
int32_t chips = 0;
int32_t currentBet = 0;
uint8_t timesRaised = 0;
bool_t isFolded = false;
bool_t isOut = false;
bool_t hasBetThisRound = false;
bool_t isShowingHand = false;
bool_t isHuman = false;
std::vector<struct Card> hand;
Event<> eventChipsChanged;
/**
* Creates a PokerPlayer instance.
*
* @param item Item that this poker player belongs to.
*/
PokerPlayer(std::weak_ptr<SceneItem> item);
/** Override for scene item component event for init */
void onStart() override;
/**
* Adds chips to the player. This will also update the players' state.
*
* @param chips Count of chips to add.
*/
void addChips(int32_t chips);
/**
* Sets the chips a player has.
*
* @param chips Chips to set to the player.
*/
void setChips(int32_t chips);
/**
* Returns true if the player still needs to bet this betting round.
*
* @return True if betting is still required by this player.
*/
bool_t needsToBetThisRound();
/**
* Let a player bet chips into the pot.
*
* @param pot Poker pot to bet in to.
* @param amount The amount of chips the player is betting.
*/
void bet(struct PokerPot *pot, int32_t amount);
/**
* Let a player bet chips into the current pot.
*
* @param amount The amount of chips the player is betting.
*/
void bet(int32_t amount);
/**
* Player folds.
*/
void fold();
/**
* Returns the AI result for a turn done by a non human player.
*
* @return Some information about the move the player is trying to perform
*/
struct PokerTurn getAITurn();
/**
* Calculates and returns the winning state for a given player
*
* @return The winning state for this current players hand.
*/
struct PokerWinning getWinning();
/**
* Returns the sum of chips in the pot(s) that the specified player is in.
* This does not consider the pot, player or hand, just the pure sum of
* chips.
*
* @return The sum of chips from the pots the player is within.
*/
int32_t getSumOfChips();
/**
* Get the bet necessary for a specific player to make a call. This takes
* the players current bet and the bet necessary to call into the pot and
* will return the difference.
*
* @return The count of chips needed to call into the current active pot.
*/
int32_t getCallBet();
/**
* Returns whether or not the player can check, or if they need to either
* fold, call or bet.
*
* @return True if they can check.
*/
bool_t canCheck();
};
}

View File

@ -1,111 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "PokerPot.hpp"
#include "PokerGame.hpp"
#include "PokerPlayer.hpp"
using namespace Dawn;
void PokerPotWinning::award() {
auto it = this->winners.begin();
while(it != this->winners.end()) {
if(it == this->winners.begin()) {
(*it)->addChips(this->chipsEach + this->chipsOverflow);
} else {
(*it)->addChips(this->chipsEach);
}
++it;
}
}
struct PokerPotWinning PokerPot::getWinners(PokerGame *game) {
struct PokerPotWinning winning;
winning.pot = this;
// Calculate the winnings first.
auto it = this->players.begin();
while(it != this->players.end()) {
auto player = *it;
if(player->isOut || player->isFolded) {
++it;
continue;
}
winning.participants.push_back(player);
winning.winnings[player] = player->getWinning();
++it;
}
// Compare participating players
auto it2 = winning.participants.begin();
while(it2 != winning.participants.end()) {
auto playerLeft = *it2;
auto winnerLeft = &winning.winnings[playerLeft];
bool_t isWinner = true;
enum CardValue highNumber = CARD_VALUE_INVALD;
enum CardValue number = CARD_VALUE_INVALD;
struct Card highCard(0xFF);
struct Card card(0xFF);
auto it3 = winning.participants.begin();
while(it3 != winning.participants.end()) {
if(it2 == it3) {
++it3;
continue;
}
auto playerRight = *it3;
auto winnerRight = &winning.winnings[playerRight];
// Am I the better hand / Is it the better hand?
if(winnerLeft->type < winnerRight->type) {
++it3;
continue;
}
if(winnerLeft->type > winnerRight->type) {
isWinner = false;
break;
}
// Equal, compare hands.
card = PokerWinning::compare(winnerLeft, winnerRight);
if(card.cardValue == 0xFF) {
isWinner = false;
break;
}
// Determine high card.
number = card.getValue();
if(
highNumber == CARD_VALUE_INVALD ||
number == CARD_ACE ||
number > highNumber
) {
highCard = card;
highNumber = number;
}
++it3;
}
if(!isWinner) {
++it2;
continue;
}
winnerLeft->kicker = highCard;
winning.winners.push_back(playerLeft);
++it2;
}
winning.chipsEach = this->chips / (int32_t)winning.winners.size();
winning.chipsOverflow = this->chips - (
winning.chipsEach * (int32_t)winning.winners.size()
);
return winning;
}

View File

@ -1,34 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "PokerWinning.hpp"
namespace Dawn {
class PokerPlayer;
class PokerGame;
struct PokerPot;
struct PokerPotWinning {
public:
std::map<PokerPlayer*,struct PokerWinning> winnings;
std::vector<PokerPlayer*> winners;
std::vector<PokerPlayer*> participants;
struct PokerPot *pot;
int32_t chipsEach;
int32_t chipsOverflow;
void award();
};
struct PokerPot {
public:
int32_t chips;
int32_t call;
std::vector<PokerPlayer*> players;
struct PokerPotWinning getWinners(PokerGame *game);
};
}

View File

@ -1,72 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "PokerTurn.hpp"
#include "PokerPlayer.hpp"
#include "PokerGame.hpp"
using namespace Dawn;
struct PokerTurn PokerTurn::bet(PokerPlayer *player, int32_t chips) {
struct PokerTurn turn;
int32_t i;
assertNotNull(player);
assertTrue(chips >= 0);
turn.player = player;
turn.confidence = 1;
if(chips == 0) {
turn.type = POKER_TURN_TYPE_CHECK;
turn.chips = 0;
} else if(player->chips <= chips) {
turn.chips = player->chips;
turn.type = POKER_TURN_TYPE_ALL_IN;
} else {
turn.chips = chips;
turn.type = POKER_TURN_TYPE_BET;
i = player->pokerGame->getCurrentCallValue();
if(chips == (i - player->currentBet)) {
turn.type = POKER_TURN_TYPE_CALL;
}
}
return turn;
}
struct PokerTurn PokerTurn::fold(PokerPlayer *player) {
struct PokerTurn turn;
turn.player = player;
turn.chips = 0;
turn.confidence = 1;
turn.type = POKER_TURN_TYPE_FOLD;
return turn;
}
void PokerTurn::action() {
assertNotNull(this->player);
switch(this->type) {
case POKER_TURN_TYPE_BET:
case POKER_TURN_TYPE_CALL:
case POKER_TURN_TYPE_ALL_IN:
this->player->bet(this->chips);
break;
case POKER_TURN_TYPE_CHECK:
player->bet(0);
break;
case POKER_TURN_TYPE_FOLD:
player->fold();
break;
default:
assertUnreachable();
break;
}
}

View File

@ -1,53 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
namespace Dawn {
class PokerPlayer;
enum PokerTurnType {
POKER_TURN_TYPE_OUT,
POKER_TURN_TYPE_FOLD,
POKER_TURN_TYPE_BET,
POKER_TURN_TYPE_CALL,
POKER_TURN_TYPE_CHECK,
POKER_TURN_TYPE_ALL_IN
};
struct PokerTurn {
public:
/** What type of action the turn is */
enum PokerTurnType type;
/** How many chips they did in their turn (if applicable) */
int32_t chips;
/** How confident the AI is about their turn. 0 = none, 1 = full */
float_t confidence;
/** Player that this action belongs to */
PokerPlayer *player;
/**
* Generate a turn action for betting as a player.
*
* @param player Player index who is betting.
* @param chips Chips to raise by.
* @return A turn for a bet action.
*/
static struct PokerTurn bet(PokerPlayer *player, int32_t chips);
/**
* Return a turn action for the given player to fold.
*
* @return A turn for a fold action.
*/
static struct PokerTurn fold(PokerPlayer *player);
/**
* Actions / Performs this turn against the defined player.
*/
void action();
};
}

View File

@ -1,159 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "PokerWinning.hpp"
#include "PokerPlayer.hpp"
using namespace Dawn;
float_t PokerWinning::getWinningTypeConfidence(enum PokerWinningType type) {
switch(type) {
case POKER_WINNING_TYPE_ROYAL_FLUSH:
return POKER_WINNING_CONFIDENCE_ROYAL_FLUSH;
case POKER_WINNING_TYPE_STRAIGHT_FLUSH:
return POKER_WINNING_CONFIDENCE_STRAIGHT_FLUSH;
case POKER_WINNING_TYPE_FOUR_OF_A_KIND:
return POKER_WINNING_CONFIDENCE_FOUR_OF_A_KIND;
case POKER_WINNING_TYPE_FULL_HOUSE:
return POKER_WINNING_CONFIDENCE_FULL_HOUSE;
case POKER_WINNING_TYPE_FLUSH:
return POKER_WINNING_CONFIDENCE_FLUSH;
case POKER_WINNING_TYPE_STRAIGHT:
return POKER_WINNING_CONFIDENCE_STRAIGHT;
case POKER_WINNING_TYPE_THREE_OF_A_KIND:
return POKER_WINNING_CONFIDENCE_THREE_OF_A_KIND;
case POKER_WINNING_TYPE_TWO_PAIR:
return POKER_WINNING_CONFIDENCE_TWO_PAIR;
case POKER_WINNING_TYPE_PAIR:
return POKER_WINNING_CONFIDENCE_PAIR;
default:
return POKER_WINNING_CONFIDENCE_HIGH_CARD;
}
}
struct Card PokerWinning::compare(
struct PokerWinning *left,
struct PokerWinning *right
) {
assertNotNull(left);
assertNotNull(right);
uint8_t i;
enum CardValue number = CARD_VALUE_INVALD;
enum CardValue highNumberLeft = CARD_VALUE_INVALD;
enum CardValue highNumberRight = CARD_VALUE_INVALD;
struct Card card(0xFF), highCardLeft(0xFF), highCardRight(0xFF);
int32_t index;
uint8_t countCardsSame;
countCardsSame = 0;
for(i = 0; i < left->set.size(); i++) {
card = left->set[i];
number = card.getValue();
// Quick check
if(highNumberLeft != CARD_VALUE_INVALD && number < highNumberLeft) continue;
// Check if this number is within the other hand or not
index = Card::containsNumber(&right->set, number);
if(index != -1) {
// This number IS within the other hand, let's check that the EXACT card
// is a match/isn't a match.
index = Card::contains(&right->set, card);
// Exact card match
if(index != -1) {
countCardsSame++;
continue;
}
// Not exact card match.. ?
}
if(
highNumberLeft == CARD_VALUE_INVALD ||
number == CARD_ACE ||
highNumberLeft < number
) {
highNumberLeft = number;
highCardLeft = card;
}
}
for(i = 0; i < right->set.size(); i++) {
card = right->set[i];
number = card.getValue();
if(highNumberRight != CARD_VALUE_INVALD && number < highNumberRight) {
continue;
}
index = Card::containsNumber(&left->set, number);
if(index != -1) {
index = Card::contains(&left->set, card);
if(index != -1) continue;
}
if(
highNumberRight == CARD_VALUE_INVALD ||
number == CARD_ACE || highNumberRight < number
) {
highNumberRight = number;
highCardRight = card;
}
}
if(countCardsSame == left->set.size()) {
for(i = 0; i < left->set.size(); i++) {
card = left->set[i];
number = card.getValue();
if(
highNumberLeft == CARD_VALUE_INVALD ||
number == CARD_ACE ||
highNumberLeft < number
) {
highNumberLeft = number;
highCardLeft = card;
}
}
return highCardLeft;
}
if(highCardLeft.cardValue == CARD_VALUE_INVALD) return 0xFF;
if(highNumberLeft < highNumberRight) return 0xFF;
return highCardLeft;// Greater or Equal to.
}
void PokerWinning::fillRemaining() {
uint8_t i, highest, current;
struct Card highestCard(0x00);
struct Card currentCard(0x00);
// Set the kicker
this->kicker = 0xFF;
// Fill the remaining cards
while(this->set.size() < POKER_WINNING_SET_SIZE) {
highest = 0xFF;
for(i = 0; i < this->full.size(); i++) {
currentCard = this->full[i];
if(Card::contains(&this->set, currentCard) != -1) continue;
if(highest == 0xFF) {
highestCard = currentCard;
highest = highestCard.getValue();
} else {
current = currentCard.getValue();
if(current != CARD_ACE && current < highest) continue;
highestCard = currentCard;
highest = current;
}
}
if(highest == 0xFF) break;
this->set.push_back(highestCard);
}
Card::sort(&this->set);
}

View File

@ -1,86 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dawnlibs.hpp"
#include "Card.hpp"
#define POKER_WINNING_CONFIDENCE_ROYAL_FLUSH 1.0f
#define POKER_WINNING_CONFIDENCE_STRAIGHT_FLUSH 0.99f
#define POKER_WINNING_CONFIDENCE_FOUR_OF_A_KIND 0.9f
#define POKER_WINNING_CONFIDENCE_FULL_HOUSE 0.85f
#define POKER_WINNING_CONFIDENCE_FLUSH 0.8f
#define POKER_WINNING_CONFIDENCE_STRAIGHT 0.7f
#define POKER_WINNING_CONFIDENCE_THREE_OF_A_KIND 0.5f
#define POKER_WINNING_CONFIDENCE_TWO_PAIR 0.4f
#define POKER_WINNING_CONFIDENCE_PAIR 0.2f
#define POKER_WINNING_CONFIDENCE_HIGH_CARD 0.1f
/** How many cards in the winning set */
#define POKER_WINNING_SET_SIZE 5
namespace Dawn {
class PokerPlayer;
enum PokerWinningType {
POKER_WINNING_TYPE_NULL,
POKER_WINNING_TYPE_ROYAL_FLUSH,
POKER_WINNING_TYPE_STRAIGHT_FLUSH,
POKER_WINNING_TYPE_FOUR_OF_A_KIND,
POKER_WINNING_TYPE_FULL_HOUSE,
POKER_WINNING_TYPE_FLUSH,
POKER_WINNING_TYPE_STRAIGHT,
POKER_WINNING_TYPE_THREE_OF_A_KIND,
POKER_WINNING_TYPE_TWO_PAIR,
POKER_WINNING_TYPE_PAIR,
POKER_WINNING_TYPE_HIGH_CARD
};
struct PokerWinning {
public:
/**
* Get the confidence of the bet for a given winning type.
*
* @param type Winning type type.
* @return The confidence.
*/
static float_t getWinningTypeConfidence(enum PokerWinningType type);
/**
* Compares two winning sets. The returned card is the kicker if the LEFT
* side is the winner. If LEFT is not a winner then 0xFF will be returned.
*
* @param left Left winning set.
* @param right Right winning set.
* @return The kicker card from left's hand or 0xFF if not the winner.
*/
static struct Card compare(
struct PokerWinning *left,
struct PokerWinning *right
);
/** Winning Type */
enum PokerWinningType type;
/** The full set of both the dealer and player's hand */
std::vector<struct Card> full;
/** Holds the winning set */
std::vector<struct Card> set;
/** If there was a kicker card it will be here */
struct Card kicker;
/* The player this winning state belongs to */
PokerPlayer *player;
PokerWinning() : kicker(0xFF) {}
/**
* Fills the remaining cards for a given poker player winning hand.
* Essentially this will just take the highest cards and slot them into
* the array. This also sorts the cards.
*
* @param winning Pointer to the poker winning to fill out.
*/
void fillRemaining();
};
}

View File

@ -1,10 +0,0 @@
# Copyright (c) 2022 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
PokerDetermineBetterEvent.cpp
)

View File

@ -1,142 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "PokerGameEvent.hpp"
#define POKER_DEAL_EVENT_CARD_COUNT 2
namespace Dawn {
class PokerAIBetEvent : public PokerGameEvent {
protected:
IVisualNovelEvent * eventFold = nullptr;
IVisualNovelEvent * eventBet = nullptr;
IVisualNovelEvent * eventCall = nullptr;
IVisualNovelEvent * eventCheck = nullptr;
IVisualNovelEvent * eventAllIn = nullptr;
void onStart(IVisualNovelEvent *previous) override {
PokerGameEvent::onStart(previous);
auto better = this->pokerGame->getNextBetterIndex();
this->pokerGame->betterIndex = better;
auto player = this->pokerGame->players[better];
this->turn = player->getAITurn();
this->turn.action();
switch(this->turn.type) {
case POKER_TURN_TYPE_FOLD:
this->then(this->eventFold);
this->eventFold = nullptr;
break;
case POKER_TURN_TYPE_BET:
this->then(this->eventBet);
this->eventBet = nullptr;
break;
case POKER_TURN_TYPE_CALL:
this->then(this->eventCall);
this->eventCall = nullptr;
break;
case POKER_TURN_TYPE_CHECK:
this->then(this->eventCheck);
this->eventCheck = nullptr;
break;
case POKER_TURN_TYPE_ALL_IN:
this->then(this->eventAllIn);
this->eventAllIn = nullptr;
break;
default:
assertUnreachable();
}
}
bool_t onUpdate() override {
return false;
}
void onEnd() override {
}
public:
uint8_t better;
struct PokerTurn turn;
PokerAIBetEvent(VisualNovelManager *manager) : PokerGameEvent(manager) {
}
/**
* Event that is triggered when the action was a folded event.
*
* @param event Event to trigger.
*/
template<class T>
T * whenFolded(T *event) {
assertNotNull(event);
this->eventFold = event;
return event;
}
/**
* Event that is triggered when the action was a bet event.
*
* @param event Event to trigger.
*/
template<class T>
T * whenBetting(T *event) {
assertNotNull(event);
this->eventBet = event;
return event;
}
/**
* Event that is triggered when the action was a call event.
*
* @param event Event to trigger.
*/
template<class T>
T * whenCalling(T *event) {
assertNotNull(event);
this->eventCall = event;
return event;
}
/**
* Event that is triggered when the action was a check event.
*
* @param event Event to trigger.
*/
template<class T>
T * whenChecking(T *event) {
assertNotNull(event);
this->eventCheck = event;
return event;
}
/**
* Event that is triggered when the action was an all-in event.
*
* @param event Event to trigger.
*/
template<class T>
T * whenAllIn(T *event) {
assertNotNull(event);
this->eventAllIn = event;
return event;
}
~PokerAIBetEvent() {
if(this->eventFold != nullptr) delete this->eventFold;
if(this->eventBet != nullptr) delete this->eventBet;
if(this->eventCall != nullptr) delete this->eventCall;
if(this->eventCheck != nullptr) delete this->eventCheck;
if(this->eventAllIn != nullptr) delete this->eventAllIn;
}
};
}

View File

@ -1,31 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "PokerGameEvent.hpp"
#define POKER_DEAL_EVENT_CARD_COUNT 2
namespace Dawn {
class PokerDealEvent : public PokerGameEvent {
protected:
void onStart(IVisualNovelEvent *previous) override {
PokerGameEvent::onStart(previous);
std::cout << "Dealing to everyone" << std::endl;
this->pokerGame->dealToEveryone(POKER_DEAL_EVENT_CARD_COUNT);
}
bool_t onUpdate() override {
return false;
}
void onEnd() override {
}
public:
PokerDealEvent(VisualNovelManager *manager) : PokerGameEvent(manager) {
}
};
}

View File

@ -1,67 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "PokerDetermineBetterEvent.hpp"
using namespace Dawn;
PokerDetermineBetterEvent::PokerDetermineBetterEvent(VisualNovelManager *man) :
PokerGameEvent(man)
{
std::cout << "Inited determine better" << std::endl << std::endl;
}
void PokerDetermineBetterEvent::onStart(IVisualNovelEvent *previous) {
PokerGameEvent::onStart(previous);
std::cout << "better evt" << std::endl;
if(this->pokerGame->getRemainingPlayersCount() <= 1) {
this->then(this->eventEveryoneFolded);
this->eventEveryoneFolded = nullptr;
std::cout << "Everyone folded" << std::endl;
return;
}
uint8_t nextBetterIndex = this->pokerGame->getNextBetterIndex();
if(nextBetterIndex == 0xFF) {
if(this->pokerGame->getCountOfCardsToTurn() == 0xFF) {
this->then(this->eventBettingFinished);
this->eventBettingFinished = nullptr;
std::cout << "Betting Finished" << std::endl;
} else {
this->then(this->eventTurn);
this->eventTurn = nullptr;
std::cout << "Turn Time?" << std::endl;
}
return;
}
auto nextBetter = this->pokerGame->players[nextBetterIndex];
if(nextBetter->isHuman) {
this->then(this->eventHumanBet);
this->eventHumanBet = nullptr;
std::cout << "Human Better" << std::endl;
} else {
this->then(this->eventAiBet);
this->eventAiBet = nullptr;
std::cout << "AI Better" << std::endl;
}
}
bool_t PokerDetermineBetterEvent::onUpdate() {
return false;
}
void PokerDetermineBetterEvent::onEnd() {
}
PokerDetermineBetterEvent::~PokerDetermineBetterEvent() {
if(this->eventEveryoneFolded != nullptr) delete this->eventEveryoneFolded;
if(this->eventBettingFinished != nullptr) delete this->eventBettingFinished;
if(this->eventTurn != nullptr) delete this->eventTurn;
if(this->eventAiBet != nullptr) delete this->eventAiBet;
if(this->eventHumanBet != nullptr) delete this->eventHumanBet;
}

View File

@ -1,101 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "PokerGameEvent.hpp"
namespace Dawn {
class PokerDetermineBetterEvent : public PokerGameEvent {
protected:
IVisualNovelEvent * eventEveryoneFolded = nullptr;
IVisualNovelEvent * eventBettingFinished = nullptr;
IVisualNovelEvent * eventTurn = nullptr;
IVisualNovelEvent * eventAiBet = nullptr;
IVisualNovelEvent * eventHumanBet = nullptr;
void onStart(IVisualNovelEvent *previous) override;
bool_t onUpdate() override;
void onEnd() override;
public:
/**
* Construct a better determine event.
*
* @param manager VN manager that this event belongs to.
*/
PokerDetermineBetterEvent(VisualNovelManager *manager);
/**
* Sets the event that is triggered when everyone is folded.
*
* @param event Event to trigger.
* @return The event that was passed in.
*/
template<class T>
T * whenEveryoneFolded(T *event) {
assertNotNull(event);
this->eventEveryoneFolded = event;
return event;
}
/**
* Sets the event that is triggered after everyone has finished betting.
*
* @param event Event to trigger.
* @return The event that was passed in.
*/
template<class T>
T * whenBettingFinished(T *event) {
assertNotNull(event);
this->eventBettingFinished = event;
return event;
}
/**
* Event to trigger when all betting is finished and its time to turn the
* next set of community cards.
*
* @param event Event to trigger.
* @return The event that was passed in.
*/
template<class T>
T * whenTurn(T *event) {
assertNotNull(event);
this->eventTurn = event;
return event;
}
/**
* Sets the event to trigger when its an AI's turn to bet.
*
* @param event Event to trigger.
* @return The event that was passed in.
*/
template<class T>
T * whenAiBet(T *event) {
assertNotNull(event);
this->eventAiBet = event;
return event;
}
/**
* Sets the event to trigger when its a human's turn to bet.
*
* @param event Event to trigger.
* @return The event that was passed in.
*/
template<class T>
T * whenHumanBet(T *event) {
assertNotNull(event);
this->eventHumanBet = event;
return event;
}
/**
* Dispose override for the event.
*/
~PokerDetermineBetterEvent();
};
}

View File

@ -1,27 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "visualnovel/VisualNovelManager.hpp"
#include "poker/PokerGame.hpp"
namespace Dawn {
class PokerGameEvent : public IVisualNovelEvent {
protected:
PokerGame *pokerGame;
void onStart(IVisualNovelEvent *previous) override {
pokerGame = this->manager->getScene()->findComponent<PokerGame>();
assertNotNull(pokerGame);
}
public:
PokerGameEvent(VisualNovelManager *manager) :
IVisualNovelEvent(manager)
{
}
};
}

View File

@ -1,29 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "PokerGameEvent.hpp"
namespace Dawn {
class PokerNewBettingRoundEvent : public PokerGameEvent {
protected:
void onStart(IVisualNovelEvent *previous) override {
PokerGameEvent::onStart(previous);
std::cout << "New Betting Round" << std::endl;
this->pokerGame->newBettingRound();
}
bool_t onUpdate() override {
return false;
}
void onEnd() override {
}
public:
PokerNewBettingRoundEvent(VisualNovelManager *manager) : PokerGameEvent(manager) {
}
};
}

View File

@ -1,29 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "PokerGameEvent.hpp"
namespace Dawn {
class PokerNewGameEvent : public PokerGameEvent {
protected:
void onStart(IVisualNovelEvent *previous) override {
PokerGameEvent::onStart(previous);
std::cout << "New Game" << std::endl;
this->pokerGame->newGame();
}
bool_t onUpdate() override {
return false;
}
void onEnd() override {
}
public:
PokerNewGameEvent(VisualNovelManager *manager) : PokerGameEvent(manager) {
}
};
}

View File

@ -1,29 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "PokerGameEvent.hpp"
namespace Dawn {
class PokerNewRoundEvent : public PokerGameEvent {
protected:
void onStart(IVisualNovelEvent *previous) override {
PokerGameEvent::onStart(previous);
std::cout << "New Round" << std::endl;
this->pokerGame->newRound();
}
bool_t onUpdate() override {
return false;
}
void onEnd() override {
}
public:
PokerNewRoundEvent(VisualNovelManager *manager) : PokerGameEvent(manager) {
}
};
}

View File

@ -1,29 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "PokerGameEvent.hpp"
namespace Dawn {
class PokerTakeBlindsEvent : public PokerGameEvent {
protected:
void onStart(IVisualNovelEvent *previous) override {
PokerGameEvent::onStart(previous);
std::cout << "Take Blinds" << std::endl;
this->pokerGame->takeBlinds();
}
bool_t onUpdate() override {
return false;
}
void onEnd() override {
}
public:
PokerTakeBlindsEvent(VisualNovelManager *manager) : PokerGameEvent(manager) {
}
};
}

View File

@ -1,34 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "PokerGameEvent.hpp"
namespace Dawn {
class PokerTurnEvent : public PokerGameEvent {
protected:
void onStart(IVisualNovelEvent *previous) override {
PokerGameEvent::onStart(previous);
this->cardsTurned = this->pokerGame->getCountOfCardsToTurn();
assertTrue(this->cardsTurned != 0xFF);
this->pokerGame->turn(this->cardsTurned);
}
bool_t onUpdate() override {
return false;
}
void onEnd() override {
}
public:
uint8_t cardsTurned;
PokerTurnEvent(VisualNovelManager *manager) : PokerGameEvent(manager) {
}
};
}

View File

@ -1,46 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "PokerGameEvent.hpp"
#include "poker/PokerPot.hpp"
namespace Dawn {
class PokerWinnerEvent : public PokerGameEvent {
protected:
void onStart(IVisualNovelEvent *previous) override {
PokerGameEvent::onStart(previous);
std::cout << "Poker Winning" << std::endl;
// Calculate
auto it = this->pokerGame->pots.begin();
while(it != this->pokerGame->pots.end()) {
auto pot = &(*it);
this->winnings[pot] = pot->getWinners(this->pokerGame);
++it;
}
// Award
auto it2 = this->winnings.begin();
while(it2 != this->winnings.end()) {
it2->second.award();
it2++;
}
}
bool_t onUpdate() override {
return false;
}
void onEnd() override {
}
public:
std::map<struct PokerPot*, struct PokerPotWinning> winnings;
PokerWinnerEvent(VisualNovelManager *manager) : PokerGameEvent(manager) {
}
};
}

View File

@ -1,10 +0,0 @@
# Copyright (c) 2023 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
TicTacToeLogic.cpp
)

View File

@ -1,137 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "TicTacToeLogic.hpp"
using namespace Dawn;
enum TicTacToeTileState Dawn::ticTacToeDetermineWinner(
const std::map<uint8_t, enum TicTacToeTileState> board,
std::vector<uint8_t> *winningCombo
) {
uint8_t i;
assertNotNull(winningCombo, "ticTacToeDetermineWinner: winningCombo cannot be null");
// Check rows
for(i = 0; i < 9; i += 3) {
if(board.at(i) == board.at(i + 1) && board.at(i) == board.at(i + 2) && board.at(i) != 0) {
*winningCombo = { i, (uint8_t)(i + 0x01), (uint8_t)(i + 0x02) };
return board.at(i);
}
}
// Check columns
for(i = 0; i < 3; i++) {
if(board.at(i) == board.at(i + 3) && board.at(i) == board.at(i + 6) && board.at(i) != 0) {
*winningCombo = { i, (uint8_t)(i + 0x03), (uint8_t)(i + 0x06) };
return board.at(i);
}
}
// Check diagonals
if(board.at(0) == board.at(4) && board.at(0) == board.at(8) && board.at(0) != 0) {
*winningCombo = { 0, 0x04, 0x08 };
return board.at(0);
}
if(board.at(2) == board.at(4) && board.at(2) == board.at(6) && board.at(2) != 0) {
*winningCombo = { 0x02, 0x04, 0x06 };
return board.at(2);
}
return TIC_TAC_TOE_EMPTY;
}
bool_t Dawn::ticTacToeIsGameOver(
const std::map<uint8_t, enum TicTacToeTileState> board
) {
auto it = board.begin();
while(it != board.end()) {
if(it->second == TIC_TAC_TOE_EMPTY) return false;
++it;
}
return true;
}
int32_t Dawn::ticTacToeGetBoardScore(
std::map<uint8_t, enum TicTacToeTileState> board,
enum TicTacToeTileState player
) {
int32_t score = 0;
uint8_t lines[8][3] = {
{0, 1, 2}, {3, 4, 5}, {6, 7, 8},
{0, 3, 6}, {1, 4, 7}, {2, 5, 8},
{0, 4, 8}, {2, 4, 6}
};
for (uint8_t i = 0; i < 8; i++) {
uint8_t countPlayer = 0;
uint8_t countEmpty = 0;
for (uint8_t j = 0; j < 3; j++) {
if(board[lines[i][j]] == player) {
countPlayer++;
} else if(board[lines[i][j]] == TIC_TAC_TOE_EMPTY) {
countEmpty++;
}
}
if(countPlayer == 2 && countEmpty == 1) {
score += 10;
} else if(countPlayer == 1 && countEmpty == 2) {
score += 1;
}
}
return score;
}
uint8_t Dawn::ticTacToeGetAiMove(
std::map<uint8_t, enum TicTacToeTileState> board,
enum TicTacToeTileState player
) {
std::vector<uint8_t> winningCombo;
// First, check if there's an immediate winning move for the AI
for(uint8_t i = 0; i < 9; i++) {
if(board[i] != TIC_TAC_TOE_EMPTY) continue;
board[i] = player;
if(ticTacToeDetermineWinner(board, &winningCombo) == player) {
board[i] = TIC_TAC_TOE_EMPTY;
return i;
}
board[i] = TIC_TAC_TOE_EMPTY;
}
// Next, check if the player has an immediate winning move and block it
auto opponent = (player == TIC_TAC_TOE_NOUGHT) ? TIC_TAC_TOE_CROSS : TIC_TAC_TOE_NOUGHT;
for(uint8_t i = 0; i < 9; i++) {
if(board[i] != TIC_TAC_TOE_EMPTY) continue;
board[i] = opponent;
if(ticTacToeDetermineWinner(board, &winningCombo) == opponent) {
board[i] = TIC_TAC_TOE_EMPTY;
return i;
}
board[i] = TIC_TAC_TOE_EMPTY;
}
// If neither player has an immediate winning move, use the simple heuristic to choose a move
uint8_t bestMove = -1;
int32_t bestScore = -1000;
for(uint8_t i = 0; i < 9; i++) {
if(board[i] != TIC_TAC_TOE_EMPTY) continue;
board[i] = player;
auto score = ticTacToeGetBoardScore(board, player);
board[i] = TIC_TAC_TOE_EMPTY;
if(score > bestScore) {
bestMove = i;
bestScore = score;
}
}
return bestMove;
}

View File

@ -1,65 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "assert/assert.hpp"
namespace Dawn {
enum TicTacToeTileState {
TIC_TAC_TOE_EMPTY,
TIC_TAC_TOE_NOUGHT,
TIC_TAC_TOE_CROSS
};
/**
* Determine the winner of the given board.
*
* @param board Tic tac toe board.
* @param winningCombo The output winning combo (if any).
* @return The winning player, or EMPTY if no winner is present.
*/
enum TicTacToeTileState ticTacToeDetermineWinner(
const std::map<uint8_t, enum TicTacToeTileState> board,
std::vector<uint8_t> *winningCombo
);
/**
* Returns true if the tic tac toe game is over. Will also consider ties as a
* game over state.
*
* @param board Board to check if game has ended.
* @return True if game is over, otherwise false.
*/
bool_t ticTacToeIsGameOver(
const std::map<uint8_t, enum TicTacToeTileState> board
);
/**
* Returns the score / value of a given board for the given player. Mostly
* used by the AI to determine whether a given board is better or worse than
* any other.
*
* @param board Board to get the score of.
* @param player Player to get the score for.
* @return The weighted score of this board.
*/
int32_t ticTacToeGetBoardScore(
std::map<uint8_t, enum TicTacToeTileState> board,
enum TicTacToeTileState player
);
/**
* Returns which cell should be used by the given player for their AI as the
* best move for them.
*
* @param board Tic tac toe board.
* @param player Player to get the AI move for.
* @return The recommended cell to fill.
*/
uint8_t ticTacToeGetAiMove(
std::map<uint8_t, enum TicTacToeTileState> board,
enum TicTacToeTileState player
);
}

View File

@ -1,8 +0,0 @@
# Copyright (c) 2023 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Subdirs
add_subdirectory(components)
add_subdirectory(events)

View File

@ -1,11 +0,0 @@
# Copyright (c) 2023 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
VNManager.cpp
VNTextboxScroller.cpp
)

View File

@ -1,38 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "VNManager.hpp"
#include "games/vn/events/VNEvent.hpp"
using namespace Dawn;
VNManager::VNManager(std::weak_ptr<SceneItem> item) :
SceneItemComponent(item),
defaultFont("<font font=\"font_main\">{{ text }}</font>")
{
}
void VNManager::onStart() {
if(this->currentEvent != nullptr) {
this->currentEvent->start(this, nullptr);
}
}
void VNManager::setEvent(VNEvent *event) {
this->currentEvent = event;
}
void VNManager::setFlag(std::string key, std::string value) {
this->flags[key] = value;
}
std::string VNManager::getFlag(std::string key) {
if(this->flags.find(key) == this->flags.end()) return "";
return this->flags[key];
}
void VNManager::onDispose() {
}

View File

@ -1,76 +0,0 @@
// Copyright (c) 2022 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "scene/SceneItemComponent.hpp"
namespace Dawn {
class VNEvent;
class IVNEventParent {
public:
VNEvent *currentEvent = nullptr;
};
class VNManager :
public SceneItemComponent,
public IVNEventParent
{
protected:
std::vector<VNEvent*> events;
std::map<std::string, std::string> flags;
public:
// @optional
std::string defaultFont;
/**
* Constructs a visual novel manager, scene item component.
*
* @param item Item that the VN manager belongs to.
*/
VNManager(std::weak_ptr<SceneItem> item);
/**
* Creats an event for you to decide how to queue.
*/
template<class T>
T * createEvent() {
auto event = new T();
event->init(this);
this->events.push_back(event);
return event;
}
/**
* Sets the currently active visual novel event. This is assumed to be
* the only way to handle events (no multiples currently).
*
* @param event Event to set.
*/
void setEvent(VNEvent *event);
/**
* Sets a flag for the visual novel.
*
* @param key Key of the flag.
* @param value Value of the flag.
*/
void setFlag(std::string key, std::string value);
/**
* Gets a flag for the visual novel.
*
* @param key Key of the flag.
* @return Value of the flag, or an empty string if it doesn't exist.
*/
std::string getFlag(std::string key);
void onStart() override;
void onDispose() override;
friend class VNEvent;
};
}

View File

@ -1,104 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "VNTextboxScroller.hpp"
#include "game/DawnGame.hpp"
using namespace Dawn;
VNTextboxScroller::VNTextboxScroller(std::weak_ptr<SceneItem> item) :
SceneItemComponent(item),
label(nullptr),
visibleLines(4)
{
}
void VNTextboxScroller::onStart() {
assertNotNull(label, "VNTextboxScroller::onStart: Label cannot be null");
std::function<void()> x = [&]{
this->lineCurrent = 0;
this->timeCharacter = 0;
this->label->quadStart = 0;
this->label->quadCount = 0;
this->label->textOffset = glm::vec2();
this->readyToClose = false;
};
x();
useEvent(x, this->label->eventTextChanged);
useEffect(x, visibleLines);
useEvent([&](float_t delta){
auto game = this->getGame();
this->timeCharacter += delta;
if(this->hasRevealedAllCurrentCharacters()) {
if(this->hasRevealedAllCharacters()) {
if(!this->label->lines.empty()) {
this->label->quadStart = this->label->lines[this->lineCurrent].quadStart;
}
this->label->quadCount = this->getTotalQuadsToBeRevealed();
if(!this->readyToClose) {
this->readyToClose = true;
this->eventReadyToClose.invoke();
}
} else {
if(game->inputManager.isPressed(INPUT_BIND_ACCEPT)) {
this->lineCurrent += this->visibleLines;
if(!this->label->lines.empty()) {
this->label->quadStart = this->label->lines[this->lineCurrent].quadStart;
}
this->label->quadCount = 0;
this->timeCharacter = 0.0f;
this->label->textOffset = (
-this->label->lines[this->lineCurrent].position
);
}
}
return;
}
auto lastTimeCharacter = mathFloor<int32_t>(this->timeCharacter);
if(game->inputManager.isDown(INPUT_BIND_ACCEPT)) {
this->timeCharacter += game->timeManager.delta * VN_TEXTBOX_SPEED_FASTER;
} else {
this->timeCharacter += game->timeManager.delta * VN_TEXTBOX_SPEED;
}
auto newCount = mathFloor<int32_t>(this->timeCharacter);
if(newCount == this->label->quadCount) return;
this->label->quadCount = mathFloor<int32_t>(this->timeCharacter);
this->eventCharacterRevealed.invoke();
}, getScene()->eventSceneUpdate);
}
int32_t VNTextboxScroller::getTotalQuadsToBeRevealed() {
int32_t quadsTotal = 0;
for(
size_t i = this->lineCurrent;
i < mathMin<size_t>(
this->label->lines.size(),
this->lineCurrent + this->visibleLines
);
i++
) {
quadsTotal += this->label->lines[i].quadCount;
}
return quadsTotal;
}
bool_t VNTextboxScroller::hasRevealedAllCurrentCharacters() {
return mathFloor<int32_t>(this->timeCharacter) >= this->getTotalQuadsToBeRevealed();
}
bool_t VNTextboxScroller::hasRevealedAllCharacters() {
return (
this->lineCurrent + this->visibleLines >=
this->label->lines.size()
);
}

View File

@ -1,58 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "scene/SceneItemComponent.hpp"
#include "scene/components/ui/text/UIRichTextLabel.hpp"
#include "input/InputManager.hpp"
#define VN_TEXTBOX_SPEED 25.0f
#define VN_TEXTBOX_SPEED_FASTER 40.0f
namespace Dawn {
class VNTextboxScroller : public SceneItemComponent {
public:
// @optional
StateProperty<UIRichTextLabel*> label;
// @optional
StateProperty<int32_t> visibleLines;
StateEvent<> eventReadyToClose;
StateEvent<> eventCharacterRevealed;
bool_t readyToClose = false;
size_t lineCurrent = 0;
float_t timeCharacter = 0.0f;
VNTextboxScroller(std::weak_ptr<SceneItem> item);
virtual void onStart() override;
/**
* Returns the count of quads, relative to the current visible line to be
* revealed based on the current time.
*
* @return The count of quads to be revealed.
*/
int32_t getTotalQuadsToBeRevealed();
/**
* Returns true if all of the characters that can be made visible for the
* current textbox size have finished revealing, or false if not.
*
* @return True if above statement is met.
*/
bool_t hasRevealedAllCurrentCharacters();
/**
* Returns true only when every character passed previously in setText
* has been revealed by scrolling.
*
* @return True if above statement is true.
*/
bool_t hasRevealedAllCharacters();
};
}

View File

@ -1,10 +0,0 @@
# Copyright (c) 2023 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DAWN_TARGET_NAME}
PRIVATE
VNEvent.cpp
)

View File

@ -1,49 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "VNEvent.hpp"
#include "display/animation/SimplerCallbackAnimation.hpp"
namespace Dawn {
template<typename T>
class VNAnimateEvent : public VNEvent {
public:
T from;
T to;
float_t duration;
protected:
SimplerCallbackAnimation<T> animation;
void onStart() override {
if(duration > 0) {
animation.clear();
animation.addKeyframe(0, from);
animation.addKeyframe(duration, to);
animation.callback = [&](T v){
this->setValue(v);
};
// On-end
useEvent([&]() {
this->next();
}, animation.event2AnimationEnd);
useEvent([&](float_t delta) {
animation.tick(delta);
}, getScene()->eventSceneUpdate);
} else {
this->setValue(to);
this->next();
}
}
virtual void setValue(T value) = 0;
};
}

View File

@ -1,52 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "VNEvent.hpp"
namespace Dawn {
class VNChoiceEvent : public VNEvent {
public:
std::string text;
std::string key;
std::map<std::string, std::string> choices;
int32_t choice = 0;
protected:
void onStart() override {
choice = 0;
std::cout << "CHOICE: " << text << std::endl;
for(auto& choice : choices) {
std::cout << " " << choice.first << ": " << choice.second << std::endl;
}
useEvent([&](float_t delta) {
auto im = &getScene()->game->inputManager;
if(im->isPressed(INPUT_BIND_ACCEPT)) {
auto it = choices.begin();
std::advance(it, choice);
std::string choiceMade = it->first;
std::cout << "Choice made " << choiceMade << std::endl;
this->manager->setFlag(this->key, choiceMade);
this->next();
} else if(im->isPressed(INPUT_BIND_NEGATIVE_Y)) {
choice = mathClamp<int32_t>((choice - 1), 0, choices.size() - 1);
std::cout << "Current choice: state" << choice << std::endl;
} else if(im->isPressed(INPUT_BIND_POSITIVE_Y)) {
choice = mathClamp<int32_t>((choice + 1), 0, choices.size() - 1);
std::cout << "Current choice: state" << choice << std::endl;
}
}, getScene()->eventSceneUpdate);
}
public:
std::string getChoiceKey() {
auto it = choices.begin();
std::advance(it, choice);
return it->first;
}
};
}

View File

@ -1,21 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "VNEvent.hpp"
namespace Dawn {
class VNChoiceSetEvent : public VNEvent {
public:
std::string key;
std::string value;
protected:
void onStart() override {
this->manager->setFlag(this->key, this->value);
this->next();
}
};
}

View File

@ -1,16 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "VNEvent.hpp"
namespace Dawn {
class VNDummyEvent : public VNEvent {
protected:
void onStart() override {
this->next();
}
};
}

View File

@ -1,50 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#include "VNEvent.hpp"
using namespace Dawn;
void VNEvent::init(VNManager *manager) {
this->manager = manager;
}
void VNEvent::start(
IVNEventParent *parent,
VNEvent *previous
) {
this->parent = parent;
finished = false;
this->onStart();
}
Scene * VNEvent::getScene() {
return this->manager->getScene();
}
VNEvent * VNEvent::getNextEvent() {
return this->doNext;
}
void VNEvent::next() {
assertNotNull(this->manager, "VNEvent::next: Manager cannot be null");
assertNotNull(this->parent, "VNEvent::next: Parent cannot be null");
this->end();
auto next = this->getNextEvent();
this->parent->currentEvent = next;
if(next != nullptr) next->start(this->parent, this);
}
void VNEvent::end() {
this->finished = true;
this->unsubscribeAllEvents();
this->onEnd();
this->eventFinished.invoke();
}
void VNEvent::onStart() {}
void VNEvent::onEnd() {}

View File

@ -1,91 +0,0 @@
// Copyright (c) 2023 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "games/vn/components/VNManager.hpp"
#include "scene/SceneItem.hpp"
namespace Dawn {
class VNEvent : public StateOwner {
public:
StateEvent<> eventFinished;
/**
* Invoked by a parent VN Manager. This is the start of the event.
*
* @param parent The VN Event Parent. Usually the manager but not always.
* @param previous The previous event that was running before this one.
*/
void start(
IVNEventParent *parent,
VNEvent *previous
);
protected:
VNManager *manager = nullptr;
IVNEventParent *parent = nullptr;
VNEvent *doNext = nullptr;
VNEvent *previous = nullptr;
bool_t finished = false;
/**
* Initializes the event. This is called by the VNManager, and should not
* be called by anything else.
*
* @param manager The VNManager that is running this event.
*/
void init(VNManager *manager);
/**
* Invoked by the VNManager, this is the end of the event. Perform the
* necessary cleanup, but remember that events may be re-started again
* later.
*/
void end();
/**
* Overrideable method that is called when the event is started.
*/
virtual void onStart();
/**
* Overrideable method that is called when the event is ended.
*/
virtual void onEnd();
public:
/**
* Returns the scene this event is running in.
* @return Pointer to the scene.
*/
Scene * getScene();
/**
* End this event and move on to the next event.
*/
void next();
/**
* Returns the next event to be executed after this one. Can be overridden
* to return a different event other than the doNext event.
* @return Pointer to the next event.
*/
virtual VNEvent * getNextEvent();
/**
* Chains an event to be executed after this event has finished.
*
* @param next Event to process next.
* @return Whatever you pass in to next.
*/
template<class T>
T * then(T *next) {
this->doNext = next;
return next;
}
friend class VNManager;
};
}

Some files were not shown because too many files have changed in this diff Show More