Compare commits
235 Commits
Prerelease
...
master
Author | SHA1 | Date | |
---|---|---|---|
791f6bf4c3 | |||
54eb43044d | |||
6f0d7d734f | |||
35298ec28d | |||
1cbed8ce7f | |||
79d8720282 | |||
c40c7c3cdf | |||
f154c256d8 | |||
c5109c2c1b | |||
ab0fd9455c | |||
b7b06cd48b | |||
f9e4f93aaa | |||
f82157d240 | |||
b10d1e30dc | |||
e748bf95ae | |||
28dda7d12d | |||
15d816f04f | |||
d9d8f7f3a2 | |||
0ee1ef5592 | |||
0d7306b9ae | |||
d55a64163b | |||
c7f98a1be6 | |||
4538aa963b | |||
60dbd6d725 | |||
5cd84258d8 | |||
6e9224ead6 | |||
1a09845770 | |||
f94c8b8e72 | |||
3dd9d65964 | |||
7d71f32d09 | |||
a53bc42a82 | |||
06ec0ca885 | |||
c9b037bf39 | |||
41749893a5 | |||
79f77a4fcf | |||
e0367b971c | |||
dfb60a45c4 | |||
9a10ff81d0 | |||
27f748ceca | |||
6b37c03079 | |||
23fb2892a9 | |||
50205fef99 | |||
4553a0c8ac | |||
8072af2c49 | |||
d9de2502ea | |||
83afdcaebc | |||
e6f67e1052 | |||
2f19b910de | |||
13cc7f2104 | |||
0dcb7d5a73 | |||
ba39db02d3 | |||
10e82cd4aa | |||
a599775eee | |||
185016ed49 | |||
8a8f1321b9 | |||
84bc213229 | |||
de8b45018a | |||
c604b69487 | |||
8a3ed23302 | |||
6d1652e0af | |||
699c1021c1 | |||
4e0c1dac86 | |||
b2e4732b14 | |||
e912645a24 | |||
a7063675dc | |||
f81098d7b2 | |||
cc00350dc3 | |||
809abae082 | |||
f2512dddf1 | |||
7d5d8bcd46 | |||
e547dd0045 | |||
6aeef77904 | |||
83731c08f0 | |||
0b9c5eb449 | |||
4a11961c0f | |||
f978605005 | |||
d4468421f7 | |||
84765fbaf4 | |||
7ae6003e23 | |||
7707d6d3a6 | |||
0f776f2a6f | |||
d058774519 | |||
2e55d42733 | |||
c5015b608d | |||
0a033fc103 | |||
2cc0ca2033 | |||
06021b9396 | |||
7c46f2939b | |||
5127d50f04 | |||
baf1f517a5 | |||
54a704a091 | |||
b8a59d98c5 | |||
24790c5c0c | |||
55746d45b9 | |||
48b303260d | |||
0a174af0e2 | |||
1f18f08244 | |||
d82c821e6f | |||
6ba265e9e2 | |||
fba1546a9b | |||
01edeb9e37 | |||
dd6a284b6f | |||
f20dec85ce | |||
0121af5178 | |||
3bd4716fcd | |||
9adc5c9ac7 | |||
a5f52f7f73 | |||
3c8e194f00 | |||
a6cf319c1c | |||
be1e04eac9 | |||
d27119ef28 | |||
2fb24c60b2 | |||
e2719d9954 | |||
40db7f9378 | |||
7af8d41e1e | |||
cc497fdb1e | |||
f0cdddebcc | |||
85db6dd46d | |||
9375776835 | |||
abed11e447 | |||
65f383cbc1 | |||
8f7e2f04df | |||
1b94a2c2ce | |||
b78081d6b9 | |||
b211df9c8f | |||
ae797f91b5 | |||
98b340379b | |||
915371004d | |||
a2ec22aab7 | |||
c069af8ed5 | |||
c92d74a20c | |||
e2a1724bcf | |||
fe730ee3b5 | |||
fc303375ad | |||
f9e14016ef | |||
6131372d48 | |||
8c430def05 | |||
a425e2bc78 | |||
3498d514ad | |||
69b2cbc32b | |||
b24f1e9039 | |||
9720f82315 | |||
414ed4a711 | |||
9df6bbbda2 | |||
24e7474769 | |||
e2e26a484b | |||
4909052c4d | |||
0e8f6600a0 | |||
71b95b2615 | |||
350e93da09 | |||
eaf6c3d6fb | |||
6cf8bb3d05 | |||
1f0cc1bbfc | |||
c80b9482a4 | |||
4d7e8d2fc8 | |||
86e00e7b35 | |||
a0c0ccdf5e | |||
5caf84560e | |||
5b819e5a87 | |||
daddf0e257 | |||
b075e221c4 | |||
f6152d9df7 | |||
3fac7e61ce | |||
ebdcae945f | |||
916e44c7e5 | |||
9295435304 | |||
4445c084dd | |||
4ec166c1d9 | |||
fd2391e03b | |||
7a47f47388 | |||
e77304cac2 | |||
ac54243e4f | |||
79958d395d | |||
dfb20b548e | |||
0ee0dca397 | |||
5a48438e11 | |||
823889cc09 | |||
09773fbe5c | |||
9c9debbf5d | |||
e2e6918c3b | |||
0a254808c4 | |||
b8ac6e8d9c | |||
145ad027f8 | |||
dc3e923a46 | |||
5d7e8d8eb3 | |||
d6ba89c257 | |||
37dc8c1f5e | |||
9c7a5a0201 | |||
695789afa9 | |||
15aec2cfe6 | |||
3b17ac0ee3 | |||
da23a91de1 | |||
438a7ae632 | |||
bd8ce016bf | |||
c456d04da5 | |||
d8c27dafb8 | |||
860f544467 | |||
34a9092f59 | |||
51803d37f5 | |||
7fe651b195 | |||
2f7ed84e2a | |||
a75043ef66 | |||
5d042e9353 | |||
57ceb8adce | |||
2ee33d4575 | |||
59794655a0 | |||
24fa29408a | |||
14960eec4f | |||
e3b8901bcd | |||
534ae32f36 | |||
929231f0ff | |||
3fb2db4fd4 | |||
2418ddfc46 | |||
cd2d119abb | |||
e01bc5f7b8 | |||
52aad685d9 | |||
32d27767c3 | |||
8e8ad07d59 | |||
88d7ead713 | |||
b4860c14c9 | |||
d36818cf66 | |||
0f38031c1e | |||
44ec829aa0 | |||
20e60ba613 | |||
a656383741 | |||
8529649dc2 | |||
ede437eabd | |||
9875e5d0b0 | |||
717182c35c | |||
fcd580cc3b | |||
232aed317a | |||
96417bf66d | |||
c64a21a1f1 | |||
099782a17a | |||
f566b800fa |
1
.gitignore
vendored
@@ -3,4 +3,5 @@
|
||||
/.ccls-cache
|
||||
/compile_commands.json
|
||||
/cmake-build-debug
|
||||
/cmake-build-release
|
||||
/build
|
||||
|
@@ -1,6 +1,8 @@
|
||||
# Build Options
|
||||
set(CLIENT_BUILD_WITH_STEAM false)
|
||||
|
||||
#cmake_policy(SET CMP0169 OLD)
|
||||
|
||||
cmake_minimum_required(VERSION 3.18..3.29)
|
||||
project(ReCaveGame
|
||||
VERSION 1.0
|
||||
@@ -11,34 +13,63 @@ if (PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)
|
||||
message(FATAL_ERROR "In-source builds are not allowed")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
|
||||
# Set for profiling
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0")
|
||||
|
||||
if (UNIX)
|
||||
# TODO: Enable ALL optimization flags for RELEASE builds.
|
||||
|
||||
#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3")
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -floop-nest-optimize -funroll-loops")
|
||||
endif()
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||
|
||||
#if(CMAKE_BUILD_TYPE EQUAL "Release")
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib")
|
||||
#endif()
|
||||
include(cmake/CPM.cmake)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME mcolor
|
||||
URL https://git.redacted.cc/maxine/mcolor/archive/Release-1.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME jjx
|
||||
URL https://git.redacted.cc/josh/jjx/archive/Release-1.1.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME jlog
|
||||
URL https://git.redacted.cc/josh/jlog/archive/Prerelease-19.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME J3ML
|
||||
URL https://git.redacted.cc/josh/j3ml/archive/3.4.3.zip
|
||||
URL https://git.redacted.cc/josh/j3ml/archive/3.4.5.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME jstick
|
||||
URL https://git.redacted.cc/josh/jstick/archive/Prerelease-4.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME ReWindow
|
||||
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-26.zip
|
||||
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-34.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME JGL
|
||||
URL https://git.redacted.cc/josh/JGL/archive/Prerelease-42.zip
|
||||
URL https://git.redacted.cc/josh/JGL/archive/Prerelease-58.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME JUI
|
||||
URL https://git.redacted.cc/josh/ReJUI/archive/Prerelease-5.5.zip
|
||||
URL https://git.redacted.cc/josh/ReJUI/archive/Prerelease-6.2.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
@@ -46,6 +77,11 @@ CPMAddPackage(
|
||||
URL https://git.redacted.cc/josh/jjx/archive/Prerelease-2.zip
|
||||
)
|
||||
|
||||
CPMAddPackage(
|
||||
NAME Sockets
|
||||
URL https://git.redacted.cc/josh/Sockets/archive/Prerelease-3.1.zip
|
||||
)
|
||||
|
||||
add_subdirectory(Core)
|
||||
add_subdirectory(Server)
|
||||
add_subdirectory(Client)
|
||||
|
@@ -8,7 +8,7 @@ if (UNIX)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
add_library(CaveClient SHARED ${CaveClient_SRC})
|
||||
add_library(CaveClient STATIC ${CaveClient_SRC})
|
||||
endif()
|
||||
|
||||
target_include_directories(CaveClient PUBLIC
|
||||
@@ -20,7 +20,6 @@ target_include_directories(CaveClient PUBLIC
|
||||
|
||||
target_include_directories(CaveClient PUBLIC "include")
|
||||
|
||||
|
||||
set_target_properties(CaveClient PROPERTIES LINKER_LANGUAGE CXX)
|
||||
|
||||
target_link_libraries(CaveClient PUBLIC CaveCore J3ML JGL JUI)
|
||||
|
110
Client/include/Client/AssetService.hpp
Normal file
@@ -0,0 +1,110 @@
|
||||
/// CaveGame - A procedural 2D platformer sandbox.
|
||||
/// Created by Josh O'Leary @ Redacted Software, 2020-2025
|
||||
/// Contact: josh@redacted.cc
|
||||
/// Contributors: william@redacted.cc maxi@redacted.cc
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file AssetService.hpp
|
||||
/// @desc Manages game asset data.
|
||||
/// @edit 3/31/2025
|
||||
/// @auth Josh O'Leary
|
||||
|
||||
/// The AssetService is a class / static library that manages on-file game assets.
|
||||
/// This class service provides asynchronous loading of game content, with introspection for hooking to a loading menu.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <JGL/types/Texture.h>
|
||||
#include <JGL/types/Font.h>
|
||||
#include <queue>
|
||||
#include <Core/Singleton.hpp>
|
||||
#include <Core/Macros.hpp>
|
||||
|
||||
namespace CaveGame::Client {
|
||||
|
||||
using namespace JGL;
|
||||
|
||||
enum AssetType { DATA, TEXT, AUDIO, MODEL, TEXTURE, FONT, SHADER };
|
||||
enum AssetLifestyle { STATIC, STREAMED};
|
||||
|
||||
|
||||
struct AssetRequest {
|
||||
std::string name;
|
||||
std::filesystem::path path;
|
||||
AssetType type;
|
||||
};
|
||||
|
||||
class AssetService {
|
||||
private:
|
||||
public:
|
||||
|
||||
static AssetService* Get();
|
||||
|
||||
|
||||
JGL::Texture *player_sprite;
|
||||
JGL::Texture *explosion_sprite;
|
||||
JGL::Texture *title_img;
|
||||
|
||||
AssetService();
|
||||
|
||||
~AssetService() {}
|
||||
|
||||
void TempLoadSimple();
|
||||
|
||||
|
||||
std::shared_ptr<Texture> GetTexture(const std::string &textureName) {
|
||||
return textures[textureName];
|
||||
}
|
||||
|
||||
std::shared_ptr<JGL::Font> GetFont(const std::string &fontName) {
|
||||
//return fonts[fontName];
|
||||
}
|
||||
|
||||
void EnqueueTexture(const std::string& name, const std::filesystem::path &path);
|
||||
void EnqueueFont(const std::string& name, const std::filesystem::path& path);
|
||||
void EnqueueSound(const std::string& name, const std::filesystem::path& path);
|
||||
|
||||
void LoadAllFromQueue();
|
||||
|
||||
/// Performs one "Load Cycle" on the current thread. This means we will attempt to load a number of queued items until we've spent at least maxTTL seconds.
|
||||
bool LoadFromQueue(float maxTimeExpenditure = 1e-3f);
|
||||
|
||||
bool LoadAsset(const AssetRequest& request);
|
||||
|
||||
|
||||
bool IsLoadComplete() const { return queue.empty(); }
|
||||
|
||||
std::string LastAsset() const { return last_asset_processed; }
|
||||
|
||||
unsigned int TotalQueued() const { return total_queued; }
|
||||
unsigned int TotalLoaded() const { return total_loaded; }
|
||||
|
||||
/// Read the manifest.json file to know which assets to enqueue for the loading screen sequence.
|
||||
void ParseManifest();
|
||||
|
||||
/// Load certain critical assets immediately.
|
||||
void PreloadCertainAssets();
|
||||
|
||||
protected:
|
||||
|
||||
/// @returns only the filename of the given path.
|
||||
static std::string FilenameFromPath(const std::filesystem::path& path);
|
||||
|
||||
/// @returns only the filename, without a file extension, of the given path.
|
||||
static std::string FilenameFromPathWithoutExtension(const std::filesystem::path& path);
|
||||
|
||||
protected:
|
||||
std::unordered_map<std::string, std::shared_ptr<Font>> fonts;
|
||||
std::unordered_map<std::string, std::shared_ptr<Texture>> textures;
|
||||
std::queue<AssetRequest> queue;
|
||||
std::string last_asset_processed;
|
||||
|
||||
unsigned int total_queued = 0;
|
||||
unsigned int total_loaded = 0;
|
||||
|
||||
};
|
||||
}
|
@@ -21,6 +21,8 @@ namespace CaveGame::Client
|
||||
/// The default constructor does not initialize any values.
|
||||
Camera2D();
|
||||
|
||||
// TODO: Implement camera shaking for explosions and other effects.
|
||||
void ApplyShake(const Vector2& impulse);
|
||||
/// Performs one iteration of the camera's internal logic.
|
||||
/// @param elapsed The delta-time (in seconds) between this iteration and the last.
|
||||
void Update(float elapsed);
|
||||
@@ -28,6 +30,8 @@ namespace CaveGame::Client
|
||||
AABB2D Viewport() const;
|
||||
/// Returns the bounds of the screen-viewport, in gameworld-coordinates, with scaling by the camera's zoom.
|
||||
AABB2D ScaledViewport() const;
|
||||
|
||||
AABB2D ScaledViewportOversized(float oversize) const;
|
||||
/// Returns the target coordinates of this camera. The camera will move over time to reach this point.
|
||||
Vector2 Target() const;
|
||||
/// Sets the target coordinates of this camera. The camera will move over time to reach this point.
|
||||
@@ -45,9 +49,18 @@ namespace CaveGame::Client
|
||||
/// Sets the amount of zoom in the camera transformation.
|
||||
void Zoom(float val);
|
||||
|
||||
/// Enables free camera movement controls.
|
||||
void SetFreeCam(bool freeCam);
|
||||
|
||||
/// Returns whether free camera controls are enabled.
|
||||
bool GetFreeCam() const;
|
||||
|
||||
/// Zooms the camera in by the given amount.
|
||||
void ZoomIn(float amt);
|
||||
|
||||
/// Zooms the camera out by the given amount.
|
||||
void ZoomOut(float amt);
|
||||
|
||||
/// Returns the amount of rotation (in degrees) in the camera transformation.
|
||||
[[nodiscard]] float Rotation() const;
|
||||
|
||||
@@ -57,13 +70,10 @@ namespace CaveGame::Client
|
||||
/// Rotates the camera by the given amount (in degrees).
|
||||
void Rotate(float angles);
|
||||
|
||||
Vector2 ScreenCenterPosition() const;
|
||||
Vector2 ScreenTopLeftPosition() const;
|
||||
Vector2 ScreenToWorld(const Vector2& device_coordinates) const
|
||||
{
|
||||
Vector2 pos = ScaledTopLeft() + (device_coordinates/zoom);
|
||||
return pos;
|
||||
}
|
||||
/// Converts top-left origin screen coordinates into world-space coordinates, with respect to zoom scale.
|
||||
Vector2 ScreenToWorld(const Vector2& device_coordinates) const;
|
||||
|
||||
/// Converts world-space coordinates into top-left origin screen coordinates, with respect to zoom scale.
|
||||
Vector2 WorldToScreen(const Vector2& position) const;
|
||||
|
||||
/// Returns the current cartesian coordinates of the camera transformation.
|
||||
@@ -76,6 +86,7 @@ namespace CaveGame::Client
|
||||
Matrix4x4 Transform() const;
|
||||
|
||||
|
||||
void Move(const Vector2& v);
|
||||
/// Moves the camera to the left at a given rate times the base camera speed.
|
||||
void MoveLeft(float rate = 1.f);
|
||||
/// Moves the camera to the right at a given rate times the base camera speed.
|
||||
@@ -93,10 +104,15 @@ namespace CaveGame::Client
|
||||
|
||||
/// Returns the gameworld-coordinates at the top-left of the screen, with scaling taken into account.
|
||||
[[nodiscard]] Vector2 ScaledTopLeft() const;
|
||||
|
||||
/// Returns the world-space coordinates at the focal point of the camera. Generally, this should also be the
|
||||
/// world-space coordinates of the center of the screen.
|
||||
[[nodiscard]] Vector2 Center() const;
|
||||
|
||||
/// Returns the "radius" of the viewport. (size / 2.f)
|
||||
[[nodiscard]] Vector2 HalfSizeOffset() const;
|
||||
|
||||
/// Returns the "radius" of the viewport, scaled by the camera zoom.
|
||||
[[nodiscard]] Vector2 ScaledHalfSizeOffset() const;
|
||||
|
||||
/// Returns the base movement speed of the camera.
|
||||
@@ -104,17 +120,24 @@ namespace CaveGame::Client
|
||||
|
||||
/// Sets the base movement speed of the camera.
|
||||
void MoveSpeed(float value);
|
||||
protected:
|
||||
/// Process user-input for freecam.
|
||||
void UpdateFreecamControls(float delta);
|
||||
protected:
|
||||
Vector2 velocity;
|
||||
Vector2 position;
|
||||
Vector2 final_pos;
|
||||
float zoom = 1.f;
|
||||
float rotation = 0.f;
|
||||
Vector2 target = {0, 0};
|
||||
Vector2 viewport_size = {800, 600};
|
||||
float speed = 0.5f;
|
||||
float lerp_factor = 10.f;
|
||||
float move_speed = 150.f;
|
||||
float move_smoothing = 0.975f;
|
||||
bool lerp_to_target = true;
|
||||
bool freecam = true;
|
||||
float free_move_cooldown = 1.25f;
|
||||
float last_free_move = 0.f;
|
||||
private:
|
||||
|
||||
// https://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/
|
||||
};
|
||||
}
|
17
Client/include/Client/Chat.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
/// CaveGame - A procedural 2D platformer sandbox.
|
||||
/// Created by Josh O'Leary @ Redacted Software, 2020-2025
|
||||
/// Contact: josh@redacted.cc
|
||||
/// Contributors: william@redacted.cc maxi@redacted.cc
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file Chat.hpp
|
||||
/// @desc The client-side user interface for in-game chat.
|
||||
/// @edit 1/28/2025
|
||||
/// @auth Josh O'Leary
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CaveGame::Client
|
||||
{
|
||||
|
||||
}
|
@@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <JUI/Widgets/Window.hpp>
|
||||
|
||||
namespace CaveGame::Client
|
||||
{
|
||||
class Console : public JUI::Window
|
||||
{
|
||||
public:
|
||||
protected:
|
||||
private:
|
||||
};
|
||||
}
|
50
Client/include/Client/ContainerWindow.hpp
Normal file
@@ -0,0 +1,50 @@
|
||||
/// CaveGame - A procedural 2D platformer sandbox.
|
||||
/// Created by Josh O'Leary @ Redacted Software, 2020-2025
|
||||
/// Contact: josh@redacted.cc
|
||||
/// Contributors: william@redacted.cc maxi@redacted.cc
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file ContainerWindow.hpp
|
||||
/// @desc The UI window for a container that holds items.
|
||||
/// @edit 3/18/2025
|
||||
/// @auth Josh O'Leary
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <JUI/Widgets/Window.hpp>
|
||||
#include <Core/Container.hpp>
|
||||
|
||||
namespace CaveGame {
|
||||
|
||||
using namespace JUI::UDimLiterals;
|
||||
|
||||
/// An inventory container window widget.
|
||||
class ContainerWindow : public JUI::Window
|
||||
{
|
||||
public:
|
||||
ContainerWindow(Widget* parent, int rows, int cols, std::string title = "Container")
|
||||
: JUI::Window(parent)
|
||||
{
|
||||
auto layout = this->ViewportInstance();
|
||||
this->Title(title);
|
||||
this->Size(JUI::UDim2::FromPixels(48*rows, 48*cols));
|
||||
this->MinSize(Vector2(48*rows, 48*cols));
|
||||
this->SetResizable(false);
|
||||
|
||||
for (int row = 0; row < rows; row++) {
|
||||
for (int col = 0; col < cols; col++) {
|
||||
auto* cell = new JUI::Rect(layout);
|
||||
cell->Name(std::format("cell_{},{}", row, col));
|
||||
cell->Size({48_px, 48_px});
|
||||
cell->Position(JUI::UDim2::FromPixels(48*row, 48*col));
|
||||
}
|
||||
}
|
||||
}
|
||||
void SetContainerSize(int rows, int cols);
|
||||
void RebuildContainerElements();
|
||||
protected:
|
||||
Core::Container* contents = nullptr;
|
||||
private:
|
||||
};
|
||||
|
||||
}
|
@@ -1,6 +1,15 @@
|
||||
/// CaveGame - A procedural 2D platformer sandbox.
|
||||
/// Created by Josh O'Leary @ Redacted Software, 2020-2025
|
||||
/// Contact: josh@redacted.cc
|
||||
/// Contributors: william@redacted.cc maxi@redacted.cc
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file CreditsWindow.hpp
|
||||
/// @desc The UI window for the credits.
|
||||
/// @edit 1/28/2025
|
||||
/// @auth Josh O'Leary
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "JUI/Widgets/Window.hpp"
|
||||
#include "JUI/Widgets/TextRect.hpp"
|
||||
#include "JUI/Widgets/ListLayout.hpp"
|
||||
@@ -9,29 +18,34 @@ namespace CaveGame::Client
|
||||
{
|
||||
using namespace JUI;
|
||||
|
||||
struct Accreditation {
|
||||
unsigned int text_size;
|
||||
Color4 text_color;
|
||||
std::string content;
|
||||
};
|
||||
|
||||
// We currently lack Rich Text, so we'll do this...
|
||||
std::vector<std::tuple<uint, Color4, std::string>> Credits =
|
||||
{
|
||||
{34, Colors::White, "cavegame"},
|
||||
{20, Colors::White, "A Redacted Software Product"},
|
||||
{18, Colors::Yellows::Moccasin, "Josh O'Leary"},
|
||||
{14, Colors::White, "Programming & Design"},
|
||||
{18, Colors::White, "William Redacted"},
|
||||
{14, Colors::White, "Redacted3D Game Technologies"},
|
||||
{18, Colors::White, "Ash Tray"},
|
||||
{14, Colors::White, "Art & Design"},
|
||||
{18, Colors::White, "Evan Walter"},
|
||||
{14, Colors::White, "Music"},
|
||||
|
||||
{18, Colors::White, "Maxine Hayes"},
|
||||
{18, Colors::White, "Karl Darling"},
|
||||
{18, Colors::White, "bumpylegoman02 "},
|
||||
|
||||
|
||||
{32, Colors::White, "Made Possible By "},
|
||||
{24, Colors::Purples::Violet, "Asperger's Syndrome"},
|
||||
{16, Colors::Gray, "(c) 2024 redacted.cc"},
|
||||
// NOTE: this must be static const, or the program will segfault upon exit.
|
||||
static const std::vector<Accreditation> Credits = {
|
||||
{50, Colors::White, "CaveGame"},
|
||||
{12, Colors::Gray, "Version 3 Alpha, Prerelease-3"},
|
||||
{24, Colors::Blues::LightSteelBlue, "A Redacted Software Product"},
|
||||
{20, Colors::Yellows::Moccasin, "Josh O'Leary"},
|
||||
{14, Colors::Grays::LightSlateGray, "Programming & Design"},
|
||||
{20, Colors::Grays::DarkSlateGray, "William Redacted"},
|
||||
{14, Colors::Grays::LightSlateGray, "Programming"},
|
||||
{20, Colors::Reds::LightCoral, "Ash Tray"},
|
||||
{14, Colors::Grays::LightSlateGray, "Art & Design"},
|
||||
{18, Colors::White, "Special Thanks"},
|
||||
{12, Colors::Grays::LightSlateGray, "Maxine Hayes"},
|
||||
{12, Colors::White, "Evan Walter"},
|
||||
{12, Colors::Grays::LightSlateGray, "Karl Darling"},
|
||||
{12, Colors::White, "bumpylegoman02"},
|
||||
{12, Colors::Grays::LightSlateGray, "ConcurrentSquared"},
|
||||
{12, Colors::White, "Chase Carlson"},
|
||||
{16, Colors::White, "Made Possible By "},
|
||||
{14, Colors::Purples::Violet, "Asperger's Syndrome"},
|
||||
{16, Colors::Gray, "(c) 2019-2025 redacted.cc"},
|
||||
|
||||
};
|
||||
|
||||
|
@@ -1,114 +1,89 @@
|
||||
/// CaveGame - A procedural 2D platformer sandbox.
|
||||
/// Created by Josh O'Leary @ Redacted Software, 2020-2025
|
||||
/// Contact: josh@redacted.cc
|
||||
/// Contributors: william@redacted.cc maxi@redacted.cc
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file GameSession.hpp
|
||||
/// @desc The scene that runs and manages a client game session.
|
||||
/// @edit 1/28/2025
|
||||
/// @auth Josh O'Leary
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Client/Scene.hpp>
|
||||
#include <J3ML/LinearAlgebra.hpp>
|
||||
#include <Core/Entity.hpp>
|
||||
#include <Core/Player.hpp>
|
||||
#include <Core/World.hpp>
|
||||
#include "LocalWorld.hpp"
|
||||
#include "Hotbar.hpp"
|
||||
#include <JUI/Widgets/Scene.hpp>
|
||||
#include <JUI/Widgets/Rect.hpp>
|
||||
#include <JUI/Widgets/TextRect.hpp>
|
||||
#include <Core/Item.hpp>
|
||||
#include "TileTool.hpp"
|
||||
#include "ContainerWindow.hpp"
|
||||
#include <Client/PauseMenu.hpp>
|
||||
|
||||
namespace CaveGame::Client {
|
||||
using namespace Core;
|
||||
|
||||
class LootTable
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class LootTable {};
|
||||
class CraftingStation {};
|
||||
|
||||
|
||||
class Container
|
||||
{
|
||||
public:
|
||||
Container(int rows, int columns);
|
||||
|
||||
protected:
|
||||
private:
|
||||
};
|
||||
|
||||
class Storage : Container {};
|
||||
class PlayerHotbar : Container {};
|
||||
class PlayerPockets : Container {};
|
||||
class PlayerBackpack : Container {};
|
||||
class TradeMenu : Container {};
|
||||
|
||||
class PlayerInventory : Container
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
class ContainerGUI {
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
class HotbarGUI
|
||||
{
|
||||
public:
|
||||
protected:
|
||||
private:
|
||||
};
|
||||
class ContainerGUI {};
|
||||
|
||||
using namespace JUI::UDimLiterals;
|
||||
using CaveGame::Core::Entity;
|
||||
class GameSession : public Scene {
|
||||
public:
|
||||
|
||||
Event<> OnSessionExit;
|
||||
Event<> RequestToggleSettings;
|
||||
|
||||
GameSession() = default;
|
||||
GameSession(bool createNewWorld);
|
||||
|
||||
explicit GameSession(bool createNewWorld);
|
||||
void Update(float elapsed) override;
|
||||
|
||||
void Draw() override;
|
||||
|
||||
void Load() override;
|
||||
|
||||
void Unload() override;
|
||||
void PassWindowSize(const J3ML::LinearAlgebra::Vector2 &size) override;
|
||||
void PassMouseInput(unsigned int, bool) override
|
||||
{ }
|
||||
void PassMouseMovement(const J3ML::LinearAlgebra::Vector2 &pos) override
|
||||
{ }
|
||||
void PassMouseInput(unsigned int a, bool b) override;
|
||||
|
||||
void PassMouseMovement(const J3ML::LinearAlgebra::Vector2 &pos) override;
|
||||
|
||||
void PassMouseWheel(int wheel) override;
|
||||
|
||||
void PassKeyInput(Key key, bool pressed) override;
|
||||
|
||||
void SaveAndExit();
|
||||
|
||||
void ConstructHUD();
|
||||
|
||||
void UnloadHUD();
|
||||
|
||||
void HUDTick()
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
hotbar_elements[i]->Size({44_px, 48_px});
|
||||
hotbar_elements[i]->CornerRounding(5);
|
||||
hotbar_elements[i]->SetBorderWidth(1.5f);
|
||||
hotbar_elements[i]->AnchorPoint({0.f, 0.8f});
|
||||
}
|
||||
uint16_t GetTileIDUnderMouse();
|
||||
|
||||
hotbar_elements[active_hotbar_slot]->CornerRounding(8);
|
||||
hotbar_elements[active_hotbar_slot]->SetBorderWidth(1.5f);
|
||||
hotbar_elements[active_hotbar_slot]->Size({58_px, 56_px});
|
||||
hotbar_elements[active_hotbar_slot]->AnchorPoint({0.f, 0.75f});
|
||||
}
|
||||
[[nodiscard]] JUI::Scene* HUD() const { return hud; }
|
||||
LocalWorld* World() const { return world;}
|
||||
TileHotbar Hotbar() const { return hotbar;}
|
||||
|
||||
LocalWorld* world;
|
||||
void WorldEditToolControlsUpdate(float elapsed);
|
||||
void ToggleWorldEdit();
|
||||
TileTool* WorldEditToolWindow() const { return tile_tool;}
|
||||
|
||||
|
||||
JUI::Scene* hud;
|
||||
JUI::Rect* hotbar_elements[10];
|
||||
JUI::TextRect* item_label;
|
||||
Core::Player* GetLocalPlayerEntity();
|
||||
protected:
|
||||
int active_hotbar_slot = 0;
|
||||
TileTool* tile_tool = nullptr;
|
||||
LocalWorld* world = nullptr;
|
||||
TileHotbar hotbar;
|
||||
JUI::Scene* hud = nullptr;
|
||||
PauseMenuWidget* pause_menu = nullptr;
|
||||
Vector2 mouse_pos = {0,0};
|
||||
RNG tool_rng;
|
||||
|
||||
private:
|
||||
|
||||
void WorldEditToolDrawTiles(TileID tile);
|
||||
void WorldEditToolDrawTiles(int x, int y, int radius, int density, TileID tile);
|
||||
void WorldEditToolDrawOverlay();
|
||||
Vector2 MouseWorldPos() const;
|
||||
};
|
||||
}
|
||||
|
76
Client/include/Client/Hotbar.hpp
Normal file
@@ -0,0 +1,76 @@
|
||||
/// CaveGame - A procedural 2D platformer sandbox.
|
||||
/// Created by Josh O'Leary @ Redacted Software, 2020-2025
|
||||
/// Contact: josh@redacted.cc
|
||||
/// Contributors: william@redacted.cc maxi@redacted.cc
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file Hotbar.hpp
|
||||
/// @desc The hotbar controls and user-interface.
|
||||
/// @edit 1/28/2025
|
||||
/// @auth Josh O'Leary
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <JUI/Widgets/Scene.hpp>
|
||||
#include <JUI/Widgets/Rect.hpp>
|
||||
#include <JUI/Widgets/TextRect.hpp>
|
||||
#include <Core/Item.hpp>
|
||||
#include <Core/Data.hpp>
|
||||
#include <Client/LocalWorld.hpp>
|
||||
|
||||
namespace CaveGame::Client
|
||||
{
|
||||
/// The input and gui driver for the temporary tile hotbar.
|
||||
/// Basically, this object controls what items are in the hotbar, and which slot is selected.
|
||||
/// The visual appearance is applied to JUI::Widgets that are part of the GameSession::hud JUI::Scene.
|
||||
/// @see GameSession::Update
|
||||
|
||||
// TODO: Refactor TileHotbar into a derived JUI Widget?
|
||||
|
||||
class TileHotbar {
|
||||
public:
|
||||
void NextSlot();
|
||||
void PrevSlot();
|
||||
Core::TileID GetCurrentSlotTileID() const;
|
||||
int GetSlotIndex() const;
|
||||
JUI::Rect *GetRootWidget() const;
|
||||
JUI::Rect *GetSlotWidget(int slotIdx) const;
|
||||
Core::TileID GetSlotTileID(int slotIdx) const;
|
||||
void SetSlotTileID(int slotIdx, Core::TileID tid);
|
||||
|
||||
void Load(LocalWorld * world);
|
||||
void OnKeyInput(const Key& key, bool pressed);
|
||||
void OnMouseWheel(int mwheeldir)
|
||||
{
|
||||
if (mwheeldir > 0)
|
||||
NextSlot();
|
||||
if (mwheeldir < 0)
|
||||
PrevSlot();
|
||||
}
|
||||
|
||||
void Unload()
|
||||
{
|
||||
// TODO: Run this in GameSession
|
||||
//delete hud;
|
||||
}
|
||||
|
||||
void Update(float elapsed);
|
||||
void Draw() { }
|
||||
protected:
|
||||
static constexpr int slot_count = 10;
|
||||
Core::TileID slots[slot_count]{};
|
||||
/*= {
|
||||
//Core::TileID::GRASS, Core::TileID::STONE, Core::TileID::WATER, Core::TileID::LAVA, Core::TileID::GREEN_MOSS, Core::TileID::SAND,
|
||||
//Core::TileID::CLAY, Core::TileID::OAK_PLANK, Core::TileID::COBBLESTONE, Core::TileID::STONE_BRICK
|
||||
};*/
|
||||
/*TileID Slot(int index)
|
||||
{
|
||||
switch(index)
|
||||
}*/
|
||||
int slot_index = 1;
|
||||
JUI::Rect* hotbar_elements[10];
|
||||
JUI::TextRect* item_label;
|
||||
JUI::Rect* hotbar_root;
|
||||
private:
|
||||
};
|
||||
}
|
@@ -1,3 +1,14 @@
|
||||
/// CaveGame - A procedural 2D platformer sandbox.
|
||||
/// Created by Josh O'Leary @ Redacted Software, 2020-2025
|
||||
/// Contact: josh@redacted.cc
|
||||
/// Contributors: william@redacted.cc maxi@redacted.cc
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file LocalWorld.hpp
|
||||
/// @desc The client implementation for game world behavior.
|
||||
/// @edit 1/28/2025
|
||||
/// @auth Josh O'Leary
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Core/World.hpp>
|
||||
@@ -8,62 +19,129 @@
|
||||
#include <thread>
|
||||
#include <Core/ConcurrentQueue.hpp>
|
||||
#include <Core/ThreadPool.hpp>
|
||||
#include "Particle.hpp"
|
||||
|
||||
namespace CaveGame::Client
|
||||
{
|
||||
using Core::ConcurrentQueue;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
|
||||
|
||||
|
||||
class AsyncChunkRenderer : public ThreadPool
|
||||
{
|
||||
public:
|
||||
|
||||
};
|
||||
|
||||
class LocalWorld : public CaveGame::Core::World
|
||||
|
||||
/// A class object that renders an overlay on chunks for debugging. This includes grid-lines, cell coordinates, activity stats, etc.
|
||||
class ChunkDebugGizmo
|
||||
{
|
||||
public:
|
||||
//JGL::Font font;
|
||||
|
||||
void DrawGrid(const AABB2D& bounds) {
|
||||
Vector2 viewport_topleft = bounds.minPoint;
|
||||
Vector2 viewport_bottomright = bounds.maxPoint;
|
||||
|
||||
int nearest_grid_left = Math::Floor(viewport_topleft.x / Core::Chunk::ChunkSize);
|
||||
int nearest_grid_right = Math::Floor(viewport_bottomright.x / Core::Chunk::ChunkSize);
|
||||
|
||||
for (int x = nearest_grid_left; x <= nearest_grid_right; x++) {
|
||||
auto top = Vector2(x * Core::Chunk::ChunkSize, viewport_topleft.y);
|
||||
auto bottom = Vector2(x * Core::Chunk::ChunkSize, viewport_bottomright.y);
|
||||
JGL::J2D::DrawLine(grid_color, top, bottom, 1 / bounds.Width());
|
||||
}
|
||||
|
||||
int nearest_grid_top = Math::Floor(viewport_topleft.y / Core::Chunk::ChunkSize);
|
||||
int nearest_grid_bottom = Math::Floor(viewport_bottomright.y / Core::Chunk::ChunkSize);
|
||||
|
||||
for (int y = nearest_grid_top; y <= nearest_grid_bottom; y++) {
|
||||
auto left = Vector2(viewport_topleft.x, y * Core::Chunk::ChunkSize);
|
||||
auto right = Vector2(viewport_bottomright.x, y * Core::Chunk::ChunkSize);
|
||||
JGL::J2D::DrawLine(grid_color, left, right, 1 / bounds.Height());
|
||||
}
|
||||
}
|
||||
void DrawCellCoords(const Vector2i& coords, float scale) {
|
||||
// TODO: Fix offset on text.
|
||||
JGL::J2D::DrawString(Colors::Black, std::format("{}, {}", coords.x, coords.y), coords.x * Core::Chunk::ChunkSize, (coords.y * Core::Chunk::ChunkSize)-5, scale, 10);
|
||||
}
|
||||
void DrawStats(Core::Chunk* chunk, float scale) {
|
||||
Vector2i cell = chunk->GetChunkCell();
|
||||
Vector2i coords = chunk->GetChunkRealCoordinates();
|
||||
|
||||
JGL::J2D::DrawString(Colors::Black, std::format("RenderTarget Age: {}", chunk->time_since_refresh), coords.x, coords.y, scale, 10);
|
||||
}
|
||||
void DrawHeatmap() {}
|
||||
|
||||
void Enable(bool enable = true)
|
||||
{
|
||||
this->enabled = enable;
|
||||
}
|
||||
void Disable() { Enable(false); }
|
||||
[[nodiscard]] bool IsEnabled() const { return enabled; }
|
||||
|
||||
protected:
|
||||
bool enabled = false;
|
||||
Color4 grid_color = {128, 128, 128, 128};
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
/// An extension of the world object that provides rendering for the game world.
|
||||
class LocalWorld : public CaveGame::Core::World {
|
||||
|
||||
public:
|
||||
LocalWorld(const std::string& world_name, int seed, bool overwrite);
|
||||
void PostInit();
|
||||
void Draw();
|
||||
void Update(float elapsed) override;
|
||||
void RefreshAll() override;
|
||||
Camera2D camera;
|
||||
std::thread chunk_thread;
|
||||
std::atomic<bool> run_chunk_thread = true;
|
||||
ConcurrentQueue<Vector2> RequestedChunks;
|
||||
std::vector<Vector2> chunks_in_waiting;
|
||||
ConcurrentQueue<Core::Chunk> ServedChunks;
|
||||
Vector2 mouse_pos;
|
||||
void DebugChunks(bool enabled);
|
||||
|
||||
unsigned int GetRenderTargetCount() const;
|
||||
|
||||
void SaveAndExit() override;
|
||||
|
||||
void SetShowTileActivity(bool enabled);
|
||||
|
||||
bool IsShowTileActivityEnabled() const;
|
||||
|
||||
void RenderTile(const Core::TileID &tid, int wx, int wy, int tx, int ty);
|
||||
|
||||
//Camera2D* Camera() { return &camera;}
|
||||
|
||||
void Emit(const Particle& p);
|
||||
|
||||
protected:
|
||||
|
||||
std::unordered_map<Vector2, JGL::RenderTarget*> cached_chunk_sprites;
|
||||
|
||||
bool IsChunkCellWithinViewport(const Vector2& coords) const;
|
||||
|
||||
// TODO: Chunk should render itself, no? Logical Separation issue here aswell...
|
||||
void RenderChunk(const Vector2& coords, const Core::Chunk& chunk);
|
||||
|
||||
/// Determines whether a given chunk coordinate lies within the viewable space, with an optional 'oversize' parameter.
|
||||
/// @param extraChunkRadius Specifies how many extra chunks can be considered within the viewport when they are in fact, just outside of the viewport.
|
||||
/// @note This is used to overdraw
|
||||
bool IsChunkCellWithinViewport(const Vector2i& coords, int extraChunkRadius = 0) const;
|
||||
void RenderChunk(const Vector2i& coords);
|
||||
void LookForChunksNeedLoading();
|
||||
void LookForChunksNeedUnloading();
|
||||
|
||||
void RequestChunk(const Vector2& cell);
|
||||
|
||||
|
||||
void ChunkServerThread();
|
||||
|
||||
void RenderChunkTexture(const Vector2 &coords, JGL::RenderTarget* destination, const Core::Chunk& chunk);
|
||||
|
||||
/// Render chunk boundaries and relative coordinates, around the camera.
|
||||
void DrawChunkGrid() const;
|
||||
void RenderChunkTexture(const Vector2i &coords, JGL::RenderTarget* destination, Core::Chunk* chunk);
|
||||
/// Checks for missing, or out-of-date, cached sprites of chunks, and renders them.
|
||||
void CheckCachedChunkSprites();
|
||||
static Color4 GetSkyColorInterpolatedForTimeOfDay(float time);
|
||||
static Color4 GetSkyColorBaseForTimeOfDay(float time);
|
||||
void DrawSky();
|
||||
|
||||
public:
|
||||
Camera2D camera;
|
||||
Vector2 mouse_pos;
|
||||
ChunkDebugGizmo chunk_debug;
|
||||
JGL::Font font;
|
||||
protected:
|
||||
|
||||
float check_chunks_timer = 0.f;
|
||||
|
||||
std::unordered_map<Vector2i, JGL::RenderTarget*> cached_chunk_sprites;
|
||||
JGL::RenderTarget* canvas_render_target;
|
||||
|
||||
std::vector<Particle> particles;
|
||||
|
||||
|
||||
|
||||
bool show_tile_activity = false;
|
||||
|
||||
bool AwaitingChunkAtCell(const Vector2 &cell);
|
||||
};
|
||||
}
|
@@ -1,3 +1,14 @@
|
||||
/// CaveGame - A procedural 2D platformer sandbox.
|
||||
/// Created by Josh O'Leary @ Redacted Software, 2020-2025
|
||||
/// Contact: josh@redacted.cc
|
||||
/// Contributors: william@redacted.cc maxi@redacted.cc
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file MainMenu.hpp
|
||||
/// @desc The scene for the home screen, aka main menu.
|
||||
/// @edit 1/28/2025
|
||||
/// @auth Josh O'Leary
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Client/Scene.hpp>
|
||||
@@ -19,7 +30,9 @@ namespace CaveGame::Client
|
||||
public:
|
||||
Event<SingleplayerSessionInfo> RequestWorld;
|
||||
Event<> RequestQuit;
|
||||
Event<> RequestShowCredits;
|
||||
Event<> RequestToggleCredits;
|
||||
Event<> RequestToggleSettings;
|
||||
|
||||
MainMenu();
|
||||
void Update(float elapsed) override;
|
||||
void Draw() override;
|
||||
@@ -31,11 +44,12 @@ namespace CaveGame::Client
|
||||
void PassMouseInput(unsigned int, bool) override;
|
||||
void PassMouseMovement(const J3ML::LinearAlgebra::Vector2 &pos) override;
|
||||
protected:
|
||||
JUI::Scene* scene;
|
||||
JUI::TextRect* title;
|
||||
JUI::Rect* button_group;
|
||||
JUI::Rect* changelog;
|
||||
JGL::Texture* bg; // TODO: RAII on this
|
||||
JUI::Scene* scene = nullptr;
|
||||
JUI::Rect* title = nullptr;
|
||||
JUI::Rect* button_group = nullptr;
|
||||
JUI::Rect* changelog = nullptr;
|
||||
std::shared_ptr<JGL::Texture> bg = nullptr; // TODO: RAII on this
|
||||
|
||||
Vector2 mpos {0,0};
|
||||
Vector2 goal {0,0};
|
||||
private:
|
||||
|
44
Client/include/Client/Particle.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
/// CaveGame - A procedural 2D platformer sandbox.
|
||||
/// Created by Josh O'Leary @ Redacted Software, 2020-2025
|
||||
/// Contact: josh@redacted.cc
|
||||
/// Contributors: william@redacted.cc maxi@redacted.cc
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file Particle.hpp
|
||||
/// @desc Client-side particle engine, still a work-in-progress.
|
||||
/// @edit 1/28/2025
|
||||
/// @auth Josh O'Leary
|
||||
|
||||
#pragma once
|
||||
#include <Color4.hpp>
|
||||
#include <J3ML/LinearAlgebra/Vector3.hpp>
|
||||
#include <JGL/JGL.h>
|
||||
|
||||
namespace CaveGame::Client
|
||||
{
|
||||
|
||||
struct Particle
|
||||
{
|
||||
Vector2 position;
|
||||
Vector2 velocity;
|
||||
Color4 color;
|
||||
float size;
|
||||
float life; // Remaining life of the particle. If < 0: dead and unused.
|
||||
float angle;
|
||||
float weight;
|
||||
void Update(float elapsed)
|
||||
{
|
||||
life -= elapsed;
|
||||
if (life <= 0)
|
||||
return;
|
||||
|
||||
position += velocity * elapsed;
|
||||
}
|
||||
|
||||
void Draw()
|
||||
{
|
||||
JGL::J2D::DrawPoint(color, position, size);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
52
Client/include/Client/PauseMenu.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <JUI/Widgets/Rect.hpp>
|
||||
#include <JUI/Widgets/ListLayout.hpp>
|
||||
#include <JUI/Widgets/TextButton.hpp>
|
||||
|
||||
namespace CaveGame::Client
|
||||
{
|
||||
using namespace JUI::UDimLiterals;
|
||||
|
||||
/// CaveGame's pause menu is implemented as a JUI::Rect overlay, parented to the GameSession::HUD().
|
||||
class PauseMenuWidget : public JUI::Rect {
|
||||
public:
|
||||
Event<> OnResumeButtonPressed;
|
||||
Event<> OnSettingsButtonPressed;
|
||||
Event<> OnQuitButtonPressed;
|
||||
|
||||
/// The default constructor initializes the pause menu, subordinate widgets, and lays out the visual properties of each.
|
||||
PauseMenuWidget();
|
||||
|
||||
/// Construct the Pause menu by specifying it's parent widget.
|
||||
/// It is safe to assume the parent will **ALWAYS** be the Scene* instance GameSession::hud.
|
||||
explicit PauseMenuWidget(Widget* parent);
|
||||
|
||||
bool ObserveMouseInput(JUI::MouseButton btn, bool pressed) override;
|
||||
|
||||
/// Switches the PauseMenu to it's alternate state, which is either "Open" or "Closed".
|
||||
void Toggle();
|
||||
|
||||
/// Sets the state of the PauseMenu.
|
||||
void SetOpen(bool value);
|
||||
|
||||
/// @return The state of the PauseMenu.
|
||||
[[nodiscard]] bool GetOpen() const;
|
||||
|
||||
/// Sets the state of the PauseMenu to "Open".
|
||||
void Open();
|
||||
|
||||
/// Sets the state of the Paus
|
||||
void Close();
|
||||
|
||||
protected:
|
||||
JUI::Rect* button_box;
|
||||
JUI::VerticalListLayout* button_list;
|
||||
JUI::TextButton* resume_btn;
|
||||
JUI::TextButton* settings_btn;
|
||||
JUI::TextButton* quit_btn;
|
||||
|
||||
bool is_open;
|
||||
private:
|
||||
};
|
||||
}
|
@@ -1,28 +1,65 @@
|
||||
/// CaveGame - A procedural 2D platformer sandbox.
|
||||
/// Created by Josh O'Leary @ Redacted Software, 2020-2025
|
||||
/// Contact: josh@redacted.cc
|
||||
/// Contributors: william@redacted.cc maxi@redacted.cc
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file Scene.hpp
|
||||
/// @desc The base class for game scenes. A scene is a self-contained logical grouping of game activity.
|
||||
/// @edit 1/28/2025
|
||||
/// @auth Josh O'Leary
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <J3ML/LinearAlgebra/Vector2.hpp>
|
||||
#include "SceneManager.hpp"
|
||||
#include <rewindow/types/key.h>
|
||||
#include <ReWindow/types/Key.h>
|
||||
|
||||
namespace CaveGame::Client
|
||||
{
|
||||
|
||||
class SceneManager;
|
||||
|
||||
/// The scene class is a self-contained environment for logic, input, and rendering.
|
||||
class Scene
|
||||
{
|
||||
public:
|
||||
Scene();
|
||||
virtual ~Scene() = default;
|
||||
|
||||
/// This function is called by the SceneManager when a scene is changed.
|
||||
/// @note Make sure to call this base when overriding in derived Scene classes.
|
||||
virtual void Load();
|
||||
/// This function is called by the SceneManager when a scene is changed.
|
||||
/// @note Make sure to call this base when overriding in derived Scene classes.
|
||||
virtual void Unload();
|
||||
|
||||
/// This function is called by the SceneManager every frame. It is not called when the scene is inactive.
|
||||
virtual void Update(float elapsed) = 0;
|
||||
|
||||
/// This function is called by the SceneManager every frame. It is not called when the scene is inactive.
|
||||
virtual void Draw() = 0;
|
||||
|
||||
/// This function is called by the SceneManager when the game window changes dimensions.
|
||||
virtual void PassWindowSize(const Vector2& size);
|
||||
|
||||
/// This function is called by the SceneManager when user mouse button input is detected.
|
||||
/// @param mouse_btn A number indicating the mouse button ID.
|
||||
/// @param pressed True if the button was pressed, false if the button was released.
|
||||
virtual void PassMouseInput(unsigned int mouse_btn, bool pressed) {}
|
||||
|
||||
/// This function is called by the SceneManager when user key input is detected.
|
||||
/// @param key An enumeration indicating the key.
|
||||
/// @param pressed True if the key was pressed, false if the key was released.
|
||||
virtual void PassKeyInput(Key key, bool pressed) {}
|
||||
|
||||
/// This function is called by the SceneManager when user mouse movement is detected.
|
||||
/// @param pos The up-to-date mouse position.
|
||||
virtual void PassMouseMovement(const Vector2& pos) {}
|
||||
|
||||
virtual void PassMouseWheel(int wheel) {}
|
||||
|
||||
/// Returns whether this scene is currently the "Active" scene.
|
||||
[[nodiscard]] bool Active() const;
|
||||
protected:
|
||||
bool active;
|
||||
|
@@ -1,3 +1,14 @@
|
||||
/// CaveGame - A procedural 2D platformer sandbox.
|
||||
/// Created by Josh O'Leary @ Redacted Software, 2020-2025
|
||||
/// Contact: josh@redacted.cc
|
||||
/// Contributors: william@redacted.cc maxi@redacted.cc
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file SceneManager.hpp
|
||||
/// @desc A mixin class that implements management, running, and transition of scene objects. Only one scene is active at a given time.
|
||||
/// @edit 1/28/2025
|
||||
/// @auth Josh O'Leary
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace CaveGame::Client
|
||||
@@ -10,7 +21,10 @@ namespace CaveGame::Client
|
||||
void ChangeScene(Scene* new_scene);
|
||||
Scene* CurrentScene();
|
||||
protected:
|
||||
void UpdateSceneState(float elapsed);
|
||||
protected:
|
||||
Scene* next_scene = nullptr;
|
||||
Scene* current_scene = nullptr;
|
||||
private:
|
||||
Scene* prev_scene = nullptr;
|
||||
};
|
||||
}
|
@@ -1,7 +1,77 @@
|
||||
/// CaveGame - A procedural 2D platformer sandbox.
|
||||
/// Created by Josh O'Leary @ Redacted Software, 2020-2025
|
||||
/// Contact: josh@redacted.cc
|
||||
/// Contributors: william@redacted.cc maxi@redacted.cc
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file SettingsMenu.hpp
|
||||
/// @desc The window menu for in-game options.
|
||||
/// @edit 1/28/2025
|
||||
/// @auth Josh O'Leary
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <JUI/Widgets/Window.hpp>
|
||||
#include <JUI/Widgets/Collapsible.hpp>
|
||||
#include <JUI/Widgets/ListLayout.hpp>
|
||||
#include <Core/Singleton.hpp>
|
||||
|
||||
namespace CaveGame::Client
|
||||
{
|
||||
c
|
||||
|
||||
namespace CaveGame::Client {
|
||||
|
||||
// TODO: Analyze behavior of singleton on an object that requires specific initialization, like this.
|
||||
class SettingsMenu : public JUI::Window
|
||||
{
|
||||
public:
|
||||
SettingsMenu()
|
||||
{
|
||||
|
||||
using namespace JUI::UDimLiterals;
|
||||
|
||||
Title("Settings");
|
||||
Size({400_px, 400_px});
|
||||
|
||||
auto* root_layout = new JUI::VerticalListLayout(this->ViewportInstance());
|
||||
|
||||
general_section = new JUI::Collapsible(root_layout);
|
||||
general_section->Size({100_percent, 100_px});
|
||||
general_section->Title("General");
|
||||
|
||||
auto* gen_layout = new JUI::VerticalListLayout(general_section);
|
||||
|
||||
|
||||
sound_section = new JUI::Collapsible(root_layout);
|
||||
sound_section->Size({100_percent, 100_px});
|
||||
sound_section->Title("Sound");
|
||||
|
||||
auto* snd_layout = new JUI::VerticalListLayout(sound_section);
|
||||
|
||||
graphics_section = new JUI::Collapsible(root_layout);
|
||||
graphics_section->Size({100_percent, 100_px});
|
||||
graphics_section->Title("Graphics");
|
||||
|
||||
auto* gfx_layout = new JUI::VerticalListLayout(graphics_section);
|
||||
|
||||
input_section = new JUI::Collapsible(root_layout);
|
||||
input_section->Size({100_percent, 100_px});
|
||||
input_section->Title("Input");
|
||||
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
explicit SettingsMenu(Widget* parent) : SettingsMenu()
|
||||
{
|
||||
Parent(parent);
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
JUI::Collapsible* general_section;
|
||||
JUI::Collapsible* sound_section;
|
||||
JUI::Collapsible* graphics_section;
|
||||
JUI::Collapsible* input_section;
|
||||
private:
|
||||
};
|
||||
}
|
@@ -1,4 +1,13 @@
|
||||
///
|
||||
/// CaveGame - A procedural 2D platformer sandbox.
|
||||
/// Created by Josh O'Leary @ Redacted Software, 2020-2025
|
||||
/// Contact: josh@redacted.cc
|
||||
/// Contributors: william@redacted.cc maxi@redacted.cc
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file Splash.hpp
|
||||
/// @desc The start-screen for the game. Displays our logo, and a loading bar.
|
||||
/// @edit 1/28/2025
|
||||
/// @auth Josh O'Leary
|
||||
|
||||
#pragma once
|
||||
|
||||
@@ -13,33 +22,29 @@ namespace CaveGame::Client
|
||||
{
|
||||
public:
|
||||
Splash();
|
||||
//~Splash();
|
||||
~Splash() override;
|
||||
void Draw() override;
|
||||
void Update(float elapsed) override;
|
||||
void Load() override;
|
||||
void Unload() override;
|
||||
|
||||
//void PassFont(JGL::Font passed);
|
||||
|
||||
bool SplashComplete() const;
|
||||
private:
|
||||
[[nodiscard]] bool SplashComplete() const;
|
||||
protected:
|
||||
float splash_timer = 1.5f;
|
||||
float load_percent = 0.f;
|
||||
JGL::Texture* splash; // TODO: RAII on this
|
||||
//JGL::Font font; // TODO: RAII on this
|
||||
float increment = 0;
|
||||
std::shared_ptr<JGL::Texture> splash = nullptr;
|
||||
|
||||
std::array<JGL::RenderTarget*, 16> column_textures{};
|
||||
|
||||
int column_width = 0;
|
||||
|
||||
std::vector<float> scroll_offsets;
|
||||
std::vector<std::vector<char>> matrix;
|
||||
|
||||
protected:
|
||||
void ComputeMatrixGlyphTable();
|
||||
void ComputeMatrixTextureCache();
|
||||
|
||||
void DrawMatrix();
|
||||
void DrawProgressBar();
|
||||
|
||||
};
|
||||
}
|
32
Client/include/Client/StatsWindow.hpp
Normal file
@@ -0,0 +1,32 @@
|
||||
/// CaveGame - A procedural 2D platformer sandbox.
|
||||
/// Created by Josh O'Leary @ Redacted Software, 2020-2025
|
||||
/// Contact: josh@redacted.cc
|
||||
/// Contributors: william@redacted.cc maxi@redacted.cc
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file StatsWindow.hpp
|
||||
/// @desc Window that displays debugging stats.
|
||||
/// @edit 1/28/2025
|
||||
/// @auth Josh O'Leary
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <JUI/Widgets/Window.hpp>
|
||||
#include <JUI/Widgets/ListLayout.hpp>
|
||||
#include <JUI/Widgets/TextRect.hpp>
|
||||
|
||||
namespace CaveGame::Client {
|
||||
using namespace JUI;
|
||||
|
||||
class StatsWindow : public JUI::Window
|
||||
{
|
||||
public:
|
||||
void AddLine(const std::string& info)
|
||||
{ }
|
||||
protected:
|
||||
private:
|
||||
};
|
||||
|
||||
|
||||
}
|
71
Client/include/Client/TileTool.hpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/// CaveGame - A procedural 2D platformer sandbox.
|
||||
/// Created by Josh O'Leary @ Redacted Software, 2020-2025
|
||||
/// Contact: josh@redacted.cc
|
||||
/// Contributors: william@redacted.cc maxi@redacted.cc
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file TileTool.hpp
|
||||
/// @desc Re-WorldEdit Terrain Editor Tool.
|
||||
/// @edit 1/28/2025
|
||||
/// @auth Josh O'Leary
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <JUI/Widgets/Window.hpp>
|
||||
#include <JUI/Widgets/Slider.hpp>
|
||||
#include <JUI/Widgets/Checkbox.hpp>
|
||||
#include <JUI/Widgets/ListLayout.hpp>
|
||||
|
||||
namespace CaveGame::Client {
|
||||
|
||||
using namespace JUI;
|
||||
|
||||
enum class BrushShape {
|
||||
Circle,
|
||||
Square,
|
||||
Diamond,
|
||||
Cross,
|
||||
X
|
||||
};
|
||||
|
||||
|
||||
|
||||
class TileTool : public JUI::Window
|
||||
{
|
||||
static const bool WorldEditorEnabledByDefault = false;
|
||||
public:
|
||||
Event<float> BrushSizeChanged;
|
||||
Event<float> BrushPercentChanged;
|
||||
Event<bool> TileSimulationDisabledChanged;
|
||||
Event<int> TileSimulationStep;
|
||||
|
||||
float BrushRadius();
|
||||
void BrushRadius(float size);
|
||||
float BrushDensity() const;
|
||||
void BrushDensity(float percent);
|
||||
|
||||
|
||||
|
||||
explicit TileTool(Widget* parent);
|
||||
void Enable(bool value);
|
||||
bool IsEnabled() const { return enabled; }
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
float brush_radius = 8.f;
|
||||
float brush_density = 100.f;
|
||||
protected:
|
||||
const int row_height = 20;
|
||||
const std::string tool_title = "Re-WorldEdit";
|
||||
Slider* brush_size_slider;
|
||||
Slider* brush_percent_slider;
|
||||
TextRect* tool_size_label;
|
||||
TextButton* step_btn;
|
||||
TextButton* step2_btn;
|
||||
TextButton* step3_btn;
|
||||
bool enabled = false;
|
||||
bool tile_sim_disabled = false;
|
||||
private:
|
||||
};
|
||||
}
|
@@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <JUI/JUI.hpp>
|
||||
#include <JUI/Widgets/Scene.hpp>
|
||||
|
||||
namespace CaveGame::Client
|
||||
{
|
||||
|
||||
}
|
@@ -1,3 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/Chunk.hpp>
|
158
Client/src/Client/AssetService.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
#include <Client/AssetService.hpp>
|
||||
#include <thread>
|
||||
#include <JGL/types/Texture.h>
|
||||
#include <JJX/JSON.hpp>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static CaveGame::Client::AssetService* singleton;
|
||||
|
||||
CaveGame::Client::AssetService * CaveGame::Client::AssetService::Get() { return singleton; }
|
||||
|
||||
CaveGame::Client::AssetService::AssetService() {
|
||||
singleton = this;
|
||||
|
||||
ParseManifest();
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string CaveGame::Client::AssetService::FilenameFromPath(const std::filesystem::path &path) {
|
||||
return path.string().substr(path.string().find_last_of("/\\") + 1);
|
||||
}
|
||||
|
||||
std::string CaveGame::Client::AssetService::FilenameFromPathWithoutExtension(const std::filesystem::path &path) {
|
||||
auto base = path.string().substr(path.string().find_last_of("/\\") + 1);
|
||||
|
||||
std::string::size_type const p(base.find_last_of('.'));
|
||||
return base.substr(0, p);
|
||||
}
|
||||
|
||||
void CaveGame::Client::AssetService::EnqueueTexture(const std::string& name, const std::filesystem::path &path) {
|
||||
queue.push({name, path, AssetType::TEXTURE});
|
||||
total_queued++;
|
||||
}
|
||||
|
||||
void CaveGame::Client::AssetService::EnqueueFont(const std::string& name, const std::filesystem::path &path) {
|
||||
queue.push({name, path, AssetType::FONT});
|
||||
total_queued++;
|
||||
}
|
||||
|
||||
void CaveGame::Client::AssetService::EnqueueSound(const std::string& name, const std::filesystem::path &path) {
|
||||
queue.push({name, path, AssetType::AUDIO});
|
||||
total_queued++;
|
||||
}
|
||||
|
||||
bool CaveGame::Client::AssetService::LoadAsset(const CaveGame::Client::AssetRequest &request) {
|
||||
last_asset_processed = request.path.string();
|
||||
|
||||
switch(request.type) {
|
||||
case AssetType::TEXTURE: {
|
||||
if (textures.contains(request.name)) // TODO: Note repeat request.
|
||||
return true;
|
||||
|
||||
auto texture = std::make_shared<Texture>(request.path, FilteringMode::NEAREST);
|
||||
textures[request.name] = texture;
|
||||
}
|
||||
default: {
|
||||
// TODO: We don't support this asset type yet!!!
|
||||
}
|
||||
}
|
||||
last_asset_processed = request.path.string(); //FilenameFromPath(request.path);
|
||||
total_loaded++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CaveGame::Client::AssetService::LoadFromQueue(float maxTimeExpenditure) {
|
||||
|
||||
using fsec = std::chrono::duration<float>;
|
||||
using clock = std::chrono::high_resolution_clock;
|
||||
|
||||
if (queue.empty())
|
||||
return true;
|
||||
|
||||
|
||||
auto start = clock::now();
|
||||
|
||||
//while (!queue.empty() && maxTimeExpenditure > 0.f)
|
||||
//{
|
||||
AssetRequest request = queue.front();
|
||||
queue.pop();
|
||||
|
||||
if (!LoadAsset(request))
|
||||
{
|
||||
std::cerr << "bruh" << std::endl;
|
||||
// Failed to load!!!
|
||||
}
|
||||
auto cur_time = clock::now();
|
||||
fsec fs = cur_time - start;
|
||||
maxTimeExpenditure -= fs.count();
|
||||
//std::this_thread::sleep_for(250ms);
|
||||
//}
|
||||
return queue.empty();
|
||||
|
||||
}
|
||||
|
||||
void CaveGame::Client::AssetService::LoadAllFromQueue() {
|
||||
|
||||
while (!queue.empty())
|
||||
{
|
||||
AssetRequest request = queue.front();
|
||||
queue.pop();
|
||||
if (!LoadAsset(request))
|
||||
{
|
||||
std::cerr << "bruh" << std::endl;
|
||||
// Failed to load!!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CaveGame::Client::AssetService::ParseManifest() {
|
||||
std::string contents = read_file("assets/data/manifest.json");
|
||||
|
||||
using namespace JJX;
|
||||
|
||||
auto [obj, errcode] = json::parse(contents);
|
||||
|
||||
json::object catalog = obj.as_object();
|
||||
|
||||
// TODO: in this case, calling obj.at("textures") results in a bad_alloc?
|
||||
|
||||
if (catalog.contains("textures")) {
|
||||
json::array texlist = catalog.at("textures").as_array();
|
||||
for (auto& texture_entry : texlist) {
|
||||
std::string name = texture_entry[0].string.value();
|
||||
std::string path = texture_entry[1].string.value();
|
||||
EnqueueTexture(name, path);
|
||||
}
|
||||
}
|
||||
|
||||
if (catalog.contains("fonts")) {
|
||||
for (auto &font_entry: catalog["fonts"].as_array()) {
|
||||
std::string name = font_entry[0].string.value();
|
||||
std::string path = font_entry[1].string.value();
|
||||
EnqueueFont(name, path);
|
||||
}
|
||||
}
|
||||
|
||||
if (catalog.contains("sfx")) {
|
||||
for (auto &sfx_entry: catalog["sfx"].as_array()) {
|
||||
std::string name = sfx_entry[0].string.value();
|
||||
std::string path = sfx_entry[1].string.value();
|
||||
EnqueueSound(name, path);
|
||||
}
|
||||
}
|
||||
|
||||
if (catalog.contains("music")) {
|
||||
for (auto &sfx_entry: catalog["music"].as_array()) {
|
||||
std::string name = sfx_entry[0].string.value();
|
||||
std::string path = sfx_entry[1].string.value();
|
||||
EnqueueSound(name, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CaveGame::Client::AssetService::PreloadCertainAssets() {
|
||||
LoadAsset({"redacted", "assets/textures/redacted.png", AssetType::TEXTURE});
|
||||
LoadAsset({"title", "assets/textures/title_1.png", AssetType::TEXTURE});
|
||||
}
|
@@ -1,4 +1,6 @@
|
||||
#include <Client/Camera2D.hpp>
|
||||
#include <ReWindow/InputService.h>
|
||||
#include <jstick.hpp>
|
||||
|
||||
namespace CaveGame::Client
|
||||
{
|
||||
@@ -6,15 +8,67 @@ namespace CaveGame::Client
|
||||
|
||||
}
|
||||
|
||||
void Camera2D::UpdateFreecamControls(float elapsed)
|
||||
{
|
||||
// TODO: implement freecam panning via mouse.
|
||||
|
||||
|
||||
|
||||
Vector2 m = {0,0};
|
||||
|
||||
if (jstick::GetLeftThumbstickAxis().Magnitude() > 0.f)
|
||||
m = jstick::GetLeftThumbstickAxisNormalized();
|
||||
|
||||
if (m.Magnitude() > 0.1f)
|
||||
Move(m*elapsed);
|
||||
|
||||
if (InputService::IsKeyDown(Keys::LeftArrow))
|
||||
MoveLeft(elapsed);
|
||||
if (InputService::IsKeyDown(Keys::RightArrow))
|
||||
MoveRight(elapsed);
|
||||
if (InputService::IsKeyDown(Keys::UpArrow))
|
||||
MoveUp(elapsed);
|
||||
if (InputService::IsKeyDown(Keys::DownArrow))
|
||||
MoveDown(elapsed);
|
||||
|
||||
if (InputService::IsKeyDown(Keys::LeftBracket))
|
||||
Rotate(-5.f*elapsed);
|
||||
if (InputService::IsKeyDown(Keys::RightBracket))
|
||||
Rotate(5.f*elapsed);
|
||||
|
||||
if (InputService::IsKeyDown(Keys::Minus) || jstick::IsButtonDown(jstick::XBoxButton::Back))
|
||||
ZoomOut(elapsed);
|
||||
if (InputService::IsKeyDown(Keys::Equals) || jstick::IsButtonDown(jstick::XBoxButton::Start))
|
||||
ZoomIn(elapsed);
|
||||
}
|
||||
|
||||
void Camera2D::Update(float elapsed) {
|
||||
if (lerp_to_target)
|
||||
position = Vector2::Lerp(position, target, Math::Min(elapsed*lerp_factor, 1.f));
|
||||
else
|
||||
position = target;
|
||||
|
||||
last_free_move += elapsed;
|
||||
|
||||
|
||||
|
||||
|
||||
float t = 1 - move_smoothing;
|
||||
float step = 1.f - Math::Pow(t, elapsed);
|
||||
//float step = Math::Exp(- (move_smoothing*elapsed) );
|
||||
|
||||
if (freecam)
|
||||
UpdateFreecamControls(elapsed);
|
||||
|
||||
if (last_free_move >= free_move_cooldown)
|
||||
{
|
||||
if (lerp_to_target)
|
||||
position = Vector2::Lerp(position, target, step);
|
||||
else
|
||||
position = target;
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
|
||||
// If **really** close, snap to the final position to stabilize.
|
||||
if (position.DistanceSq(target) < 1e-3f)
|
||||
{
|
||||
if (position.DistanceSq(target) < 1e-7f) {
|
||||
position = target;
|
||||
}
|
||||
|
||||
@@ -33,13 +87,24 @@ namespace CaveGame::Client
|
||||
|
||||
void Camera2D::Zoom(float val) {
|
||||
zoom = val;
|
||||
zoom = Math::Clamp(0.125f, zoom, 50.f);
|
||||
zoom = Math::Clamp(zoom, 0.125f, 50.f);
|
||||
}
|
||||
|
||||
void Camera2D::ZoomIn(float amt) {
|
||||
zoom += amt;
|
||||
// Scale zoom amount to be relative to current scaling.
|
||||
amt *= zoom;
|
||||
|
||||
Zoom(Zoom()+amt);
|
||||
|
||||
}
|
||||
|
||||
void Camera2D::ZoomOut(float amt)
|
||||
{
|
||||
// Scale zoom amount to be relative to current scaling.
|
||||
amt *= zoom;
|
||||
|
||||
Zoom(Zoom()-amt);
|
||||
|
||||
zoom = Math::Clamp(0.125f, zoom, 50.f);
|
||||
}
|
||||
|
||||
float Camera2D::Rotation() const { return rotation; }
|
||||
@@ -48,22 +113,37 @@ namespace CaveGame::Client
|
||||
|
||||
void Camera2D::Rotate(float angles) { rotation+=angles;}
|
||||
|
||||
Vector2 Camera2D::ScreenToWorld(const Vector2 &device_coordinates) const {
|
||||
Vector2 pos = ScaledTopLeft() + (device_coordinates/zoom);
|
||||
return pos;
|
||||
}
|
||||
|
||||
Vector2 Camera2D::Position() const { return position; }
|
||||
|
||||
void Camera2D::Move(const Vector2& dir)
|
||||
{
|
||||
position += dir * move_speed;
|
||||
last_free_move = 0;
|
||||
}
|
||||
|
||||
void Camera2D::MoveLeft(float rate) {
|
||||
target -= Vector2(speed*rate, 0);
|
||||
position -= Vector2(move_speed * rate, 0);
|
||||
last_free_move = 0;
|
||||
}
|
||||
|
||||
void Camera2D::MoveRight(float rate) {
|
||||
target += Vector2(speed*rate, 0);
|
||||
position += Vector2(move_speed * rate, 0);
|
||||
last_free_move = 0;
|
||||
}
|
||||
|
||||
void Camera2D::MoveUp(float rate) {
|
||||
target -= Vector2(0, speed*rate);
|
||||
position -= Vector2(0, move_speed * rate);
|
||||
last_free_move = 0;
|
||||
}
|
||||
|
||||
void Camera2D::MoveDown(float rate) {
|
||||
target += Vector2(0, speed*rate);
|
||||
position += Vector2(0, move_speed * rate);
|
||||
last_free_move = 0;
|
||||
}
|
||||
|
||||
void Camera2D::PassViewportSize(const Vector2 &size) {
|
||||
@@ -95,9 +175,9 @@ namespace CaveGame::Client
|
||||
|
||||
void Camera2D::Target(const Vector2 &pos) { target = pos; }
|
||||
|
||||
float Camera2D::PositionSmoothingFactor() const { return lerp_factor;}
|
||||
float Camera2D::PositionSmoothingFactor() const { return move_smoothing;}
|
||||
|
||||
void Camera2D::PositionSmoothingFactor(float smoothing) { lerp_factor = smoothing;}
|
||||
void Camera2D::PositionSmoothingFactor(float smoothing) { move_smoothing = smoothing;}
|
||||
|
||||
bool Camera2D::PositionSmoothingEnabled() const { return lerp_to_target;}
|
||||
|
||||
@@ -107,9 +187,15 @@ namespace CaveGame::Client
|
||||
position = newPos;
|
||||
}
|
||||
|
||||
float Camera2D::MoveSpeed() const { return speed;}
|
||||
float Camera2D::MoveSpeed() const { return move_speed;}
|
||||
|
||||
void Camera2D::MoveSpeed(float value) {
|
||||
speed = value;
|
||||
move_speed = value;
|
||||
}
|
||||
|
||||
AABB2D Camera2D::ScaledViewportOversized(float oversize) const {
|
||||
return AABB2D(
|
||||
position - ScaledHalfSizeOffset() - Vector2(oversize),
|
||||
position + ScaledHalfSizeOffset() + Vector2(oversize*2));
|
||||
}
|
||||
}
|
@@ -1 +0,0 @@
|
||||
#include <Client/Console.hpp>
|
6
Client/src/Client/ContainerWindow.cpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#include <Client/ContainerWindow.hpp>
|
||||
|
||||
|
||||
namespace CaveGame::Client {
|
||||
|
||||
}
|
@@ -1,16 +1,18 @@
|
||||
#include <Client/CreditsWindow.hpp>
|
||||
|
||||
JUI::TextRect* line_item(const std::string& content, int size, const Color4& color = Colors::White)
|
||||
{
|
||||
int line_index = 0;
|
||||
JUI::TextRect* line_item(const std::string& content, int size, const Color4& color = Colors::White) {
|
||||
auto* item = new JUI::TextRect();
|
||||
item->SetFont(JGL::Fonts::Jupiteroid);
|
||||
item->Font(JGL::Fonts::Jupiteroid);
|
||||
item->Size({0,4+size,1.f,0.f});
|
||||
item->SetTextSize(size);
|
||||
item->SetContent(content);
|
||||
item->AlignLeft();
|
||||
item->TextSize(size);
|
||||
item->Content(content);
|
||||
item->AlignCenterHorizontally();
|
||||
item->AlignTop();
|
||||
item->BGColor({0,0,0,0});
|
||||
item->SetTextColor(color);
|
||||
item->TextColor(color);
|
||||
item->BorderWidth(0);
|
||||
item->LayoutOrder(line_index++);
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -18,22 +20,28 @@ JUI::Window *CaveGame::Client::CreateCreditsWindowWidget(JUI::Widget *parent) {
|
||||
|
||||
auto* credits_window = new JUI::Window(parent);
|
||||
credits_window->SetTitleFont(JGL::Fonts::Jupiteroid);
|
||||
credits_window->SetTitle("Credits");
|
||||
credits_window->MinSize({50, 50});
|
||||
credits_window->Size({300, 400, 0, 0});
|
||||
credits_window->Title("Credits");
|
||||
//credits_window->MinSize({250, 450});
|
||||
//credits_window->Size({300, 450, 0, 0});
|
||||
credits_window->Visible(false);
|
||||
credits_window->ViewportInstance()->BGColor({48, 48, 48});
|
||||
|
||||
auto* listing = new JUI::VerticalListLayout(credits_window->GetViewportInstance());
|
||||
listing->Margin(4_px);
|
||||
listing->Padding(5_px);
|
||||
auto* listing = new JUI::VerticalListLayout(credits_window->ViewportInstance());
|
||||
listing->LayoutOrder(JUI::LayoutOrder::V::BOTTOM);
|
||||
//listing->Margin(4_px);
|
||||
//listing->Padding(5_px);
|
||||
|
||||
int min_height = 0;
|
||||
for (auto[size, color, msg] : Credits)
|
||||
for (auto line : Credits)
|
||||
{
|
||||
listing->Add(line_item(msg, size, color));
|
||||
min_height += size;
|
||||
listing->Add(line_item(line.content, line.text_size, line.text_color));
|
||||
min_height += line.text_size + 6;
|
||||
}
|
||||
|
||||
credits_window->MinSize({300, (float)min_height});
|
||||
credits_window->Size(UDim2::FromPixels(300, min_height));
|
||||
credits_window->SetResizable(false);
|
||||
|
||||
/*
|
||||
line_item("CaveGame", 40, listing);
|
||||
line_item("A Redacted Software Product", 16, listing);
|
||||
@@ -50,7 +58,7 @@ JUI::Window *CaveGame::Client::CreateCreditsWindowWidget(JUI::Widget *parent) {
|
||||
line_item("Karl Darling", 24, listing);
|
||||
line_item("Financial Advisor", 16, listing);*/
|
||||
|
||||
credits_window->Size({300, Math::Max(400, min_height), 0, 0});
|
||||
//credits_window->Size({300, Math::Max(400, min_height), 0, 0});
|
||||
|
||||
return credits_window;
|
||||
}
|
||||
|
6
Client/src/Client/Entity.cpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#include <Client/AssetService.hpp>
|
||||
#include <Core/Entity.hpp>
|
||||
#include <Core/Player.hpp>
|
||||
#include <JGL/JGL.h>
|
||||
#include <ReWindow/InputService.h>
|
||||
|
41
Client/src/Client/Explosion.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#include <Client/AssetService.hpp>
|
||||
#include <Core/Explosion.hpp>
|
||||
#include <JGL/JGL.h>
|
||||
|
||||
|
||||
namespace CaveGame::Core {
|
||||
|
||||
|
||||
void Explosion::DrawFrame(const Texture* texture, const AABB2D& quad, const Vector2& pos, const Vector2& scale)
|
||||
{
|
||||
JGL::J2D::DrawPartialSprite(texture, pos, quad.minPoint, quad.maxPoint, rotation, {0.5f, 0.5f}, scale);
|
||||
}
|
||||
|
||||
void Explosion::Draw() {
|
||||
if (!HasDetonated()) { return; }
|
||||
if (anim_timer >= 1.25f) { return; }
|
||||
|
||||
float draw_radius = radius * 1.25f;
|
||||
|
||||
auto* tex = Client::AssetService::Get()->explosion_sprite;
|
||||
|
||||
Vector2 pos = position - (Vector2(draw_radius));
|
||||
Vector2 scale {draw_radius/16.f, draw_radius/16.f};
|
||||
|
||||
DrawFrame(tex, CurrentFrame(), pos, scale);
|
||||
|
||||
/*if (anim_timer > (4.f / 5.f)) {
|
||||
JGL::J2D::DrawPartialSprite(tex, position-(Vector2(draw_radius)), SP_EXPLOSION4.minPoint, SP_EXPLOSION4.maxPoint, rotation, {0.5f,0.5f}, {draw_radius/16.f, draw_radius/16.f});
|
||||
} else if (anim_timer > (3.f / 5.f)) {
|
||||
JGL::J2D::DrawPartialSprite(tex, position-(Vector2(draw_radius)), SP_EXPLOSION3.minPoint, SP_EXPLOSION3.maxPoint, rotation, {0.5f, 0.5f}, {draw_radius/16.f, draw_radius/16.f});
|
||||
} else if (anim_timer > (2.f / 5.f)) {
|
||||
JGL::J2D::DrawPartialSprite(tex, position-(Vector2(draw_radius)), SP_EXPLOSION2.minPoint, SP_EXPLOSION2.maxPoint, rotation, {0.5f, 0.5f}, {draw_radius/16.f, draw_radius/16.f});
|
||||
} else if (anim_timer > (1.f / 5.f)) {
|
||||
JGL::J2D::DrawPartialSprite(tex, position-(Vector2(draw_radius)), SP_EXPLOSION1.minPoint, SP_EXPLOSION1.maxPoint, rotation, {0.5f, 0.5f}, {draw_radius/16.f, draw_radius/16.f});
|
||||
} else {
|
||||
JGL::J2D::DrawPartialSprite(tex, position-(Vector2(draw_radius)), SP_EXPLOSION0.minPoint, SP_EXPLOSION0.maxPoint, rotation, {0.5f, 0.5f}, {draw_radius/16.f, draw_radius/16.f});
|
||||
}*/
|
||||
|
||||
//JGL::J2D::End();
|
||||
}
|
||||
}
|
@@ -1,11 +1,18 @@
|
||||
#include <Client/GameSession.hpp>
|
||||
#include <Core/Loggers.hpp>
|
||||
#include <Client/SettingsMenu.hpp>
|
||||
#include <JUI/Widgets/Rect.hpp>
|
||||
#include <JUI/Widgets/ListLayout.hpp>
|
||||
#include <JUI/UDim.hpp>
|
||||
#include <JUI/UDim2.hpp>
|
||||
#include <ReWindow/InputService.h>
|
||||
|
||||
#include "JUI/Widgets/TextRect.hpp"
|
||||
#include "JUI/UDim.hpp"
|
||||
#include <JUI/Widgets/ImageRect.hpp>
|
||||
#include <JUI/Widgets/Image.hpp>
|
||||
#include <Core/Player.hpp>
|
||||
#include "jstick.hpp"
|
||||
|
||||
void CaveGame::Client::GameSession::Unload() {
|
||||
Scene::Unload();
|
||||
@@ -14,38 +21,208 @@ void CaveGame::Client::GameSession::Unload() {
|
||||
|
||||
void CaveGame::Client::GameSession::Load() {
|
||||
Scene::Load();
|
||||
ConstructHUD();
|
||||
|
||||
world->PostInit();
|
||||
Logs::Info("Building player HUD.");
|
||||
hud = new JUI::Scene();
|
||||
|
||||
pause_menu = new PauseMenuWidget(hud);
|
||||
pause_menu->OnQuitButtonPressed += [this] (){ SaveAndExit();};
|
||||
pause_menu->OnResumeButtonPressed += [this] () { pause_menu->Close(); };
|
||||
pause_menu->OnSettingsButtonPressed += [this] () { RequestToggleSettings.Invoke(); };
|
||||
|
||||
hotbar = TileHotbar();
|
||||
// TODO: Redundant, use the constructor.
|
||||
hotbar.Load(world);
|
||||
hotbar.GetRootWidget()->Parent(hud);
|
||||
|
||||
auto conn = jstick::ButtonPressed += [&, this] (jstick::XBoxButton btn) mutable {
|
||||
if (btn == jstick::XBoxButton::BumperL)
|
||||
hotbar.PrevSlot();
|
||||
|
||||
if (btn == jstick::XBoxButton::BumperR)
|
||||
hotbar.NextSlot();
|
||||
};
|
||||
|
||||
//conn.Invoke(jstick::XBoxButton::BumperR);
|
||||
|
||||
//jstick::ButtonPressed.Invoke(jstick::XBoxButton::BumperL);
|
||||
|
||||
tile_tool = new TileTool(hud);
|
||||
|
||||
tile_tool->TileSimulationDisabledChanged += [this] (bool value) {
|
||||
World()->SetTileSimulationEnabled(!value);
|
||||
};
|
||||
|
||||
tile_tool->TileSimulationStep += [this] (int steps) {
|
||||
for (int i = 0; i < steps; i++)
|
||||
World()->DoTileTiccs(0.f);
|
||||
};
|
||||
}
|
||||
|
||||
void CaveGame::Client::GameSession::Draw() {
|
||||
world->Draw();
|
||||
hud->Draw();
|
||||
//for (auto& entity: entity_list)
|
||||
//{
|
||||
// entity->Draw();
|
||||
//}
|
||||
|
||||
WorldEditToolDrawOverlay();
|
||||
}
|
||||
|
||||
void CaveGame::Client::GameSession::SaveAndExit()
|
||||
{
|
||||
void CaveGame::Client::GameSession::SaveAndExit() {
|
||||
world->SaveAndExit();
|
||||
OnSessionExit.Invoke();
|
||||
}
|
||||
|
||||
|
||||
void CaveGame::Client::GameSession::WorldEditToolDrawTiles(int x, int y, int radius, int density, TileID tile) {
|
||||
tool_rng = RNG(x + y);
|
||||
World()->SetTile(x, y, tile);
|
||||
|
||||
for (int dx = -radius; dx <= radius; ++dx)
|
||||
for (int dy = -radius; dy <= radius; ++dy)
|
||||
if (density >= 100 || density > tool_rng.Float(0, 99))
|
||||
if ( Math::Abs(dx*dx) + Math::Abs(dy*dy) < (radius*radius))
|
||||
World()->SetTile(x+dx, y+dy, tile);
|
||||
}
|
||||
|
||||
bool following = false;
|
||||
Vector2 last = {0,0};
|
||||
|
||||
|
||||
Vector2 CaveGame::Client::GameSession::MouseWorldPos() const {
|
||||
return World()->camera.ScreenToWorld(mouse_pos);
|
||||
}
|
||||
|
||||
void CaveGame::Client::GameSession::WorldEditToolDrawTiles(TileID tile) {
|
||||
Vector2 tile_radius = {0.5f, 0.5f};
|
||||
Vector2 world_coords = MouseWorldPos() - tile_radius;
|
||||
//Vector2i rounded_coords = {Math::FloorInt(world_coords.x), Math::FloorInt(world_coords.y)};
|
||||
|
||||
|
||||
int radius = Math::FloorInt(tile_tool->BrushRadius());
|
||||
int density = Math::FloorInt(tile_tool->BrushDensity());
|
||||
int x = Math::FloorInt(world_coords.x);
|
||||
int y = Math::FloorInt(world_coords.y);
|
||||
|
||||
Vector2 floor_wc = Vector2(x, y);
|
||||
|
||||
float dist = world_coords.Distance(last);
|
||||
|
||||
//if (dist > 1)
|
||||
//{
|
||||
for (int i = 0; i < dist; i++)
|
||||
{
|
||||
|
||||
Vector2 step = last.Lerp(world_coords, i / dist);
|
||||
|
||||
x = Math::FloorInt(step.x);
|
||||
y = Math::FloorInt(step.y);
|
||||
|
||||
WorldEditToolDrawTiles(x, y, radius, density, tile);
|
||||
|
||||
}
|
||||
last = floor_wc;
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
void CaveGame::Client::GameSession::WorldEditToolControlsUpdate(float elapsed){
|
||||
if (InputService::IsMouseButtonDown(MouseButtons::Left) || jstick::GetLeftTriggerNormalized() > 0.1f) {
|
||||
if (!following)
|
||||
{
|
||||
last = MouseWorldPos();
|
||||
following = true;
|
||||
}
|
||||
|
||||
|
||||
WorldEditToolDrawTiles(Tiles()["air"].numeric_id);
|
||||
} else {
|
||||
if (following)
|
||||
following = false;
|
||||
}
|
||||
|
||||
|
||||
if (InputService::IsMouseButtonDown(MouseButtons::Right) || jstick::GetRightTriggerNormalized() > 0.1f) {
|
||||
if (!following)
|
||||
{
|
||||
last = MouseWorldPos();
|
||||
following = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
WorldEditToolDrawTiles(hotbar.GetCurrentSlotTileID());
|
||||
} else
|
||||
if (following)
|
||||
following = false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool lp = false;
|
||||
bool rp = false;
|
||||
|
||||
void CaveGame::Client::GameSession::Update(float elapsed) {
|
||||
world->Update(elapsed);
|
||||
hud->Update(elapsed);
|
||||
HUDTick();
|
||||
//for (auto& entity: entity_list)
|
||||
//{
|
||||
// entity->Update(elapsed);
|
||||
//}
|
||||
hotbar.Update(elapsed);
|
||||
|
||||
Vector2 transformed = World()->camera.ScreenToWorld(mouse_pos);
|
||||
|
||||
// Find closest player to camera and target them.
|
||||
for (auto* e : world->GetEntities()) {
|
||||
if ( dynamic_cast<Player*>(e) != nullptr)
|
||||
{
|
||||
world->camera.Target(e->Position());
|
||||
}
|
||||
}
|
||||
|
||||
// Move the tile cursor when controller thumbstick is moved.
|
||||
Vector2 rstick = {0,0};
|
||||
if (jstick::GetRightThumbstickAxis().Magnitude() > 0)
|
||||
rstick = jstick::GetRightThumbstickAxisNormalized();
|
||||
|
||||
if (rstick.Magnitude() > 0.1f)
|
||||
{
|
||||
float joystick_cursor_speed = 250.f;
|
||||
mouse_pos += rstick*elapsed*joystick_cursor_speed;
|
||||
}
|
||||
}
|
||||
|
||||
void CaveGame::Client::GameSession::WorldEditToolDrawOverlay()
|
||||
{
|
||||
JGL::J2D::Begin();
|
||||
|
||||
auto camera = World()->camera;
|
||||
|
||||
// TODO: The following Translation, Rotation, Scale transformation code is duplicated between here and LocalWorld.cpp:Draw.
|
||||
|
||||
// Shift the origin to the center of the screen.
|
||||
glTranslatef(camera.HalfSizeOffset().x, camera.HalfSizeOffset().y, 0);
|
||||
|
||||
// Apply rotation, zoom, and translation.
|
||||
glRotatef(camera.Rotation(), 0, 0, 1);
|
||||
glScalef(camera.Zoom(), camera.Zoom(), 1);
|
||||
|
||||
glTranslatef((int64_t) -camera.Position().x, (int64_t) -camera.Position().y, 0);
|
||||
|
||||
auto mpos = Vector2(mouse_pos.x, mouse_pos.y);
|
||||
|
||||
auto pos = camera.ScreenToWorld(mpos);
|
||||
|
||||
JGL::J2D::OutlineCircle(Colors::Red, pos, tile_tool->BrushRadius());
|
||||
|
||||
JGL::J2D::DrawPoint({128, 128, 128, 128}, pos, 1.1f);
|
||||
|
||||
JGL::J2D::End();
|
||||
}
|
||||
|
||||
|
||||
CaveGame::Client::GameSession::GameSession(bool overwite_world) : Scene() {
|
||||
world = new LocalWorld("test_world", 0, overwite_world);
|
||||
|
||||
// Spawn in the player when the world is first loaded up.
|
||||
world->AddEntity(new Core::Player({0, 0}));
|
||||
//hud = new JUI::Scene();
|
||||
}
|
||||
|
||||
@@ -54,109 +231,65 @@ void CaveGame::Client::GameSession::PassWindowSize(const Vector2 &size) {
|
||||
hud->SetViewportSize(size);
|
||||
}
|
||||
|
||||
void CaveGame::Client::GameSession::PassMouseInput(unsigned int a, bool b) {
|
||||
hud->ObserveMouseInput((JUI::MouseButton)a, b);
|
||||
}
|
||||
|
||||
void CaveGame::Client::GameSession::PassMouseMovement(const J3ML::LinearAlgebra::Vector2 &pos) {
|
||||
|
||||
mouse_pos = pos;
|
||||
world->mouse_pos = pos;
|
||||
hud->ObserveMouseMovement(pos);
|
||||
}
|
||||
|
||||
void CaveGame::Client::GameSession::PassMouseWheel(int wheel) {
|
||||
if (InputService::IsKeyDown(Keys::LeftShift)) {
|
||||
float radius = tile_tool->BrushRadius();
|
||||
radius -= wheel / 2.f;
|
||||
radius = Math::Max(radius, 0.45f); // TODO: perform clamping inside the setter maybe?
|
||||
tile_tool->BrushRadius(radius);
|
||||
} else {
|
||||
// TODO: hud->ObserveMouseWheel(wheel);
|
||||
hotbar.OnMouseWheel(wheel);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CaveGame::Client::GameSession::PassKeyInput(Key key, bool pressed) {
|
||||
// DO NOT chain input conditions with else; you will introduce biasing
|
||||
|
||||
if (key == Keys::One)
|
||||
active_hotbar_slot = 0;
|
||||
if (key == Keys::Two)
|
||||
active_hotbar_slot = 1;
|
||||
if (key == Keys::Three)
|
||||
active_hotbar_slot = 2;
|
||||
if (key == Keys::Four)
|
||||
active_hotbar_slot = 3;
|
||||
if (key == Keys::Five)
|
||||
active_hotbar_slot = 4;
|
||||
if (key == Keys::Six)
|
||||
active_hotbar_slot = 5;
|
||||
if (key == Keys::Seven)
|
||||
active_hotbar_slot = 6;
|
||||
if (key == Keys::Eight)
|
||||
active_hotbar_slot = 7;
|
||||
if (key == Keys::Nine)
|
||||
active_hotbar_slot = 8;
|
||||
if (key == Keys::Zero)
|
||||
active_hotbar_slot = 9;
|
||||
|
||||
|
||||
/*if (key == Keys::LeftArrow)
|
||||
{
|
||||
world->camera.MoveLeft();
|
||||
}
|
||||
if (key == Keys::RightArrow)
|
||||
{
|
||||
world->camera.MoveRight();
|
||||
}
|
||||
if (key == Keys::UpArrow)
|
||||
{
|
||||
world->camera.MoveUp();
|
||||
}
|
||||
if (key == Keys::DownArrow)
|
||||
{
|
||||
world->camera.MoveDown();
|
||||
}*/
|
||||
}
|
||||
|
||||
void CaveGame::Client::GameSession::ConstructHUD() {
|
||||
|
||||
Logs::Info("Building player HUD.");
|
||||
|
||||
hud = new JUI::Scene();
|
||||
|
||||
auto* hotbar_rect = new JUI::Rect(hud);
|
||||
hotbar_rect->BGColor(Colors::LightGray);
|
||||
hotbar_rect->BorderColor(Colors::White);
|
||||
hotbar_rect->SetBorderWidth(1.5f);
|
||||
hotbar_rect->CornerRounding(8);
|
||||
hotbar_rect->Size({488_px, 18_px});
|
||||
hotbar_rect->Position({50_percent, 98_percent});
|
||||
hotbar_rect->AnchorPoint({0.5f, 1.f});
|
||||
hotbar_rect->BorderMode(JUI::BorderMode::Outline);
|
||||
|
||||
item_label = new JUI::TextRect(hotbar_rect);
|
||||
item_label->BGColor({0,0,0,0});
|
||||
item_label->AnchorPoint({1,0});
|
||||
item_label->SetContent("Item Name Here");
|
||||
item_label->SetTextColor(Colors::White);
|
||||
item_label->SetTextSize(22);
|
||||
item_label->Size({200_px, 30_px});
|
||||
item_label->Position({0_percent, JUI::UDim(-25, 0)});
|
||||
item_label->AlignRight();
|
||||
|
||||
auto* layout = new JUI::HorizontalListLayout(hotbar_rect);
|
||||
layout->PaddingLeft(4_px);
|
||||
layout->PaddingRight(4_px);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
auto* cell = new JUI::Rect(layout);
|
||||
cell->Name("hotbar_" + std::to_string(i));
|
||||
cell->BGColor(Colors::LightGray);
|
||||
cell->BorderColor(Colors::White);
|
||||
cell->Size({48_px, 48_px});
|
||||
cell->AnchorPoint({0.f, 0.8f});
|
||||
cell->CornerRounding(5);
|
||||
cell->SetBorderWidth(1.5f);
|
||||
cell->BorderMode(JUI::BorderMode::Outline);
|
||||
cell->PaddingBottom(10_px);
|
||||
cell->PaddingRight(5_px);
|
||||
|
||||
auto* amount_label = new JUI::TextRect(cell);
|
||||
amount_label->BGColor({0,0,0,0});
|
||||
amount_label->AnchorPoint({1,1});
|
||||
amount_label->SetContent("99x");
|
||||
amount_label->SetTextColor(Colors::White);
|
||||
amount_label->SetTextSize(18);
|
||||
amount_label->Size({30_px, 30_px});
|
||||
amount_label->Position({95_percent, 95_percent});
|
||||
amount_label->AlignRight();
|
||||
|
||||
hotbar_elements[i] = cell;
|
||||
}
|
||||
if (key == Keys::Escape && pressed)
|
||||
pause_menu->Toggle();
|
||||
|
||||
hud->ObserveKeyInput(key, pressed);
|
||||
hotbar.OnKeyInput(key, pressed);
|
||||
}
|
||||
|
||||
void CaveGame::Client::GameSession::UnloadHUD() {
|
||||
Logs::Info("Unloading player HUD.");
|
||||
delete hud;
|
||||
}
|
||||
|
||||
uint16_t CaveGame::Client::GameSession::GetTileIDUnderMouse() {
|
||||
auto ipos = InputService::GetMousePosition();
|
||||
Vector2 pos = Vector2(ipos.x, ipos.y);
|
||||
|
||||
Vector2 coords = world->camera.ScreenToWorld(pos);
|
||||
return world->GetTile(coords.x, coords.y);
|
||||
}
|
||||
|
||||
void CaveGame::Client::GameSession::ToggleWorldEdit() {
|
||||
tile_tool->Enable(!tile_tool->IsEnabled());
|
||||
}
|
||||
|
||||
CaveGame::Core::Player *CaveGame::Client::GameSession::GetLocalPlayerEntity() {
|
||||
for (auto* e : world->GetEntities()) {
|
||||
auto maybe_plr = dynamic_cast<Core::Player *>(e);
|
||||
if (maybe_plr != nullptr) {
|
||||
return maybe_plr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
178
Client/src/Client/Hotbar.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
#include <Client/Hotbar.hpp>
|
||||
#include <JUI/Widgets/ListLayout.hpp>
|
||||
#include <JUI/Widgets/Image.hpp>
|
||||
#include <Core/TileRegistry.hpp>
|
||||
|
||||
using namespace JUI::UDimLiterals;
|
||||
using namespace CaveGame::Core;
|
||||
|
||||
void CaveGame::Client::TileHotbar::Load(CaveGame::Client::LocalWorld *world) {
|
||||
|
||||
|
||||
|
||||
slots[0] = Tiles()["grass"].numeric_id;
|
||||
slots[1] = Tiles()["stone"].numeric_id;
|
||||
slots[2] = Tiles()["water"].numeric_id;
|
||||
slots[3] = Tiles()["lava"].numeric_id;
|
||||
slots[4] = Tiles()["green-moss"].numeric_id;
|
||||
slots[5] = Tiles()["sand"].numeric_id;
|
||||
slots[6] = Tiles()["clay"].numeric_id;
|
||||
slots[7] = Tiles()["oak-plank"].numeric_id;
|
||||
slots[8] = Tiles()["cobblestone"].numeric_id;
|
||||
slots[9] = Tiles()["stone-brick"].numeric_id;
|
||||
|
||||
hotbar_root = new JUI::Rect();
|
||||
hotbar_root->BGColor(Colors::LightGray);
|
||||
hotbar_root->BorderColor(Colors::White);
|
||||
hotbar_root->BorderWidth(1.5f);
|
||||
hotbar_root->CornerRounding(8);
|
||||
hotbar_root->Size({488_px, 18_px});
|
||||
hotbar_root->Position({50_percent, 98_percent});
|
||||
hotbar_root->AnchorPoint({0.5f, 1.f});
|
||||
hotbar_root->BorderMode(JUI::BorderMode::Outline);
|
||||
|
||||
item_label = new JUI::TextRect(hotbar_root);
|
||||
item_label->BorderWidth(0);
|
||||
item_label->BGColor({0,0,0,0});
|
||||
item_label->AnchorPoint({1,0});
|
||||
item_label->Content("Item Name Here");
|
||||
item_label->TextColor(Colors::White);
|
||||
item_label->TextSize(22);
|
||||
item_label->Size({200_px, 30_px});
|
||||
item_label->Position({0_percent, JUI::UDim(-25, 0)});
|
||||
item_label->AlignRight();
|
||||
|
||||
auto* layout = new JUI::HorizontalListLayout(hotbar_root);
|
||||
layout->PaddingLeft(4_px);
|
||||
layout->PaddingRight(4_px);
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
auto item = Tiles().Get(slots[i]);
|
||||
|
||||
auto* cell = new JUI::Rect(layout);
|
||||
cell->Name("hotbar_" + std::to_string(i));
|
||||
cell->BGColor(Colors::LightGray);
|
||||
cell->BorderColor(Colors::White);
|
||||
cell->Size({48_px, 48_px});
|
||||
cell->AnchorPoint({0.f, 0.8f});
|
||||
cell->CornerRounding(5);
|
||||
cell->BorderWidth(1.5f);
|
||||
cell->BorderMode(JUI::BorderMode::Outline);
|
||||
cell->PaddingBottom(10_px);
|
||||
cell->PaddingRight(5_px);
|
||||
cell->BGColor(item.color);
|
||||
|
||||
// Add tile-sample rendertarget as item icon.
|
||||
auto* img = new JUI::Image(cell);
|
||||
img->FitImageToParent(true);
|
||||
img->Padding({2_px});
|
||||
|
||||
int icon_size = 16;
|
||||
|
||||
auto* canvas_texture = new Texture(Vector2i( icon_size, icon_size));
|
||||
auto* tile_icon_canvas = new RenderTarget(canvas_texture, {0, 0, 0, 0});
|
||||
|
||||
JGL::J2D::Begin(tile_icon_canvas, nullptr, true);
|
||||
for (int x = 0; x < icon_size; x++)
|
||||
for (int y = 0; y < icon_size; y++)
|
||||
world->RenderTile(item.numeric_id, x, y, x, y);
|
||||
JGL::J2D::End();
|
||||
|
||||
delete tile_icon_canvas;
|
||||
img->Content(canvas_texture);
|
||||
|
||||
auto* amount_label = new JUI::TextRect(cell);
|
||||
amount_label->BGColor({0,0,0,0});
|
||||
amount_label->AnchorPoint({1,1});
|
||||
amount_label->Content("99x");
|
||||
amount_label->TextColor(Colors::White);
|
||||
amount_label->TextSize(18);
|
||||
amount_label->Size({30_px, 30_px});
|
||||
amount_label->Position({95_percent, 95_percent});
|
||||
amount_label->AlignRight();
|
||||
amount_label->BorderWidth(0);
|
||||
|
||||
hotbar_elements[i] = cell;
|
||||
}
|
||||
}
|
||||
|
||||
void CaveGame::Client::TileHotbar::OnKeyInput(const Key& key, bool pressed) {
|
||||
if (key == Keys::One)
|
||||
slot_index = 0;
|
||||
if (key == Keys::Two)
|
||||
slot_index = 1;
|
||||
if (key == Keys::Three)
|
||||
slot_index = 2;
|
||||
if (key == Keys::Four)
|
||||
slot_index = 3;
|
||||
if (key == Keys::Five)
|
||||
slot_index = 4;
|
||||
if (key == Keys::Six)
|
||||
slot_index = 5;
|
||||
if (key == Keys::Seven)
|
||||
slot_index = 6;
|
||||
if (key == Keys::Eight)
|
||||
slot_index = 7;
|
||||
if (key == Keys::Nine)
|
||||
slot_index = 8;
|
||||
if (key == Keys::Zero)
|
||||
slot_index = 9;
|
||||
}
|
||||
|
||||
void CaveGame::Client::TileHotbar::Update(float elapsed) {
|
||||
// TODO: Subtly Animate the UI. (JUI has Tweens on the roadmap.)
|
||||
|
||||
// Reset appearance of all slots.
|
||||
for (auto & hotbar_element : hotbar_elements)
|
||||
{
|
||||
hotbar_element->Size({44_px, 48_px});
|
||||
hotbar_element->CornerRounding(5);
|
||||
hotbar_element->BorderWidth(1.5f);
|
||||
hotbar_element->AnchorPoint({0.f, 0.8f});
|
||||
}
|
||||
|
||||
// Update item name label.
|
||||
Core::TileID active_item = slots[slot_index];
|
||||
item_label->Content(Tiles()[active_item].display_name);
|
||||
|
||||
// Set appearance of selected slot.
|
||||
hotbar_elements[slot_index]->CornerRounding(8);
|
||||
hotbar_elements[slot_index]->BorderWidth(1.5f);
|
||||
hotbar_elements[slot_index]->Size({58_px, 56_px});
|
||||
hotbar_elements[slot_index]->AnchorPoint({0.f, 0.75f});
|
||||
}
|
||||
|
||||
CaveGame::Core::TileID CaveGame::Client::TileHotbar::GetSlotTileID(int slotIdx) const {
|
||||
return slots[slotIdx];
|
||||
}
|
||||
|
||||
void CaveGame::Client::TileHotbar::SetSlotTileID(int slotIdx, CaveGame::Core::TileID tid) {
|
||||
slots[slotIdx] = tid;
|
||||
}
|
||||
|
||||
JUI::Rect *CaveGame::Client::TileHotbar::GetSlotWidget(int slotIdx) const {
|
||||
return hotbar_elements[slotIdx];
|
||||
}
|
||||
|
||||
JUI::Rect *CaveGame::Client::TileHotbar::GetRootWidget() const { return hotbar_root; }
|
||||
|
||||
int CaveGame::Client::TileHotbar::GetSlotIndex() const { return slot_index; }
|
||||
|
||||
CaveGame::Core::TileID CaveGame::Client::TileHotbar::GetCurrentSlotTileID() const {
|
||||
return slots[slot_index];
|
||||
}
|
||||
|
||||
void CaveGame::Client::TileHotbar::PrevSlot() {
|
||||
if (slot_index > 0)
|
||||
slot_index -= 1;
|
||||
else
|
||||
slot_index = slot_count-1;
|
||||
}
|
||||
|
||||
void CaveGame::Client::TileHotbar::NextSlot() {
|
||||
if (slot_index < (slot_count-1))
|
||||
slot_index += 1;
|
||||
else
|
||||
slot_index = 0;
|
||||
}
|
@@ -5,11 +5,13 @@
|
||||
#include <fstream>
|
||||
#include <Core/Loggers.hpp>
|
||||
#include <JUI/Widgets/Scene.hpp>
|
||||
#include <Core/TileRegistry.hpp>
|
||||
|
||||
namespace CaveGame::Client {
|
||||
using namespace CaveGame::Core;
|
||||
|
||||
LocalWorld::LocalWorld(const std::string& world_name, int seed, bool overwrite)
|
||||
: World(world_name, seed, overwrite) {
|
||||
: World(world_name, seed, overwrite) {
|
||||
Logs::Info("Spinning up ChunkServerThread");
|
||||
chunk_thread = std::thread(&LocalWorld::ChunkServerThread, this);
|
||||
chunk_thread.detach();
|
||||
@@ -20,101 +22,153 @@ namespace CaveGame::Client {
|
||||
//font = JGL::Font("assets/fonts/Jupiteroid.ttf");
|
||||
}
|
||||
|
||||
void LocalWorld::DrawChunkGrid() const {
|
||||
|
||||
|
||||
void LocalWorld::CheckCachedChunkSprites() {
|
||||
for (auto& [chunk_pos, chunk] : loaded_chunks) {
|
||||
if (!IsChunkCellWithinViewport(chunk_pos))
|
||||
continue;
|
||||
|
||||
auto it = cached_chunk_sprites.find(chunk_pos);
|
||||
if (it != cached_chunk_sprites.end()) {
|
||||
if (!chunk->touched)
|
||||
continue;
|
||||
|
||||
chunk->touched = false;
|
||||
RenderChunkTexture(chunk_pos, it->second, chunk);
|
||||
}
|
||||
|
||||
auto* chunk_sprite = new RenderTarget(Vector2i(Chunk::ChunkSize), Colors::Transparent,false,
|
||||
SampleRate::NONE, FilteringMode::MIPMAP_NEAREST);
|
||||
RenderChunkTexture(chunk_pos, chunk_sprite, chunk);
|
||||
cached_chunk_sprites.insert({chunk_pos, chunk_sprite});
|
||||
}
|
||||
}
|
||||
|
||||
Color4 LocalWorld::GetSkyColorInterpolatedForTimeOfDay(float time) {
|
||||
float rounded_low = Math::Floor(time);
|
||||
float rounded_high = Math::Ceil(time);
|
||||
|
||||
float delta = (time - rounded_low);
|
||||
|
||||
Color4 color_low = GetSkyColorBaseForTimeOfDay(rounded_low);
|
||||
Color4 color_high = GetSkyColorBaseForTimeOfDay(rounded_high);
|
||||
|
||||
return Color4::Lerp(color_low, color_high, delta);
|
||||
}
|
||||
|
||||
Color4 LocalWorld::GetSkyColorBaseForTimeOfDay(float time) {
|
||||
if (time >= 23*60)
|
||||
return Colors::Black;
|
||||
else if (time >= 22*60)
|
||||
return Colors::Black;
|
||||
else if (time >= 21*60)
|
||||
return Colors::White;
|
||||
else if (time >= 20*60)
|
||||
return Colors::White;
|
||||
else if (time >= 19*60)
|
||||
return Colors::White;
|
||||
else if (time >= 18*60)
|
||||
return Colors::White;
|
||||
else if (time >= 17*60)
|
||||
return Colors::White;
|
||||
else if (time >= 16*60)
|
||||
return Colors::White;
|
||||
else if (time >= 15*60)
|
||||
return Colors::White;
|
||||
|
||||
return Colors::White;
|
||||
}
|
||||
|
||||
void LocalWorld::DrawSky() {
|
||||
|
||||
Vector2 viewport_topleft = camera.ScaledViewport().minPoint;
|
||||
Vector2 viewport_bottomright = camera.ScaledViewport().maxPoint;
|
||||
|
||||
int nearest_grid_left = Math::Floor(viewport_topleft.x / Core::Chunk::ChunkSize);
|
||||
int nearest_grid_right = Math::Floor(viewport_bottomright.x / Core::Chunk::ChunkSize);
|
||||
Color4 base_bg_color = Colors::Blues::CornflowerBlue;
|
||||
|
||||
for (int x = nearest_grid_left; x <= nearest_grid_right; x++) {
|
||||
auto top = Vector2(x * Core::Chunk::ChunkSize, viewport_topleft.y);
|
||||
auto bottom = Vector2(x * Core::Chunk::ChunkSize, viewport_bottomright.y);
|
||||
JGL::J2D::DrawLine(Colors::Red, top, bottom);
|
||||
float space_starts_at = -2000.f;
|
||||
|
||||
float depth = camera.Position().y;
|
||||
|
||||
// Draw space bg.
|
||||
if (depth < -space_starts_at) {
|
||||
float t = Math::Clamp( -((camera.Position().y+space_starts_at) / space_starts_at), 0.f, 1.f);
|
||||
base_bg_color = base_bg_color.Lerp(Colors::Blues::MidnightBlue, t);
|
||||
}
|
||||
|
||||
int nearest_grid_top = Math::Floor(viewport_topleft.y / Core::Chunk::ChunkSize);
|
||||
int nearest_grid_bottom = Math::Floor(viewport_bottomright.y / Core::Chunk::ChunkSize);
|
||||
float deep_starts_at = 2000.f;
|
||||
|
||||
for (int y = nearest_grid_top; y <= nearest_grid_bottom; y++) {
|
||||
auto left = Vector2(viewport_topleft.x, y * Core::Chunk::ChunkSize);
|
||||
auto right = Vector2(viewport_bottomright.x, y * Core::Chunk::ChunkSize);
|
||||
JGL::J2D::DrawLine(Colors::Red, left, right);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LocalWorld::CheckCachedChunkSprites()
|
||||
{
|
||||
for (auto&[chunk_pos, chunk] : loaded_chunks) {
|
||||
if (IsChunkCellWithinViewport(chunk_pos))
|
||||
{
|
||||
// No rendertarget for this chunk.
|
||||
if (!cached_chunk_sprites.contains(chunk_pos))
|
||||
{
|
||||
chunk.touched = false;
|
||||
auto* target = new JGL::RenderTarget({Core::Chunk::ChunkSize, Core::Chunk::ChunkSize}, {0,0,0,0});
|
||||
RenderChunkTexture(chunk_pos, target, chunk);
|
||||
cached_chunk_sprites.insert({chunk_pos, target});
|
||||
}
|
||||
// rendertarget needs updating.
|
||||
else if (chunk.touched) {
|
||||
// TODO: Modify RenderTarget in place.
|
||||
chunk.touched = false;
|
||||
|
||||
auto* target = cached_chunk_sprites[chunk_pos];
|
||||
|
||||
RenderChunkTexture(chunk_pos, target, chunk);
|
||||
|
||||
|
||||
//cached_chunk_sprites.erase(chunk_pos);
|
||||
//auto* target = new JGL::RenderTarget({Core::Chunk::ChunkSize, Core::Chunk::ChunkSize}, {0,0,0,0});
|
||||
//RenderChunkTexture(chunk_pos, target, chunk);
|
||||
//cached_chunk_sprites.insert({chunk_pos, target});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
// Draw deep bg.
|
||||
if (depth > deep_starts_at) {
|
||||
float t = Math::Clamp((camera.Position().y-deep_starts_at) / deep_starts_at, 0.f, 1.f);
|
||||
base_bg_color = base_bg_color.Lerp(Colors::Black, t);
|
||||
}
|
||||
|
||||
|
||||
glClearColor(base_bg_color.RN(), base_bg_color.GN(), base_bg_color.BN(),1.f);
|
||||
|
||||
//if ()
|
||||
|
||||
}
|
||||
/*
|
||||
void LocalWorld::DrawBeforeProjection(){ }
|
||||
void LocalWorld::DrawDuringProjection() { }
|
||||
void LocalWorld::DrawAfterProjection() { }
|
||||
*/
|
||||
|
||||
void LocalWorld::Draw() {
|
||||
CheckCachedChunkSprites();
|
||||
|
||||
JGL::J2D::Begin();
|
||||
DrawSky();
|
||||
|
||||
J2D::Begin();
|
||||
|
||||
glClearColor(0.5f,0.5f,1.f,1.f);
|
||||
// TODO: The following Translation, Rotation, Scale transformation code is duplicated between here and CaveGameWindow.cpp:Draw.
|
||||
// THIS BELONGS IN THE CAMERA ITSELF. You "Render" the camera first, And everything else after such that the camera can observe it. - Redacted.
|
||||
|
||||
// Shift the origin to the center of the screen.
|
||||
glTranslatef(camera.HalfSizeOffset().x, camera.HalfSizeOffset().y, 0);
|
||||
|
||||
//DrawSky();
|
||||
|
||||
// Apply rotation, zoom, and translation.
|
||||
glRotatef(camera.Rotation(), 0, 0, 1);
|
||||
glScalef(camera.Zoom(), camera.Zoom(), 1);
|
||||
glTranslatef(-camera.Position().x, -camera.Position().y, 0);
|
||||
|
||||
glTranslatef((int64_t) -camera.Position().x, (int64_t) -camera.Position().y, 0);
|
||||
|
||||
// Draw the cached RenderTargets for our chunks.
|
||||
for (const auto&[chunk_pos, chunk] : loaded_chunks)
|
||||
for (const auto& [chunk_pos, chunk] : loaded_chunks)
|
||||
{
|
||||
if (IsChunkCellWithinViewport(chunk_pos))
|
||||
RenderChunk(chunk_pos, chunk);
|
||||
{
|
||||
RenderChunk(chunk_pos);
|
||||
|
||||
//JGL::J2D::DrawString(Colors::Black, std::format("{}, {}", chunk_pos.x, chunk_pos.y), chunk.GetChunkRealCoordinates().x, chunk.GetChunkRealCoordinates().y,
|
||||
// 1, 8, font);
|
||||
// Debug Grid
|
||||
if (chunk_debug.IsEnabled())
|
||||
{
|
||||
chunk_debug.DrawStats(chunk, 0.99f / camera.Zoom());
|
||||
chunk_debug.DrawCellCoords(chunk_pos, 0.99f / camera.Zoom());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Debug Grid
|
||||
DrawChunkGrid();
|
||||
if (chunk_debug.IsEnabled())
|
||||
chunk_debug.DrawGrid(camera.ScaledViewport());
|
||||
|
||||
for (auto* entity : entities) {
|
||||
entity->Draw();
|
||||
}
|
||||
|
||||
Vector2 transformed = camera.ScreenToWorld(mouse_pos);
|
||||
for (auto& p : particles)
|
||||
{
|
||||
if (p.life > 0)
|
||||
p.Draw();
|
||||
}
|
||||
|
||||
JGL::J2D::OutlineCircle(Colors::Red, transformed, 5);
|
||||
|
||||
|
||||
// Banana for scale.
|
||||
JGL::J2D::FillRect(Colors::Yellow, {0,0}, {32, 48});
|
||||
|
||||
JGL::J2D::End();
|
||||
|
||||
@@ -122,126 +176,276 @@ namespace CaveGame::Client {
|
||||
//hud->Draw();
|
||||
}
|
||||
|
||||
bool LocalWorld::IsChunkCellWithinViewport(const Vector2 &coords) const {
|
||||
AABB2D chunk_bounding_box = AABB2D(coords*Core::Chunk::ChunkSize, coords*Core::Chunk::ChunkSize+Vector2(Core::Chunk::ChunkSize));
|
||||
return Core::Solver::AABB2Dvs(
|
||||
camera.ScaledViewport(), chunk_bounding_box);
|
||||
unsigned int LocalWorld::GetRenderTargetCount() const {
|
||||
return cached_chunk_sprites.size();
|
||||
}
|
||||
|
||||
void LocalWorld::RenderChunkTexture(const Vector2 &coords, JGL::RenderTarget* destination, const Core::Chunk& chunk)
|
||||
bool LocalWorld::IsChunkCellWithinViewport(const Vector2i &coords, int extraChunkRadius) const {
|
||||
int extraChunkComputedPixelsRequired = extraChunkRadius*Core::Chunk::ChunkSize;
|
||||
AABB2D chunk_bounding_box = AABB2D(Vector2(coords)*Core::Chunk::ChunkSize, Vector2(coords)*Core::Chunk::ChunkSize+Vector2(Core::Chunk::ChunkSize));
|
||||
return Core::Solver::AABB2Dvs(
|
||||
camera.ScaledViewportOversized(extraChunkComputedPixelsRequired), chunk_bounding_box);
|
||||
}
|
||||
|
||||
RNG rng;
|
||||
|
||||
|
||||
void LocalWorld::RenderTile(const TileID& t_id, int wx, int wy, int tx, int ty)
|
||||
{
|
||||
|
||||
using namespace CaveGame::Core;
|
||||
|
||||
static Tile stone = Tiles()["stone"];
|
||||
|
||||
static TileID air = Tiles()["air"].numeric_id;
|
||||
static TileID void_tile = Tiles()["void"].numeric_id;
|
||||
static TileID cobblestone = Tiles()["cobblestone"].numeric_id;
|
||||
static TileID stone_id = stone.numeric_id;
|
||||
static TileID oak_plank = Tiles()["oak-plank"].numeric_id;
|
||||
static TileID stone_brick = Tiles()["stone-brick"].numeric_id;
|
||||
|
||||
|
||||
|
||||
// TODO: Migrate custom tile render code to an override Draw method in tile classes?
|
||||
// TODO: See class Tile::Draw to see why this is halted.
|
||||
|
||||
if (t_id == air || t_id == void_tile)
|
||||
return;
|
||||
|
||||
|
||||
Color4 t_color;
|
||||
|
||||
|
||||
const Core::Tile& t_data = Tiles()[t_id];
|
||||
|
||||
if (t_id == cobblestone) {
|
||||
float val = generator.Perlin(wx, wy, 4, 4, 0.25f, 1.5f);
|
||||
|
||||
|
||||
unsigned int rand = generator.ColorMap(stone.pallet->size(), wx, wy);
|
||||
t_color = t_data.pallet->operator[](rand);
|
||||
|
||||
if (val > 0.40f || val < -0.40f) {
|
||||
t_color.r += 32;
|
||||
t_color.g += 32;
|
||||
t_color.b += 32;
|
||||
} else if (val > 0.15f || val < -0.10f)
|
||||
{ } else {
|
||||
t_color.r -= 64;
|
||||
t_color.g -= 64;
|
||||
t_color.b -= 64;
|
||||
}
|
||||
|
||||
|
||||
//Core::Tiles::Cobblestone.color_pallet
|
||||
} else if (t_id == oak_plank) {
|
||||
|
||||
// TODO: Make each plank have a slightly different base color.
|
||||
|
||||
int plank_height = 4;
|
||||
|
||||
int plank_row = ((int) Math::Floor(wy / plank_height));
|
||||
|
||||
int plank_row_idx = plank_row % 5;
|
||||
|
||||
int plank_length_modulus = 12;
|
||||
|
||||
uint8_t base_r = 212;// - shift;
|
||||
uint8_t base_g = 184;// - shift;
|
||||
uint8_t base_b = 125;// - shift;
|
||||
|
||||
|
||||
if (plank_row_idx == 0) { plank_length_modulus = 14; }
|
||||
if (plank_row_idx == 1) { plank_length_modulus = 19; }
|
||||
if (plank_row_idx == 2) { plank_length_modulus = 21; }
|
||||
if (plank_row_idx == 3) { plank_length_modulus = 17; }
|
||||
if (plank_row_idx == 4) { plank_length_modulus = 27; }
|
||||
|
||||
int plank_col = (int) Math::Floor(wx / plank_length_modulus);
|
||||
|
||||
uint8_t plank_base_rng = generator.ColorMap(20, plank_col, plank_row);
|
||||
uint8_t rng = generator.ColorMap(25, wx, wy);
|
||||
|
||||
base_r -= plank_base_rng + rng;
|
||||
base_g -= plank_base_rng + rng;
|
||||
base_b -= plank_base_rng + rng;
|
||||
|
||||
|
||||
if (Math::Abs(wy % plank_height) == (plank_height-1) || Math::Abs((wx + (plank_row * plank_height)) % plank_length_modulus) == 1) {
|
||||
base_r += 30;
|
||||
base_g += 25;
|
||||
base_b += 20;
|
||||
}
|
||||
|
||||
if (wy % plank_height == 0 || (wx + (plank_row * plank_height)) % plank_length_modulus == 0) {
|
||||
base_r -= 60;
|
||||
base_g -= 45;
|
||||
base_b -= 30;
|
||||
}
|
||||
|
||||
t_color = {base_r, base_g, base_b};
|
||||
} else if (t_id == Tiles()["stone-brick"].numeric_id) {
|
||||
|
||||
|
||||
uint8_t shift = generator.ColorMap(30, wx, wy);
|
||||
uint8_t base_r = 130 - shift;
|
||||
uint8_t base_g = 125 - shift;
|
||||
uint8_t base_b = 120 - shift;
|
||||
|
||||
int brick_height = 5;
|
||||
int brick_length = 9;
|
||||
int brick_row = ((int)Math::Floor(wy / brick_height));
|
||||
|
||||
int brick_offset = 0;
|
||||
|
||||
if (brick_row % 2 == 0)
|
||||
brick_offset = 3;
|
||||
|
||||
|
||||
if (Math::Abs(wy) % brick_height == (brick_height-1) || (Math::Abs(wx) + brick_offset) % brick_length == 1)
|
||||
{
|
||||
base_r += 35;
|
||||
base_g += 35;
|
||||
base_b += 30;
|
||||
}
|
||||
|
||||
if (wy % brick_height == 0 || (wx + brick_offset) % brick_length == 0) {
|
||||
base_r -= 55;
|
||||
base_g -= 60;
|
||||
base_b -= 55;
|
||||
}
|
||||
t_color = {base_r, base_g, base_b};
|
||||
} else if (t_data.pallet.has_value()) {
|
||||
unsigned int rand = generator.ColorMap(t_data.pallet->size(), wx, wy);
|
||||
t_color = t_data.pallet.value()[rand];
|
||||
} else {
|
||||
t_color = t_data.color;
|
||||
}
|
||||
|
||||
JGL::J2D::DrawPoint(t_color, tx, ty);
|
||||
}
|
||||
|
||||
void LocalWorld::RenderChunkTexture(const Vector2i &coords, JGL::RenderTarget* destination, Core::Chunk* chunk)
|
||||
{
|
||||
|
||||
#define DEBUG_TILE_UPDATES
|
||||
|
||||
using CaveGame::Core::TileID;
|
||||
|
||||
std::vector<Vector2> stone_coords;
|
||||
std::vector<Vector2> dirt_coords;
|
||||
std::vector<Vector2> grass_coords;
|
||||
std::vector<Vector2> clay_coords;
|
||||
std::vector<Vector2> mud_coords;
|
||||
TileID t_id;
|
||||
Core::Tile t_data;
|
||||
|
||||
JGL::J2D::Begin(destination, nullptr, true);
|
||||
for (int x = 0; x < Core::Chunk::ChunkSize; x++)
|
||||
{
|
||||
for (int y = 0; y < Core::Chunk::ChunkSize; y++)
|
||||
{
|
||||
TileID tile = chunk.GetTile(x, y);
|
||||
t_id = chunk->GetTile(x, y);
|
||||
|
||||
Vector2 relative_tile_coords = Vector2(x, y);
|
||||
Vector2 real_tile_coords = relative_tile_coords;
|
||||
const Vector2& tile_coords = relative_tile_coords;
|
||||
int wx = (coords.x*Core::Chunk::ChunkSize) + x;
|
||||
int wy = (coords.y*Core::Chunk::ChunkSize) + y;
|
||||
|
||||
if (tile == TileID::AIR) // Air
|
||||
continue;
|
||||
//if (t_id == TileID::AIR || t_id == TileID::VOID) // Air
|
||||
//continue;
|
||||
|
||||
RenderTile(t_id, wx, wy, x, y);
|
||||
|
||||
if (show_tile_activity)
|
||||
{
|
||||
if (chunk->GetTileUpdateFlag(x, y))
|
||||
JGL::J2D::DrawPoint(Color4(255, 0, 0, 64), tile_coords);
|
||||
|
||||
if (chunk->GetTileUpdateBufferFlag(x, y))
|
||||
JGL::J2D::DrawPoint(Color4(255, 0, 0, 64), tile_coords);
|
||||
}
|
||||
|
||||
|
||||
/*else if (tile == TileID::STONE) // Stone
|
||||
stone_coords.push_back(real_tile_coords);
|
||||
else if (tile == TileID::DIRT) // Dirt
|
||||
dirt_coords.push_back(real_tile_coords);
|
||||
else if (tile == TileID::GRASS) // Grass
|
||||
grass_coords.push_back(real_tile_coords);
|
||||
else if (tile == TileID::CLAY) // Ore
|
||||
clay_coords.push_back(real_tile_coords);
|
||||
else if (tile == TileID::MUD) // Water
|
||||
mud_coords.push_back(real_tile_coords);*/
|
||||
/*t_data = Core::GetByNumeric(t_id);
|
||||
|
||||
if (t_id == TileID::COBBLESTONE) {
|
||||
float val = generator.Perlin(wx, wy, 12, 12, 12, 2);
|
||||
|
||||
if (val > 0.60f || val < -0.60f)
|
||||
t_color = Core::Tiles::Cobblestone.color_pallet[1];
|
||||
else if (val > 0.40f || val < -0.40f)
|
||||
t_color = Core::Tiles::Cobblestone.color_pallet[0];
|
||||
else if (val > 0.2f || val < -0.2f)
|
||||
t_color = Core::Tiles::Cobblestone.color_pallet[2];
|
||||
else
|
||||
t_color = Core::Tiles::Cobblestone.color_pallet[3];
|
||||
//Core::Tiles::Cobblestone.color_pallet
|
||||
} else if (t_id == TileID::OAK_PLANK) {
|
||||
|
||||
if (Math::Mod(y, 3) == 0 || Math::Mod(x, 9) == 0) {
|
||||
t_color = Colors::Browns::Brown;
|
||||
} else
|
||||
t_color = Colors::Browns::BurlyWood;
|
||||
|
||||
} else if (t_data->has_color_pallet) {
|
||||
uint rand = generator.ColorMap(t_data->color_pallet.size(), x, y);
|
||||
t_color = t_data->color_pallet[rand];
|
||||
} else {
|
||||
t_color = t_data->base_color;
|
||||
}
|
||||
|
||||
JGL::J2D::DrawPoint(t_color, tile_coords);*/
|
||||
|
||||
}
|
||||
}
|
||||
JGL::J2D::Begin(destination, true);
|
||||
JGL::J2D::DrawPoints(Colors::Gray, stone_coords.data(), stone_coords.size());
|
||||
JGL::J2D::DrawPoints(Colors::Browns::Chocolate, dirt_coords.data(), dirt_coords.size());
|
||||
JGL::J2D::DrawPoints(Colors::Green, grass_coords.data(), grass_coords.size());
|
||||
JGL::J2D::DrawPoints(Colors::Reds::Firebrick, clay_coords.data(), clay_coords.size());
|
||||
JGL::J2D::DrawPoints(Colors::Browns::BurlyWood, mud_coords.data(), mud_coords.size());
|
||||
JGL::J2D::End();
|
||||
}
|
||||
|
||||
void LocalWorld::RenderChunk(const Vector2& coords, const Core::Chunk& chunk) {
|
||||
auto it = cached_chunk_sprites.find(coords);
|
||||
if (it != cached_chunk_sprites.end())
|
||||
JGL::J2D::DrawRenderTarget(it->second, chunk.GetChunkRealCoordinates());
|
||||
Vector2i GetChunkRealCoordinates(const Vector2i& cell) {
|
||||
return cell * Core::Chunk::ChunkSize;
|
||||
}
|
||||
|
||||
void LocalWorld::LookForChunksNeedUnloading()
|
||||
{
|
||||
void LocalWorld::RenderChunk(const Vector2i& coords) {
|
||||
auto it = cached_chunk_sprites.find(coords);
|
||||
if (it != cached_chunk_sprites.end())
|
||||
JGL::J2D::DrawRenderTarget(it->second, Vector2(GetChunkRealCoordinates(coords)));
|
||||
}
|
||||
|
||||
void LocalWorld::LookForChunksNeedUnloading() {
|
||||
for (auto it = loaded_chunks.begin(); it != loaded_chunks.end();)
|
||||
{
|
||||
const auto coords = it->first;
|
||||
if (!IsChunkCellWithinViewport(coords))
|
||||
{
|
||||
|
||||
if (!IsChunkCellWithinViewport(coords, 2)) {
|
||||
// TODO: Move off main thread.
|
||||
SaveChunkToFile(coords, it->second);
|
||||
|
||||
//delete it->second;
|
||||
delete it->second;
|
||||
loaded_chunks.erase(it++);
|
||||
|
||||
cached_chunk_sprites.erase(coords);
|
||||
} else
|
||||
{
|
||||
++it;
|
||||
auto sprite_it = cached_chunk_sprites.find(coords);
|
||||
if (sprite_it != cached_chunk_sprites.end())
|
||||
delete sprite_it->second, cached_chunk_sprites.erase(sprite_it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
bool LocalWorld::AwaitingChunkAtCell(const Vector2& cell)
|
||||
{
|
||||
int cnt = std::count(chunks_in_waiting.begin(), chunks_in_waiting.end(), cell);
|
||||
|
||||
if (cnt > 1)
|
||||
{
|
||||
// Not good.
|
||||
std::cerr << "Fuckiddy fuck" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (cnt == 1)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//void LocalWorld::LookForChunksNeedLoading()
|
||||
void LocalWorld::LookForChunksNeedLoading()
|
||||
{
|
||||
Vector2 viewport_topleft = camera.ScaledViewportOversized(2*Core::Chunk::ChunkSize).minPoint;
|
||||
Vector2 viewport_bottomright = camera.ScaledViewportOversized(2*Core::Chunk::ChunkSize).maxPoint;
|
||||
|
||||
// TODO: Implement mechanism to load chunk on separate thread.
|
||||
// TODO: ConcurrentQueue<Vector2> RequestedChunks;
|
||||
// TODO: Chunk Generator / Loader From File on separate thread.
|
||||
int lower_bound_h = Math::Floor(viewport_topleft.x / Core::Chunk::ChunkSize)/*-2*/;
|
||||
int upper_bound_h = Math::Floor(viewport_bottomright.x / Core::Chunk::ChunkSize)/*+2*/;
|
||||
|
||||
// TODO: Make sure an extra layer of chunks beyond the viewport is generated.
|
||||
// This will limit the amount of "pop-in" the player will notice.
|
||||
|
||||
Vector2 viewport_topleft = camera.ScaledViewport().minPoint;
|
||||
Vector2 viewport_bottomright = camera.ScaledViewport().maxPoint;
|
||||
|
||||
int lower_bound_h = Math::Floor(viewport_topleft.x / Core::Chunk::ChunkSize);
|
||||
int upper_bound_h = Math::Floor(viewport_bottomright.x / Core::Chunk::ChunkSize);
|
||||
|
||||
int lower_bound_v = Math::Floor(viewport_topleft.y / Core::Chunk::ChunkSize);
|
||||
int upper_bound_v = Math::Floor(viewport_bottomright.y / Core::Chunk::ChunkSize);
|
||||
int lower_bound_v = Math::Floor(viewport_topleft.y / Core::Chunk::ChunkSize)/*-2*/;
|
||||
int upper_bound_v = Math::Floor(viewport_bottomright.y / Core::Chunk::ChunkSize)/*+2*/;
|
||||
|
||||
for (int x = lower_bound_h; x <= upper_bound_h; x++)
|
||||
{
|
||||
for (int y = lower_bound_v; y <= upper_bound_v; y++)
|
||||
{
|
||||
Vector2 cell = Vector2(x,y);
|
||||
if (!HasChunkAtCell(cell) || !AwaitingChunkAtCell(cell))
|
||||
Vector2i cell(x, y);
|
||||
if (!HasChunkAtCell(cell) && !AwaitingChunkAtCell(cell))
|
||||
RequestChunk(cell);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -251,69 +455,81 @@ namespace CaveGame::Client {
|
||||
|
||||
camera.Update(elapsed);
|
||||
|
||||
LookForChunksNeedUnloading();
|
||||
LookForChunksNeedLoading();
|
||||
|
||||
|
||||
|
||||
// TODO: Unload chunks once they're offscreen for a certain time threshold.
|
||||
|
||||
|
||||
// Check our generator queue for complete chunks, and pull them into our loaded chunks.
|
||||
while (!ServedChunks.empty())
|
||||
for (auto& particle : particles)
|
||||
{
|
||||
Core::Chunk c = Core::Chunk({0,0});
|
||||
ServedChunks.front_pop(c);
|
||||
loaded_chunks.emplace(c.GetChunkCell(), c);
|
||||
|
||||
if (std::find(chunks_in_waiting.begin(), chunks_in_waiting.end(), c.GetChunkCell()) != chunks_in_waiting.end()) {
|
||||
chunks_in_waiting.erase(std::remove(chunks_in_waiting.begin(), chunks_in_waiting.end(), c.GetChunkCell()), chunks_in_waiting.end());
|
||||
}
|
||||
if (particle.life > 0)
|
||||
particle.Update(elapsed);
|
||||
}
|
||||
}
|
||||
|
||||
void LocalWorld::RequestChunk(const Vector2 &cell) {
|
||||
// TODO: Ensure we cannot request the same chunk twice..
|
||||
if (std::find(chunks_in_waiting.begin(), chunks_in_waiting.end(), cell) == chunks_in_waiting.end())
|
||||
{
|
||||
std::erase_if(particles, [&](Particle& p) {return p.life <= 0; });
|
||||
|
||||
RequestedChunks.push(cell);
|
||||
chunks_in_waiting.push_back(cell);
|
||||
}
|
||||
}
|
||||
/*std::vector<Particle>::iterator it;
|
||||
for (it = particles.begin(); it != particles.end();)
|
||||
{aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
if (it->life <= 0)
|
||||
it = particles.erase(it);
|
||||
//else
|
||||
//it->Update(elapsed);
|
||||
}*/
|
||||
|
||||
void LocalWorld::ChunkServerThread() {
|
||||
while (run_chunk_thread)
|
||||
{
|
||||
while (!RequestedChunks.empty())
|
||||
check_chunks_timer += elapsed;
|
||||
|
||||
// TODO: Move this block to World class
|
||||
|
||||
if (check_chunks_timer > 1/8.f) {
|
||||
// TODO: These procedures need to be performed asynchronously.
|
||||
check_chunks_timer = 0.f;
|
||||
LookForChunksNeedUnloading();
|
||||
LookForChunksNeedLoading();
|
||||
//auto a1 = std::async(&LocalWorld::LookForChunksNeedLoading, this);
|
||||
|
||||
// TODO: Unload chunks once they're offscreen for a certain time threshold.
|
||||
|
||||
|
||||
// Check our generator queue for complete chunks, and pull them into our loaded chunks.
|
||||
while (!ServedChunks.empty())
|
||||
{
|
||||
Vector2 cell;
|
||||
RequestedChunks.front_pop(cell);
|
||||
Core::Chunk* c = nullptr;
|
||||
ServedChunks.front_pop(c);
|
||||
loaded_chunks.emplace(c->GetChunkCell(), c);
|
||||
|
||||
if (HasChunkOnFile(cell)) {
|
||||
ServedChunks.emplace(Core::Chunk(cell, GetChunkFullPath(cell)));
|
||||
} else {
|
||||
Core::Chunk chunk(cell);
|
||||
generator.FirstPass(chunk);
|
||||
//SaveChunkToFile(cell, chunk);
|
||||
ServedChunks.emplace(chunk);
|
||||
}
|
||||
if (std::find(chunks_in_waiting.begin(), chunks_in_waiting.end(), c->GetChunkCell()) != chunks_in_waiting.end())
|
||||
chunks_in_waiting.erase(std::remove(chunks_in_waiting.begin(), chunks_in_waiting.end(), c->GetChunkCell()), chunks_in_waiting.end());
|
||||
|
||||
|
||||
//generator.FirstPass()
|
||||
//std::this_thread::sleep_for(100ms);
|
||||
//generator.SecondPass(this, c);
|
||||
}
|
||||
//std::this_thread::sleep_for(250ns);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void LocalWorld::RefreshAll() {
|
||||
//World::RefreshAll();
|
||||
cached_chunk_sprites.clear();
|
||||
chunks_in_waiting.clear();
|
||||
}
|
||||
|
||||
void LocalWorld::DebugChunks(bool enabled) {
|
||||
chunk_debug.Enable(enabled);
|
||||
}
|
||||
|
||||
void LocalWorld::SaveAndExit() {
|
||||
World::SaveAndExit();
|
||||
|
||||
}
|
||||
|
||||
void LocalWorld::SetShowTileActivity(bool enabled) {
|
||||
show_tile_activity = enabled;
|
||||
}
|
||||
|
||||
bool LocalWorld::IsShowTileActivityEnabled() const { return show_tile_activity;}
|
||||
|
||||
void LocalWorld::Emit(const Particle &p) {
|
||||
particles.push_back(p);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@@ -2,6 +2,9 @@
|
||||
#include <JUI/Widgets/TextRect.hpp>
|
||||
#include "JUI/Widgets/ListLayout.hpp"
|
||||
#include "JUI/Widgets/Button.hpp"
|
||||
#include "JUI/Widgets/Image.hpp"
|
||||
#include "Client/AssetService.hpp"
|
||||
#include <Client/SettingsMenu.hpp>
|
||||
|
||||
CaveGame::Client::MainMenu::MainMenu() : Scene()
|
||||
{
|
||||
@@ -29,12 +32,12 @@ void CaveGame::Client::MainMenu::Draw() {
|
||||
JGL::J2D::DrawSprite(*bg, {-(mpos.x/2.f),-mpos.y*2.f}, 0.f, {0,0}, {aspect,aspect});
|
||||
JGL::J2D::End();
|
||||
scene->Draw();
|
||||
|
||||
}
|
||||
|
||||
void CaveGame::Client::MainMenu::Load() {
|
||||
|
||||
bg = new JGL::Texture("assets/textures/bg.png");
|
||||
bg = AssetService::Get()->GetTexture("bg");
|
||||
|
||||
|
||||
Scene::Load();
|
||||
}
|
||||
@@ -52,17 +55,17 @@ T* create_widget(JUI::Widget* parent)
|
||||
JUI::TextButton* CaveGame::Client::MainMenu::create_mainmenu_btn(const std::string& content, JUI::Widget* parent)
|
||||
{
|
||||
auto* btn = create_widget<JUI::TextButton>(parent);
|
||||
btn->SetFont(JGL::Fonts::Jupiteroid);
|
||||
btn->SetTextSize(24);
|
||||
btn->SetTextColor(Colors::White);
|
||||
btn->BaseBGColor(Colors::LightGray);
|
||||
btn->Font(JGL::Fonts::Jupiteroid);
|
||||
btn->TextSize(24);
|
||||
btn->TextColor(Colors::White);
|
||||
btn->BaseBGColor(Colors::DarkGray);
|
||||
btn->HoveredBGColor(Colors::Blues::LightSteelBlue);
|
||||
btn->BGColor(btn->BaseBGColor());
|
||||
|
||||
btn->Size({0, 40, 1.f, 0});
|
||||
btn->Enable();
|
||||
btn->Center();
|
||||
btn->SetContent(content);
|
||||
btn->Content(content);
|
||||
return btn;
|
||||
}
|
||||
|
||||
@@ -71,55 +74,61 @@ void CaveGame::Client::MainMenu::BuildWidgets() {
|
||||
using namespace JUI;
|
||||
|
||||
title = new TextRect(scene);
|
||||
title->SetFont(JGL::Fonts::Jupiteroid);
|
||||
title->SetContent("CaveGame");
|
||||
title->SetTextColor(Colors::White);
|
||||
title->SetTextSize(64);
|
||||
//title->SetFont(JGL::Fonts::Jupiteroid);
|
||||
//title->SetContent("CaveGame");
|
||||
//title->SetTextColor(Colors::White);
|
||||
//title->SetTextSize(64);
|
||||
title->Position({0, 0, 0.5f, 0.2f});
|
||||
title->Size({0, 0, 0.3f, 0.15f});
|
||||
title->Size({0, 0, 0.5f, 0.25f});
|
||||
title->AnchorPoint({0.5f, 0.5f});
|
||||
title->Center();
|
||||
//title->Center();
|
||||
title->BGColor({0,0,0,0});
|
||||
title->SetBorderStyling(Colors::Cyans::Cyan, 1);
|
||||
title->BorderWidth(0);
|
||||
|
||||
auto* content = new JUI::Image(title);
|
||||
// TODO: Unsafe!
|
||||
content->Content(AssetService::Get()->GetTexture("title").get());
|
||||
content->FitImageToParent(true);
|
||||
|
||||
button_group = new Rect(scene);
|
||||
button_group->Size({250, 400, 0, 0});
|
||||
button_group->Position({0, 0, 0.5f, 0.3f});
|
||||
button_group->Position({0, 0, 0.5f, 0.4f});
|
||||
button_group->AnchorPoint({0.5f, 0.f});
|
||||
button_group->BGColor({0,0,0,0});
|
||||
button_group->SetBorderStyling(Colors::Cyans::Cyan, 1);
|
||||
button_group->BorderWidth(0);
|
||||
|
||||
auto *button_list = new JUI::VerticalListLayout(button_group);
|
||||
button_list->PaddingBottom(5_px);
|
||||
|
||||
auto *new_world_btn = create_mainmenu_btn("New World", button_list);
|
||||
|
||||
new_world_btn->OnReleaseEvent += [this](Vector2 pos, MouseButton btn, bool b)
|
||||
new_world_btn->OnReleaseEvent += [this](Vector2 pos, JUI::MouseButton btn, bool b)
|
||||
{
|
||||
RequestWorld.Invoke({.NewWorld = true});
|
||||
};
|
||||
|
||||
auto *load_world_btn = create_mainmenu_btn("Load World", button_list);
|
||||
|
||||
load_world_btn->OnReleaseEvent += [this](Vector2 pos, MouseButton btn, bool b)
|
||||
load_world_btn->OnReleaseEvent += [this](Vector2 pos, JUI::MouseButton btn, bool b)
|
||||
{
|
||||
RequestWorld.Invoke({.NewWorld = false});
|
||||
};
|
||||
|
||||
|
||||
//auto *sp_btn = create_mainmenu_btn("Singleplayer", button_list);
|
||||
auto *settings_btn = create_mainmenu_btn("Settings", button_list);
|
||||
|
||||
//sp_btn->OnReleaseEvent += [this](Vector2 pos, MouseButton btn, bool b) {
|
||||
// if (b)
|
||||
// RequestWorld.Invoke({});
|
||||
//};
|
||||
settings_btn->OnReleaseEvent += [this] (Vector2 pos, JUI::MouseButton btn, bool b) mutable {
|
||||
if (b)
|
||||
RequestToggleSettings.Invoke();
|
||||
};
|
||||
|
||||
auto *mp_btn = create_mainmenu_btn("Multiplayer", button_list);
|
||||
auto *credits_btn = create_mainmenu_btn("Credits", button_list);
|
||||
|
||||
credits_btn->OnReleaseEvent += [this](Vector2 pos, MouseButton btn, bool b) {
|
||||
credits_btn->OnReleaseEvent += [this](Vector2 pos, JUI::MouseButton btn, bool b) {
|
||||
if (b)
|
||||
RequestShowCredits.Invoke();
|
||||
RequestToggleCredits.Invoke();
|
||||
};
|
||||
|
||||
// TODO: Replace with IconButton with steam logo
|
||||
@@ -127,7 +136,7 @@ void CaveGame::Client::MainMenu::BuildWidgets() {
|
||||
auto *quit_btn = create_mainmenu_btn("Quit", button_list);
|
||||
|
||||
|
||||
quit_btn->OnReleaseEvent += [this](Vector2 pos, MouseButton btn, bool b)
|
||||
quit_btn->OnReleaseEvent += [this](Vector2 pos, JUI::MouseButton btn, bool b)
|
||||
{
|
||||
if (b)
|
||||
RequestQuit.Invoke();
|
||||
|
6
Client/src/Client/Particle.cpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#include <Client/Particle.hpp>
|
||||
|
||||
|
||||
namespace CaveGame::Client {
|
||||
|
||||
}
|
98
Client/src/Client/PauseMenu.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#include <Client/PauseMenu.hpp>
|
||||
|
||||
namespace CaveGame::Client
|
||||
{
|
||||
|
||||
PauseMenuWidget::PauseMenuWidget() {
|
||||
|
||||
this->Size({100_percent, 100_percent}); // Full Screen.
|
||||
this->BGColor({32, 32, 32, 128}); // Transparent Gray.
|
||||
this->ZIndex(1);
|
||||
|
||||
button_box = new JUI::Rect(this);
|
||||
button_box->Size({150_px, 170_px});
|
||||
button_box->AnchorPoint({0.f, 1.f});
|
||||
button_box->Position({50_px, 100_percent - 50_px});
|
||||
button_box->BGColor(Colors::Transparent);
|
||||
button_box->BorderWidth(0);
|
||||
|
||||
button_list = new JUI::VerticalListLayout(button_box);
|
||||
button_list->PaddingBottom(5_px);
|
||||
|
||||
|
||||
resume_btn = new JUI::TextButton(button_list);
|
||||
resume_btn->Size({100_percent, 50_px});
|
||||
resume_btn->CornerRounding(7);
|
||||
resume_btn->Center();
|
||||
resume_btn->TextSize(24);
|
||||
resume_btn->TextColor(Colors::White);
|
||||
resume_btn->BaseBGColor(Colors::DarkGray);
|
||||
resume_btn->HoveredBGColor(Colors::Blues::LightSteelBlue);
|
||||
resume_btn->BGColor(resume_btn->BaseBGColor());
|
||||
resume_btn->Content("Resume");
|
||||
|
||||
resume_btn->OnClickEvent += [this] (Vector2 pos, JUI::MouseButton state) {
|
||||
OnResumeButtonPressed.Invoke();
|
||||
};
|
||||
|
||||
settings_btn = new JUI::TextButton(button_list);
|
||||
settings_btn->Size({100_percent, 50_px});
|
||||
settings_btn->CornerRounding(7);
|
||||
settings_btn->Center();
|
||||
settings_btn->TextSize(24);
|
||||
settings_btn->TextColor(Colors::White);
|
||||
settings_btn->BaseBGColor(Colors::DarkGray);
|
||||
settings_btn->HoveredBGColor(Colors::Blues::LightSteelBlue);
|
||||
settings_btn->BGColor(settings_btn->BaseBGColor());
|
||||
settings_btn->Content("Settings");
|
||||
|
||||
settings_btn->OnClickEvent += [this] (Vector2 pos, JUI::MouseButton state) {
|
||||
OnSettingsButtonPressed.Invoke();
|
||||
};
|
||||
|
||||
quit_btn = new JUI::TextButton(button_list);
|
||||
quit_btn->Size({100_percent, 50_px});
|
||||
quit_btn->CornerRounding(7);
|
||||
quit_btn->Center();
|
||||
quit_btn->TextSize(24);
|
||||
quit_btn->TextColor(Colors::White);
|
||||
quit_btn->BaseBGColor(Colors::DarkGray);
|
||||
quit_btn->HoveredBGColor(Colors::Blues::LightSteelBlue);
|
||||
quit_btn->BGColor(quit_btn->BaseBGColor());
|
||||
quit_btn->Content("Save & Exit World");
|
||||
|
||||
quit_btn->OnClickEvent += [this] (Vector2 pos, JUI::MouseButton state) {
|
||||
OnQuitButtonPressed.Invoke();
|
||||
};
|
||||
|
||||
// Default-initialize as closed.
|
||||
this->Close();
|
||||
}
|
||||
|
||||
PauseMenuWidget::PauseMenuWidget(Widget *parent): PauseMenuWidget() {
|
||||
this->Parent(parent);
|
||||
}
|
||||
|
||||
bool PauseMenuWidget::ObserveMouseInput(JUI::MouseButton btn, bool pressed) {
|
||||
if (is_open)
|
||||
return Rect::ObserveMouseInput(btn, pressed);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void PauseMenuWidget::Toggle() {
|
||||
SetOpen(!GetOpen());
|
||||
}
|
||||
|
||||
void PauseMenuWidget::SetOpen(bool value) {
|
||||
is_open = value;
|
||||
this->Visible(is_open);
|
||||
}
|
||||
|
||||
bool PauseMenuWidget::GetOpen() const { return is_open; }
|
||||
|
||||
void PauseMenuWidget::Open() { SetOpen(true); }
|
||||
|
||||
void PauseMenuWidget::Close() { SetOpen(false); }
|
||||
}
|
||||
|
89
Client/src/Client/Player.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#include <Core/Player.hpp>
|
||||
#include <Client/AssetService.hpp>
|
||||
#include <Core/Entity.hpp>
|
||||
#include <JGL/JGL.h>
|
||||
#include <ReWindow/InputService.h>
|
||||
#include <jstick.hpp>
|
||||
|
||||
|
||||
// TODO: Move this shit to Client/Player.cpp
|
||||
void CaveGame::Core::Player::Draw() {
|
||||
|
||||
Direction dir = Direction::None;
|
||||
|
||||
if (!facing_left)
|
||||
dir = Direction::Horizontal;
|
||||
|
||||
|
||||
AABB2D quad = Frame_Idle;
|
||||
|
||||
// TODO: Work on conditions to make the player sprite not rapidly change to 'falling' when dropping 1-tall steps.
|
||||
if (!on_ground && ( airtime > 0.1f || Math::Abs(velocity.y) > 10.f ))
|
||||
{
|
||||
if (velocity.y < 0)
|
||||
quad = Frame_Ascend;
|
||||
else
|
||||
quad = Frame_Descend;
|
||||
} else if (walking) { // Math::Abs(velocity.x) > 2) {
|
||||
//float clamped_velocity = (Math::Clamp(Math::Abs(velocity.x) - 5.f, 0.f, 50.f) / 20.f);
|
||||
float walk_cycle = Math::Mod(anim_timer*2.5f, 1);
|
||||
|
||||
if (walk_cycle > 2.f/3.f) {
|
||||
quad = Frame_Walk3;
|
||||
} else if (walk_cycle > 1.f / 3.f) {
|
||||
quad = Frame_Walk2;
|
||||
} else {
|
||||
quad = Frame_Walk1;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Utilize land quad when falling enough to take fall damage, player can't move for a half second.
|
||||
|
||||
auto myAsset = Client::AssetService::Get()->GetTexture("player");
|
||||
JGL::J2D::DrawPartialSprite(myAsset.get(), RenderTopLeft(), quad.minPoint, {16, 24}, 0, {0,0}, {1,1}, Colors::White, dir);
|
||||
JGL::J2D::OutlineRect(Colors::Red, RenderTopLeft(), texture_center * 2.f);
|
||||
JGL::J2D::OutlineRect(Colors::Blue, TopLeft(), bounding_box);
|
||||
J2D::DrawString(Colors::White, std::format("vel: {},{}", Math::Round(velocity.x, 2), Math::Round(velocity.y, 2)), position.x, position.y-8, 8, 0.5f);
|
||||
J2D::DrawString(Colors::White, std::format("ct: {} cd: {}", coll_tests, coll_hits), position.x, position.y-16, 8, 0.5f);
|
||||
}
|
||||
|
||||
void CaveGame::Core::Player::Update(float elapsed) {
|
||||
Humanoid::Update(elapsed);
|
||||
|
||||
anim_timer += elapsed;
|
||||
walking = false;
|
||||
|
||||
|
||||
Vector2 dpad = jstick::GetDPadAxis();
|
||||
|
||||
if (noclip) {
|
||||
if (InputService::IsKeyDown(Keys::A) || dpad.x <= -0.5f)
|
||||
Accelerate({-500*elapsed, 0});
|
||||
|
||||
if (InputService::IsKeyDown(Keys::D) || dpad.x >= +0.5f)
|
||||
Accelerate({500*elapsed, 0});
|
||||
|
||||
if (InputService::IsKeyDown(Keys::W) || dpad.y <= -0.5f)
|
||||
Accelerate({0, -500*elapsed});
|
||||
|
||||
if (InputService::IsKeyDown(Keys::S) || dpad.y >= 0.5f)
|
||||
Accelerate({0, 500*elapsed});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (InputService::IsKeyDown(Keys::A) || dpad.x <= -0.5f)
|
||||
WalkLeft(elapsed);
|
||||
|
||||
if (InputService::IsKeyDown(Keys::D) || dpad.x >= +0.5f)
|
||||
WalkRight(elapsed);
|
||||
|
||||
if (InputService::IsKeyDown(Keys::W) || dpad.y <= -0.5f)
|
||||
Jump(elapsed);
|
||||
|
||||
|
||||
|
||||
|
||||
//if (InputService::IsKeyDown(Keys::S))
|
||||
//Accelerate({0, -1});
|
||||
}
|
@@ -6,11 +6,31 @@ namespace CaveGame::Client
|
||||
{
|
||||
void SceneManager::ChangeScene(Scene* new_scene)
|
||||
{
|
||||
if (current_scene != nullptr)
|
||||
current_scene->Unload();
|
||||
next_scene = new_scene;
|
||||
|
||||
current_scene = new_scene;
|
||||
current_scene->Load();
|
||||
//if (current_scene != nullptr)
|
||||
// current_scene->Unload();
|
||||
|
||||
//current_scene = new_scene;
|
||||
//current_scene->Load();
|
||||
}
|
||||
|
||||
void SceneManager::UpdateSceneState(float elapsed)
|
||||
{
|
||||
if (next_scene != nullptr)
|
||||
{
|
||||
if (current_scene != nullptr)
|
||||
{
|
||||
current_scene->Unload();
|
||||
prev_scene = current_scene;
|
||||
}
|
||||
|
||||
current_scene = next_scene;
|
||||
current_scene->Load();
|
||||
|
||||
|
||||
next_scene = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Scene* SceneManager::CurrentScene() { return current_scene; }
|
||||
|
@@ -1,13 +1,10 @@
|
||||
#include <Client/Splash.hpp>
|
||||
#include <JGL/JGL.h>
|
||||
|
||||
#include "Client/AssetService.hpp"
|
||||
CaveGame::Client::Splash::Splash() : Scene()
|
||||
{
|
||||
|
||||
//font = JGL::Font();
|
||||
|
||||
ComputeMatrixGlyphTable();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -40,14 +37,14 @@ void CaveGame::Client::Splash::ComputeMatrixTextureCache()
|
||||
for (int i = 0; i < column_textures.size(); i++)
|
||||
{
|
||||
|
||||
Vector2 column_size = {glyph_measurement.x, glyph_measurement.y * 100};
|
||||
Vector2i column_size = {(int) glyph_measurement.x, (int) glyph_measurement.y * 100};
|
||||
auto* column = new JGL::RenderTarget(column_size, {0, 0, 0, 0}, false);
|
||||
|
||||
JGL::J2D::Begin(column, true);
|
||||
JGL::J2D::Begin(column, nullptr, true);
|
||||
|
||||
for (int col = 0; col < 50; col++) {
|
||||
Color4 text_col = Color4(32, 192, 92, 255 - (col*4));
|
||||
JGL::J2D::DrawString(text_col, std::to_string(rand() % 2), 0, glyph_measurement.y * col, 1, 16);
|
||||
JGL::J2D::DrawString(text_col, std::to_string(rand() % 2), 0, glyph_measurement.y * col, 16);
|
||||
}
|
||||
JGL::J2D::End();
|
||||
|
||||
@@ -72,6 +69,8 @@ void CaveGame::Client::Splash::DrawProgressBar()
|
||||
float progress_bar_length = bar_length * load_percent;
|
||||
|
||||
JGL::J2D::FillRect(Colors::White, bar_pos, {progress_bar_length, bar_height});
|
||||
|
||||
JGL::J2D::DrawString(Colors::Black, AssetService::Get()->LastAsset(), bar_pos.x, bar_pos.y, 1, 16);
|
||||
}
|
||||
|
||||
|
||||
@@ -100,7 +99,7 @@ void CaveGame::Client::Splash::Draw()
|
||||
|
||||
Vector2 middle = screen_dimensions/2.f;
|
||||
|
||||
// TODO: Implement draw-point offset to maintain sensible aspect ratio on the image.
|
||||
// TODO: Compute screen aspect ratio to derive ideal splash image scale.
|
||||
|
||||
|
||||
JGL::J2D::Begin();
|
||||
@@ -116,29 +115,36 @@ void CaveGame::Client::Splash::Draw()
|
||||
void CaveGame::Client::Splash::Update(float elapsed)
|
||||
{
|
||||
|
||||
if (load_percent < 1)
|
||||
load_percent += elapsed/2.f;
|
||||
else
|
||||
load_percent = 1;
|
||||
AssetService::Get()->LoadFromQueue();
|
||||
|
||||
splash_timer -= elapsed;
|
||||
|
||||
load_percent = (float)AssetService::Get()->TotalLoaded() / (float)AssetService::Get()->TotalQueued();
|
||||
|
||||
}
|
||||
|
||||
void CaveGame::Client::Splash::Load() {
|
||||
splash = new JGL::Texture("assets/textures/redacted.png");
|
||||
|
||||
column_textures.fill(nullptr);
|
||||
splash = AssetService::Get()->GetTexture("redacted"); //new JGL::Texture("assets/textures/redacted.png");
|
||||
ComputeMatrixTextureCache();
|
||||
|
||||
Scene::Load();
|
||||
|
||||
}
|
||||
|
||||
void CaveGame::Client::Splash::Unload() {
|
||||
Scene::Unload();
|
||||
for (auto& r : column_textures)
|
||||
delete r;
|
||||
|
||||
splash = nullptr;
|
||||
|
||||
column_textures.fill(nullptr);
|
||||
Scene::Unload();
|
||||
}
|
||||
|
||||
bool CaveGame::Client::Splash::SplashComplete() const {
|
||||
return splash_timer < 0;
|
||||
return AssetService::Get()->IsLoadComplete();
|
||||
//return splash_timer < 0;
|
||||
}
|
||||
|
||||
CaveGame::Client::Splash::~Splash() {
|
||||
Splash::Unload();
|
||||
}
|
||||
|
1
Client/src/Client/StatsWindow.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#
|
151
Client/src/Client/TileTool.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
#include <Client/TileTool.hpp>
|
||||
#include <Core/Loggers.hpp>
|
||||
|
||||
CaveGame::Client::TileTool::TileTool(JUI::Widget *parent) : JUI::Window(parent)
|
||||
{
|
||||
this->Title(tool_title);
|
||||
this->MinSize({200, 400});
|
||||
this->Size({200, 400, 0, 0});
|
||||
this->Visible(false);
|
||||
|
||||
auto* column_layout = new HorizontalListLayout(this->ViewportInstance());
|
||||
|
||||
auto* col_left = new Rect(column_layout);
|
||||
col_left->BGColor({64, 64, 64, 128});
|
||||
col_left->Size({0, 0, 0.35f, 1.f});
|
||||
|
||||
auto* left_row_layout = new VerticalListLayout(col_left);
|
||||
|
||||
tool_size_label = new TextRect(left_row_layout);
|
||||
tool_size_label->Size({0, row_height, 1, 0});
|
||||
tool_size_label->Content("Size: 8");
|
||||
tool_size_label->BGColor(Colors::Transparent);
|
||||
//tool_size_label->LayoutOrder(1);
|
||||
|
||||
auto* tool_percent_label = new TextRect(left_row_layout);
|
||||
tool_percent_label->Size({0, row_height, 1, 0});
|
||||
tool_percent_label->Content("Percent: 100%");
|
||||
tool_percent_label->BGColor(Colors::Transparent);
|
||||
|
||||
auto* tile_sim_label = new TextRect(left_row_layout);
|
||||
tile_sim_label->Size({0, row_height, 1, 0});
|
||||
tile_sim_label->Content("Pause Tile Sim:");
|
||||
tile_sim_label->BGColor(Colors::Transparent);
|
||||
|
||||
auto* col_right = new Rect(column_layout);
|
||||
col_right->BGColor({128, 128, 128, 128});
|
||||
col_right->Size({0, 0, 0.65f, 1.f});
|
||||
|
||||
auto* right_row_layout = new VerticalListLayout(col_right);
|
||||
|
||||
brush_size_slider = new Slider(right_row_layout);
|
||||
|
||||
brush_size_slider->Size({0, row_height, 1, 0});
|
||||
brush_size_slider->Maximum(1.f);
|
||||
brush_size_slider->Minimum(0.01f);
|
||||
brush_size_slider->Interval(0.001f);
|
||||
brush_size_slider->ValueChanged += [&, this] (float val) mutable
|
||||
{
|
||||
float newval = val * 50;
|
||||
tool_size_label->Content(std::format("Size: {}", Math::Round(newval, 1)));
|
||||
BrushSizeChanged(newval);
|
||||
};
|
||||
brush_size_slider->CurrentValue(0.5f);
|
||||
brush_size_slider->SetClicked(false);
|
||||
|
||||
|
||||
brush_percent_slider = new Slider(right_row_layout);
|
||||
brush_percent_slider->Size({0, row_height, 1, 0});
|
||||
brush_percent_slider->Maximum(1.f);
|
||||
brush_percent_slider->Minimum(0.f);
|
||||
brush_percent_slider->Interval(0.001f);
|
||||
brush_percent_slider->ValueChanged += [&, this, tool_percent_label] (float val) mutable
|
||||
{
|
||||
float newval = val * 100.f;
|
||||
tool_percent_label->Content(std::format("Percent: {}%", Math::Floor(newval)));
|
||||
BrushPercentChanged(newval);
|
||||
};
|
||||
brush_percent_slider->CurrentValue(1.f);
|
||||
brush_percent_slider->SetClicked(false);
|
||||
|
||||
|
||||
auto* tile_sim_checkbox = new Checkbox(right_row_layout);
|
||||
tile_sim_checkbox->Size({row_height, row_height, 0, 0});
|
||||
tile_sim_checkbox->OnClickEvent += [&, this] (Vector2 _, JUI::MouseButton _2) mutable {
|
||||
tile_sim_disabled = !tile_sim_disabled;
|
||||
TileSimulationDisabledChanged.Invoke(tile_sim_disabled);
|
||||
|
||||
// Set the visual state of the "step" buttonns
|
||||
step_btn->SetEnabled(tile_sim_disabled);
|
||||
step2_btn->SetEnabled(tile_sim_disabled);
|
||||
step3_btn->SetEnabled(tile_sim_disabled);
|
||||
};
|
||||
|
||||
|
||||
|
||||
step_btn = new TextButton(left_row_layout);
|
||||
step_btn->Size({100_percent, UDim(row_height, 0)});
|
||||
step_btn->Content("Step");
|
||||
step_btn->BGColor(Colors::LightGray);
|
||||
step_btn->BaseBGColor(Colors::LightGray);
|
||||
step_btn->Disable();
|
||||
step_btn->OnClickEvent += [&, this] (auto a, auto b) mutable {
|
||||
TileSimulationStep.Invoke(1);
|
||||
};
|
||||
|
||||
step2_btn = new TextButton(left_row_layout);
|
||||
step2_btn->Size({100_percent, UDim(row_height, 0)});
|
||||
step2_btn->Content("Step 10");
|
||||
step2_btn->BGColor(Colors::LightGray);
|
||||
step2_btn->BaseBGColor(Colors::LightGray);
|
||||
step2_btn->Disable();
|
||||
step2_btn->OnClickEvent += [&, this] (auto a, auto b) mutable {
|
||||
TileSimulationStep.Invoke(10);
|
||||
};
|
||||
|
||||
step3_btn = new TextButton(left_row_layout);
|
||||
step3_btn->Size({100_percent, UDim(row_height, 0)});
|
||||
step3_btn->Content("Step 100");
|
||||
step3_btn->BGColor(Colors::LightGray);
|
||||
step3_btn->BaseBGColor(Colors::LightGray);
|
||||
step3_btn->Disable();
|
||||
step3_btn->OnClickEvent += [&, this] (auto a, auto b) mutable {
|
||||
TileSimulationStep.Invoke(100);
|
||||
};
|
||||
|
||||
BrushSizeChanged += [this] (float value) mutable {
|
||||
brush_radius = value;
|
||||
};
|
||||
|
||||
BrushPercentChanged += [this] (float value) mutable {
|
||||
brush_density = value;
|
||||
};
|
||||
|
||||
|
||||
Enable(WorldEditorEnabledByDefault);
|
||||
}
|
||||
|
||||
void CaveGame::Client::TileTool::BrushRadius(float size) {
|
||||
brush_size_slider->CurrentValue(size/50.f);
|
||||
tool_size_label->Content(std::format("Size: {}", Math::Round(size, 1)));
|
||||
brush_radius = size;
|
||||
}
|
||||
|
||||
|
||||
void CaveGame::Client::TileTool::Enable(bool value) {
|
||||
this->enabled = value;
|
||||
|
||||
CaveGame::Logs::Info("Tile Editor Tool Toggled");
|
||||
|
||||
this->Visible(enabled);
|
||||
}
|
||||
|
||||
float CaveGame::Client::TileTool::BrushRadius() { return brush_radius;}
|
||||
|
||||
void CaveGame::Client::TileTool::BrushDensity(float percent) {
|
||||
brush_percent_slider->CurrentValue(percent / 100.f);
|
||||
tool_size_label->Content(std::format("Density: {}", Math::Round(percent, 1)));
|
||||
brush_density = percent;
|
||||
}
|
||||
|
||||
float CaveGame::Client::TileTool::BrushDensity() const { return brush_density;}
|
@@ -1,2 +0,0 @@
|
||||
#include <Client/WindowWidgetManager.hpp>
|
||||
|
@@ -1 +0,0 @@
|
||||
#include "Client/temp.hpp"
|
@@ -8,6 +8,8 @@ file(GLOB_RECURSE CaveClientApp_SRC "src/*.cpp")
|
||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/assets/"
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/assets)
|
||||
|
||||
|
||||
|
||||
if (CLIENT_BUILD_WITH_STEAM)
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/steam_appid.txt
|
||||
@@ -25,17 +27,29 @@ target_include_directories(CaveClientApp PUBLIC
|
||||
|
||||
target_include_directories(CaveClientApp PUBLIC "include")
|
||||
|
||||
target_link_libraries(CaveClientApp PUBLIC CaveClient ReWindowLibrary J3ML JGL )
|
||||
target_link_libraries(CaveClientApp PUBLIC CaveClient ReWindow J3ML JGL )
|
||||
|
||||
target_compile_definitions(CaveClientApp PUBLIC CAVE_CLIENT)
|
||||
|
||||
if (CLIENT_BUILD_WITH_STEAM)
|
||||
|
||||
target_compile_definitions(CaveClientApp PUBLIC STEAM_BUILD)
|
||||
|
||||
add_library(SteamworksSDK SHARED IMPORTED)
|
||||
set_target_properties(SteamworksSDK PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/assets/steamworks_sdk/linux64/libsteam_api.so")
|
||||
|
||||
# TODO: Can we legally ship the steam SDK with source code?
|
||||
#set_target_properties(SteamworksSDK PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/assets/steamworks_sdk/linux64/libsteam_api.so")
|
||||
|
||||
|
||||
target_include_directories(CaveClientApp PUBLIC "../Steam/")
|
||||
target_link_libraries(CaveClientApp PUBLIC SteamworksSDK)
|
||||
endif()
|
||||
|
||||
|
||||
add_custom_command(TARGET CaveClientApp POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${PROJECT_BINARY_DIR}/lib/" ${CMAKE_CURRENT_BINARY_DIR}/lib
|
||||
COMMENT "Copied libraries to build directory")
|
||||
|
||||
#file(COPY "${PROJECT_BINARY_DIR}/lib/"
|
||||
#DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/lib)
|
||||
|
||||
|
9
ClientApp/assets/data/generator.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"veins": {
|
||||
"clay": {
|
||||
"hi-pass-scale-x": 200,
|
||||
"hi-pass-scale-y": 205,
|
||||
"hi-pass-offset": 0
|
||||
}
|
||||
}
|
||||
}
|
100
ClientApp/assets/data/items.json
Normal file
@@ -0,0 +1,100 @@
|
||||
[
|
||||
{
|
||||
"mnemonic-id": "gel",
|
||||
"display-name": "Gel",
|
||||
"tooltip": "",
|
||||
"stack": 9999,
|
||||
"value": 1,
|
||||
"rarity": 0,
|
||||
"sprite": "gel.png",
|
||||
"usable" : true
|
||||
},
|
||||
{
|
||||
"mnemonic-id": "glowstick",
|
||||
"display-name": "Glowstick",
|
||||
"tooltip": "",
|
||||
"stack": 9999,
|
||||
"value": 1,
|
||||
"rarity": 0,
|
||||
"sprite": "gel.png",
|
||||
"usable" : true
|
||||
},
|
||||
{
|
||||
"mnemonic-id": "copper-bar",
|
||||
"display-name": "Copper Bar",
|
||||
"sprite:" : "ingot"
|
||||
},
|
||||
{
|
||||
"mnemonic-id": "iron-bar",
|
||||
"display-name": "Iron Bar",
|
||||
"sprite:" : "ingot"
|
||||
},
|
||||
{
|
||||
"mnemonic-id": "silver-bar",
|
||||
"display-name": "Silver Bar",
|
||||
"sprite:" : "ingot"
|
||||
},
|
||||
{
|
||||
"mnemonic-id": "tungsten-bar",
|
||||
"display-name": "Tungsten Bar",
|
||||
"sprite:" : "ingot"
|
||||
},
|
||||
{
|
||||
"mnemonic-id": "platinum-bar",
|
||||
"display-name": "Platinum Bar",
|
||||
"sprite:" : "ingot.png"
|
||||
},
|
||||
{
|
||||
"mnemonic-id": "gold-bar",
|
||||
"display-name": "Gold Bar",
|
||||
"sprite:" : "ingot.png"
|
||||
},
|
||||
{
|
||||
"mnemonic-id": "lead-bar",
|
||||
"display-name": "Lead Bar"
|
||||
},
|
||||
{
|
||||
"mnemonic-id": "ziggy-stardust",
|
||||
"display-name": "Ziggy Stardust"
|
||||
},
|
||||
{
|
||||
"mnemonic-id": "nimdoc",
|
||||
"display-name": "Nimdoc"
|
||||
},
|
||||
{
|
||||
"mnemonic-id": "sawns",
|
||||
"display-name": "Sawed-off Shotgun"
|
||||
},
|
||||
{
|
||||
"mnemonic-id": "occupied-dwellinator",
|
||||
"display-name": "Occupied Dwellinator"
|
||||
},
|
||||
{
|
||||
"mnemonic-id": "copper-dagger",
|
||||
"display-name": "Copper Dagger",
|
||||
"sprite": "dagger.png",
|
||||
"stack": 1,
|
||||
"tags": ["dagger", "blade", "melee", "metal", "copper"]
|
||||
},
|
||||
{
|
||||
"mnemonic-id": "iron-dagger",
|
||||
"display-name": "Iron Dagger",
|
||||
"sprite": "dagger.png",
|
||||
"stack": 1,
|
||||
"tags": ["dagger", "blade", "melee", "metal", "iron"]
|
||||
},
|
||||
{
|
||||
"mnemonic-id": "rusty-iron-dagger",
|
||||
"display-name": "Rusty Iron Dagger",
|
||||
"sprite": "dagger.png",
|
||||
"stack": 1,
|
||||
|
||||
"tags": ["dagger", "blade", "melee", "metal", "iron", "rusty"]
|
||||
},
|
||||
{
|
||||
"mnemonic-id": "smoke-n-dip"
|
||||
},
|
||||
{
|
||||
"mnemonic-id": "blick-axe"
|
||||
}
|
||||
]
|
18
ClientApp/assets/data/manifest.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"textures": [
|
||||
["ingot", "assets/textures/ingot.png"],
|
||||
["player", "assets/textures/player.png"],
|
||||
["bg", "assets/textures/bg.png"],
|
||||
["explosion", "assets/textures/explosion.png"],
|
||||
["redacted", "assets/textures/redacted.png"],
|
||||
["title", "assets/textures/title_1.png"],
|
||||
["gill_potion", "assets/textures/gill_potion.png"],
|
||||
["honey_jar", "assets/textures/honey_jar.png"],
|
||||
["hp_potion", "assets/textures/hp_potion.png"],
|
||||
["hp_potion_2x", "assets/textures/hp_potion_2x.png"],
|
||||
["hp_potion_3x", "assets/textures/hp_potion_3x.png"]
|
||||
],
|
||||
"music": [
|
||||
|
||||
]
|
||||
}
|
20
ClientApp/assets/data/recipes.json
Normal file
@@ -0,0 +1,20 @@
|
||||
[
|
||||
{ "station": "furnace", "consume" : ["copper-ore", 3], "produce" : "copper-bar" },
|
||||
{ "station": "furnace", "consume" : ["iron-ore", 3], "produce" : "iron-bar" },
|
||||
{ "station": "furnace", "consume" : ["silver-ore", 3], "produce" : "silver-bar" },
|
||||
{ "station": "furnace", "consume" : ["tungsten-ore", 3], "produce" : "tungsten-bar" },
|
||||
{ "station": "furnace", "consume" : ["platinum-ore", 3], "produce" : "platinum-bar" },
|
||||
{ "station": "furnace", "consume" : ["gold-ore", 3], "produce" : "gold-bar" },
|
||||
{ "station": "furnace", "consume" : ["lead-ore", 3], "produce" : "lead-bar" },
|
||||
{
|
||||
"station": "any",
|
||||
"consume" : "wood-plank",
|
||||
"produce" : ["stick", 8]
|
||||
},
|
||||
{
|
||||
"station": "alchemy-lab",
|
||||
"consume" : "lead-nugget",
|
||||
"produce" : "gold-nugget",
|
||||
"catalyst": "philosophers-stone"
|
||||
}
|
||||
]
|
365
ClientApp/assets/data/tiles.json
Normal file
@@ -0,0 +1,365 @@
|
||||
[
|
||||
{
|
||||
"mnemonic-id": "void",
|
||||
"display-name": "Void",
|
||||
"item-tooltip": "How did you even get this?",
|
||||
"solid": true,
|
||||
"color": "#FFFFFFFF",
|
||||
"hardcoded-id": 65535,
|
||||
"render": false
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "air",
|
||||
"display-name" : "Air",
|
||||
"solid": true,
|
||||
"color": "#FFFFFFFF",
|
||||
"hardcoded-id": 0,
|
||||
"render": false
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "stone",
|
||||
"display-name" : "Stone",
|
||||
"solid": true,
|
||||
"color": [112, 128, 144],
|
||||
"pallet": [[112, 122, 148], [119, 136, 153], [121, 115, 138]]
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "dirt",
|
||||
"display-name" : "Dirt",
|
||||
"solid": true,
|
||||
"color": [210, 105, 30],
|
||||
"pallet": [[210, 125, 30], [195, 105, 40], [210, 105, 30]]
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "mud",
|
||||
"display-name" : "Mud",
|
||||
"solid": true,
|
||||
"color": [139, 69, 19]
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "limestone",
|
||||
"display-name" : "Limestone",
|
||||
"solid": true,
|
||||
"color": [238, 232, 170]
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "basalt",
|
||||
"display-name" : "Basalt",
|
||||
"solid": true,
|
||||
"color": [105, 105, 105]
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "cobblestone",
|
||||
"display-name" : "Cobblestone",
|
||||
"solid": true,
|
||||
"color": [112, 128, 144],
|
||||
"pallet": [[64, 64, 64], [92, 92, 92], [112, 128, 144], [119, 136, 153]],
|
||||
"drops" : null
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "red-moss",
|
||||
"display-name" : "Red Moss",
|
||||
"solid": true,
|
||||
"color": "#CC4444"
|
||||
},
|
||||
|
||||
{
|
||||
"mnemonic-id" : "brown-moss",
|
||||
"display-name" : "Brown Moss",
|
||||
"solid": true,
|
||||
"color": "#BB7755"
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "green-moss",
|
||||
"display-name" : "Green Moss",
|
||||
"solid": true,
|
||||
"color": "#006633",
|
||||
"pallet": ["#007733", "#006644", "#116633"]
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "lava-moss",
|
||||
"display-name" : "Lava Moss",
|
||||
"solid": true,
|
||||
"color": "#DD6655"
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "granite",
|
||||
"display-name" : "Granite",
|
||||
"solid": true,
|
||||
|
||||
"color": "#FFFFFF",
|
||||
"drops" : null
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "marble",
|
||||
"display-name" : "Marble",
|
||||
"solid": true,
|
||||
"color": "#FFFFFF"
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "grass",
|
||||
"display-name" : "Grass",
|
||||
"solid": true,
|
||||
"forced-ticc-func": "grass-forced",
|
||||
"random-ticc-func": "grass-random",
|
||||
"color": [124, 252, 0],
|
||||
"pallet": [[126, 252, 5], [122, 238, 0], [124, 248, 12]]
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "glowy-grass",
|
||||
"display-name" : "Glowy Grass",
|
||||
"solid": true,
|
||||
"color": "#FFFFFF"
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "vine",
|
||||
"display-name" : "Vine",
|
||||
"solid": false,
|
||||
"color": [32, 139, 34],
|
||||
"forced-ticc-func": "vine-forced",
|
||||
"random-ticc-func": "vine-random"
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "sand",
|
||||
"display-name" : "Sand",
|
||||
"solid": true,
|
||||
"forced-ticc-func": "sand-grav",
|
||||
"color": [238, 232, 170],
|
||||
"pallet": [[238, 232, 170], [232, 238, 160], [218, 212, 175]]
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "gravel",
|
||||
"display-name" : "Gravel",
|
||||
"forced-ticc-func": "sand-grav",
|
||||
"solid": true,
|
||||
"color": [102, 118, 124],
|
||||
"pallet": [[92, 102, 128], [82, 82, 82], [102, 102, 102], [132, 132, 132], [89, 116, 123], [121, 95, 108]]
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "white-sand",
|
||||
"display-name" : "White Sand",
|
||||
"solid": false,
|
||||
"forced-ticc-func": "sand-grav",
|
||||
"color": "#FFFFFF"
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "red-sand",
|
||||
"display-name" : "Red Sand",
|
||||
"solid": false,
|
||||
"forced-ticc-func": "sand-grav",
|
||||
"color": "#FFFFFF"
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "black-sand",
|
||||
"display-name" : "Black Sand",
|
||||
"solid": false,
|
||||
"forced-ticc-func": "sand-grav",
|
||||
"color": "#FFFFFF"
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "sandstone",
|
||||
"display-name" : "Sandstone",
|
||||
"solid": false,
|
||||
"color": "#FFFFFF"
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "white-sandstone",
|
||||
"display-name" : "White Sandstone",
|
||||
"solid": false,
|
||||
"color": "#FFFFFF"
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "black-sandstone",
|
||||
"display-name" : "Black Sandstone",
|
||||
"solid": false,
|
||||
"color": "#FFFFFF"
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "ash",
|
||||
"display-name" : "Ash",
|
||||
"solid": false,
|
||||
"color": "#FFFFFF"
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "clay",
|
||||
"display-name" : "Clay",
|
||||
"solid": false,
|
||||
"color": "#660000"
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "snow",
|
||||
"display-name" : "Snow",
|
||||
"solid": false,
|
||||
"color": "#FFFFFF"
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "ice",
|
||||
"display-name" : "Ice",
|
||||
"solid": false,
|
||||
"color": "#FFFFFF"
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "slush",
|
||||
"display-name" : "Slush",
|
||||
"solid": false,
|
||||
"color": "#FFFFFF",
|
||||
"pallet": []
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "stone-brick",
|
||||
"display-name" : "Gray Brick",
|
||||
"solid": false,
|
||||
"color": "#A0A0A0",
|
||||
"pallet": []
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "copper-ore",
|
||||
"display-name" : "Copper Ore",
|
||||
"solid": true,
|
||||
"color": [255, 140, 0],
|
||||
"pallet": [[255, 140, 0], [234, 150, 3], [241, 138, 5]]
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "tin-ore",
|
||||
"display-name" : "Tin Ore",
|
||||
"solid": true,
|
||||
"color": [255, 140, 0],
|
||||
"pallet": [[255, 140, 0], [234, 150, 3], [241, 138, 5]]
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "iron-ore",
|
||||
"display-name" : "Iron Ore",
|
||||
"solid": true,
|
||||
"color": [255, 140, 0],
|
||||
"pallet": [[255, 140, 0], [234, 150, 3], [241, 138, 5]]
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "lead-ore",
|
||||
"display-name" : "Lead Ore",
|
||||
"solid": true,
|
||||
"color": [255, 140, 0],
|
||||
"pallet": [[255, 140, 0], [234, 150, 3], [241, 138, 5]]
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "silver-ore",
|
||||
"display-name" : "Silver Ore",
|
||||
"solid": true,
|
||||
"color": [255, 140, 0],
|
||||
"pallet": [[255, 140, 0], [234, 150, 3], [241, 138, 5]]
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "tungsten-ore",
|
||||
"display-name" : "Tungsten Ore",
|
||||
"solid": true,
|
||||
"color": [255, 140, 0],
|
||||
"pallet": [[255, 140, 0], [234, 150, 3], [241, 138, 5]]
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "gold-ore",
|
||||
"display-name" : "Gold Ore",
|
||||
"solid": true,
|
||||
"color": [255, 140, 0],
|
||||
"pallet": [[255, 140, 0], [234, 150, 3], [241, 138, 5]]
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "platinum-ore",
|
||||
"display-name" : "Platinum Ore",
|
||||
"solid": true,
|
||||
"color": [255, 140, 0],
|
||||
"pallet": [[255, 140, 0], [234, 150, 3], [241, 138, 5]]
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "cobalt-ore",
|
||||
"display-name" : "Cobalt Ore",
|
||||
"solid": true,
|
||||
"color": [255, 140, 0],
|
||||
"pallet": [[255, 140, 0], [234, 150, 3], [241, 138, 5]]
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "cobalt-ore",
|
||||
"display-name" : "Cobalt Ore",
|
||||
"solid": true,
|
||||
"color": [255, 140, 0],
|
||||
"pallet": [[255, 140, 0], [234, 150, 3], [241, 138, 5]]
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "titanium-ore",
|
||||
"display-name" : "Titanium Ore",
|
||||
"solid": true,
|
||||
"color": [255, 140, 0],
|
||||
"pallet": [[255, 140, 0], [234, 150, 3], [241, 138, 5]]
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "uranium-ore",
|
||||
"display-name" : "Uranium Ore",
|
||||
"solid": true,
|
||||
"color": [255, 140, 0],
|
||||
"pallet": [[255, 140, 0], [234, 150, 3], [241, 138, 5]]
|
||||
},
|
||||
|
||||
{
|
||||
"mnemonic-id" : "oak-plank",
|
||||
"display-name" : "Oak Plank",
|
||||
"solid": true,
|
||||
"color": [222, 184, 135]
|
||||
},
|
||||
|
||||
{
|
||||
"mnemonic-id" : "water",
|
||||
"display-name" : "Water",
|
||||
"solid": true,
|
||||
"color": "#0000FF",
|
||||
"pallet": [],
|
||||
"drops" : null
|
||||
},
|
||||
|
||||
{
|
||||
"mnemonic-id" : "blood",
|
||||
"display-name" : "Blood",
|
||||
"solid": true,
|
||||
"color": "#0000FF",
|
||||
"pallet": [],
|
||||
"drops" : null
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "sludge",
|
||||
"display-name" : "Sludge",
|
||||
"solid": true,
|
||||
"color": "#0000FF",
|
||||
"pallet": [],
|
||||
"drops" : null
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "lava",
|
||||
"display-name" : "Lava",
|
||||
"solid": true,
|
||||
"color": "#0000FF",
|
||||
"pallet": [],
|
||||
"drops" : null
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "ectoplasm",
|
||||
"display-name" : "Ectoplasm",
|
||||
"solid": true,
|
||||
"color": "#0000FF",
|
||||
"pallet": [],
|
||||
"drops" : null
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "milk",
|
||||
"display-name" : "Milk",
|
||||
"solid": true,
|
||||
"color": "#0000FF",
|
||||
"pallet": [],
|
||||
"drops" : null
|
||||
},
|
||||
{
|
||||
"mnemonic-id" : "honey",
|
||||
"display-name" : "Honey",
|
||||
"solid": true,
|
||||
|
||||
"color": "#0000FF",
|
||||
"pallet": [],
|
||||
"drops" : null
|
||||
}
|
||||
]
|
BIN
ClientApp/assets/textures/ash_wip_potions.png
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
ClientApp/assets/textures/explosion.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
ClientApp/assets/textures/gill_potion.png
Normal file
After Width: | Height: | Size: 402 B |
BIN
ClientApp/assets/textures/honey_jar.png
Normal file
After Width: | Height: | Size: 730 B |
BIN
ClientApp/assets/textures/hp_potion.png
Normal file
After Width: | Height: | Size: 691 B |
BIN
ClientApp/assets/textures/hp_potion_2x.png
Normal file
After Width: | Height: | Size: 748 B |
BIN
ClientApp/assets/textures/hp_potion_3x.png
Normal file
After Width: | Height: | Size: 779 B |
BIN
ClientApp/assets/textures/hp_potion_4x.png
Normal file
After Width: | Height: | Size: 818 B |
BIN
ClientApp/assets/textures/ingot.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
ClientApp/assets/textures/player.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
ClientApp/assets/textures/speedball.png
Normal file
After Width: | Height: | Size: 718 B |
BIN
ClientApp/assets/textures/title_1.png
Normal file
After Width: | Height: | Size: 36 KiB |
@@ -1,105 +1,277 @@
|
||||
/// CaveGame - A procedural 2D platformer sandbox.
|
||||
/// Created by Josh O'Leary @ Redacted Software, 2020-2024
|
||||
/// Contact: josh@redacted.cc
|
||||
/// Contributors: william@redacted.cc maxi@redacted.cc
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file CaveGameWindow.hpp
|
||||
/// @desc The Client App Window. See ReWindow's Documentation.
|
||||
/// @edit 11/1/2024
|
||||
/// @auth Josh O'Leary
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "J3ML/Geometry.hpp"
|
||||
#include "Client/Splash.hpp"
|
||||
#include "Client/MainMenu.hpp"
|
||||
#include "Client/GameSession.hpp"
|
||||
#include "Client/WindowWidgetManager.hpp"
|
||||
#include "JUI/Widgets/ListLayout.hpp"
|
||||
#include "JUI/UDim.hpp"
|
||||
#include "rewindow/types/window.h"
|
||||
#include "JUI/Widgets/Window.hpp"
|
||||
#include "Client/Scene.hpp"
|
||||
#include "Client/SceneManager.hpp"
|
||||
#include <Client/AssetService.hpp>
|
||||
|
||||
namespace CaveGame::ClientApp
|
||||
{
|
||||
#include <J3ML/Geometry.hpp>
|
||||
#include <Client/Splash.hpp>
|
||||
#include <Client/MainMenu.hpp>
|
||||
#include <Client/GameSession.hpp>
|
||||
#include <JUI/Widgets/ListLayout.hpp>
|
||||
#include <JUI/UDim.hpp>
|
||||
#include <ReWindow/types/Window.h>
|
||||
#include <JUI/Widgets/Window.hpp>
|
||||
#include <Client/Scene.hpp>
|
||||
#include <Client/SceneManager.hpp>
|
||||
#include <JUI/Widgets/CommandLine.hpp>
|
||||
#include <Client/TileTool.hpp>
|
||||
#include "Command.hpp"
|
||||
#include <Core/Player.hpp>
|
||||
#include <Core/Explosion.hpp>
|
||||
|
||||
|
||||
namespace CaveGame::ClientApp {
|
||||
|
||||
using CaveGame::Client::Scene;
|
||||
using CaveGame::Client::SceneManager;
|
||||
|
||||
using CommandArgs = std::vector<std::string>;
|
||||
|
||||
/// The main program class. Everything originates here.
|
||||
/// This class is derived from RWindow class.
|
||||
class CaveGameWindow : public ReWindow::RWindow, public SceneManager
|
||||
{
|
||||
class CaveGameWindow : public ReWindow::OpenGLWindow, public SceneManager {
|
||||
public:
|
||||
|
||||
[[nodiscard]] bool InGameSession() const;
|
||||
[[nodiscard]] bool InMainMenu() const;
|
||||
|
||||
CaveGame::Client::Splash* splash_ctx;
|
||||
CaveGame::Client::MainMenu* menu_ctx;
|
||||
CaveGame::Client::GameSession* game_ctx;
|
||||
JUI::Scene* wm;
|
||||
|
||||
JUI::Window* settings_window;
|
||||
JUI::Window* credits_window;
|
||||
JUI::Window* console_window;
|
||||
JUI::Window* stats_window;
|
||||
|
||||
//Scene* current_scene = nullptr;
|
||||
|
||||
bool render_grid = true;
|
||||
bool generate_grid = true;
|
||||
|
||||
bool mbd = false;
|
||||
|
||||
float our_avg = 0.f;
|
||||
|
||||
/// Constructs and returns an instance of the game program.
|
||||
/// @param title
|
||||
/// @param width
|
||||
/// @param height
|
||||
CaveGameWindow(const std::string& title, int width, int height);
|
||||
|
||||
~CaveGameWindow();
|
||||
/// Destructor called when deleting the CaveGameWindow.
|
||||
~CaveGameWindow() override;
|
||||
|
||||
/// This function runs the totality of the game loop, start to finish.
|
||||
/// Calls Open, Init, Gameloop(), and Cleanup() in that order
|
||||
/// @note The game loop runs until Die() is called, at which point final cleanup is performed.
|
||||
void Run();
|
||||
|
||||
/// This function triggers an internal signal that the game window is ready to close.
|
||||
/// @note This does not immediately close the window, rather, time is given to allow for final data saving and other processes.
|
||||
void Die();
|
||||
|
||||
void create_console_window() { }
|
||||
|
||||
void create_stats_window();
|
||||
|
||||
void create_settings_window();
|
||||
|
||||
void create_window_widgets();
|
||||
|
||||
void Die(); void MarkReadyToClose(bool close = true);
|
||||
/// This function sets up the initial program state, particularly that which must be performed **after** the window is opened.
|
||||
void Init();
|
||||
/// This function cleans up any data or assets before closing the game.
|
||||
void Cleanup();
|
||||
|
||||
/// This function performs logic calculation routines, once per refresh.
|
||||
void Update(float elapsed);
|
||||
|
||||
/// This function performs rendering routines, once per refresh.
|
||||
void Draw();
|
||||
|
||||
/// @return True when a flag is set that indicates the game is "preparing" to close. Certain procedures may still be taking place, such as saving and serialization.
|
||||
bool ReadyToClose() const;
|
||||
|
||||
/// Returns the game's console GUI, which accepts commands and displays log messages.
|
||||
JUI::CommandLine* Console();
|
||||
/// Returns the Scene that holds global menu windows, which can appear in any game-context.
|
||||
JUI::Scene* WindowManager();
|
||||
/// Returns the active game-world session, if it exists.
|
||||
Client::GameSession* GameSession();
|
||||
|
||||
/// @return the user's mouse coordinates from the RWindow layer.
|
||||
[[nodiscard]] Vector2 GetMouseV2() const;
|
||||
/// @return the window's size from the RWindow layer.
|
||||
[[nodiscard]] Vector2 GetSizeV2() const;
|
||||
/// @return true if the game is currently in-session.
|
||||
[[nodiscard]] bool InGame() const;
|
||||
/// @returns true if the game is currently in the main menu.
|
||||
[[nodiscard]] bool InMainMenu() const;
|
||||
|
||||
public:
|
||||
#pragma region Input Callbacks
|
||||
/// Called by the window on each frame.
|
||||
void OnRefresh(float elapsed) override;
|
||||
/// Called by the window upon the user pressing a mouse button.
|
||||
void OnMouseButtonDown(const ReWindow::MouseButtonDownEvent &ev) override;
|
||||
|
||||
/// Called by the window upon the user releasing a mouse button.
|
||||
void OnMouseButtonUp(const ReWindow::MouseButtonUpEvent &ev) override;
|
||||
/// Called by the window upon the user scrolling a mouse wheel.
|
||||
void OnMouseWheel(const ReWindow::MouseWheelEvent &ev) override;
|
||||
/// Called by the window upon the user releasing a keyboard key.
|
||||
void OnKeyUp(const ReWindow::KeyUpEvent &ev) override;
|
||||
/// Called by the window upon the user pressing a keyboard key.
|
||||
void OnKeyDown(const ReWindow::KeyDownEvent& ev) override;
|
||||
/// Called by the window upon the user moving their pointer device, currently just mice.
|
||||
void OnMouseMove(const ReWindow::MouseMoveEvent &ev) override;
|
||||
/// Called by the window when it receives a request from the operating-system to resize.
|
||||
bool OnResizeRequest(const ReWindow::WindowResizeRequestEvent &ev) override;
|
||||
|
||||
/// Called by the window **before** it closes.
|
||||
void OnClosing() override;
|
||||
#pragma endregion
|
||||
public:
|
||||
/// Forwards a message to the console log.
|
||||
void Log(const std::string& msg, const Color4& color = Colors::White);
|
||||
bool HasCommand(const std::string &command);
|
||||
Command GetCommand(const std::string &command);
|
||||
protected:
|
||||
|
||||
/// Calls Step() in a loop until the window wants to close.
|
||||
void Gameloop();
|
||||
/// Runs exactly one iteration of the game loop. Currently, this is one call to Update(), and then to Draw().
|
||||
/// @see Refresh().
|
||||
void Step();
|
||||
/// Creates the in-game console menu.
|
||||
void create_console_window();
|
||||
void create_stats_window();
|
||||
void create_settings_window();
|
||||
void create_window_widgets();
|
||||
/// Draws a sequence of 'debug' information strings to the screen, in a descending list.
|
||||
void draw_debug_info(std::vector<std::string> tokens);
|
||||
void CreateMenuWindows();
|
||||
/// Constructs the Splash screen, Main Menu screen, and In-game session.
|
||||
void CreateContexts();
|
||||
void Gameloop();
|
||||
void Step();
|
||||
|
||||
bool wanna_die = false; // This field indicates the program is ready to close.
|
||||
|
||||
|
||||
// TODO: Refactor this into irrelevance.
|
||||
void InGameControls(float elapsed);
|
||||
void OnConsoleCommandInput(const std::string &command);
|
||||
/// Toggles tile simulation if we are in-game.
|
||||
bool ToggleTileSim(const CommandArgs& args);
|
||||
/// Opens a singleplayer session.
|
||||
/// @param info A structure containing information for joining a singleplayer world.
|
||||
void OpenWorld(Client::SingleplayerSessionInfo info);
|
||||
|
||||
/// Logs help information to the console. Called when the user runs the 'help' command.
|
||||
bool HelpCommand(const CommandArgs& args);
|
||||
/// Shows a list of every command label.
|
||||
bool ListCommand(const CommandArgs& args);
|
||||
bool TileListCmd(const CommandArgs& args);
|
||||
bool ItemListCmd(const CommandArgs& args);
|
||||
|
||||
|
||||
bool NoclipCmd(const CommandArgs &args);
|
||||
|
||||
bool FpsLimitCmd(const CommandArgs &args);
|
||||
|
||||
/// This table defines the supported console commands, their aliases, and the callback lambda.
|
||||
#pragma region Commands
|
||||
const std::vector<Command> commands {
|
||||
{"help", {}, [this](const CommandArgs& args) { HelpCommand(args); }},
|
||||
{"list", {}, [this](const CommandArgs& args) { ListCommand(args); }},
|
||||
{"quit", {"q"}, [this](const CommandArgs& args) { Close(); }},
|
||||
{"worldedit", {"we"}, [this](const CommandArgs& args) {
|
||||
if (InGame())
|
||||
GameSession()->ToggleWorldEdit();
|
||||
//tile_tool->Enable(!tile_tool->IsEnabled());
|
||||
return true;
|
||||
}},
|
||||
{"tilesim", {"q"}, [this](const CommandArgs& args) { return ToggleTileSim(args);}},
|
||||
{"grid", {"q"}, [this](const CommandArgs& args) {}},
|
||||
{"credits", {"q"}, [this](const CommandArgs& args) { credits_window->Toggle(); }},
|
||||
{"settings", {"q"}, [this](const CommandArgs& args) {}},
|
||||
{"spawn", {}, [this](const CommandArgs& args) {
|
||||
if (!InGame())
|
||||
return Log("Error: This command is only available when in a world!", Colors::Red);
|
||||
|
||||
if (args.size() == 1)
|
||||
return Log("Available entities: player, explosion, particle");
|
||||
|
||||
if (args[1] == "player")
|
||||
{
|
||||
auto pos = GameSession()->World()->camera.ScreenToWorld(GetMouseV2());
|
||||
auto* plr = new CaveGame::Core::Player(pos);
|
||||
GameSession()->World()->AddEntity(plr);
|
||||
return;
|
||||
}
|
||||
if (args[1] == "explosion")
|
||||
{
|
||||
auto pos = GameSession()->World()->camera.ScreenToWorld(GetMouseV2());
|
||||
auto* plr = new CaveGame::Core::Explosion(GameSession()->World(), pos, 1.f, GameSession()->WorldEditToolWindow()->BrushRadius());
|
||||
GameSession()->World()->AddEntity(plr);
|
||||
return;
|
||||
}
|
||||
if (args[1] == "particle")
|
||||
{
|
||||
/*auto pos = GameSession()->World()->camera.ScreenToWorld(GetMouseV2());
|
||||
auto* plr = new CaveGame::Core::Player(pos);
|
||||
GameSession()->World()->AddEntity(plr);
|
||||
return;*/
|
||||
}
|
||||
}},
|
||||
{"settings", {"q"}, [this](const CommandArgs& args) {}},
|
||||
{"give", {"q"}, [this](const CommandArgs& args) {}},
|
||||
{"tp", {"q"}, [this](const CommandArgs& args) {}},
|
||||
{"freecam", {"q"}, [this](const CommandArgs& args) {}},
|
||||
{"god", {"q"}, [this](const CommandArgs& args) {}},
|
||||
{"fullbright", {"q"}, [this](const CommandArgs& args) {}},
|
||||
{"connect", {"q"}, [this](const CommandArgs& args) {}},
|
||||
{"disconnect", {"q"}, [this](const CommandArgs& args) {}},
|
||||
{"server", {"q"}, [this](const CommandArgs& args) {}},
|
||||
{"sandbox", {"q"}, [this](const CommandArgs& args) {}},
|
||||
{"crash", {"q"}, [this](const CommandArgs& args) {}},
|
||||
{"time", {"q"}, [this](const CommandArgs& args) {}},
|
||||
{"juidebug", {"q"}, [this](const CommandArgs& args) {}},
|
||||
{"fpslimit", {"fps"}, [this](const CommandArgs& args) { return FpsLimitCmd(args); }},
|
||||
{"grid", {}, [this](const CommandArgs& args) {
|
||||
if (current_scene == game_ctx)
|
||||
{
|
||||
game_ctx->World()->chunk_debug.Enable(!game_ctx->World()->chunk_debug.IsEnabled());
|
||||
} else
|
||||
Log("Must be in-game to use this command!");
|
||||
}},
|
||||
{"tileactivity", {}, [this](const CommandArgs& args) {
|
||||
if (current_scene == game_ctx)
|
||||
{
|
||||
game_ctx->World()->SetShowTileActivity(!game_ctx->World()->IsShowTileActivityEnabled());
|
||||
} else
|
||||
Log("Must be in-game to use this command!");
|
||||
}},
|
||||
{"save-world", {}, [this](const CommandArgs& args) {
|
||||
if (InGame())
|
||||
GameSession()->World()->Save();
|
||||
}},
|
||||
{"tile-list", {}, [this](const CommandArgs& args) { return TileListCmd(args); }},
|
||||
{"item-list", {}, [this](const CommandArgs& args) { return ItemListCmd(args); }},
|
||||
{"container", {}, [this](const CommandArgs& args) {
|
||||
Log(std::format("Size: {}", args.size()));
|
||||
if (args.size() == 3)
|
||||
{
|
||||
int w = std::stoi(args[1]);
|
||||
int h = std::stoi(args[2]);
|
||||
|
||||
GameSession()->HUD()->Add(new ContainerWindow(GameSession()->HUD(), w, h, "Test Container"));
|
||||
}
|
||||
}},
|
||||
{"noclip", {}, [this](const CommandArgs& args) { return NoclipCmd(args); }}
|
||||
};
|
||||
|
||||
Command InvalidCommand {
|
||||
"n/a", {}, [this](const CommandArgs& args) {
|
||||
Log("Nope");
|
||||
}
|
||||
};
|
||||
#pragma endregion
|
||||
|
||||
|
||||
|
||||
protected: // Complex class members.
|
||||
Client::AssetService assets;
|
||||
CaveGame::Client::Splash* splash_ctx = nullptr;
|
||||
CaveGame::Client::MainMenu* menu_ctx = nullptr;
|
||||
CaveGame::Client::GameSession* game_ctx = nullptr;
|
||||
JUI::Scene* wm = nullptr;
|
||||
JUI::Window* settings_window = nullptr;
|
||||
JUI::Window* credits_window = nullptr;
|
||||
|
||||
JUI::CommandLine* console_window = nullptr;
|
||||
JUI::Window* stats_window = nullptr;
|
||||
protected: // Primitive class members.
|
||||
/// This field indicates the program is ready to close.
|
||||
bool wanna_die = false;
|
||||
bool render_grid = true;
|
||||
bool generate_grid = true;
|
||||
bool mbd = false;
|
||||
float our_avg = 0.f;
|
||||
float max_fps = 60.f;
|
||||
|
||||
|
||||
};
|
||||
}
|
16
ClientApp/include/ClientApp/Command.hpp
Normal file
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
namespace CaveGame::ClientApp {
|
||||
|
||||
using CommandArgs = std::vector<std::string>;
|
||||
|
||||
struct Command {
|
||||
std::string name;
|
||||
std::vector<std::string> aliases;
|
||||
std::function<void(CommandArgs)> callback;
|
||||
};
|
||||
}
|
@@ -13,33 +13,38 @@
|
||||
#include <steam_api.h>
|
||||
#endif
|
||||
|
||||
#include <JGL/JGL.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <JGL/JGL.h>
|
||||
#include <ClientApp/CaveGameWindow.hpp>
|
||||
#include <Core/Loggers.hpp>
|
||||
#include <rewindow/logger/logger.h>
|
||||
#include <ReWindow/Logger.h>
|
||||
#include <Core/Tile.hpp>
|
||||
#include <JGL/logger/logger.h>
|
||||
#include <JJX/JSON.hpp>
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
mcolor::windowsSaneify();
|
||||
// Hide logs from engine components so we can focus on CaveGame.
|
||||
JGL::Logger::Warning.EnableConsole(false);
|
||||
JGL::Logger::Debug.EnableConsole(false);
|
||||
ReWindow::Logger::Debug.EnableConsole(false);
|
||||
ReWindow::Logger::Debug.EnableFile(false);
|
||||
//ReWindow::Logger::Debug.EnableFile(false);
|
||||
|
||||
// Reduce verbosity of our base logger.
|
||||
CaveGame::Logs::Info.IncludeLocation(false);
|
||||
|
||||
// Let 'em know we're coming.
|
||||
CaveGame::Logs::Info("Starting client program.");
|
||||
|
||||
// TODO: Steam
|
||||
// If we compiled with steam, poke the system for the steam API, and launch with it, if we have it.
|
||||
#ifdef STEAM_BUILD
|
||||
CaveGame::Logs::Info.Log("Running steam build.");
|
||||
bool steam_success = SteamAPI_Init();
|
||||
#endif
|
||||
|
||||
//srand(0);
|
||||
|
||||
CaveGame::Core::DefineTiles();
|
||||
|
||||
//for (auto& tile : result)
|
||||
//std:: cout << tile << std::endl;
|
||||
|
||||
auto* window = new CaveGame::ClientApp::CaveGameWindow("Re-CaveGame", 800, 600);
|
||||
//window->SetResizable(true);
|
||||
window->Run();
|
||||
@@ -51,6 +56,6 @@ int main(int argc, char** argv) {
|
||||
#endif
|
||||
|
||||
CaveGame::Logs::Info.Log("Exiting client program.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,11 +1,31 @@
|
||||
#include <Client/CreditsWindow.hpp>
|
||||
#include "ClientApp/CaveGameWindow.hpp"
|
||||
#include <Core/Loggers.hpp>
|
||||
#include <Client/SettingsMenu.hpp>
|
||||
|
||||
namespace CaveGame::ClientApp
|
||||
{
|
||||
CaveGameWindow::CaveGameWindow(const std::string& title, int width, int height): ReWindow::RWindow(title, width, height)
|
||||
#include <Core/Explosion.hpp>
|
||||
#include <Core/Loggers.hpp>
|
||||
#include <Core/Player.hpp>
|
||||
#include <ReWindow/InputService.h>
|
||||
#include <Core/Macros.hpp>
|
||||
#include <jstick.hpp>
|
||||
#include <Core/ItemRegistry.hpp>
|
||||
#include <Core/TileRegistry.hpp>
|
||||
#include <JJX/JSON.hpp>
|
||||
#include <JUI/Widgets/FpsGraph.hpp>
|
||||
|
||||
namespace CaveGame::ClientApp {
|
||||
|
||||
using namespace CaveGame::Core;
|
||||
using namespace CaveGame::Client;
|
||||
|
||||
void ReadRecipesAndRegister() {}
|
||||
|
||||
CaveGameWindow::CaveGameWindow(const std::string& title, int width, int height): ReWindow::OpenGLWindow(title, width, height, 2, 1)
|
||||
{
|
||||
Logs::Info("Parsing Tile Data.");
|
||||
CaveGame::Core::LoadTileMetadata();
|
||||
Logs::Info("Parsing Item Data.");
|
||||
CaveGame::Core::LoadItemMetadata();
|
||||
Logs::Info("Creating game window.");
|
||||
|
||||
CreateContexts();
|
||||
@@ -14,13 +34,30 @@ namespace CaveGame::ClientApp
|
||||
|
||||
CaveGameWindow::~CaveGameWindow() {
|
||||
|
||||
Logs::Info("Closing game window.");
|
||||
DestroyWindow();
|
||||
Logs::Info("Removing game objects.");
|
||||
delete splash_ctx;
|
||||
delete menu_ctx;
|
||||
delete game_ctx;
|
||||
delete wm;
|
||||
|
||||
Logs::Info("Closing game window.");
|
||||
}
|
||||
|
||||
void CaveGameWindow::OpenWorld(Client::SingleplayerSessionInfo info)
|
||||
{
|
||||
Logs::Info(std::format("Game session requested.", info.NewWorld));
|
||||
game_ctx = new CaveGame::Client::GameSession(info.NewWorld);
|
||||
|
||||
// TODO: Parse Info to construct gameworld files.
|
||||
ChangeScene(game_ctx);
|
||||
|
||||
game_ctx->RequestToggleSettings += [&, this] {
|
||||
settings_window->Toggle();
|
||||
};
|
||||
|
||||
game_ctx->OnSessionExit += [&, this] {
|
||||
ChangeScene(menu_ctx);
|
||||
};
|
||||
}
|
||||
|
||||
void CaveGameWindow::CreateContexts()
|
||||
@@ -29,21 +66,15 @@ namespace CaveGame::ClientApp
|
||||
splash_ctx = new CaveGame::Client::Splash();
|
||||
Logs::Info("Building main menu.");
|
||||
menu_ctx = new CaveGame::Client::MainMenu();
|
||||
game_ctx = new CaveGame::Client::GameSession();
|
||||
|
||||
menu_ctx->RequestWorld += [this](Client::SingleplayerSessionInfo info) {
|
||||
Logs::Info(std::format("Game session requested.", info.NewWorld));
|
||||
game_ctx = new CaveGame::Client::GameSession(info.NewWorld);
|
||||
// TODO: Parse Info to construct gameworld files.
|
||||
ChangeScene(game_ctx);
|
||||
};
|
||||
menu_ctx->RequestQuit += [this]() {
|
||||
Die();
|
||||
};
|
||||
menu_ctx->RequestShowCredits += [this]()
|
||||
{
|
||||
credits_window->Visible(true);
|
||||
OpenWorld(info);
|
||||
};
|
||||
menu_ctx->RequestQuit += [this] { Die(); };
|
||||
menu_ctx->RequestToggleCredits += [this]{ credits_window->Toggle(); };
|
||||
menu_ctx->RequestToggleSettings += [this] { settings_window->Toggle(); };
|
||||
|
||||
game_ctx = new CaveGame::Client::GameSession();
|
||||
}
|
||||
|
||||
void CaveGameWindow::CreateMenuWindows()
|
||||
@@ -51,9 +82,25 @@ namespace CaveGame::ClientApp
|
||||
wm = new JUI::Scene();
|
||||
}
|
||||
|
||||
void CaveGameWindow::Step()
|
||||
{
|
||||
this->ManagedRefresh();
|
||||
void CaveGameWindow::Step() {
|
||||
|
||||
auto begin = GetTimestamp();
|
||||
Refresh();
|
||||
auto possible_end = GetTimestamp();
|
||||
|
||||
float dt = ComputeElapsedFrameTimeSeconds(begin, possible_end);
|
||||
|
||||
if (this->max_fps > 0) {
|
||||
float min_delta = 1.f / max_fps;
|
||||
float delta_diff = min_delta - dt;
|
||||
// TODO: only guaranteed to sleep *at least* as long as we request!
|
||||
// Fake bias to sleep for less time than we want, so it averages out?
|
||||
//std::this_thread::sleep_for(std::chrono::microseconds((long long)(delta_diff*1000*500)));
|
||||
|
||||
}
|
||||
auto sure_end = GetTimestamp();
|
||||
dt = ComputeElapsedFrameTimeSeconds(begin, sure_end);
|
||||
UpdateFrameTiming(dt);
|
||||
}
|
||||
|
||||
void CaveGameWindow::Gameloop()
|
||||
@@ -65,17 +112,42 @@ namespace CaveGame::ClientApp
|
||||
|
||||
void CaveGameWindow::Run()
|
||||
{
|
||||
this->SetRenderer(RenderingAPI::OPENGL);
|
||||
this->Open();
|
||||
int js = 0;//jstick::Connect();
|
||||
|
||||
//this->SetRenderer(RenderingAPI::OPENGL);
|
||||
bool success = this->Open();
|
||||
|
||||
if (!success)
|
||||
{
|
||||
Logs::Info("Error initializing!!");
|
||||
return;
|
||||
}
|
||||
|
||||
this->Init();
|
||||
this->SetResizable(true);
|
||||
this->SetVsyncEnabled(false);
|
||||
|
||||
this->assets.PreloadCertainAssets();
|
||||
this->assets.ParseManifest();
|
||||
|
||||
//for (int i = 0; i < 10; i++)
|
||||
//{
|
||||
//assets.EnqueueTexture("assets/textures/redacted.png");
|
||||
//assets.EnqueueTexture("assets/textures/bg.png");
|
||||
//assets.EnqueueTexture("assets/textures/player.png");
|
||||
//assets.EnqueueTexture("assets/textures/explosion.png");
|
||||
//assets.EnqueueTexture("assets/textures/title_1.png");
|
||||
//assets.EnqueueTexture("assets/textures/ash_wip_potions.png");
|
||||
//}
|
||||
|
||||
|
||||
ChangeScene(splash_ctx);
|
||||
//OpenWorld({.NewWorld = false});
|
||||
//this->ChangeScene(this->game_ctx);
|
||||
|
||||
// TODO: Implement Resource/Asset manager rather than passing asset references around like this.
|
||||
this->menu_ctx->BuildWidgets();
|
||||
|
||||
this->ChangeScene(this->splash_ctx);
|
||||
|
||||
Gameloop();
|
||||
|
||||
processOnClose();
|
||||
@@ -91,18 +163,27 @@ namespace CaveGame::ClientApp
|
||||
|
||||
void CaveGameWindow::create_settings_window()
|
||||
{
|
||||
settings_window = new JUI::Window(wm);
|
||||
settings_window->SetTitleFont(JGL::Fonts::Jupiteroid);
|
||||
settings_window->SetTitle("Settings");
|
||||
settings_window->Visible(false);
|
||||
settings_window = new Client::SettingsMenu(wm);
|
||||
}
|
||||
|
||||
|
||||
void CaveGameWindow::create_window_widgets()
|
||||
{
|
||||
create_console_window();
|
||||
credits_window = Client::CreateCreditsWindowWidget(wm);
|
||||
create_stats_window();
|
||||
create_settings_window();
|
||||
|
||||
|
||||
// TODO: Swap out with FpsGraphWindow on next JUI release.
|
||||
auto* graph_window = new JUI::Window(wm);
|
||||
graph_window->Name("FPS Graph");
|
||||
graph_window->Title("FPS Graph");
|
||||
graph_window->Size({500_px, 50_px});
|
||||
|
||||
auto* graph = new JUI::FpsGraph(graph_window->Content());
|
||||
graph->Name("Graph");
|
||||
graph->Size({500_px, 50_px});
|
||||
}
|
||||
|
||||
void CaveGameWindow::Init()
|
||||
@@ -111,9 +192,11 @@ namespace CaveGame::ClientApp
|
||||
gladLoadGL();
|
||||
|
||||
// Init Josh Graphics
|
||||
bool result = JGL::Init(GetSize(), 0.f, 0.f);
|
||||
auto size = GetSize();
|
||||
Vector2i vsize = Vector2i(size.x, size.y);
|
||||
bool result = JGL::Init(vsize, 0.f, 0.f);
|
||||
//JGL::InitTextEngine();
|
||||
JGL::Update(GetSize());
|
||||
JGL::Update(vsize);
|
||||
|
||||
// Fetch core assets
|
||||
// TODO: Asset manager? Need to share references and pointers of assets between many files..
|
||||
@@ -124,6 +207,9 @@ namespace CaveGame::ClientApp
|
||||
glDepthFunc(GL_LESS);
|
||||
glDepthMask(GL_TRUE);
|
||||
|
||||
// TODO: Delete when we update to the next release of JGL
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // NOTE: This MUST be called for text rendering to work properly!!!
|
||||
|
||||
// Construct menu elements
|
||||
create_window_widgets();
|
||||
}
|
||||
@@ -143,7 +229,7 @@ namespace CaveGame::ClientApp
|
||||
{
|
||||
text_size = JGL::Fonts::Jupiteroid.MeasureString(token, font_size);
|
||||
JGL::J2D::FillRect(bg_color, pos, text_size);
|
||||
JGL::J2D::DrawString(text_color, token, pos.x, pos.y, 1.f, font_size);
|
||||
JGL::J2D::DrawString(text_color, token, pos.x, pos.y, font_size);
|
||||
pos.y += text_size.y;
|
||||
}
|
||||
JGL::J2D::End();
|
||||
@@ -151,59 +237,28 @@ namespace CaveGame::ClientApp
|
||||
|
||||
void CaveGameWindow::InGameControls(float elapsed)
|
||||
{
|
||||
// TODO: Ideally, we want LocalWorld class to manage it's camera input.
|
||||
// however, we cannot use the "polling" set of input methods in the Context, GameSession, or LocalWorld classes,
|
||||
// so we had to bring this code up here.
|
||||
// @maxi suggests function pointers and closure.
|
||||
// Tile placer tool code
|
||||
|
||||
if (IsKeyDown(Keys::LeftArrow))
|
||||
game_ctx->world->camera.MoveLeft();
|
||||
if (IsKeyDown(Keys::RightArrow))
|
||||
game_ctx->world->camera.MoveRight();
|
||||
if (IsKeyDown(Keys::UpArrow))
|
||||
game_ctx->world->camera.MoveUp();
|
||||
if (IsKeyDown(Keys::DownArrow))
|
||||
game_ctx->world->camera.MoveDown();
|
||||
// Temp hack until Input Focus Priority is implemented.
|
||||
for (auto window : wm->GetChildren())
|
||||
if (window->IsVisible() && window->IsMouseInside())
|
||||
return;
|
||||
|
||||
if (IsKeyDown(Keys::LeftBracket))
|
||||
game_ctx->world->camera.Rotate(-5*elapsed);
|
||||
if (IsKeyDown(Keys::RightBracket))
|
||||
game_ctx->world->camera.Rotate(5*elapsed);
|
||||
// NOTE: Don't use else conditions for input code, you'll inadvertently add a bias for one key over the other.
|
||||
|
||||
if (IsKeyDown(Keys::Minus))
|
||||
game_ctx->world->camera.ZoomIn(-elapsed);
|
||||
if (IsKeyDown(Keys::Equals))
|
||||
game_ctx->world->camera.ZoomIn(elapsed);
|
||||
|
||||
|
||||
if (IsKeyDown(Keys::R))
|
||||
game_ctx->world->RefreshAll();
|
||||
|
||||
if (IsMouseButtonDown(MouseButtons::Left))
|
||||
{
|
||||
|
||||
Vector2 transformed = game_ctx->world->camera.ScreenToWorld(GetMouseCoordinates());
|
||||
game_ctx->world->SetTile(transformed.x, transformed.y, Core::TileID::AIR);
|
||||
game_ctx->world->SetTile(transformed.x+1, transformed.y, Core::TileID::AIR);
|
||||
game_ctx->world->SetTile(transformed.x-1, transformed.y, Core::TileID::AIR);
|
||||
game_ctx->world->SetTile(transformed.x, transformed.y+1, Core::TileID::AIR);
|
||||
game_ctx->world->SetTile(transformed.x, transformed.y-1, Core::TileID::AIR);
|
||||
}
|
||||
|
||||
if (IsMouseButtonDown(MouseButtons::Right))
|
||||
{
|
||||
|
||||
Vector2 transformed = game_ctx->world->camera.ScreenToWorld(GetMouseCoordinates());
|
||||
game_ctx->world->SetTile(transformed.x, transformed.y, Core::TileID::GRASS);
|
||||
game_ctx->world->SetTile(transformed.x+1, transformed.y, Core::TileID::GRASS);
|
||||
game_ctx->world->SetTile(transformed.x-1, transformed.y, Core::TileID::GRASS);
|
||||
game_ctx->world->SetTile(transformed.x, transformed.y+1, Core::TileID::GRASS);
|
||||
game_ctx->world->SetTile(transformed.x, transformed.y-1, Core::TileID::GRASS);
|
||||
}
|
||||
// TODO: Nuke this once JUI can consume input.
|
||||
if (InGame())
|
||||
GameSession()->WorldEditToolControlsUpdate(elapsed);
|
||||
}
|
||||
|
||||
void CaveGameWindow::Update(float elapsed)
|
||||
{
|
||||
jstick::ReadEventLoop();
|
||||
|
||||
SceneManager::UpdateSceneState(elapsed);
|
||||
|
||||
// Update floating windows.
|
||||
wm->Update(elapsed);
|
||||
|
||||
// TODO: We can't tell the game to change scenes from within a scene, currently.
|
||||
if (current_scene == splash_ctx && splash_ctx->SplashComplete())
|
||||
@@ -212,26 +267,27 @@ namespace CaveGame::ClientApp
|
||||
// Update Current Scene
|
||||
if (current_scene != nullptr)
|
||||
{
|
||||
current_scene->PassWindowSize(GetSize());
|
||||
auto isize = GetSize();
|
||||
Vector2 size = Vector2(isize.x, isize.y);
|
||||
current_scene->PassWindowSize(size);
|
||||
current_scene->Update(elapsed);
|
||||
// TODO: current_scene->PassInputState(...); ?
|
||||
}
|
||||
|
||||
// Update floating windows.
|
||||
wm->Update(elapsed);
|
||||
|
||||
|
||||
if (current_scene == game_ctx)
|
||||
if (current_scene == GameSession())
|
||||
{
|
||||
game_ctx->world->mouse_pos = GetMouseCoordinates();
|
||||
InGameControls(elapsed);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void CaveGameWindow::Draw()
|
||||
{
|
||||
JGL::Update(GetSize());
|
||||
auto isize = GetSize();
|
||||
Vector2i size = Vector2i(isize.x, isize.y);
|
||||
|
||||
JGL::Update(size);
|
||||
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
@@ -245,16 +301,26 @@ namespace CaveGame::ClientApp
|
||||
// Draw floating windows.
|
||||
wm->Draw();
|
||||
|
||||
draw_debug_info({
|
||||
"Re-CaveGame - Redacted Software",
|
||||
std::format("frame: {}", refresh_count),
|
||||
std::format("delta: {}ms", J3ML::Math::Round(delta_time*1000)),
|
||||
std::format("fps: {}", J3ML::Math::Round(refresh_rate)),
|
||||
std::format("avg: {}", J3ML::Math::Round(avg_refresh_rate))
|
||||
});
|
||||
std::vector<std::string> debug_lines = {
|
||||
"Re-CaveGame - Redacted Software",
|
||||
std::format("frame: {}", refresh_count),
|
||||
std::format("delta: {}ms", J3ML::Math::Round(delta_time*1000)),
|
||||
std::format("fps: {}", J3ML::Math::Round(refresh_rate)),
|
||||
std::format("avg: {}", J3ML::Math::Round(avg_refresh_rate))
|
||||
};
|
||||
|
||||
|
||||
if (current_scene == GameSession()) {
|
||||
u16 id = game_ctx->GetTileIDUnderMouse();
|
||||
Core::Tile data = Tiles().Get(id);
|
||||
|
||||
debug_lines.push_back(std::format("tile: {} id: {}", data.mnemonic_id, (unsigned int)data.numeric_id));
|
||||
debug_lines.push_back(std::format("entities: {}", game_ctx->World()->GetCurrentEntityCount()));
|
||||
debug_lines.push_back(std::format("tile_simulation: {}", game_ctx->World()->GetTileSimulationEnabled()));
|
||||
debug_lines.push_back(std::format("rendertargets: {}", game_ctx->World()->GetRenderTargetCount()));
|
||||
}
|
||||
|
||||
draw_debug_info(debug_lines);
|
||||
}
|
||||
|
||||
|
||||
@@ -268,12 +334,11 @@ namespace CaveGame::ClientApp
|
||||
if (glError != GL_NO_ERROR) {
|
||||
Logs::Error(std::format("OpenGL Error: {}", glError));
|
||||
}
|
||||
GLSwapBuffers();
|
||||
SwapBuffers();
|
||||
|
||||
}
|
||||
|
||||
void CaveGameWindow::OnKeyUp(const ReWindow::KeyUpEvent& ev)
|
||||
{
|
||||
void CaveGameWindow::OnKeyUp(const ReWindow::KeyUpEvent& ev) {
|
||||
if (current_scene != nullptr)
|
||||
current_scene->PassKeyInput(ev.key, false);
|
||||
|
||||
@@ -283,22 +348,53 @@ namespace CaveGame::ClientApp
|
||||
if (ev.key == Keys::V)
|
||||
render_grid = !render_grid;
|
||||
|
||||
if (ev.key == Keys::Grave) {
|
||||
|
||||
// TODO: Key Input Hierarchy: Determine and handle priority of where certain keys go.
|
||||
console_window->SetOpen(!console_window->IsOpen());
|
||||
return;
|
||||
}
|
||||
|
||||
//RWindow::OnKeyUp(ev);
|
||||
|
||||
wm->ObserveKeyInput(ev.key, false);
|
||||
}
|
||||
|
||||
void CaveGameWindow::OnKeyDown(const ReWindow::KeyDownEvent &ev) {
|
||||
|
||||
if (ev.key == Keys::Escape)
|
||||
if (current_scene == game_ctx) {
|
||||
game_ctx->SaveAndExit();
|
||||
ChangeScene(menu_ctx);
|
||||
}
|
||||
//Die();
|
||||
if (current_scene == GameSession()) {
|
||||
|
||||
if (ev.key == Keys::F5)
|
||||
GameSession()->World()->RefreshAll();
|
||||
|
||||
if (ev.key == Keys::F7)
|
||||
GameSession()->World()->SetTileSimulationEnabled(!GameSession()->World()->GetTileSimulationEnabled());
|
||||
|
||||
if (ev.key == Keys::F6) {
|
||||
std::srand(std::time(nullptr));
|
||||
GameSession()->World()->SetSeed(std::rand());
|
||||
}
|
||||
|
||||
if (ev.key == Keys::Escape) {
|
||||
// TODO: "Pause menu"
|
||||
|
||||
if (Console()->IsOpen())
|
||||
Console()->SetOpen(false);
|
||||
|
||||
|
||||
//GameSession()->SaveAndExit();
|
||||
// TODO: GameContext needs mechanism to **inform** the higher-level object that we are done.
|
||||
//ChangeScene(menu_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
if (ev.key == Keys::Grave)
|
||||
return;
|
||||
|
||||
if (current_scene != nullptr)
|
||||
current_scene->PassKeyInput(ev.key, true);
|
||||
|
||||
wm->ObserveKeyInput(ev.key, true);
|
||||
|
||||
}
|
||||
|
||||
@@ -341,29 +437,32 @@ namespace CaveGame::ClientApp
|
||||
RWindow::OnMouseButtonUp(ev);
|
||||
}
|
||||
|
||||
void CaveGameWindow::OnMouseMove(const ReWindow::MouseMoveEvent& ev)
|
||||
{
|
||||
void CaveGameWindow::OnMouseMove(const ReWindow::MouseMoveEvent& ev) {
|
||||
|
||||
Vector2 pos = Vector2(ev.Position.x, ev.Position.y);
|
||||
|
||||
if (current_scene != nullptr)
|
||||
current_scene->PassMouseMovement(ev.Position);
|
||||
current_scene->PassMouseMovement(pos);
|
||||
|
||||
wm->ObserveMouseMovement(ev.Position);
|
||||
wm->ObserveMouseMovement(pos);
|
||||
|
||||
RWindow::OnMouseMove(ev);
|
||||
}
|
||||
|
||||
bool CaveGameWindow::OnResizeRequest(const ReWindow::WindowResizeRequestEvent& ev)
|
||||
{
|
||||
bool CaveGameWindow::OnResizeRequest(const ReWindow::WindowResizeRequestEvent& ev) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CaveGameWindow::Die() { wanna_die = true; }
|
||||
void CaveGameWindow::OnMouseWheel(const ReWindow::MouseWheelEvent &ev) {
|
||||
if (current_scene)
|
||||
current_scene->PassMouseWheel(ev.WheelMovement);
|
||||
|
||||
bool CaveGameWindow::InGameSession() const {
|
||||
return (current_scene == game_ctx);
|
||||
// wm->ObserveMouseWheel(ev.WheelMovement);
|
||||
}
|
||||
|
||||
void CaveGameWindow::Die() { wanna_die = true; }
|
||||
|
||||
bool CaveGameWindow::InMainMenu() const {
|
||||
return (current_scene == menu_ctx);
|
||||
}
|
||||
@@ -373,14 +472,203 @@ namespace CaveGame::ClientApp
|
||||
}
|
||||
|
||||
void CaveGameWindow::OnClosing() {
|
||||
if (current_scene == game_ctx)
|
||||
if (current_scene == GameSession())
|
||||
{
|
||||
game_ctx->SaveAndExit();
|
||||
GameSession()->SaveAndExit();
|
||||
}
|
||||
|
||||
wanna_die = true;
|
||||
}
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
std::string str_tolower(const std::string& s)
|
||||
{
|
||||
std::string copy = s;
|
||||
std::transform(copy.begin(), copy.end(), copy.begin(), ::tolower);
|
||||
return copy;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool CaveGameWindow::HasCommand(const std::string& command)
|
||||
{
|
||||
for (const Command& c : commands) {
|
||||
if (c.name == command)
|
||||
return true;
|
||||
for(const std::string& alias : c.aliases)
|
||||
if (alias == command)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Command CaveGameWindow::GetCommand(const std::string& command) {
|
||||
for (Command c : commands) {
|
||||
if (c.name == command)
|
||||
return c;
|
||||
|
||||
for (auto& alias : c.aliases)
|
||||
{
|
||||
if (command == alias)
|
||||
return c;
|
||||
}
|
||||
}
|
||||
// TODO: sensible error.
|
||||
return InvalidCommand;
|
||||
}
|
||||
|
||||
|
||||
void CaveGameWindow::OnConsoleCommandInput(const std::string& command_sequence) {
|
||||
|
||||
|
||||
auto tokens = string_split(command_sequence, ' ');
|
||||
if (tokens.empty())
|
||||
return;
|
||||
|
||||
std::string command_str = str_tolower(tokens[0]);
|
||||
|
||||
if (!HasCommand(command_str)) {
|
||||
console_window->Log("Invalid command!");
|
||||
return;
|
||||
}
|
||||
|
||||
Command cmd = GetCommand(command_str);
|
||||
|
||||
|
||||
cmd.callback(tokens);
|
||||
|
||||
}
|
||||
|
||||
void CaveGameWindow::create_console_window() {
|
||||
console_window = new JUI::CommandLine(this->wm);
|
||||
|
||||
console_window->Visible(false);
|
||||
|
||||
CaveGame::Logs::Info.OnLog += [this](std::string m, Color4 c) {Log(m, c); };
|
||||
CaveGame::Logs::Debug.OnLog += [this](std::string m, Color4 c) {Log(m, c); };
|
||||
CaveGame::Logs::Warning.OnLog += [this](std::string m, Color4 c) {Log(m, c); };
|
||||
CaveGame::Logs::Error.OnLog += [this](std::string m, Color4 c) {Log(m, c); };
|
||||
CaveGame::Logs::Fatal.OnLog += [this](std::string m, Color4 c) {Log(m, c); };
|
||||
|
||||
CaveGame::Logs::Client.OnLog += [this](std::string m, Color4 c) {Log(m, c); };
|
||||
CaveGame::Logs::Server.OnLog += [this](std::string m, Color4 c) {Log(m, c); };
|
||||
CaveGame::Logs::Generator.OnLog += [this](std::string m, Color4 c) {Log(m, c); };
|
||||
CaveGame::Logs::Lighting.OnLog += [this](std::string m, Color4 c) {Log(m, c); };
|
||||
|
||||
|
||||
//console_window->
|
||||
console_window->OnInput += [this] (const std::string& msg)
|
||||
{
|
||||
OnConsoleCommandInput(msg);
|
||||
};
|
||||
}
|
||||
|
||||
bool CaveGameWindow::InGame() const {
|
||||
return current_scene == game_ctx;
|
||||
}
|
||||
|
||||
Vector2 CaveGameWindow::GetMouseV2() const {
|
||||
auto coords = GetMouseCoordinates();
|
||||
return Vector2(coords.x, coords.y);
|
||||
}
|
||||
|
||||
Vector2 CaveGameWindow::GetSizeV2() const {
|
||||
auto isize = GetSize();
|
||||
return Vector2(isize.x, isize.y);
|
||||
}
|
||||
|
||||
void CaveGameWindow::Log(const std::string &msg, const Color4 &color) {
|
||||
console_window->Log(msg, color);
|
||||
}
|
||||
|
||||
bool CaveGameWindow::HelpCommand(const CommandArgs &args) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CaveGameWindow::ListCommand(const CommandArgs &args) {
|
||||
for (Command command: commands)
|
||||
{
|
||||
Log(std::format("{}", command.name));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Client::GameSession *CaveGameWindow::GameSession() { return game_ctx; }
|
||||
|
||||
JUI::Scene *CaveGameWindow::WindowManager() { return wm;}
|
||||
|
||||
JUI::CommandLine *CaveGameWindow::Console() { return console_window; }
|
||||
|
||||
bool CaveGameWindow::ToggleTileSim(const CommandArgs &args) {
|
||||
if (InGame())
|
||||
{
|
||||
GameSession()->World()->SetTileSimulationEnabled(!GameSession()->World()->GetTileSimulationEnabled());
|
||||
return true;
|
||||
}
|
||||
|
||||
Log("ERROR: Not in game context!", Colors::Red);
|
||||
return false;
|
||||
}
|
||||
|
||||
void CaveGameWindow::MarkReadyToClose(bool close) { wanna_die = close;}
|
||||
|
||||
bool CaveGameWindow::TileListCmd(const CommandArgs &args) {
|
||||
|
||||
// TODO: Tile iterator that returns vector of **valid** tiles.
|
||||
const auto& tile_list = Core::Tiles().GetTileArray();
|
||||
|
||||
for (const Core::Tile& tile : tile_list) {
|
||||
if (tile.numeric_id == 0 && tile.mnemonic_id != "air")
|
||||
continue; // Empty tile?
|
||||
Log(std::format("ID: {}, Mnemonic: {}, DisplayName: {}", tile.numeric_id, tile.mnemonic_id, tile.display_name));
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CaveGameWindow::ItemListCmd(const CommandArgs &args) {
|
||||
auto item_list = Items().GetItemMap();
|
||||
|
||||
for (const auto& [name, item] : item_list) {
|
||||
//if (item != nullptr)
|
||||
Log(std::format("Mnemonic: {}", name));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CaveGameWindow::NoclipCmd(const CommandArgs& args) {
|
||||
Log("Bet");
|
||||
if (InGame()) {
|
||||
auto plr = GameSession()->GetLocalPlayerEntity();
|
||||
if (plr != nullptr)
|
||||
plr->SetNoclip(!plr->IsNoclip());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CaveGameWindow::FpsLimitCmd(const CommandArgs& args) {
|
||||
if (args.size() < 2) {
|
||||
Log(std::format("Current FPS Limit: {}fps", max_fps));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int val = stoi(args[1]);
|
||||
std::cout << val << std::endl;
|
||||
max_fps = val;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CaveGameWindow::ReadyToClose() const { return wanna_die; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@@ -15,7 +15,10 @@ target_include_directories(CaveCore PUBLIC "include")
|
||||
|
||||
set_target_properties(CaveCore PROPERTIES LINKER_LANGUAGE CXX)
|
||||
target_include_directories(CaveCore PUBLIC ${J3ML_SOURCE_DIR}/include)
|
||||
|
||||
target_include_directories(CaveCore PUBLIC ${mcolor_SOURCE_DIR}/include)
|
||||
target_include_directories(CaveCore PUBLIC ${jjx_SOURCE_DIR}/include)
|
||||
target_include_directories(CaveCore PUBLIC ${Sockets_SOURCE_DIR}/include)
|
||||
target_include_directories(CaveCore PUBLIC ${JGL_SOURCE_DIR}/include)
|
||||
target_include_directories(CaveCore PUBLIC ${jstick_SOURCE_DIR}/include)
|
||||
|
||||
target_link_libraries(CaveCore PUBLIC J3ML jjx)
|
||||
target_link_libraries(CaveCore PUBLIC J3ML JGL jjx mcolor Sockets jstick)
|
56
Core/include/Core/Biomes.hpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
/// A biome defines a region in the world with distinct characteristics, such as:
|
||||
// Background textures / effects
|
||||
// Terrain Generation
|
||||
// Entity Spawns
|
||||
// Generated Structures / Dungeons
|
||||
// Music / Ambiance
|
||||
// Weather
|
||||
// Physics Modifiers
|
||||
// Multiple biomes may be occur in a place at a time.
|
||||
class Biome { };
|
||||
|
||||
#define BIOME static const Biome
|
||||
|
||||
/// List of biomes.
|
||||
namespace Biomes {
|
||||
|
||||
/// The world is split into horizontal slices, each controlled by a higher-level "Elevation Biome"
|
||||
#pragma region Elevation Biomes
|
||||
BIOME Space; // -1000 < Y
|
||||
BIOME Surface; // 1000 < Y <= -1000
|
||||
BIOME Subsurface; // 2000 < Y <= 1000
|
||||
BIOME Underground; // 10000 < T <= 2000
|
||||
BIOME Deep; // 50000 < Y <= 10000
|
||||
BIOME Mantle; // Y <= 50000
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Environmental Biomes
|
||||
BIOME Grassland;
|
||||
BIOME Forest;
|
||||
BIOME DenseForest;
|
||||
BIOME Tundra;
|
||||
BIOME Taiga;
|
||||
BIOME RedwoodForest;
|
||||
BIOME WinterWonderland;
|
||||
BIOME AshForest;
|
||||
BIOME Desert;
|
||||
BIOME Badlands;
|
||||
BIOME BloodForest;
|
||||
BIOME BlackForest;
|
||||
BIOME Mountains;
|
||||
BIOME SuperMountains;
|
||||
BIOME Marsh;
|
||||
BIOME Lake;
|
||||
BIOME ContaminationZome;
|
||||
BIOME Jungle;
|
||||
BIOME FungusForest;
|
||||
#pragma endregion
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -5,7 +5,7 @@
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file Chunk.hpp
|
||||
/// @desc A subdivision of the gameworld into discretized blocks.
|
||||
/// @desc A subdivision of the game-world into discrete blocks.
|
||||
/// @edit 12/2/2024
|
||||
|
||||
#pragma once
|
||||
@@ -13,94 +13,140 @@
|
||||
#include <J3ML/Geometry/QuadTree.hpp>
|
||||
#include "Tile.hpp"
|
||||
#include "Data.hpp"
|
||||
#include "Serialization.hpp"
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <bitset>
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
|
||||
|
||||
class Chunk {
|
||||
public:
|
||||
static constexpr int ChunkSize = 256;
|
||||
static constexpr int ChunkSize = 128;
|
||||
public:
|
||||
/// The default constructor does not initialize any members.
|
||||
Chunk() {}
|
||||
Chunk() = default;
|
||||
~Chunk() = default;
|
||||
|
||||
|
||||
~Chunk()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// Constructs a chunk with empty (air) tiles, located at the given chunk-cell.
|
||||
/// @param cell_coords Specifies the chunk-cell this chunk will occupy.
|
||||
explicit Chunk(const Vector2 &cell_coords);
|
||||
explicit Chunk(const Vector2i &cell);
|
||||
|
||||
explicit Chunk(const Vector2 &cell_coords, const std::filesystem::path &file) : Chunk(cell_coords)
|
||||
{
|
||||
std::ifstream inp;
|
||||
/// Constructs a chunk from a filesystem path. This constructor will load and parse the chunk file.
|
||||
explicit Chunk(const Vector2i &cell, const std::filesystem::path &file);
|
||||
|
||||
inp.open(file, std::ios::binary | std::ios::in);
|
||||
inp.seekg(0, std::ios::end);
|
||||
int length = inp.tellg();
|
||||
inp.seekg(0, std::ios::beg);
|
||||
|
||||
char buffer[length];
|
||||
inp.read(buffer, length);
|
||||
inp.close();
|
||||
|
||||
SetData(buffer);
|
||||
/// Returns the TileID enumeration that occupies the given tile-cell.
|
||||
[[nodiscard]] inline TileID GetTile(int x, int y) const {
|
||||
/* This is kind-of a cheat, If x or y is negative, the cast to unsigned will wrap it around to a large positive.
|
||||
* That makes it so you only have to check two conditions rather than 4 - Redacted. */
|
||||
if ((unsigned int) x >= (unsigned int)ChunkSize || (unsigned int) y >= (unsigned int)ChunkSize)
|
||||
throw std::runtime_error("Out of bounds!");
|
||||
return tiles[x][y];
|
||||
}
|
||||
|
||||
TileID GetTile(int x, int y) const;
|
||||
void SetTile(int x, int y, TileID t);
|
||||
inline TileID GetTile(const Vector2i& coords) const { return GetTile(coords.x, coords.y); }
|
||||
|
||||
[[nodiscard]] Vector2 GetChunkCell() const;
|
||||
[[nodiscard]] Vector2 GetChunkRealCoordinates() const;
|
||||
/// Sets the tile ID at the given tile-cell.
|
||||
/// @param trigger_tile_updates Whether to force adjacent tiles to update.
|
||||
void SetTile(int x, int y, TileID t, bool trigger_tile_updates = true);
|
||||
|
||||
/// Sets the tile ID at the given tile-cell, and explicitly does not update adjacent tiles.
|
||||
void SetTileSilent(int x, int y, TileID t);
|
||||
|
||||
/// Returns the value of the update flag field at the given tile-cell.
|
||||
[[nodiscard]] bool GetTileUpdateFlag(int x, int y) const;
|
||||
|
||||
[[nodiscard]] bool GetTileUpdateBufferFlag(int x, int y) const;
|
||||
|
||||
[[nodiscard]] bool HasUpdate() const { return has_update; };
|
||||
void SetTileUpdateBufferFlag(int x, int y, bool flag = true);
|
||||
|
||||
/// Sets the value of the update flag field at the given tile-cell.
|
||||
void SetTileUpdateFlag(int x, int y, bool flag = true);
|
||||
|
||||
/// Returns the "chunk coordinates" that this chunk occupies.
|
||||
[[nodiscard]] Vector2i GetChunkCell() const;
|
||||
|
||||
/// Returns the world-coordinates of the top-leftmost tile of this chunk.
|
||||
[[nodiscard]] Vector2i GetChunkRealCoordinates() const;
|
||||
|
||||
[[nodiscard]] const TileID* ptr() const { return &tiles[0][0];}
|
||||
|
||||
[[nodiscard]] std::array<TileID, ChunkSize * ChunkSize> DataContiguous() const;
|
||||
|
||||
void write(Buffer& buf) {
|
||||
write_u32(buf, tiles_with_random_tick_count);
|
||||
write_f32(buf, cell.x);
|
||||
write_f32(buf, cell.y);
|
||||
|
||||
[[nodiscard]] std::vector<TileID> DataContiguous() const
|
||||
{
|
||||
std::vector<TileID> data;
|
||||
data.reserve(ChunkSize*ChunkSize);
|
||||
for (int x = 0; x < ChunkSize; x++) {
|
||||
for (int y = 0; y < ChunkSize; y++) {
|
||||
data.push_back(tiles[x][y]);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
void SetData(char* buffer)
|
||||
{
|
||||
auto* data = reinterpret_cast<TileID*>(buffer);
|
||||
|
||||
|
||||
for (int x = 0; x < ChunkSize; x++)
|
||||
{
|
||||
for (int y = 0; y < ChunkSize; y++)
|
||||
{
|
||||
tiles[x][y] = data[y+(x*ChunkSize)];
|
||||
write_u8(buf, (uint8_t)GetTile(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr std::size_t BufferSizeBytes()
|
||||
{
|
||||
return (ChunkSize*ChunkSize)*sizeof(TileID);
|
||||
void read(Buffer& buf) {
|
||||
tiles_with_random_tick_count = read_u32(buf);
|
||||
cell.x = read_f32(buf);
|
||||
cell.y = read_f32(buf);
|
||||
|
||||
for (int x = 0; x < ChunkSize; x++) {
|
||||
for (int y = 0; y < ChunkSize; y++) {
|
||||
SetTile(x, y, (TileID) read_u8(buf));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr std::size_t BufferSize()
|
||||
void SetData(char* buffer);
|
||||
|
||||
[[nodiscard]] static constexpr std::size_t BufferSizeBytes();
|
||||
|
||||
[[nodiscard]] static constexpr std::size_t BufferSize();
|
||||
|
||||
|
||||
void SwapTileUpdateBuffers();
|
||||
|
||||
[[nodiscard]] bool FirstPassCompleted() const;
|
||||
|
||||
[[nodiscard]] bool SecondPassCompleted() const;
|
||||
|
||||
void SetFirstPassCompleted(bool complete = true);
|
||||
|
||||
void SetSecondPassCompleted(bool complete = true);
|
||||
|
||||
// TODO: Don't do this unless debugging, iterating over every chunk just to increment this number is slow.
|
||||
void Update(float delta)
|
||||
{
|
||||
return (ChunkSize*ChunkSize);
|
||||
time_since_refresh += delta;
|
||||
}
|
||||
|
||||
public:
|
||||
bool touched;
|
||||
// For to the rendertarget!!
|
||||
float time_since_refresh = 0.f;
|
||||
int tiles_with_random_tick_count = 0;
|
||||
protected:
|
||||
Vector2 cell;
|
||||
Vector2i cell;
|
||||
TileID tiles[ChunkSize][ChunkSize];
|
||||
|
||||
std::bitset<ChunkSize * ChunkSize> tagged_for_update;
|
||||
std::bitset<ChunkSize * ChunkSize> update_buffer;
|
||||
std::vector<Vector2i> needs_update;
|
||||
bool first_pass_complete = false;
|
||||
bool second_pass_complete = false;
|
||||
bool has_update = false;
|
||||
};
|
||||
}
|
||||
|
||||
constexpr std::size_t Chunk::BufferSizeBytes()
|
||||
{
|
||||
return (ChunkSize*ChunkSize)*sizeof(TileID);
|
||||
}
|
||||
|
||||
constexpr std::size_t Chunk::BufferSize()
|
||||
{
|
||||
return (ChunkSize*ChunkSize);
|
||||
}
|
||||
}
|
||||
|
@@ -18,10 +18,7 @@
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <type_traits>
|
||||
#include <wait.h>
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
//#include <wait.h>
|
||||
|
||||
/// A simple C++11 Concurrent Queue based on std::queue.
|
||||
/// Supports waiting operations for retrieving an element when it's empty.
|
||||
@@ -44,6 +41,18 @@ namespace CaveGame::Core
|
||||
condition_variable.notify_one();
|
||||
}
|
||||
|
||||
bool contains(const T& value)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(this->mutex);
|
||||
std::queue<T, Container> copy = queue;
|
||||
while(!copy.empty())
|
||||
if (copy.front() == value)
|
||||
return true;
|
||||
else
|
||||
copy.pop();
|
||||
return false;
|
||||
}
|
||||
|
||||
void push(const T& e)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
@@ -60,7 +69,7 @@ namespace CaveGame::Core
|
||||
}
|
||||
|
||||
bool empty() {
|
||||
std::unique_lock<std::mutex> lock(this->mutex);
|
||||
//std::unique_lock<std::mutex> lock(this->mutex);
|
||||
return queue.empty();
|
||||
}
|
||||
|
||||
@@ -127,7 +136,4 @@ namespace CaveGame::Core
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
};
|
46
Core/include/Core/Container.hpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <J3ML/LinearAlgebra.hpp>
|
||||
#include <map>
|
||||
#include <Core/Itemstack.hpp>
|
||||
#include "Core/v2i_hash.hpp"
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
enum RestrictListMode
|
||||
{
|
||||
ALLOWLIST,
|
||||
DENYLIST
|
||||
};
|
||||
|
||||
class Container {
|
||||
public:
|
||||
|
||||
RestrictListMode whitelist_mode;
|
||||
|
||||
Container(int rows, int columns);
|
||||
|
||||
ItemStack &At(const Vector2i &cell);
|
||||
|
||||
/// Returns how much of the stack would be left-over if inserted into this container.
|
||||
/// Storing onto existing non-full item stacks of the same type will occur.
|
||||
int CanFitItemStack(const ItemStack &item)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool IsSlotEmpty(const Vector2i& cell)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int Insert(const ItemStack &item)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
private:
|
||||
std::unordered_map<Vector2i, ItemStack> contents;
|
||||
};
|
||||
}
|
@@ -2,29 +2,32 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
// TODO: Consider implementing traits.
|
||||
|
||||
enum class TileID : std::uint16_t
|
||||
using TileID = uint16_t;
|
||||
|
||||
|
||||
/*enum class TileID : std::uint16_t
|
||||
{
|
||||
//
|
||||
AIR = 0,
|
||||
|
||||
STONE,
|
||||
DIRT,
|
||||
MUD,
|
||||
DIRT, MUD,
|
||||
GRASS,
|
||||
MOSSY_STONE, LIMESTONE, BASALT, COBBLESTONE,
|
||||
|
||||
MOSSY_STONE,
|
||||
LIMESTONE,
|
||||
BASALT,
|
||||
COBBLESTONE,
|
||||
WET_CEMENT,
|
||||
CEMENT,
|
||||
CONCRETE,
|
||||
MUD_BRICK,
|
||||
CLAY_BRICK,
|
||||
STONE_BRICK,
|
||||
SANDSTONE,
|
||||
SAND_BRICK,
|
||||
GRANITE,
|
||||
SMOOTH_GRANTITE,
|
||||
SLATE,
|
||||
@@ -34,17 +37,24 @@ namespace CaveGame::Core
|
||||
GYPSUM,
|
||||
PUMICE,
|
||||
QUARTZ,
|
||||
MARBLE,
|
||||
|
||||
BRIMSTONE,
|
||||
OBSIDIAN,
|
||||
HARDENED_CLAY, CLAY,
|
||||
SILT, LOAM, QUAGMIRE, ASH,
|
||||
GLOWY_GRASS, DRY_GRASS, DEAD_GRASS, GMO_GRASS, FAKE_GRASS,
|
||||
MOSS,
|
||||
GREEN_MOSS, RED_MOSS, BROWN_MOSS, LAVA_MOSS,
|
||||
SNOW, PACKED_SNOW,
|
||||
ICE, PACKED_ICE, ICE_BRICK, THIN_ICE,
|
||||
SAND, WET_SAND, WHITE_SAND, BLACK_SAND, GRAVEL, DUST,
|
||||
|
||||
SLUSH,
|
||||
SAND, WET_SAND,
|
||||
SANDSTONE, SAND_BRICK,
|
||||
RED_SAND, RED_SANDSTONE, RED_SAND_BRICK,
|
||||
WHITE_SAND, WHITE_SANDSTONE, WHITE_SAND_BRICK,
|
||||
BLACK_SAND, BLACK_SANDSTONE, BLACK_SAND_BRICK,
|
||||
GRAVEL,
|
||||
DUST,
|
||||
GLASS, CRACKED_GLASS,
|
||||
RED_STAINED_GLASS,
|
||||
ORANGE_STAINED_GLASS,
|
||||
@@ -112,6 +122,10 @@ namespace CaveGame::Core
|
||||
COPPER_ORE,
|
||||
IRON_ORE,
|
||||
COAL_ORE,
|
||||
LEAD_ORE,
|
||||
SILVER_ORE,
|
||||
GOLD_ORE,
|
||||
TUNGSTEN_ORE,
|
||||
NICKEL_ORE,
|
||||
CHROMIUM_ORE,
|
||||
TITANIUM_ORE,
|
||||
@@ -120,21 +134,27 @@ namespace CaveGame::Core
|
||||
URANIUM_ORE,
|
||||
MAGNETITE_ORE,
|
||||
METEORITE_ORE,
|
||||
PLATINUM_ORE,
|
||||
COBALT_ORE,
|
||||
|
||||
|
||||
SOVITE, PYRITE, CINNABAR,
|
||||
|
||||
CLOUD,
|
||||
|
||||
WATER, LAVA, SLUDGE, OIL,
|
||||
BLOOD, PISS, HONEY, ECTOPLASM,
|
||||
|
||||
COPPER_WIRE, GOLD_WIRE,
|
||||
RED_WIRE, GREEN_WIRE,
|
||||
YELLOW_WIRE, BLUE_WIRE,
|
||||
AND_GATE, NOR_GATE, XOR_GATE,
|
||||
LED,
|
||||
|
||||
TORCH_BASE,
|
||||
TORCH_EMBER,
|
||||
TORCH_EMBER = 256,
|
||||
VOID = 65535,
|
||||
|
||||
};
|
||||
};*/
|
||||
|
||||
}
|
@@ -1,23 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <Event.h>
|
||||
#include <J3ML/LinearAlgebra.hpp>
|
||||
#include <vector>
|
||||
#include "Color4.hpp"
|
||||
#include "Interfaces.hpp"
|
||||
#include "Loggers.hpp"
|
||||
#include "SimpleAABBSolver.hpp"
|
||||
|
||||
namespace CaveGame::Core {
|
||||
class StatusEffect {
|
||||
};
|
||||
|
||||
|
||||
class Lifeform {
|
||||
public:
|
||||
virtual int Health() const;
|
||||
virtual void SetHealth(int hp);
|
||||
};
|
||||
|
||||
class DamageSource {
|
||||
|
||||
};
|
||||
|
||||
class Entity {
|
||||
public:
|
||||
int Health() const { return health; }
|
||||
|
||||
int MaxHealth() const { return max_health; }
|
||||
Event<> Died;
|
||||
|
||||
|
||||
explicit Entity(const Vector2& spawnPoint);
|
||||
|
||||
int Health() const;
|
||||
|
||||
int MaxHealth() const;
|
||||
|
||||
virtual void Draw() = 0;
|
||||
|
||||
virtual void Update(float elapsed) = 0;
|
||||
virtual void PhysicsUpdate(float elapsed) {}
|
||||
|
||||
Vector2 Position() const;
|
||||
|
||||
Vector2 TopLeft() const { return Position()-(bounding_box/2.f);}
|
||||
Vector2 RenderTopLeft() const { return Position()-(texture_center);}
|
||||
Vector2 Centroid() const { return Position();}
|
||||
AABB2D TranslatedBoundingBoxAABB() const { }
|
||||
|
||||
Vector2 Size() const;
|
||||
|
||||
virtual void CollisionTest(ITileMap* map, float step) {}
|
||||
|
||||
void SetNoclip(bool value);
|
||||
[[nodiscard]] bool IsNoclip() const;
|
||||
protected:
|
||||
int health;
|
||||
int max_health;
|
||||
@@ -25,71 +60,21 @@ namespace CaveGame::Core {
|
||||
float age;
|
||||
Vector2 position;
|
||||
Vector2 bounding_box;
|
||||
Vector2 texture_center;
|
||||
Vector2 render_center;
|
||||
std::vector<StatusEffect> active_effects;
|
||||
Color4 color;
|
||||
bool on_ground;
|
||||
float airtime = 0;
|
||||
float climbing_skill = 55;
|
||||
bool facing_left = true;
|
||||
bool walking = false;
|
||||
bool noclip = false;
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
class PhysicsEntity : public Entity {
|
||||
public:
|
||||
[[nodiscard]] Vector2 Velocity() const { return velocity; }
|
||||
|
||||
[[nodiscard]] Vector2 EstimatedNextPosition() const { return next_position; }
|
||||
|
||||
protected:
|
||||
Vector2 velocity;
|
||||
Vector2 next_position;
|
||||
float mass;
|
||||
private:
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
class Humanoid : public PhysicsEntity {
|
||||
public:
|
||||
virtual int BaseDefense() const;
|
||||
virtual int ArmorDefense() const
|
||||
{
|
||||
|
||||
}
|
||||
virtual int DefenseModifier() const;
|
||||
int Defense() const
|
||||
{
|
||||
|
||||
}
|
||||
protected:
|
||||
};
|
||||
|
||||
class Projectile : public PhysicsEntity
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
class Arrow : public Projectile {};
|
||||
class Fireball : public Projectile {};
|
||||
class Icecicle : public Projectile {};
|
||||
|
||||
class Player : public Humanoid {
|
||||
public:
|
||||
void Jump();
|
||||
void Climb();
|
||||
void Descend();
|
||||
void Crouch();
|
||||
void WalkLeft();
|
||||
void WalkRight();
|
||||
protected:
|
||||
};
|
||||
|
||||
|
||||
|
||||
class Zombie : public Humanoid {
|
||||
public:
|
||||
protected:
|
||||
};
|
||||
|
||||
class ItemStack : PhysicsEntity {};
|
||||
|
||||
class PhysicalParticle {};
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@@ -1,8 +1,60 @@
|
||||
//
|
||||
// Created by dawsh on 9/26/24.
|
||||
//
|
||||
#pragma once
|
||||
#include "Sprite.hpp"
|
||||
#include "Entity.hpp"
|
||||
#include <JGL/types/RenderTarget.h>
|
||||
#include <Core/TileRegistry.hpp>
|
||||
|
||||
#ifndef RECAVEGAME_EXPLOSION_HPP
|
||||
#define RECAVEGAME_EXPLOSION_HPP
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
|
||||
#endif //RECAVEGAME_EXPLOSION_HPP
|
||||
class Explosion : public Entity, public Sprite {
|
||||
public:
|
||||
|
||||
const AABB2D SP_EXPLOSION0 {{0, 0}, {32, 32}};
|
||||
const AABB2D SP_EXPLOSION1 {{32, 0}, {32, 32}};
|
||||
const AABB2D SP_EXPLOSION2 {{64, 0}, {32, 32}};
|
||||
const AABB2D SP_EXPLOSION3 {{96, 0}, {32, 32}};
|
||||
const AABB2D SP_EXPLOSION4 {{128, 0}, {32, 32}};
|
||||
Explosion(ITileMap* world, const Vector2& position, float force, float radius, float fuse = 0.f, bool damage_tiles = true) : Entity(position) {
|
||||
rotation = 0; //Math::Radians( std::rand() % 360);
|
||||
this->wrld = world;
|
||||
this->radius = radius;
|
||||
this->fuse = fuse;
|
||||
}
|
||||
void Detonate() {
|
||||
detonated = true;
|
||||
health = 0;
|
||||
|
||||
for (int x = -radius; x <= radius; x++) {
|
||||
for (int y = -radius; y <= radius; y++) {
|
||||
float dist = Vector2::LengthSquared(Vector2(x, y));
|
||||
if (dist <= radius*radius) {
|
||||
wrld->SetTile(position.x + x, position.y + y, Tiles()["air"].numeric_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool HasDetonated() const;
|
||||
|
||||
void Draw() override;
|
||||
void Update(float elapsed) override;
|
||||
|
||||
protected:
|
||||
float fuse = 0.f;
|
||||
float radius;
|
||||
float force;
|
||||
float rotation = 0;
|
||||
bool detonated = false;
|
||||
float anim_timer = 0.f;
|
||||
ITileMap* wrld;
|
||||
AABB2D quad;
|
||||
private:
|
||||
void DrawFrame(const JGL::Texture *texture, const AABB2D &quad, const Vector2 &pos, const Vector2 &scale);
|
||||
|
||||
AABB2D CurrentFrame() const;
|
||||
|
||||
void SetFrame(const AABB2D &frame_quad);
|
||||
};
|
||||
|
||||
}
|
||||
|
53
Core/include/Core/Furniture.hpp
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
|
||||
class Furniture {
|
||||
|
||||
};
|
||||
|
||||
/// Furniture objects are multi-tile structures that aren't **necessarily** furniture in the normal sense, many are.
|
||||
/// Furniture can be considered the CaveGame Engine's term for a contiguous structure that is larger than one tile.
|
||||
/// This is differentiated from a generated structure in that each pixel of a Furniture object is bound to the whole, and cannot be broken in isolation.
|
||||
namespace Furnitures {
|
||||
static const Furniture Door;
|
||||
static const Furniture TrapDoor;
|
||||
static const Furniture Chair;
|
||||
static const Furniture Table;
|
||||
static const Furniture Workbench;
|
||||
static const Furniture Campfire;
|
||||
static const Furniture Barrel;
|
||||
static const Furniture Safe;
|
||||
static const Furniture Chest;
|
||||
static const Furniture Furnace;
|
||||
static const Furniture Fireplace;
|
||||
static const Furniture Anvil;
|
||||
static const Furniture HeavyWorkbench;
|
||||
static const Furniture Grindstone;
|
||||
static const Furniture BlastFurnace;
|
||||
static const Furniture Printer3D;
|
||||
static const Furniture CookingPot;
|
||||
static const Furniture AlchemyStation;
|
||||
static const Furniture ChemistrySet;
|
||||
static const Furniture Bed;
|
||||
static const Furniture PiggyBank;
|
||||
static const Furniture Shelf;
|
||||
static const Furniture Ladder;
|
||||
static const Furniture Painting;
|
||||
static const Furniture Signpost;
|
||||
static const Furniture Blackboard;
|
||||
static const Furniture Fridge;
|
||||
static const Furniture Cooler;
|
||||
|
||||
static const Furniture Torch;
|
||||
static const Furniture Lantern;
|
||||
static const Furniture Candle;
|
||||
static const Furniture Bottle;
|
||||
static const Furniture CoffeeMug;
|
||||
static const Furniture BeerMug;
|
||||
static const Furniture Book;
|
||||
static const Furniture
|
||||
}
|
||||
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
//
|
||||
// Created by dawsh on 11/10/24.
|
||||
//
|
||||
|
||||
#ifndef RECAVEGAME_GAMEPROTOCOL_HPP
|
||||
#define RECAVEGAME_GAMEPROTOCOL_HPP
|
||||
|
||||
#endif //RECAVEGAME_GAMEPROTOCOL_HPP
|
@@ -14,34 +14,157 @@ namespace CaveGame::Core
|
||||
class Generator
|
||||
{
|
||||
public:
|
||||
static constexpr int PrecomputedWhiteNoiseResolution = 2048;
|
||||
/// Defines the sample count for the pre-generated RNG lookup table.
|
||||
static constexpr int PrecomputedWhiteNoiseResolution = 2048;
|
||||
/// Shifts the ground-level of terrain vertically by this many tiles.
|
||||
|
||||
#pragma region Surface Parameters
|
||||
static constexpr float BaseSurfaceLvl = 100.f;
|
||||
|
||||
static constexpr float HeightMapHighPassAmplitude = 700.f;
|
||||
static constexpr float HeightMapHighPassScale = 700.f;
|
||||
static constexpr float HeightMapLowPassAmplitude = 85.f;
|
||||
static constexpr float HeightMapLowPassScale = 64.f;
|
||||
static constexpr float TopSoilDepth = 225.f;
|
||||
static constexpr float TopSoilDepth = 345.f;
|
||||
|
||||
#pragma endregion
|
||||
#pragma region Cave Parameters
|
||||
|
||||
static constexpr float CaveInputScale = 800.f;
|
||||
static constexpr float CaveOutputScale = 1.25f;
|
||||
static constexpr float CaveErosionRange = 0.035f;
|
||||
static constexpr float CaveOutputScale = 1.20f;
|
||||
static constexpr float CaveErosionRange = 0.080f;
|
||||
static constexpr float CaveWhiteNoise = 0.005f;
|
||||
static constexpr float CaveAdditiveInputScale = 80.f;
|
||||
static constexpr float CaveAdditiveOutputScale = 0.25f;
|
||||
static constexpr float CaveAdditiveOutputScale = 0.45f;
|
||||
static constexpr float SurfaceCaveShrinkDepth = 100;
|
||||
static constexpr float SurfaceCaveShrinkFactor = 1.5f;
|
||||
|
||||
#pragma endregion
|
||||
#pragma region Ore Vein Parameters
|
||||
|
||||
const Vector2 ClayVeinHiPassScale = {200.f, 205.f};
|
||||
static constexpr float ClayVeinHiPassOffset = 0.f;
|
||||
static constexpr float ClayVeinHiPassOutputScale = 2.2f;
|
||||
|
||||
const Vector2 ClayVeinLoPassScale = {17.f, 15.f};
|
||||
static constexpr float ClayVeinLoPassOffset = 420.f;
|
||||
static constexpr float ClayVeinLoPassOutputScale = 1.2f;
|
||||
|
||||
static constexpr float ClayVeinNoise = 0.175f;
|
||||
static constexpr float ClayVeinRampFactor = 2.f;
|
||||
|
||||
const Vector2 SiltVeinHiPassScale = {85.f, 95.f};
|
||||
static constexpr float SiltVeinHiPassOffset = 5.f;
|
||||
static constexpr float SiltVeinHiPassOutputScale = 2.2f;
|
||||
|
||||
const Vector2 SiltVeinLoPassScale = {15.f, 16.f};
|
||||
static constexpr float SiltVeinLoPassOffset = 422.f;
|
||||
static constexpr float SiltVeinLoPassOutputScale = 1.25f;
|
||||
|
||||
static constexpr float SiltVeinNoise = 0.175f;
|
||||
static constexpr float SiltVeinRampFactor = 2.f;
|
||||
|
||||
const Vector2 StoneVeinHiPassScale = {30.f, 30.f};
|
||||
static constexpr float StoneVeinHiPassOffset = 666.f;
|
||||
static constexpr float StoneVeinHiPassOutputScale = 2.4f;
|
||||
|
||||
const Vector2 StoneVeinLoPassScale = {220.f, 220.f};
|
||||
static constexpr float StoneVeinLoPassOffset = 0.5f;
|
||||
static constexpr float StoneVeinLoPassOutputScale = 1.75f;
|
||||
|
||||
static constexpr float StoneVeinNoise = 0.25f;
|
||||
static constexpr float StoneVeinRampFactor = 1.75f;
|
||||
|
||||
const Vector2 DirtVeinHiPassScale = {30.f, 30.f};
|
||||
static constexpr float DirtVeinHiPassOffset = 12.f;
|
||||
static constexpr float DirtVeinHiPassOutputScale = 1.35f;
|
||||
|
||||
const Vector2 DirtVeinLoPassScale = {90.f, 90.f};
|
||||
static constexpr float DirtVeinLoPassOffset = 0.5f;
|
||||
static constexpr float DirtVeinLoPassOutputScale = 0.75f;
|
||||
|
||||
const float DirtVeinNoise = 0.05f;
|
||||
static constexpr float DirtVeinRampFactor = 0.5f;
|
||||
|
||||
const Vector2 CoalVeinHiPassScale = {20.f, 20.f};
|
||||
static constexpr float CoalVeinHiPassOffset = 12.f;
|
||||
static constexpr float CoalVeinHiPassOutputScale = 1.05f;
|
||||
|
||||
const Vector2 CoalVeinLoPassScale = {120.f, 120.f};
|
||||
static constexpr float CoalVeinLoPassOffset = 0.5f;
|
||||
static constexpr float CoalVeinLoPassOutputScale = 0.83f;
|
||||
|
||||
static constexpr float CoalVeinNoise = 0.45f;
|
||||
static constexpr float CoalVeinRampFactor = 0.75f;
|
||||
|
||||
const Vector2 CopperVeinHiPassScale = {30.f, 30.f};
|
||||
static constexpr float CopperVeinHiPassOffset = 12.f;
|
||||
static constexpr float CopperVeinHiPassOutputScale = 1.35f;
|
||||
|
||||
const Vector2 CopperVeinLoPassScale = {90.f, 90.f};
|
||||
static constexpr float CopperVeinLoPassOffset = 0.5f;
|
||||
static constexpr float CopperVeinLoPassOutputScale = 0.75f;
|
||||
|
||||
static constexpr float CopperVeinNoise = 0.05f;
|
||||
static constexpr float CopperVeinRampFactor = 0.5f;
|
||||
|
||||
const Vector2 TinVeinHiPassScale = {30.f, 30.f};
|
||||
static constexpr float TinVeinHiPassOffset = 12.f;
|
||||
static constexpr float TinVeinHiPassOutputScale = 1.35f;
|
||||
|
||||
const Vector2 TinVeinLoPassScale = {90.f, 90.f};
|
||||
static constexpr float TinVeinLoPassOffset = 0.5f;
|
||||
static constexpr float TinVeinLoPassOutputScale = 0.75f;
|
||||
|
||||
static constexpr float TinVeinNoise = 0.05f;
|
||||
static constexpr float TinVeinRampFactor = 0.5f;
|
||||
|
||||
const Vector2 IronVeinHiPassScale = {30.f, 30.f};
|
||||
static constexpr float IronVeinHiPassOffset = 12.f;
|
||||
static constexpr float IronVeinHiPassOutputScale = 1.35f;
|
||||
|
||||
const Vector2 IronVeinLoPassScale = {90.f, 90.f};
|
||||
static constexpr float IronVeinLoPassOffset = 0.5f;
|
||||
static constexpr float IronVeinLoPassOutputScale = 0.75f;
|
||||
|
||||
static constexpr float IronVeinNoise = 0.05f;
|
||||
static constexpr float IronVeinRampFactor = 0.5f;
|
||||
#pragma endregion
|
||||
|
||||
public:
|
||||
Generator() = default;
|
||||
//Generator() = default;
|
||||
explicit Generator(int seed);
|
||||
|
||||
TileID HeightMap(float depth, int wx, int wy);
|
||||
TileID ComputeTile(int wx, int wy);
|
||||
void FirstPass(Chunk& chunk);
|
||||
int GetSeed() const { return seed;}
|
||||
void SetSeed(int newSeed) { seed = newSeed; }
|
||||
|
||||
TileID HeightMap(float depth, int wx, int wy);
|
||||
float Perlin(int wx, int wy, float hScale, float vScale, float offset, float outputScale);
|
||||
|
||||
float Octave(int wx, int wy, float hScale, float vScale, float offset, float outputScale, int octaves);
|
||||
|
||||
/// Returns the sum of hi+lo and (hi*lo) / div, resulting in something approximating noise iterated in 2 octaves.
|
||||
float AddSumAndPartialProduct(float hi, float lo, float div);
|
||||
|
||||
TileID HiLoSelect(float pass, float upperBound, float lowerBound, TileID hiSelect, TileID loSelect, TileID fallback);
|
||||
|
||||
/// This function returns a floating point number (Usually in the range [-1, 1]) to be used as a condition for
|
||||
/// whether ores should spawn at this location, given a set of noise parameters.
|
||||
/// Ores are generated by computing two Perlin Noise passes, taking the sum of adding and multiplying, then adding in random noise.
|
||||
/// Hi-Pass is used to define macro (larger-scale) features of ore veins, and Lo-Pass adds minor detailing for a more organic appearance.
|
||||
float ComputeOre(int wx, int wy, const Vector2& hiPass, const Vector2& loPass, float hiOffset, float loOffset,
|
||||
float hiScale, float loScale, float noise, float ramp);
|
||||
|
||||
TileID ComputeTile(int wx, int wy);
|
||||
void FirstPass(Chunk* chunk);
|
||||
void SecondPass(ITileMap* world, Chunk* chunk);
|
||||
|
||||
|
||||
unsigned int ColorMap(int range, int wx, int wy);
|
||||
|
||||
// TODO: Implement SecondPass, the catch is, it **may** need to load an arbitrary amount of adjacent chunks to correctly place structures.
|
||||
// TODO: Expert Mode: How do we keep it threaded separately while still accomplishing the above?
|
||||
|
||||
uint PlaceTile();
|
||||
|
||||
int ModInt(int x, int mod) const {
|
||||
|
||||
x = Math::Abs(x);
|
||||
@@ -52,12 +175,14 @@ namespace CaveGame::Core
|
||||
return x % mod;
|
||||
}
|
||||
|
||||
/// Returns a value between 0 and 1.
|
||||
float GetPrecomputedWhiteNoise1D(int x) const;
|
||||
|
||||
/// Returns a value between 0 and 1.
|
||||
float GetPrecomputedWhiteNoise2D(int x, int y) const;
|
||||
|
||||
protected:
|
||||
int seed = 0;
|
||||
int seed = 42069;
|
||||
SimplexNoise simplex;
|
||||
PerlinNoise perlin;
|
||||
|
||||
|
26
Core/include/Core/Humanoid.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include <Core/PhysicsEntity.hpp>
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
class Humanoid : public PhysicsEntity {
|
||||
public:
|
||||
|
||||
explicit Humanoid(const Vector2& spawnPoint);
|
||||
|
||||
virtual int BaseDefense() const { return 0; }
|
||||
virtual int ArmorDefense() const { return 0; }
|
||||
virtual int DefenseModifier() const { return 0;}
|
||||
int Defense() const { return 0; }
|
||||
|
||||
void Update(float elapsed) override;
|
||||
void PhysicsUpdate(float elapsed) override;
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
class Zombie : public Humanoid {
|
||||
public:
|
||||
protected:
|
||||
};
|
||||
}
|
@@ -1,33 +1,31 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Core/Data.hpp>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
using TileState = uint16_t;
|
||||
using TileID = uint16_t;
|
||||
|
||||
|
||||
class ITileMap
|
||||
{
|
||||
public:
|
||||
virtual TileID GetTile(int x, int y) const = 0;
|
||||
virtual void SetTile(int x, int y, TileID tile) = 0;
|
||||
[[nodiscard]] virtual TileID GetTile(int x, int y) const = 0;
|
||||
|
||||
virtual TileState GetTileState(int x, int y) const = 0;
|
||||
virtual void SetTile(int x, int y, TileID tile, bool flag_update = true) = 0;
|
||||
//virtual void SetTile(int x, int y, const std::string& tile_name, bool flag_update = true) = 0;
|
||||
virtual void SwapTile(int source_x, int source_y, int dest_x, int dest_y, bool flag_update = true) = 0;
|
||||
[[nodiscard]] virtual bool GetTileUpdateFlag(int x, int y) const = 0;
|
||||
virtual void SetTileUpdateFlag(int x, int y, bool flag = true) = 0;
|
||||
//[[nodiscard]] virtual bool GetTileUpdateBufferFlag(int x, int y) const = 0;
|
||||
//virtual void SetTileUpdateBufferFlag(int x, int y, bool flag = true);
|
||||
[[nodiscard]] virtual TileState GetTileState(int x, int y) const = 0;
|
||||
virtual void SetTileState(int x, int y, TileState state) = 0;
|
||||
|
||||
bool GrassSpreadable(int x, int y) const;
|
||||
|
||||
bool IsNonSolidTile(int x, int y) const;
|
||||
|
||||
bool GrassShouldSuffocate(int x, int y) const;
|
||||
|
||||
void GrassRandomTicc(int wx, int wy);
|
||||
|
||||
bool IsSolidTile(int x, int y) const;
|
||||
|
||||
bool HasAdjacentOrDiagonalAirBlock(int x, int y) const;
|
||||
|
||||
bool HasAdjacentAirBlock(int x, int y) const;
|
||||
[[nodiscard]] bool IsNonSolidTile(int x, int y) const;
|
||||
[[nodiscard]] bool IsSolidTile(int x, int y) const;
|
||||
[[nodiscard]] bool HasAdjacentOrDiagonalAirBlock(int x, int y) const;
|
||||
[[nodiscard]] bool HasAdjacentAirBlock(int x, int y) const;
|
||||
};
|
||||
}
|
@@ -1,21 +1,40 @@
|
||||
#include <set>
|
||||
#include <Core/Registry.hpp>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Color4.hpp>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <J3ML/Math.hpp>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
//#include <Core/Registry.hpp>
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
|
||||
/// Implementation of items
|
||||
/// 1. Need to create a TileItem associated with each tile, automatically.
|
||||
/// 2. Not a strict requirement, but might like to be able to read item data from a data file.
|
||||
/// This would remove the need to re-compile the entire program when items are changed.
|
||||
/// 3. Each item in-game should be a single instance of the Item class.
|
||||
/// 4. Items are represented via ItemStacks and ItemStack entities, which represent quantities of items
|
||||
/// in a container, or dropped on the ground.
|
||||
/// 5. Support Item Animated Sprites
|
||||
/// 6. Support lots of unique item abilities and effects.
|
||||
/// 7. Support attaching custom metadata to certain items.
|
||||
|
||||
|
||||
/// This enum defines combinatorial category flags to items to enable smart sorting.
|
||||
enum class ItemCategory: u8
|
||||
enum class ItemCategory
|
||||
{
|
||||
ANY, TILE, ORE, TOOL, WEAPON, ARMOR, CLOTHING, FOOD, POTION, INGREDIENT, MEME, PLANT, METAL, ORGANIC, BUILDING_BLOCK,
|
||||
ANY, TILE_ITEM, ORE, TOOL, WEAPON, ARMOR, CLOTHING, FOOD, POTION, INGREDIENT, MEME, PLANT, METAL, ORGANIC, BUILDING_BLOCK,
|
||||
FLUID, SOIL, GAS, LIGHT_SOURCE, CRAFTING_STATION,
|
||||
};
|
||||
|
||||
|
||||
/// This enum defines a mutually-exclusive (one-at-a-time) 'Modifier' that items may randomly spawn with.
|
||||
/// It will affect item stats in a variety of ways, and some will even have special coding.
|
||||
enum class ItemModifiers : u8 {
|
||||
enum class ItemModifiers {
|
||||
|
||||
// These represent low-quality, crappy items, often with comical effects.
|
||||
FLIMSY, RUSTY, BROKEN, BENDY, RUBBERY, FRAGILE, FILTHY, DRAB, DULL, SCARRED, BURNED,
|
||||
@@ -45,18 +64,33 @@ namespace CaveGame::Core
|
||||
ANCIENT, VICTORIOUS, CONDEMNED, CLEAN, LUCKY, POWERFUL, SUPER,
|
||||
};
|
||||
|
||||
class Item
|
||||
/*class ItemComponent
|
||||
{
|
||||
public:
|
||||
|
||||
};
|
||||
|
||||
class UseCooldown {};
|
||||
class Consumable {};
|
||||
class Drinkable {};
|
||||
class Eatable {};*/
|
||||
|
||||
struct Item
|
||||
{
|
||||
std::string mnemonic;
|
||||
std::string display_name;
|
||||
std::string tooltip;
|
||||
std::string author;
|
||||
int max_stack;
|
||||
int value;
|
||||
std::vector<std::string> tags;
|
||||
std::vector<std::string> aliases;
|
||||
|
||||
std::optional<Color4> sprite_color;
|
||||
|
||||
protected:
|
||||
private:
|
||||
};
|
||||
|
||||
class EmptyBottle : public Item
|
||||
{
|
||||
REGISTER("EmptyBottle", Item);
|
||||
};
|
||||
|
||||
class ItemFilter
|
||||
{
|
||||
@@ -68,6 +102,136 @@ namespace CaveGame::Core
|
||||
std::set<std::string> BannedItems;
|
||||
};
|
||||
|
||||
namespace StatusEffects {
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
namespace Items {
|
||||
|
||||
static const Item Straw;
|
||||
|
||||
//static const Item Gel {"gel", "Gel", };
|
||||
|
||||
static const Item Glowstick;
|
||||
//static const Item RoastChicken {"roast-chicken", "Roast Chicken"};
|
||||
static const Item PumpkinPie;
|
||||
static const Item ShepherdsPie;
|
||||
|
||||
static const Item CopperBar;
|
||||
static const Item IronBar;
|
||||
static const Item SilverBar;
|
||||
static const Item TungstenBar;
|
||||
static const Item PlatinumBar;
|
||||
static const Item GoldBar;
|
||||
static const Item LeadBar;
|
||||
|
||||
static const Item ZiggyStardust;
|
||||
static const Item Nimdoc;
|
||||
static const Item OccupiedDwellinator;
|
||||
static const Item SawnoffShotty;
|
||||
|
||||
#pragma region Tools
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Weapons
|
||||
static const Item GutsyBlade;
|
||||
#pragma region Daggers
|
||||
static const Item CopperDagger;
|
||||
static const Item IronDagger;
|
||||
static const Item TinDagger;
|
||||
static const Item SilverDagger;
|
||||
#pragma endregion
|
||||
#pragma endregion
|
||||
|
||||
#pragma region SciFi
|
||||
static const Item Shrinkray;
|
||||
static const Item PortalGun;
|
||||
static const Item GravityGun;
|
||||
static const Item TauCannon;
|
||||
static const Item PhotonSabre;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Armor
|
||||
|
||||
#pragma endregion
|
||||
#pragma region Accessories
|
||||
static const Item KittenEars;
|
||||
static const Item WolfEars;
|
||||
#pragma region
|
||||
#pragma region Accessories
|
||||
|
||||
#pragma endregion
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Foodstuff
|
||||
|
||||
static const Item Wheat;
|
||||
static const Item Rice;
|
||||
|
||||
#pragma region Fruit
|
||||
static const Item Apple;
|
||||
static const Item Orange;
|
||||
static const Item Lemon;
|
||||
static const Item Carrot;
|
||||
static const Item Potato;
|
||||
static const Item Onion;
|
||||
static const Item Banana;
|
||||
static const Item MelonSlice;
|
||||
static const Item Tomato;
|
||||
#pragma endregion
|
||||
#pragma region Vegetables
|
||||
|
||||
#pragma endregion
|
||||
#pragma region Cooked Dishes
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Soups
|
||||
|
||||
#pragma region Drinks
|
||||
static const Item HotCocoa;
|
||||
static const Item AppleCider;
|
||||
static const Item MelonJuice;
|
||||
#pragma endregion
|
||||
#pragma region Potions
|
||||
static const Item FeatherfallPotion;
|
||||
static const Item HP100Potion;
|
||||
static const Item HP250Potion;
|
||||
static const Item HP500Potion;
|
||||
static const Item HP2XPotion;
|
||||
static const Item HP5XPotion;
|
||||
static const Item HP10XPotion;
|
||||
|
||||
#pragma endregion
|
||||
|
||||
|
||||
#pragma region Bottles
|
||||
static const Item EmptyBottle;
|
||||
static const Item WaterBottle;
|
||||
static const Item HoneyBottle;
|
||||
static const Item IceBottle;
|
||||
static const Item OilBottle;
|
||||
static const Item MilkBottle;
|
||||
static const Item SludgeBottle;
|
||||
static const Item EctoplasmBottle;
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Buckets
|
||||
static const Item EmptyBucket;
|
||||
static const Item WaterBucket;
|
||||
static const Item IceBucket;
|
||||
static const Item LavaBucket;
|
||||
static const Item MilkBucket;
|
||||
static const Item OilBucket;
|
||||
static const Item SludgeBucket;
|
||||
static const Item EctoplasmBucket;
|
||||
#pragma endregion
|
||||
|
||||
}*/
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
45
Core/include/Core/ItemRegistry.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <Core/Singleton.hpp>
|
||||
#include <Core/Item.hpp>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
|
||||
namespace CaveGame::Core {
|
||||
|
||||
/// This object contains the list of items in the game, which is pulled from json files at runtime.
|
||||
class ItemRegistry : public Singleton<ItemRegistry> {
|
||||
public:
|
||||
|
||||
/// Adds an item into the game's list, using the mnemonic ID as a key for the lookup table.
|
||||
/// @see Item.
|
||||
void Register(const Item& data);
|
||||
|
||||
/// @return True if an item with the given mnemonic string-id, or alias, exists in the item list.
|
||||
bool Exists(const std::string& name);
|
||||
|
||||
/// @return The Item associated with the given mnemonic string-id, or alias.
|
||||
/// @throws std::runtime_error if no item with the given mnemonic is found.
|
||||
const Item& Get(const std::string& name);
|
||||
|
||||
/// @return The full list of items added to the game.
|
||||
std::unordered_map<std::string, Item> GetItemMap();
|
||||
|
||||
/// @see ItemRegistry::Get()
|
||||
const Item& operator[](const std::string& name);
|
||||
protected:
|
||||
std::unordered_map<std::string, Item> registered_items;
|
||||
};
|
||||
|
||||
/// @return the global item registry singleton.
|
||||
static ItemRegistry& Items() { return ItemRegistry::Instance();}
|
||||
|
||||
/// Reads, parses, and registers items from a given JSON file path.
|
||||
bool LoadItemMetadata(const std::filesystem::path& path);
|
||||
|
||||
/// Reads, parses, and registers items from a pre-set list of JSON files.
|
||||
bool LoadItemMetadata();
|
||||
|
||||
}
|
20
Core/include/Core/Itemstack.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <Core/Item.hpp>
|
||||
#include <Core/PhysicsEntity.hpp>
|
||||
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
struct ItemStack {
|
||||
Core::Item* type;
|
||||
uint16_t stack;
|
||||
};
|
||||
|
||||
class ItemStackEntity : public PhysicsEntity, public ItemStack {
|
||||
public:
|
||||
protected:
|
||||
private:
|
||||
};
|
||||
}
|
23
Core/include/Core/JsonConversions.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <Color4.hpp>
|
||||
#include <Colors.hpp>
|
||||
#include <JJX/JSON.hpp>
|
||||
#include <map>
|
||||
|
||||
namespace JsonConversions {
|
||||
using namespace JJX;
|
||||
|
||||
/// Parses an RGBA color structure from a json value, which may be one of the following:
|
||||
/// \n * An array of 3 to 4 integers.
|
||||
/// \n * A hexadecimal color-code string.
|
||||
/// \n * A JSON object with 'r', 'g', 'b', and optional 'a' fields.
|
||||
/// @return A Color4 structure.
|
||||
Color4 parse_color(const json::value &v);
|
||||
|
||||
|
||||
/// Parses a vector of strings from a json value, which may one string, or a JSON array of strings.
|
||||
/// @return A vector containing the parsed strings, or an empty vector if the format is invalid.
|
||||
std::vector<std::string> parse_string_list(const json::value& v);
|
||||
|
||||
}
|
@@ -1,23 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include <jlog/Logger.hpp>
|
||||
#include <Event.h>
|
||||
|
||||
namespace CaveGame::Logs
|
||||
{
|
||||
|
||||
using namespace jlog;
|
||||
using Logger = GenericLogger;
|
||||
|
||||
extern Logger Info;
|
||||
extern Logger Debug;
|
||||
extern Logger Warning;
|
||||
extern Logger Error;
|
||||
extern Logger Fatal;
|
||||
std::string format_timestamp(Timestamp ts);
|
||||
|
||||
extern Logger Client;
|
||||
extern Logger Server;
|
||||
extern Logger Generator;
|
||||
extern Logger Lighting;
|
||||
extern Logger Network;
|
||||
std::string format_source_location(const std::source_location & location);
|
||||
|
||||
class CaveGameLogger : public GenericLogger {
|
||||
public:
|
||||
Event<std::string, Color4> OnLog;
|
||||
|
||||
CaveGameLogger(const std::string &context,
|
||||
std::ofstream &file,
|
||||
Color4 contextColor = Colors::White,
|
||||
Color4 timestampColor = Colors::Purples::Fuchsia,
|
||||
Color4 locationColor = Colors::Pinks::Pink,
|
||||
Color4 pointerColor = Colors::Pinks::LightPink,
|
||||
Color4 messageColor = Colors::Greens::LightGreen);
|
||||
|
||||
void Log(const std::string &message, const std::source_location &location = std::source_location::current(), const jlog::Timestamp &ts = Timestamp()) override;
|
||||
};
|
||||
|
||||
|
||||
extern CaveGameLogger Info;
|
||||
extern CaveGameLogger Debug;
|
||||
extern CaveGameLogger Warning;
|
||||
extern CaveGameLogger Error;
|
||||
extern CaveGameLogger Fatal;
|
||||
|
||||
extern CaveGameLogger Client;
|
||||
extern CaveGameLogger Server;
|
||||
extern CaveGameLogger Generator;
|
||||
extern CaveGameLogger Lighting;
|
||||
|
||||
void SetAllEnableConsole(bool log_to_console);
|
||||
void SetAllEnableFile(bool log_to_file);
|
||||
void EnableAllToConsole();
|
||||
void EnableAllToFile();
|
||||
void DisableAllToConsole();
|
||||
void DisableAllToFile();
|
||||
void EnableAll();
|
||||
void DisableAll();
|
||||
void SetIncludeSourceLocationAll(bool inc_location);
|
||||
void SetIncludeTimestampAll(bool inc_timestamp);
|
||||
void EnableSourceLocationAll();
|
||||
void DisableSourceLocationAll();
|
||||
void EnableTimestampAll();
|
||||
void DisableTimestampAll();
|
||||
|
||||
}
|
@@ -1,8 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <numeric>
|
||||
#include <filesystem>
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
std::vector<std::string> string_split(const std::string& s, char delim);
|
||||
|
||||
std::string string_build(const std::vector<std::string> &list, const std::string& delim = " ");
|
||||
|
||||
}
|
||||
std::string read_file(const std::filesystem::path& file_path);
|
@@ -10,7 +10,6 @@ namespace CaveGame::Core
|
||||
OctaveNoise(int seed) : perlin(seed)
|
||||
{ }
|
||||
|
||||
|
||||
double Noise(int octaves, double x, double y, double z)
|
||||
{
|
||||
double scale = 1.f;
|
||||
|
57
Core/include/Core/PhysicsEntity.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
/// CaveGame - A procedural 2D platformer sandbox.
|
||||
/// Created by Josh O'Leary @ Redacted Software, 2020-2025
|
||||
/// Contact: josh@redacted.cc
|
||||
/// Contributors: william@redacted.cc maxi@redacted.cc
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file PhysicsEntity.hpp
|
||||
/// @desc Abstract class of a physically-simulated game object.
|
||||
/// @edit 1/28/2025
|
||||
/// @auth Josh O'Leary
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Core/Entity.hpp>
|
||||
#include <Core/TileRegistry.hpp>
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
class PhysicsEntity : public Entity {
|
||||
public:
|
||||
constexpr static const float gravity = 9.81f;
|
||||
constexpr static const float air_resistance = 2.45f;
|
||||
|
||||
|
||||
bool freeze_in_void = true;
|
||||
explicit PhysicsEntity(const Vector2& spawnPoint);
|
||||
|
||||
[[nodiscard]] Vector2 Velocity() const;
|
||||
|
||||
[[nodiscard]] Vector2 EstimatedNextPosition() const;
|
||||
|
||||
void Accelerate(const Vector2& vector);
|
||||
|
||||
void PhysicsUpdate(float elapsed) override;
|
||||
|
||||
// TODO: Mechanism for figuring out if you're already stuck inside of tiles: i.e. sand
|
||||
void CollisionTest(ITileMap* map, float elapsed) override;
|
||||
|
||||
void Update(float elapsed) override;
|
||||
|
||||
virtual inline float Mass() { return 40;}
|
||||
|
||||
protected:
|
||||
Vector2 velocity;
|
||||
Vector2 next_position;
|
||||
int coll_tests;
|
||||
int coll_hits;
|
||||
Vector2 last_normal;
|
||||
private:
|
||||
void ApplyHorizontalFriction(float elapsed);
|
||||
|
||||
void ApplyGravityForce(float elapsed);
|
||||
|
||||
void ApplyAirResistance(float coefficient, float elapsed);
|
||||
};
|
||||
|
||||
}
|
64
Core/include/Core/Player.hpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include "Humanoid.hpp"
|
||||
|
||||
namespace CaveGame::Core {
|
||||
|
||||
class Player : public Humanoid {
|
||||
public:
|
||||
|
||||
const AABB2D Frame_Idle {{0, 0}, {16, 24}};
|
||||
const AABB2D Frame_Walk1 {{16, 0}, {16, 24}};
|
||||
const AABB2D Frame_Walk2 {{32, 0}, {16, 24}};
|
||||
const AABB2D Frame_Walk3 {{48, 0}, {16, 24}};
|
||||
const AABB2D Frame_Land {{64, 0}, {16, 24}};
|
||||
const AABB2D Frame_Ascend {{80, 0}, {16, 24}};
|
||||
const AABB2D Frame_Descend {{96, 0}, {16, 24}};
|
||||
const AABB2D Frame_Gore1 {{112, 0}, {16, 24}};
|
||||
const AABB2D Frame_Gore2 {{128, 0}, {16, 24}};
|
||||
const AABB2D Frame_Push1 {{144, 0}, {16, 24}};
|
||||
const AABB2D Frame_Push2 {{160, 0}, {16, 24}};
|
||||
|
||||
explicit Player(const Vector2& spawnPoint);
|
||||
void Draw() override;
|
||||
void Update(float elapsed) override;
|
||||
void PhysicsUpdate(float elapsed) override;
|
||||
void Jump(float elapsed) {
|
||||
// TODO: Make it so the player falls **slightly** slower
|
||||
|
||||
Vector2 current_velocity = this->Velocity();
|
||||
|
||||
float horiz = 0;
|
||||
if (current_velocity.x > 10.f)
|
||||
horiz = Math::Clamp(current_velocity.x, 10.f, 300.f);
|
||||
|
||||
if (current_velocity.x < -10.f)
|
||||
horiz = Math::Clamp(current_velocity.x, -300.f, 10.f);
|
||||
|
||||
if (on_ground) {
|
||||
Vector2 projection = Vector2(horiz*elapsed, -16000*elapsed);
|
||||
|
||||
Accelerate(projection);
|
||||
on_ground = false;
|
||||
} else
|
||||
Accelerate({0, -100*elapsed});
|
||||
}
|
||||
void Climb();
|
||||
void Descend();
|
||||
void Crouch();
|
||||
void WalkLeft(float elapsed) {
|
||||
Accelerate({-200*elapsed, 0});
|
||||
facing_left = true;
|
||||
walking = true;
|
||||
}
|
||||
void WalkRight(float elapsed) {
|
||||
Accelerate({200*elapsed, 0});
|
||||
facing_left = false;
|
||||
walking = true;
|
||||
}
|
||||
protected:
|
||||
// TODO: Duplicated in Explosion.hpp. Refactor into Sprite class?
|
||||
float anim_timer = 0;
|
||||
};
|
||||
|
||||
}
|
14
Core/include/Core/Projectile.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
class Projectile : public PhysicsEntity
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
class Arrow : public Projectile {};
|
||||
class Fireball : public Projectile {};
|
||||
class Icecicle : public Projectile {};
|
||||
}
|
18
Core/include/Core/Quadtree.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
template <typename T, typename GetBox, typename Equal = std::equal_to<T>, typename = float>
|
||||
class Quadtree
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
}
|
63
Core/include/Core/Reflection.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
// This next line assumes C++17; otherwise, replace it with
|
||||
// your own string view implementation
|
||||
#include <string_view>
|
||||
#if __cplusplus >= 202002L
|
||||
# include <source_location>
|
||||
#endif
|
||||
|
||||
namespace Reflection
|
||||
{
|
||||
template <typename T> constexpr std::string_view type_name();
|
||||
|
||||
template <>
|
||||
constexpr std::string_view type_name<void>()
|
||||
{ return "void"; }
|
||||
|
||||
namespace detail {
|
||||
|
||||
using type_name_prober = void;
|
||||
|
||||
template <typename T>
|
||||
constexpr std::string_view wrapped_type_name()
|
||||
{
|
||||
#if __cplusplus >= 202002L
|
||||
return std::source_location::current().function_name();
|
||||
#else
|
||||
# if defined(__clang__) || defined(__GNUC__)
|
||||
return __PRETTY_FUNCTION__;
|
||||
# elif defined(_MSC_VER)
|
||||
return __FUNCSIG__;
|
||||
# else
|
||||
# error "Unsupported compiler"
|
||||
# endif
|
||||
#endif // __cplusplus >= 202002L
|
||||
}
|
||||
|
||||
constexpr std::size_t wrapped_type_name_prefix_length()
|
||||
{
|
||||
return wrapped_type_name<type_name_prober>()
|
||||
.find(type_name<type_name_prober>());
|
||||
}
|
||||
|
||||
constexpr std::size_t wrapped_type_name_suffix_length()
|
||||
{
|
||||
return wrapped_type_name<type_name_prober>().length()
|
||||
- wrapped_type_name_prefix_length()
|
||||
- type_name<type_name_prober>().length();
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T>
|
||||
constexpr std::string_view type_name()
|
||||
{
|
||||
constexpr auto wrapped_name = detail::wrapped_type_name<T>();
|
||||
constexpr auto prefix_length = detail::wrapped_type_name_prefix_length();
|
||||
constexpr auto suffix_length = detail::wrapped_type_name_suffix_length();
|
||||
constexpr auto type_name_length =
|
||||
wrapped_name.length() - prefix_length - suffix_length;
|
||||
return wrapped_name.substr(prefix_length, type_name_length);
|
||||
}
|
||||
}
|
@@ -1,428 +0,0 @@
|
||||
/// Framework for performing registration of object factories.
|
||||
//
|
||||
// The major points of the framework are:
|
||||
// - header-only library
|
||||
// - no need to declare registration ahead on the base class,
|
||||
// which means header need to be included only in files
|
||||
// declaring subclasses that register themselves.
|
||||
// - registration is done via a macro called inside the declaration
|
||||
// of the class, which reduces boilerplate code, and incidentally makes
|
||||
// the registration name available to the class.
|
||||
// - ingle macro supports constructors with arbitrary number of arguments.
|
||||
// - class can be registered for different constructors, making
|
||||
// it easy to implement logic that try to instantiate an object
|
||||
// from different parameters.
|
||||
// - builtin mechanism to override registered classes, making dependency
|
||||
// injection e.g. for tests very easy.
|
||||
//
|
||||
// Basic usage
|
||||
// -----------
|
||||
// Given an interface:
|
||||
//
|
||||
// class Shape {
|
||||
// public:
|
||||
// virtual ~Shape() {}
|
||||
// virtual void Draw() const = 0;
|
||||
// };
|
||||
//
|
||||
// The framework allows to annotate with REGISTER macro any subclass:
|
||||
//
|
||||
// class Circle : public Shape {
|
||||
// REGISTER("Circle", Shape);
|
||||
// public:
|
||||
// void Draw() const override { ... }
|
||||
// };
|
||||
//
|
||||
// The first parameter of the macro can be any string and does not have to
|
||||
// match the name of the class:
|
||||
//
|
||||
// class Rect : public Shape {
|
||||
// REGISTER("Rectangle", Shape);
|
||||
// public:
|
||||
// void Draw() const override { ... }
|
||||
// };
|
||||
//
|
||||
// Note that the annotation is done only inside the derived classes.
|
||||
// Nothing needs to be added to the Shape class, and no declaration
|
||||
// other than the Shape class needs to be done. The annotation can be
|
||||
// put inside the private, protected or public section of the class.
|
||||
// The annotation adds not bytes to the class, whose size is equal to
|
||||
// the class would have without the annotation.
|
||||
//
|
||||
// With the annotation, a Shape instance can be created from a string:
|
||||
//
|
||||
// std::unique_ptr<Shape> shape = Registry<Shape>::New("Rect");
|
||||
// shape->Draw(); // will draw a rectangle!
|
||||
//
|
||||
// The function returns a unique_ptr<> which makes ownership clear and simple.
|
||||
// If no class is registered, it will return a null unique_ptr<>.
|
||||
// The CanNew() predicate can be used to check if New() would succeed without
|
||||
// actually creating an instance.
|
||||
//
|
||||
// Advanced usage
|
||||
// --------------
|
||||
//
|
||||
// The basic usage considered classes with parameter-less constructor.
|
||||
// The REGISTER macro can also be used with classes taking arbitrary
|
||||
// parameters. For example, shapes with injectable parameters
|
||||
// can be registered using REGISTER() macro with extra parameters matching
|
||||
// the constructor signature:
|
||||
//
|
||||
// class Ellipsis : public Shape {
|
||||
// REGISTER("Ellipsis", Shape, const std::string&);
|
||||
// public:
|
||||
// explicit Ellipsis(const std::string& params) { ... }
|
||||
// void Draw() const override { ... }
|
||||
// };
|
||||
//
|
||||
// The class can be instantiated using Registry<> with extra parameters
|
||||
// matching the constructor signature:
|
||||
//
|
||||
// auto shape = Registry<Shape, const string&>::New("Ellipsis");
|
||||
// shape->Draw(); // will draw a circle!
|
||||
//
|
||||
// One very interesting benefit of this approach is that client code can
|
||||
// extend the supported types without editing the base class or
|
||||
// any of the existing registered classes. The code below simply test
|
||||
// if a class can be instantiated with a parameter, and otherwise
|
||||
// tries to instantiate without parameters:
|
||||
//
|
||||
// int main(int argc, char **argv) {
|
||||
// for (int i = 1; i + 1 < argc; i += 2) {
|
||||
// const std::string key = argv[i];
|
||||
// const std::string params = argv[i + 1];
|
||||
// if (Registry<Shape, const std::string &>::CanNew(key, params)) {
|
||||
// Registry<Shape, const std::string &>::New(key, params)->Draw();
|
||||
// } else if (Registry<Shape>::CanNew(key)) {
|
||||
// Registry<Shape>::New(key)->Draw();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Additionally, it is possible to register a class with different constructors
|
||||
// so it works with old client code that only uses Registry<Shape> and
|
||||
// some client code like the one above:
|
||||
//
|
||||
// class Ellipsis : public Shape {
|
||||
// REGISTER("Ellipsis", Shape);
|
||||
// REGISTER("Ellipsis", Shape, const std::string&);
|
||||
// public:
|
||||
// explicit Ellipsis(const std::string& params = "") { ... }
|
||||
// void Draw() const override { ... }
|
||||
// };
|
||||
//
|
||||
// Testing
|
||||
// -------
|
||||
//
|
||||
// When testing client code using registered classes, it may be not desired to
|
||||
// actually instantiate real classes. If the Draw() implementations for example
|
||||
// require a graphic context to be active, calling those functions in an
|
||||
// offscreen automated test will fail. Injectors can be used to temporarily
|
||||
// replace registered classes by arbitrary factories:
|
||||
//
|
||||
// class FakeShape : public Shape {
|
||||
// public:
|
||||
// void Draw() override {}
|
||||
// };
|
||||
//
|
||||
// TEST(Draw, WorkingCase) {
|
||||
// const Registry<Shape>::Injector injectors[] =
|
||||
// Registry<Shape>::Injector("Circle", []{ return new FakeShape});
|
||||
// Registry<Shape>::Injector("Rectangle", []{ return new FakeShape});
|
||||
// Registry<Shape>::Injector("Circle", []{ return new FakeShape});
|
||||
// EXPECT_TRUE(FunctionUsingRegistryForShape());
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// Note that it may be necessary to specify the return type of lambda function
|
||||
// for code to compile as in [] -> Shape* { return .... }
|
||||
//
|
||||
// Injectors can also be used as static global variables to perform
|
||||
// registration of a class *outside* of the class, in replacement for
|
||||
// the REGISTER macro. This is useful to register classes whose code
|
||||
// cannot be edited.
|
||||
//
|
||||
// Injectors can also be used to define global or local name alias, as
|
||||
// illustrated by the example REGISTER_ALIAS macro in this file.
|
||||
//
|
||||
// REGISTER_ALIAS(Shape, "Rectangle", "Rect");
|
||||
//
|
||||
// Goodies
|
||||
// -------
|
||||
//
|
||||
// Classes registered with the REGISTER macro can know the key under
|
||||
// which they are registered. The following code:
|
||||
//
|
||||
// Registry<Vehicle>::GetKeyFor<Circle>()
|
||||
//
|
||||
// will return the string "Circle". This works on object classes,
|
||||
// passed as template parameter to GetKeyFor(), and not on object
|
||||
// instances. So there is no such functionality as:
|
||||
//
|
||||
// std::unique_ptr<Shape> shape = new Circle();
|
||||
// std::cout << Registry<Vehicle>::GetKeyFor(*shape); // Not implemented
|
||||
//
|
||||
// as it would require runtime type identification. It is possible to
|
||||
// extend the framework to support it though, by storing `type_info`
|
||||
// objects in the registry.
|
||||
//
|
||||
// The Registry<> class can also be used to list all keys that are
|
||||
// registered, along with the filename and line number at which the
|
||||
// registration is defined. It does not list though the type associated
|
||||
// to the key. Here again, it is pretty easy to extend the framework
|
||||
// to provide such functionality using type_info objects.
|
||||
//
|
||||
// Even though not necessary, one can define intermediate macros to
|
||||
// reduce boilerplate code even more. For the Shape example above,
|
||||
// one could define:
|
||||
//
|
||||
// #define REGISTER_SHAPE(KEY, ARGS...) REGISTER(#KEY, Shape, ##ARGS)
|
||||
//
|
||||
// with which the Ellipsis class above would simply be written:
|
||||
//
|
||||
// class Ellipsis : public Shape {
|
||||
// REGISTER_SHAPE(Ellipsis);
|
||||
// REGISTER_SHAPE(Ellipsis, const std::string &);
|
||||
//
|
||||
// public:
|
||||
// };
|
||||
//
|
||||
// Limitations
|
||||
// -----------
|
||||
// The code requires a C++11 compliant compiler.
|
||||
//
|
||||
// The code relies on the a GNU preprocessor feature for variadic macros:
|
||||
//
|
||||
// #define MACRO(x, opt...) call(x, ##opt)
|
||||
//
|
||||
// which extends to call(x) when opt is empty and call(x, ...) otherwise.
|
||||
//
|
||||
// None of the static methods of Registry<> can be called from
|
||||
// a global static, as it would result in an initialization order fiasco.
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <typeinfo>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#define REGISTER(KEY, TYPE, ARGS...) REGISTER_AT(__LINE__, KEY, TYPE ##ARGS)
|
||||
|
||||
#define REGISTER_ALIAS(TYPE, NAME, ALIAS) \
|
||||
REGISTER_ALIAS_AT(__LINE__, TYPE, NAME, ALIAS)
|
||||
|
||||
#define REGISTER_ALIAS_AT(LINE, TYPE, NAME, ALIAS) \
|
||||
Registry<TYPE>::Injector CONCAT_TOKENS(_xd_injector, LINE)(ALIAS, []() { \
|
||||
return Registry<TYPE>::New(NAME).release(); \
|
||||
}, __FILE__, STRINGIFY(LINE)); \
|
||||
static_assert(true, "") // enforce ; at EOL
|
||||
|
||||
|
||||
namespace factory {
|
||||
template<typename T, class... Args>
|
||||
class Registry {
|
||||
public:
|
||||
// Return 'true' if there is a class registered for `key` for
|
||||
// a constructor with signature (Args... args).
|
||||
//
|
||||
// This function can not be called from any static initializer
|
||||
// or it creates initializer order fiasco.
|
||||
static bool CanNew(const std::string &key, Args... args) {
|
||||
return GetEntry(key, args...).first;
|
||||
}
|
||||
|
||||
// If there is a class registered for `key` for a constructor
|
||||
// with signature (Args... args), instantiate an object of that class
|
||||
// passing args to the constructor. Returns a null pointer otherwise.
|
||||
//
|
||||
// This function can not be called from any static initializer
|
||||
// or it creates initializer order fiasco.
|
||||
static std::unique_ptr<T> New(const std::string &key, Args... args) {
|
||||
std::unique_ptr<T> result;
|
||||
auto entry = GetEntry(key, args...);
|
||||
if (entry.first) {
|
||||
result.reset(entry.second->function(args...));
|
||||
}
|
||||
registry_mutex_.unlock();
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
// Return the key under which class `C` is registered. The header
|
||||
// defining that class must be included by code calling this
|
||||
// function. If class is not registered, there will be a compile-
|
||||
// time failure.
|
||||
template<typename C>
|
||||
static const char *GetKeyFor() {
|
||||
return C::_xd_key(static_cast<const T *>(nullptr),
|
||||
std::function<void(Args...)>());
|
||||
}
|
||||
|
||||
// Returns the list of keys registered for the registry.
|
||||
// Keys corresponding to injectors (see below) are suffixed with
|
||||
// a star.
|
||||
//
|
||||
// This function can not be called from any static initializer
|
||||
// or it creates initializer order fiasco.
|
||||
static std::vector<std::string> GetKeys() {
|
||||
std::vector<std::string> keys;
|
||||
registry_mutex_.lock();
|
||||
for (const auto &iter: *GetRegistry()) {
|
||||
keys.emplace_back(iter.first);
|
||||
}
|
||||
for (const auto &iter: *GetInjectors()) {
|
||||
keys.emplace_back(iter.first + "*");
|
||||
}
|
||||
registry_mutex_.unlock();
|
||||
return keys;
|
||||
}
|
||||
|
||||
// Like GetKeys() function, but also returns the filename
|
||||
// and line number of the corresponding REGISTER() macros.
|
||||
// For injectors, the filename and line number are those
|
||||
// passed to the injector constructor.
|
||||
static std::vector<std::string> GetKeysWithLocations() {
|
||||
std::vector<std::string> keys;
|
||||
registry_mutex_.lock();
|
||||
for (const auto &iter: *GetRegistry()) {
|
||||
keys.emplace_back(std::string(iter.second.file) + ":" +
|
||||
std::string(iter.second.line) + ": " + iter.first);
|
||||
}
|
||||
for (const auto &iter: *GetInjectors()) {
|
||||
keys.emplace_back(std::string(iter.second.file) + ":" +
|
||||
std::string(iter.second.line) + ": " + iter.first +
|
||||
"*");
|
||||
}
|
||||
registry_mutex_.unlock();
|
||||
return keys;
|
||||
}
|
||||
|
||||
// Helper class which uses RAII to inject a factory which will be used
|
||||
// instead of any class registered with the same key, for any call
|
||||
// within the scope of the variable.
|
||||
// If there are two injectors in the same scope for the same key,
|
||||
// the last one takes precedence and cancels the first one, which will
|
||||
// never be active, even if the second one gets out of scope.
|
||||
struct Injector {
|
||||
const std::string &key;
|
||||
|
||||
Injector(const std::string &key,
|
||||
const std::function<T *(Args...)> &function,
|
||||
const char *file = "undefined", const char *line = "undefined")
|
||||
: key(key) {
|
||||
registry_mutex_.lock();
|
||||
const Entry entry = {file, line, function};
|
||||
GetInjectors()->insert(std::make_pair(key, entry));
|
||||
registry_mutex_.unlock();
|
||||
}
|
||||
|
||||
~Injector() {
|
||||
registry_mutex_.lock();
|
||||
GetInjectors()->erase(key);
|
||||
registry_mutex_.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
// Implementation details that can't be made private because used in macros
|
||||
//***************************************************************************
|
||||
typedef std::function<T *(Args...)> function_t;
|
||||
|
||||
struct Registerer {
|
||||
Registerer(function_t function, const std::string &key, const char *file,
|
||||
const char *line) {
|
||||
const Entry entry = {file, line, function};
|
||||
registry_mutex_.lock();
|
||||
GetRegistry()->insert(std::make_pair(key, entry));
|
||||
registry_mutex_.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
struct Entry {
|
||||
const char *const file;
|
||||
const char *const line;
|
||||
const function_t function;
|
||||
};
|
||||
typedef std::map<std::string, Entry> EntryMap;
|
||||
|
||||
// The registry and injectors are created on demand using static variables
|
||||
// inside a static method so that there is no order initialization fiasco.
|
||||
static EntryMap *GetRegistry() {
|
||||
static EntryMap registry;
|
||||
return ®istry;
|
||||
}
|
||||
|
||||
static EntryMap *GetInjectors() {
|
||||
static EntryMap injectors;
|
||||
return &injectors;
|
||||
};
|
||||
static std::mutex registry_mutex_;
|
||||
|
||||
static std::pair<bool, const Entry *> GetEntry(const std::string &key,
|
||||
Args... args) {
|
||||
registry_mutex_.lock();
|
||||
auto it = GetInjectors()->find(key);
|
||||
bool found = (it != GetInjectors()->end());
|
||||
if (!found) {
|
||||
it = GetRegistry()->find(key);
|
||||
found = (it != GetRegistry()->end());
|
||||
}
|
||||
registry_mutex_.unlock();
|
||||
return std::make_pair(found, &(it->second));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, class... Args>
|
||||
std::mutex Registry<T, Args...>::registry_mutex_;
|
||||
|
||||
//*****************************************************************************
|
||||
// Implementation details of REGISTER() macro.
|
||||
//
|
||||
// Creates uniquely named traits class and functions which forces the
|
||||
// instantiation of a TypeRegisterer class with a static member doing the
|
||||
// actual registration in the registry. Note that TypeRegisterer being a
|
||||
// template, there is no violation of the One Definition Rule. The use of
|
||||
// Trait class is way to pass back information from the class where the
|
||||
// macro is called, to the definition of TypeRegisterer static member.
|
||||
// This works only because the Trait functions do not reference any other
|
||||
// static variable, or it would create an initialization order fiasco.
|
||||
//*****************************************************************************
|
||||
template<typename Trait, typename base_type, typename derived_type,
|
||||
typename... Args>
|
||||
struct TypeRegisterer {
|
||||
static const typename Registry<base_type, Args...>::Registerer instance;
|
||||
};
|
||||
|
||||
template<typename Trait, typename base_type, typename derived_type,
|
||||
typename... Args>
|
||||
const typename Registry<base_type, Args...>::Registerer
|
||||
TypeRegisterer<Trait, base_type, derived_type, Args...>::instance(
|
||||
[](Args... args) { return new derived_type(args...); },
|
||||
Trait::key(), Trait::file(), Trait::line());
|
||||
|
||||
#define CONCAT_TOKENS(x, y) x##y
|
||||
#define STRINGIFY(x) #x
|
||||
|
||||
#define REGISTER_AT(LINE, KEY, TYPE, ARGS...) \
|
||||
friend class ::factory::Registry<TYPE, ##ARGS>; \
|
||||
struct CONCAT_TOKENS(_xd_Trait, LINE) { \
|
||||
static const char *key() { return KEY; } \
|
||||
static const char *file() { return __FILE__; } \
|
||||
static const char *line() { return STRINGIFY(LINE); } \
|
||||
}; \
|
||||
const void *CONCAT_TOKENS(_xd_unused, LINE)() const { \
|
||||
return &::factory::TypeRegisterer<CONCAT_TOKENS(_xd_Trait, LINE), TYPE, \
|
||||
std::decay<decltype(*this)>::type, \
|
||||
##ARGS>::instance; \
|
||||
} \
|
||||
static const char *_xd_key(const TYPE *, std::function<void(ARGS)>) { \
|
||||
return KEY; \
|
||||
} \
|
||||
static_assert(true, "") // enforce ; at EOL
|
||||
|
||||
}
|
12
Core/include/Core/Serializable.hpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include <cinttypes>
|
||||
#include <Core/Serialization.hpp>
|
||||
#pragma once
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
class Serializable {
|
||||
virtual std::size_t SizeInBytes() = 0;
|
||||
virtual ByteBuffer& Serialize(ByteBuffer& b) = 0;
|
||||
virtual ByteBuffer& Deserialize(ByteBuffer& b) = 0;
|
||||
};
|
||||
}
|
94
Core/include/Core/Serialization.hpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
/// Primitive Serialization Functions.
|
||||
|
||||
/// A data container structure which keeps track of the current position.
|
||||
|
||||
struct Buffer {
|
||||
size_t size;
|
||||
size_t index;
|
||||
uint8_t* data;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// Converts a 16-bit unsigned integer into a floating point number. @see float_to_u16.
|
||||
float u16_to_float(uint16_t value);
|
||||
/// Converts a floating point number to a 16-bit unsigned integer. This in effect, drops the accuracy of the float in exchange for compression.
|
||||
uint16_t float_to_u16(float);
|
||||
|
||||
/// Writes a 1-byte unsigned int to the buffer, in network-byte-order, and advances the index by 1.
|
||||
void write_u8(Buffer& buffer, uint8_t value);
|
||||
/// Writes a 2-byte unsigned int to the buffer, in network-byte-order, and advances the index by 2.
|
||||
void write_u16(Buffer& buffer, uint16_t value);
|
||||
/// Writes a 4-byte unsigned int to the buffer, in network-byte-order, and advances the index by 4.
|
||||
void write_u32(Buffer& buffer, uint32_t value);
|
||||
/// Writes a 8-byte unsigned int to the buffer, in network-byte-order, and advances the index by 8.
|
||||
void write_u64(Buffer& buffer, uint64_t value);
|
||||
|
||||
/// Reads a 1-byte unsigned int from the buffer, from network-byte-order, and advances the index by 1.
|
||||
uint8_t read_u8(Buffer& buffer);
|
||||
/// Reads a 2-byte unsigned int from the buffer, from network-byte-order, and advances the index by 2.
|
||||
uint16_t read_u16(Buffer& buffer);
|
||||
/// Reads a 4-byte unsigned int from the buffer, from network-byte-order, and advances the index by 4.
|
||||
uint32_t read_u32(Buffer& buffer);
|
||||
/// Reads a 8-byte unsigned int from the buffer, from network-byte-order, and advances the index by 8.
|
||||
uint64_t read_u64(Buffer& buffer);
|
||||
|
||||
/// Writes a 1-byte signed int to the buffer, in network-byte-order, and advances the index by 1.
|
||||
void write_s8(Buffer& buffer, int8_t value);
|
||||
/// Writes a 2-byte signed int to the buffer, in network-byte-order, and advances the index by 2.
|
||||
void write_s16(Buffer& buffer, int16_t value);
|
||||
/// Writes a 4-byte signed int to the buffer, in network-byte-order, and advances the index by 4.
|
||||
void write_s32(Buffer& buffer, int32_t value);
|
||||
/// Writes a 8-byte signed int to the buffer, in network-byte-order, and advances the index by 8.
|
||||
void write_s64(Buffer& buffer, int64_t value);
|
||||
|
||||
/// Reads a 1-byte signed int from the buffer, from network-byte-order, and advances the index by 1.
|
||||
int8_t read_s8(Buffer& buffer);
|
||||
/// Reads a 2-byte signed int from the buffer, from network-byte-order, and advances the index by 2.
|
||||
int16_t read_s16(Buffer& buffer);
|
||||
/// Reads a 4-byte signed int from the buffer, from network-byte-order, and advances the index by 4.
|
||||
int32_t read_s32(Buffer& buffer);
|
||||
/// Reads a 8-byte signed int from the buffer, from network-byte-order, and advances the index by 8.
|
||||
int64_t read_s64(Buffer& buffer);
|
||||
|
||||
/// Writes a half-precision float to the buffer, via reinterpreting as uint16_t, and advances the index by 2.
|
||||
void write_f16(Buffer& buffer, float value);
|
||||
/// Writes a 4-byte float to the buffer, via reinterpreting as uint32_t, and advances the index by 4.
|
||||
void write_f32(Buffer& buffer, float value);
|
||||
/// Writes a 8-byte float to the buffer, via reinterpreting as uint64_t, and advances the index by 8.
|
||||
void write_f64(Buffer& buffer, double value);
|
||||
|
||||
/// Reads a half-precision float from the buffer, via reinterpreting from uint16_t, and advances the index by 2.
|
||||
float read_f16(Buffer& buffer);
|
||||
/// Reads a 4-byte float from the buffer, via reinterpreting from uint32_t, and advances the index by 4.
|
||||
float read_f32(Buffer& buffer);
|
||||
/// Reads a 8-byte float from the buffer, via reinterpreting from uint64_t, and advances the index by 8.
|
||||
double read_f64(Buffer& buffer);
|
||||
|
||||
void write_string(Buffer& buffer, const char* value, unsigned int length);
|
||||
|
||||
void write_string(Buffer& buffer, std::string value);
|
||||
|
||||
std::string read_string(Buffer& buffer);
|
||||
|
||||
|
||||
class ByteBuffer
|
||||
{
|
||||
Buffer buff;
|
||||
/// Write a uint8_t.
|
||||
ByteBuffer& operator<<(uint8_t& val);
|
||||
/// Write a uint16_t.
|
||||
ByteBuffer& operator<<(uint16_t& val);
|
||||
/// Write a uint32_t.
|
||||
ByteBuffer& operator<<(uint32_t& val);
|
||||
};
|
||||
|
||||
}
|
@@ -71,16 +71,20 @@ namespace CaveGame::Core
|
||||
|
||||
static LineSegTestResult LineSegment2Dvs(LineSegment2D s1, LineSegment2D s2);
|
||||
|
||||
|
||||
bool AABB2Dvs(const Vector2& posA, const Vector2& sizeA, const Vector2& posB, const Vector2& sizeB);
|
||||
|
||||
bool AABB2Dvs(const AABB2D& a, const AABB2D& b);
|
||||
|
||||
bool AABB2DvsPoint(const Vector2& pos, const Vector2& size, const Vector2& point);
|
||||
|
||||
static Vector2 SolveAABB(const Vector2& posA, const Vector2& sizeA, const Vector2& posB, const Vector2& sizeB);
|
||||
bool AABB2DvsPoint(const AABB2D& box, const Vector2& point);
|
||||
|
||||
static Vector2 GetNormalForAABB(const Vector2& separation, const Vector2& velocity);
|
||||
Vector2 SolveAABBvsPoint(const Vector2& pos, const Vector2& size, const Vector2& point);
|
||||
|
||||
static Vector2 SolveAABB(const AABB2D& a, const AABB2D& b);
|
||||
Vector2 SolveAABB(const Vector2& posA, const Vector2& sizeA, const Vector2& posB, const Vector2& sizeB);
|
||||
|
||||
Vector2 GetNormalForAABB(const Vector2& separation, const Vector2& velocity);
|
||||
|
||||
Vector2 SolveAABB(const AABB2D& a, const AABB2D& b);
|
||||
}
|
||||
}
|
39
Core/include/Core/Singleton.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
/// CaveGame - A procedural 2D platformer sandbox.
|
||||
/// Created by Josh O'Leary @ Redacted Software, 2020-2024
|
||||
/// Contact: josh@redacted.cc
|
||||
/// Contributors: william@redacted.cc maxi@redacted.cc
|
||||
/// This work is dedicated to the public domain.
|
||||
|
||||
/// @file Singleton.hpp
|
||||
/// @desc An abstract class template that creates a Singleton.
|
||||
/// @edit 3/20/2025
|
||||
/// @auth Josh O'Leary
|
||||
|
||||
#pragma once
|
||||
|
||||
/// Template class for creating a Meyers' Singleton.
|
||||
/// One and only one instance of this class can exist, and is alive throughout the lifetime of the program.
|
||||
/// https://vlsiuniverse.blogspot.com/2016/04/meyers-singleton-pattern.html
|
||||
/// The instance is lazy-initialized, meaning that:
|
||||
/// if it is not a compile-time constant, and does not have a constructor,
|
||||
/// it is only created upon the first call to Instance().
|
||||
/// As per C++11, this is considered thread-safe out of the box.
|
||||
template <class T>
|
||||
class Singleton
|
||||
{
|
||||
public:
|
||||
static T& Instance()
|
||||
{
|
||||
// Since it's a static scoped variable, if the class has already been created, it won't be created again.
|
||||
// And it **is** thread safe (C++11 or later),
|
||||
static T instance;
|
||||
return instance;
|
||||
}
|
||||
protected:
|
||||
Singleton() = default;
|
||||
virtual ~Singleton() = default;
|
||||
Singleton(const Singleton&) = delete; // Copy constructor
|
||||
Singleton(Singleton&&) = delete; // Move constructor
|
||||
Singleton& operator=(const Singleton&) = delete; // Copy assign
|
||||
Singleton& operator=(Singleton&&) = delete; // Move assign
|
||||
};
|
59
Core/include/Core/Sprite.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
#include <J3ML/LinearAlgebra/Vector2.hpp>
|
||||
#include <JGL/JGL.h>
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
// TODO: Rect2D class that is distinct from AABB2D
|
||||
|
||||
struct Frame {
|
||||
Vector2 offset;
|
||||
Vector2 size;
|
||||
Vector2 origin;
|
||||
|
||||
//AABB2D quad;
|
||||
};
|
||||
|
||||
struct Rect2D {
|
||||
Vector2 position;
|
||||
Vector2 size;
|
||||
};
|
||||
|
||||
struct Keyframe {
|
||||
Vector2 transform_scale;
|
||||
Vector2 transform_offset;
|
||||
|
||||
|
||||
Frame frame;
|
||||
float time;
|
||||
};
|
||||
|
||||
struct Animation {
|
||||
std::vector<Keyframe> frames;
|
||||
};
|
||||
|
||||
|
||||
class Sprite {
|
||||
public:
|
||||
/// The default constructor does not initialize any members.
|
||||
Sprite() { }
|
||||
|
||||
/// Constructs a not-animated sprite from a source texture, which assumes.
|
||||
Sprite(JGL::Texture* source);
|
||||
Sprite(JGL::Texture* source, int width, int height);
|
||||
Sprite(JGL::Texture* source, Vector2i size);
|
||||
Sprite(JGL::Texture* source, int offset_x, int offset_y, int width, int height);
|
||||
Sprite(JGL::Texture* source, Vector2i offset, Vector2i size);
|
||||
void AddFrame(const std::string& frame_id, const Frame& frame);
|
||||
void SetCurrentFrame();
|
||||
void DrawFrame();
|
||||
void DrawCurrentFrame();
|
||||
|
||||
//void PlayAnimation(const KeyframeAnimation& anim);
|
||||
protected:
|
||||
JGL::Texture* source;
|
||||
std::vector<Frame> frames;
|
||||
std::vector<Animation> animations;
|
||||
private:
|
||||
};
|
||||
}
|
33
Core/include/Core/StatusEffects.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
class StatusEffect {};
|
||||
|
||||
|
||||
namespace Effects {
|
||||
static const StatusEffect Comfy;
|
||||
static const StatusEffect Hungry;
|
||||
static const StatusEffect Thirsty;
|
||||
static const StatusEffect Starving;
|
||||
static const StatusEffect Dehydrated;
|
||||
static const StatusEffect WellFed;
|
||||
static const StatusEffect Bleeding;
|
||||
static const StatusEffect Healing;
|
||||
static const StatusEffect Poisoned;
|
||||
static const StatusEffect Irradiated;
|
||||
static const StatusEffect Glowing;
|
||||
static const StatusEffect Burning;
|
||||
static const StatusEffect Frozen;
|
||||
static const StatusEffect Hot;
|
||||
static const StatusEffect Cold;
|
||||
static const StatusEffect Frostbite;
|
||||
static const StatusEffect HeatStroke;
|
||||
static const StatusEffect BrokenBone;
|
||||
static const StatusEffect Dizzy;
|
||||
static const StatusEffect Drunk;
|
||||
static const StatusEffect Stoned;
|
||||
static const StatusEffect Blind;
|
||||
};
|
||||
|
||||
}
|
38
Core/include/Core/Structures.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
|
||||
/// A structure is a multi-tile object that is usually generated into the terrain.
|
||||
// TODO: Structure Designer
|
||||
class Structure {};
|
||||
|
||||
|
||||
#define STRUCTURE static const Structure
|
||||
|
||||
/// List of structures in-game.
|
||||
namespace Structures
|
||||
{
|
||||
STRUCTURE TreeGrove;
|
||||
STRUCTURE LargeTree;
|
||||
STRUCTURE MegaTree;
|
||||
STRUCTURE Meadow;
|
||||
|
||||
STRUCTURE Pyramid;
|
||||
STRUCTURE SkyIsland;
|
||||
STRUCTURE UndergroundCabin;
|
||||
STRUCTURE RuinedHouse;
|
||||
STRUCTURE Shrine;
|
||||
STRUCTURE Castle;
|
||||
STRUCTURE SandCastle;
|
||||
STRUCTURE IceFortress;
|
||||
STRUCTURE Campsite;
|
||||
STRUCTURE Settlement;
|
||||
STRUCTURE Village;
|
||||
STRUCTURE Mineshaft;
|
||||
STRUCTURE SatanicTemple;
|
||||
STRUCTURE LavaTube;
|
||||
STRUCTURE
|
||||
};
|
||||
|
||||
}
|
@@ -4,15 +4,73 @@
|
||||
#include <Color4.hpp>
|
||||
#include <Colors.hpp>
|
||||
#include <Core/Data.hpp>
|
||||
#include <Core/Registry.hpp>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <Core/Interfaces.hpp>
|
||||
#include <J3ML/LinearAlgebra/Vector2i.hpp>
|
||||
#include <J3ML/J3ML.hpp>
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
|
||||
namespace CaveGame::Core {
|
||||
using J3ML::LinearAlgebra::Vector2i;
|
||||
using TileID = u16;
|
||||
|
||||
struct Tile;
|
||||
|
||||
using ColorPallet = std::vector<Color4>;
|
||||
using TileTiccFunc = std::function<void(const Tile& data, ITileMap* world, int x, int y)>;
|
||||
|
||||
void SandGravTiccFunc(const Tile& data, ITileMap* world, int x, int y);
|
||||
void LiquidSettleTiccFunc(const Tile& data, ITileMap* world, int x, int y);
|
||||
void GrassRandomTiccFunc(const Tile& data, ITileMap* world, int x, int y);
|
||||
void GrassForcedTiccFunc(const Tile& data, ITileMap* world, int x, int y);
|
||||
void MossRandomTiccFunc(const Tile& data, ITileMap* world, int x, int y);
|
||||
void MossForcedTiccFunc(const Tile& data, ITileMap* world, int x, int y);
|
||||
void VineRandomTiccFunc(const Tile& data, ITileMap* world, int x, int y);
|
||||
void VineForcedTiccFunc(const Tile& data, ITileMap* world, int x, int y);
|
||||
|
||||
|
||||
static std::map<std::string, TileTiccFunc> ticc_funcs;
|
||||
|
||||
// TODO: Tile light filling algorithm.
|
||||
// TODO: Ambient light from surface / certain biomes.
|
||||
// TODO: Directional lights.
|
||||
// TODO: Entity lights.
|
||||
// TODO: Tinting when interact with certain tiles.
|
||||
// TODO: Research recursive flood-fill algorithm.
|
||||
|
||||
struct light {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
uint8_t b;
|
||||
uint8_t intensity;
|
||||
|
||||
};
|
||||
|
||||
// TODO: Implement params for tile color interaction
|
||||
// * Opacity
|
||||
// * Illumination
|
||||
// * What if we want to make tiles reactive, glow under UV, absorb light energy?
|
||||
|
||||
struct Tile {
|
||||
std::string mnemonic_id;
|
||||
std::string display_name;
|
||||
std::optional<TileID> assigned_numeric_id;
|
||||
TileID numeric_id;
|
||||
std::optional<ColorPallet> pallet;
|
||||
bool solid = true;
|
||||
bool collides = false;
|
||||
bool does_random_ticc = false;
|
||||
bool does_forced_ticc = false;
|
||||
Color4 color;
|
||||
TileTiccFunc forced_ticc_func;
|
||||
TileTiccFunc random_ticc_func;
|
||||
std::vector<std::string> tags;
|
||||
bool gravity;
|
||||
bool opaque = true;
|
||||
std::optional<Color3> illumination;
|
||||
|
||||
};
|
||||
/*
|
||||
*
|
||||
* Tile and TileTrait example structure
|
||||
@@ -86,108 +144,332 @@ namespace CaveGame::Core
|
||||
};
|
||||
*/
|
||||
|
||||
enum TileFlags : uint16_t
|
||||
{
|
||||
SOLID,
|
||||
NONSOLID = !SOLID,
|
||||
|
||||
};
|
||||
|
||||
// Use this for space-efficiency and convert to Color3 for doing color manipulations.
|
||||
// Struct allocation is cheaper.
|
||||
struct Light3
|
||||
{
|
||||
struct Light3 {
|
||||
uint8_t r;
|
||||
uint8_t b;
|
||||
uint8_t g;
|
||||
};
|
||||
/*std::unordered_map<std::string, TileTiccFunc> tile_functions {
|
||||
{"check-spread", [](const Tile& data, ITileMap* world, int x, int y){
|
||||
|
||||
}},
|
||||
{"check-decay", [](const Tile& data, ITileMap* world, int x, int y){}},
|
||||
{"check-decay-spread", [](const Tile& data, ITileMap* world, int x, int y){}},
|
||||
{"sand-ticc", [](const Tile& data, ITileMap* world, int x, int y){}},
|
||||
{"water-ticc", [](const Tile& data, ITileMap* world, int x, int y){}},
|
||||
{"liquid-ticc", [](const Tile& data, ITileMap* world, int x, int y)
|
||||
{
|
||||
|
||||
}}
|
||||
};*/
|
||||
|
||||
|
||||
class TileGroup {};
|
||||
|
||||
class Tile
|
||||
class OnCondition
|
||||
{
|
||||
public:
|
||||
OnCondition();
|
||||
virtual bool Condition(ITileMap* world, int x, int y) { return true; }
|
||||
virtual void Perform(ITileMap* world, int x, int y) {}
|
||||
virtual void Invoke(ITileMap* world, int x, int y) {
|
||||
if (Condition(world, x, y))
|
||||
Perform(world, x, y);
|
||||
}
|
||||
};
|
||||
|
||||
/// Tiles are instantiated as static members in the Tiles namespace.
|
||||
/// Chunks store tiles by their numeric ID, which can be used to retrieve the tile
|
||||
/*class Tile
|
||||
{
|
||||
protected:
|
||||
TileID numeric_id = TileID::VOID;
|
||||
std::string mnemonic_id;
|
||||
std::string display_name = "";
|
||||
public:
|
||||
Color4 base_color;
|
||||
std::vector<Color4> color_pallet;
|
||||
bool collides;
|
||||
bool does_random_ticc;
|
||||
bool has_color_pallet = false;
|
||||
bool solid = true;
|
||||
public:
|
||||
|
||||
Tile(TileID numeric, const std::string& name, Color4 color)
|
||||
: numeric_id(numeric), mnemonic_id(name), base_color(color)
|
||||
{}
|
||||
virtual TileID NumericID() const { return numeric_id; };
|
||||
virtual std::string MnemonicID() const { return mnemonic_id; };
|
||||
bool DoesRandomTicc() const { return does_random_ticc; }
|
||||
|
||||
|
||||
virtual Color4 ComputeColor(TileState state, int x, int y) {}
|
||||
virtual void ForcedTicc(ITileMap* world, TileState state, int x, int y) {}
|
||||
virtual void RandomTicc(ITileMap* world, TileState state, int x, int y) {}
|
||||
|
||||
|
||||
template <TileID TDecaysTo>
|
||||
void DecayTo(ITileMap *world, TileState state, int x, int y)
|
||||
Tile();
|
||||
Tile(TileID id, const std::string& name, const Color4& color);
|
||||
Tile(TileID id, const std::string& name, const Color4& color, const std::vector<Color4>& pallet);
|
||||
virtual void Draw(int wx, int wy, int tx, int ty)
|
||||
{
|
||||
world->SetTile(x, y, TDecaysTo);
|
||||
// TODO: Calling JGL requires being in the client code.
|
||||
// Maybe have Client/Tile.cpp implementation?
|
||||
//JGL::J2D::DrawPoint()
|
||||
}
|
||||
|
||||
void DecayTo(ITileMap *world, TileState state, int x, int y, TileID TDecaysTo)
|
||||
{
|
||||
world->SetTile(x, y, TDecaysTo);
|
||||
[[nodiscard]] TileID NumericID() const;
|
||||
[[nodiscard]] std::string MnemonicID() const;
|
||||
std::string Name() const;
|
||||
virtual bool DoesRandomTicc() const { return false; }
|
||||
virtual bool DoesForcedTicc() const { return false;}
|
||||
virtual bool Solid() const { return false;}
|
||||
virtual void ForcedTicc(ITileMap* world, TileState state, int x, int y) {}
|
||||
virtual void RandomTicc(ITileMap* world, TileState state, int x, int y) {}
|
||||
virtual void DecayTo(ITileMap *world, TileState state, int x, int y, TileID TDecaysTo);
|
||||
virtual bool ShouldSpread(ITileMap* world, int x, int y, TileID spreads_to) const;
|
||||
virtual bool ShouldSuffocate(ITileMap* world, int x, int y) const;
|
||||
virtual bool DecayCheck(ITileMap* world, TileState state, int x, int y, TileID decays_to);
|
||||
virtual bool SpreadCheck(ITileMap* world, TileState state, int x, int y, TileID spreads_to) { return false;}
|
||||
};*/
|
||||
|
||||
/*class LiquidTile : public Tile
|
||||
{
|
||||
public:
|
||||
LiquidTile();
|
||||
LiquidTile(TileID id, const std::string& name, const Color4& color, const std::vector<Color4>& pallet);
|
||||
LiquidTile(TileID numeric, const std::string& name, Color4 color);
|
||||
bool Solid() const override { return true;}
|
||||
bool DoesForcedTicc() const override { return true;}
|
||||
void ForcedTicc(ITileMap *world, TileState state, int x, int y) override {
|
||||
|
||||
// Supremely schizophrenic water solving which still doesn't 100% work.
|
||||
// Please fix this...
|
||||
|
||||
|
||||
TileID below = world->GetTile(x, y+1);
|
||||
TileID above = world->GetTile(x, y-1);
|
||||
TileID left = world->GetTile(x-1, y);
|
||||
TileID right = world->GetTile(x+1, y);
|
||||
TileID lookaheadLeft = world->GetTile(x-2, y);
|
||||
TileID lookaheadRight = world->GetTile(x+2, y);
|
||||
|
||||
|
||||
bool rng_roll = rand() % 2 == 0;
|
||||
|
||||
if (below == TileID::AIR) {
|
||||
|
||||
if (world->GetTile(x, y+2) == TileID::AIR) {
|
||||
world->SetTile(x, y+2, numeric_id);
|
||||
world->SetTile(x, y, TileID::AIR);
|
||||
return;
|
||||
}
|
||||
|
||||
world->SetTile(x, y+1, numeric_id);
|
||||
world->SetTile(x, y, TileID::AIR);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//if (below == TileID::WATER) {
|
||||
/*
|
||||
if (world->GetTile(rng_roll ? (x + 1) : (x - 1), y) == TileID::AIR)
|
||||
{
|
||||
if (world->GetTile(rng_roll ? (x + 2) : (x - 2), y) == TileID::AIR)
|
||||
{
|
||||
if (world->GetTile(rng_roll ? (x + 3) : (x - 3), y) == TileID::AIR)
|
||||
{
|
||||
world->SwapTile(x, y, rng_roll ? (x + 3) : (x - 3), y);
|
||||
return;
|
||||
}
|
||||
world->SwapTile(x, y, rng_roll ? (x + 2) : (x - 2), y);
|
||||
return;
|
||||
}
|
||||
|
||||
//if (world->GetTile(rng_roll ? (x + 1) : (x - 1), y) == TileID::AIR) {
|
||||
TileID rollright = rng_roll ? right : left;
|
||||
TileID rollleft = !rng_roll ? right : left;
|
||||
if (rollright == TileID::AIR) {
|
||||
world->SwapTile(x, y, rng_roll ? (x + 1) : (x - 1), y);
|
||||
return;
|
||||
//} else if (world->GetTile(!rng_roll ? (x + 1) : (x - 1), y) == TileID::AIR)
|
||||
} else if (rollleft == TileID::AIR)
|
||||
{
|
||||
world->SwapTile(x, y, !rng_roll ? (x + 1) : (x - 1), y);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((lookaheadRight == right) && (right == TileID::AIR)) {
|
||||
world->SetTile(x +1 , y, TileID::WATER);
|
||||
world->SetTile(x +2 , y, TileID::WATER);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((lookaheadLeft == left) && (left == TileID::AIR)){
|
||||
world->SetTile(x -1 , y, TileID::WATER);
|
||||
world->SetTile(x -2 , y, TileID::WATER);
|
||||
return;
|
||||
}
|
||||
|
||||
if (below == TileID::WATER && above == TileID::AIR) {
|
||||
//if (world->GetTile(x + 1, y) == TileID::AIR && world->GetTile(x + 2, y) == TileID::WATER) {
|
||||
if (right == TileID::AIR && lookaheadRight == TileID::WATER) {
|
||||
world->SetTile(x + 1, y, rng_roll ? TileID::WATER : TileID::AIR);
|
||||
return;
|
||||
}
|
||||
|
||||
//if (world->GetTile(x - 1, y) == TileID::AIR && world->GetTile(x - 2, y) == TileID::WATER) {
|
||||
if (left == TileID::AIR && lookaheadLeft == TileID::WATER) {
|
||||
world->SetTile(x - 1, y, rng_roll ? TileID::WATER : TileID::AIR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// }
|
||||
|
||||
/*if (world->GetTile(x-1, y) == TileID::AIR) {
|
||||
if (world->GetTile(x-2, y) == TileID::AIR) {
|
||||
world->SetTile(x-2, y, numeric_id);
|
||||
world->SetTile(x, y, TileID::AIR);
|
||||
return;
|
||||
}
|
||||
world->SetTile(x-1, y, numeric_id);
|
||||
world->SetTile(x, y, TileID::AIR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (world->GetTile(x+1, y) == TileID::AIR) {
|
||||
|
||||
if (world->GetTile(x+2, y) == TileID::AIR) {
|
||||
world->SetTile(x+2, y, numeric_id);
|
||||
world->SetTile(x, y, TileID::AIR);
|
||||
return;
|
||||
}
|
||||
world->SetTile(x+1, y, numeric_id);
|
||||
world->SetTile(x, y, TileID::AIR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (world->GetTile(x+1, y+1) == TileID::AIR) {
|
||||
world->SetTile(x+1, y+1, numeric_id);
|
||||
world->SetTile(x, y, TileID::AIR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (world->GetTile(x-1, y+1) == TileID::AIR) {
|
||||
world->SetTile(x-1, y+1, numeric_id);
|
||||
world->SetTile(x, y, TileID::AIR);
|
||||
return;
|
||||
}
|
||||
|
||||
//if (world->GetTile(x-1, y) == TileID::AIR && world->GetTile(x+1, y) == TileID::AIR) {
|
||||
//world->SetTile(x-1, y, numeric_id);
|
||||
//world->SetTile(x, y, TileID::AIR);
|
||||
//return;
|
||||
//}
|
||||
|
||||
if (world->GetTile(x-1, y) == TileID::AIR) {
|
||||
world->SetTile(x-1, y, numeric_id);
|
||||
world->SetTile(x, y, TileID::AIR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (world->GetTile(x+1, y) == TileID::AIR) {
|
||||
world->SetTile(x+1, y, numeric_id);
|
||||
world->SetTile(x, y, TileID::AIR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class WaterTile : public LiquidTile
|
||||
{
|
||||
public:
|
||||
WaterTile();
|
||||
WaterTile(TileID id, const std::string& name, const Color4& color);
|
||||
void ForcedTicc(ITileMap* world, TileState state, int x, int y) override {
|
||||
if (world->GetTile(x, y-1) == TileID::LAVA) {
|
||||
world->SetTile(x, y-1, TileID::OBSIDIAN);
|
||||
world->SetTile(x, y, TileID::AIR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (world->GetTile(x, y+1) == TileID::LAVA) {
|
||||
world->SetTile(x, y+1, TileID::OBSIDIAN);
|
||||
world->SetTile(x, y, TileID::AIR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (world->GetTile(x-1, y) == TileID::LAVA) {
|
||||
world->SetTile(x-1, y, TileID::OBSIDIAN);
|
||||
world->SetTile(x, y, TileID::AIR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (world->GetTile(x+1, y) == TileID::LAVA) {
|
||||
world->SetTile(x+1, y, TileID::OBSIDIAN);
|
||||
world->SetTile(x, y, TileID::AIR);
|
||||
return;
|
||||
}
|
||||
LiquidTile::ForcedTicc(world, state, x, y);
|
||||
};
|
||||
bool DoesForcedTicc() const override { return true;}
|
||||
};
|
||||
|
||||
class SoilTile : public Tile
|
||||
{
|
||||
public:
|
||||
TileID decays_to;
|
||||
SoilTile(TileID numeric, const std::string& name, Color4 color, TileID decays_target)
|
||||
: Tile(numeric, name, color)
|
||||
{
|
||||
decays_to = decays_target;
|
||||
does_random_ticc = true;
|
||||
}
|
||||
|
||||
|
||||
void RandomTicc(ITileMap *world, TileState state, int x, int y) override
|
||||
{
|
||||
if (!world->HasAdjacentOrDiagonalAirBlock(x, y))
|
||||
DecayTo(world, state, x, y, decays_to);
|
||||
}
|
||||
|
||||
//void CanGrowGrass
|
||||
SoilTile();
|
||||
SoilTile(TileID id, const std::string& name, const Color4& color, const std::vector<Color4>& pallet, TileID decays_target);
|
||||
SoilTile(TileID numeric, const std::string& name, Color4 color, TileID decays_target);
|
||||
bool DoesRandomTicc() const override { return true; }
|
||||
void RandomTicc(ITileMap *world, TileState state, int x, int y) override;
|
||||
bool SpreadCheck(ITileMap* world, TileState state, int x, int y, TileID spreads_to) override;
|
||||
};
|
||||
|
||||
class DirtTile : public SoilTile
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
class GrassyTile : public SoilTile
|
||||
{
|
||||
class GrassyTile : public SoilTile {
|
||||
public:
|
||||
GrassyTile(TileID numeric, const std::string& name, Color4 color)
|
||||
: SoilTile(numeric, name, color, TileID::DIRT)
|
||||
{
|
||||
does_random_ticc = true;
|
||||
}
|
||||
GrassyTile();
|
||||
GrassyTile(TileID id, const std::string& name, const Color4& color, const std::vector<Color4>& pallet, TileID decays_target);
|
||||
GrassyTile(TileID numeric, const std::string& name, Color4 color, TileID decays_target);
|
||||
GrassyTile(TileID id, const std::string& name, const Color4& color, const std::vector<Color4>& pallet);
|
||||
GrassyTile(TileID numeric, const std::string& name, Color4 color);
|
||||
void RandomTicc(ITileMap *world, TileState state, int x, int y) override;
|
||||
};
|
||||
|
||||
void RandomTicc(ITileMap *world, TileState state, int x, int y) override
|
||||
{
|
||||
if (!world->HasAdjacentOrDiagonalAirBlock(x, y))
|
||||
{
|
||||
DecayTo(world, state, x, y, decays_to);
|
||||
return;
|
||||
}
|
||||
class MossyTile : public SoilTile {
|
||||
public:
|
||||
MossyTile();
|
||||
MossyTile(TileID id, const std::string& name, const Color4& color, TileID decays_target);
|
||||
MossyTile(TileID id, const std::string &name, const Color4 &color, const std::vector<Color4> &pallet);
|
||||
MossyTile(TileID numeric, const std::string &name, Color4 color);
|
||||
void RandomTicc(ITileMap *world, TileState state, int x, int y) override;
|
||||
};
|
||||
|
||||
class VineTile : public Tile {
|
||||
public:
|
||||
VineTile();
|
||||
VineTile(TileID id, const std::string& name, const Color4& color);
|
||||
VineTile(TileID id, const std::string& name, const Color4& color, const std::vector<Color4>& pallet);
|
||||
|
||||
bool Solid() const override { return true;}
|
||||
bool DoesRandomTicc() const override { return true; }
|
||||
void ForcedTicc(ITileMap* world, TileState state, int x, int y) override;
|
||||
void RandomTicc(ITileMap* world, TileState state, int x, int y) override;
|
||||
bool DecayCheck(ITileMap* world, TileState state, int x, int y, TileID decays_to) override;
|
||||
bool ShouldSpread(ITileMap* world, int x, int y, TileID spreads_to) const override;
|
||||
bool SpreadCheck(ITileMap* world, TileState state, int x, int y, TileID spreads_to) override;
|
||||
};
|
||||
|
||||
class GravityTile : public Tile {
|
||||
public:
|
||||
GravityTile();
|
||||
GravityTile(TileID id, const std::string& name, const Color4& color);
|
||||
GravityTile(TileID id, const std::string& name, const Color4& color, const std::vector<Color4>& pallet);
|
||||
|
||||
inline bool DoesForcedTicc() const override { return true;}
|
||||
inline bool DoesRandomTicc() const override { return true;}
|
||||
|
||||
inline void ForcedTicc(ITileMap* world, TileState state, int x, int y) override;
|
||||
};
|
||||
|
||||
class SaplingTile : public Tile {
|
||||
public:
|
||||
|
||||
world->GrassRandomTicc(x, y);
|
||||
}
|
||||
};
|
||||
|
||||
class GasTile : public Tile
|
||||
@@ -195,17 +477,166 @@ namespace CaveGame::Core
|
||||
|
||||
};
|
||||
|
||||
|
||||
// TODO: Constexpr-qualify Color4
|
||||
void DoGrassRandomTicc(ITileMap *world, TileState state, int x, int y);
|
||||
|
||||
|
||||
#define TILE static const Tile
|
||||
|
||||
void RegisterTile(Tile* data);
|
||||
|
||||
Tile* GetByNumeric(TileID id);
|
||||
/*namespace Tiles {
|
||||
using ID = Core::TileID;
|
||||
static const Tile Void {ID::VOID, "Void", Colors::Transparent};
|
||||
|
||||
Tile* GetByName(const std::string& name);
|
||||
static const Tile Air {ID::AIR, "Air", Colors::Transparent};
|
||||
static const Tile Stone {ID::STONE, "Stone", Colors::Grays::SlateGray, {Colors::Grays::SlateGray, Colors::Grays::LightSlateGray}};
|
||||
static const Tile Dirt {ID::DIRT, "Dirt", Colors::Browns::Chocolate, {Colors::Browns::Chocolate, {210, 125, 30},{195, 105, 40}}};
|
||||
static const Tile Mud {ID::MUD, "Mud", Colors::Browns::SaddleBrown};
|
||||
static const Tile Limestone {ID::LIMESTONE, "Limestone", Colors::Yellows::PaleGoldenrod};
|
||||
static const Tile Basalt {ID::BASALT, "Basalt", Colors::Grays::DimGray};
|
||||
static const Tile Cobblestone {ID::COBBLESTONE, "Cobblestone", Colors::Grays::SlateGray, {Colors::Grays::SlateGray, Colors::Grays::LightSlateGray, {64, 64, 64}, {92, 92, 92}}};
|
||||
static const MossyTile RedMoss {ID::RED_MOSS, "Red Moss ", Colors::Reds::Crimson, ID::STONE};
|
||||
static const MossyTile BrownMoss {ID::BROWN_MOSS, "Brown Moss", Colors::Browns::Brown, ID::STONE};
|
||||
static const MossyTile GreenMoss {ID::GREEN_MOSS, "Green Moss", Colors::Greens::LimeGreen, ID::STONE};
|
||||
static const MossyTile LavaMoss {ID::LAVA_MOSS, "Lava Moss", Colors::Reds::LightCoral, ID::STONE};
|
||||
static const Tile Granite {ID::GRANITE, "Granite", Colors::Whites::AntiqueWhite};
|
||||
static const Tile Marble {ID::MARBLE, "Marble", Colors::Whites::GhostWhite};
|
||||
static const GrassyTile Grass {ID::GRASS, "Grass", Colors::Greens::LawnGreen,
|
||||
{{126, 252, 5}, {122, 238, 0}, {124, 248, 12}}};
|
||||
static const GrassyTile GlowyGrass {ID::GLOWY_GRASS, "Glowy Grass", Colors::Blues::PowderBlue};
|
||||
|
||||
void DefineTiles();
|
||||
static const VineTile Vine {ID::VINE, "Vine", Colors::Greens::ForestGreen};
|
||||
|
||||
}
|
||||
static const GravityTile Sand {ID::SAND, "Sand", Colors::Yellows::PaleGoldenrod, {{238, 232, 170}, {232, 238, 160}, {218, 212, 175}}};
|
||||
|
||||
static const Tile WhiteSand {ID::WHITE_SAND, "White Sand", Colors::Whites::SeaShell};
|
||||
static const Tile RedSand {ID::RED_SAND, "Red Sand", Colors::Reds::Firebrick};
|
||||
static const Tile BlackSand {ID::BLACK_SAND, "Black Sand", Colors::Black};
|
||||
|
||||
static const Tile Sandstone {ID::SANDSTONE, "Sandstone", Colors::Yellows::PaleGoldenrod};
|
||||
static const Tile WhiteSandstone {ID::WHITE_SANDSTONE, "White Sandstone", Colors::Whites::SeaShell};
|
||||
static const Tile RedSandstone{ID::RED_SANDSTONE, "Red Sandstone", Colors::Reds::Firebrick};
|
||||
static const Tile BlackSandstone {ID::BLACK_SANDSTONE, "Black Sandstone", Colors::Black};
|
||||
|
||||
static const Tile GrayBrick {ID::STONE_BRICK, "Gray Brick", Colors::Grays::DimGray};
|
||||
|
||||
static const Tile Ash {ID::ASH, "Ash", Colors::Grays::DarkSlateGray};
|
||||
static const Tile Clay {ID::CLAY, "Clay", Colors::Browns::Brown, {{164, 42, 42}, {164, 38, 42}, {172, 42, 52}, {164, 58, 62}}};
|
||||
static const Tile Silt {ID::SILT, "Silt", Colors::Browns::SaddleBrown};
|
||||
static const Tile Snow {ID::SNOW, "Snow", Colors::Whites::MintCream};
|
||||
static const Tile Ice {ID::ICE, "Ice", Colors::Blues::LightSteelBlue};
|
||||
static const Tile Slush {ID::SLUSH, "Slush", Colors::Blues::LightBlue};
|
||||
|
||||
|
||||
static const Tile Cactus {ID::CACTUS, "Cactus", Colors::Greens::Olive};
|
||||
static const Tile CactusFlower {ID::BLOOMING_CACTUS, "Cactus Flower", Colors::Reds::Salmon};
|
||||
|
||||
static const Tile Bamboo;
|
||||
static const Tile Sugarcane;
|
||||
static const Tile VineFlower;
|
||||
static const Tile FlowerStem;
|
||||
static const Tile RedFlowerPetal;
|
||||
static const Tile BlueFlowerPetal;
|
||||
static const Tile YellowFlowerPetal;
|
||||
static const Tile WhiteFlowerPetal;
|
||||
|
||||
static const Tile CopperWire {ID::COPPER_WIRE, "Copper Wire", Colors::Oranges::DarkOrange};
|
||||
static const Tile GoldWire {ID::GOLD_WIRE, "Gold Wire", Colors::Oranges::Gold};
|
||||
static const Tile RedWire {ID::RED_WIRE, "Red Wire", Colors::Red};
|
||||
static const Tile GreenWire {ID::GREEN_WIRE, "Green Wire", Colors::Green};
|
||||
static const Tile YellowWire {ID::YELLOW_WIRE, "Yellow Wire", Colors::Yellow};
|
||||
static const Tile BlueWire {ID::BLUE_WIRE, "Blue Wire", Colors::Blue};
|
||||
|
||||
static const Tile LED;
|
||||
static const Tile TNT;
|
||||
|
||||
static const Tile Cloud {ID::CLOUD, "Cloud", Colors::Whites::Azure};
|
||||
static const Tile ThinIce;
|
||||
static const Tile RainCloud;
|
||||
static const Tile Cobweb;
|
||||
|
||||
static const Tile Coal {ID::COAL_ORE, "Coal Ore", Colors::Black};
|
||||
|
||||
|
||||
// TODO: Add AluminiumOre
|
||||
// TODO: Add ChromiumOre
|
||||
// TODO: Add NickelOre
|
||||
// TODO: Add ArsenicOre
|
||||
// TODO: Add GalliumOre
|
||||
|
||||
static const Tile CopperOre {ID::COPPER_ORE, "Copper Ore", Colors::Oranges::DarkOrange, {{255, 140, 0}, {234, 150, 3}, {241, 138, 5}}};
|
||||
static const Tile TinOre {ID::TIN_ORE, "Tin Ore", Colors::Grays::Gainsboro};
|
||||
static const Tile IronOre {ID::IRON_ORE, "Iron Ore", Colors::Pinks::LightPink};
|
||||
static const Tile LeadOre {ID::LEAD_ORE, "Lead Ore", Colors::Grays::DarkSlateGray};
|
||||
static const Tile SilverOre {ID::SILVER_ORE, "Silver Ore", Colors::Grays::Silver};
|
||||
static const Tile TungstenOre {ID::TUNGSTEN_ORE, "Tungsten Ore", Colors::Grays::DimGray};
|
||||
static const Tile GoldOre {ID::GOLD_ORE, "Gold Ore", Colors::Oranges::Gold};
|
||||
static const Tile PlatinumOre {ID::PLATINUM_ORE, "Platinum Ore", Colors::Grays::Silver};
|
||||
static const Tile CobaltOre {ID::COBALT_ORE, "Cobalt Ore", Colors::Blues::MediumSlateBlue};
|
||||
static const Tile TitaniumOre {ID::TITANIUM_ORE, "Titanium Ore", Colors::Grays::DimGray};
|
||||
static const Tile UraniumOre {ID::URANIUM_ORE, "Uranium Ore", Colors::Greens::GreenYellow};
|
||||
|
||||
static const Tile Diamond;
|
||||
static const Tile Emerald;
|
||||
static const Tile Amethyst;
|
||||
static const Tile Topaz;
|
||||
static const Tile Sapphire;
|
||||
static const Tile Ruby;
|
||||
static const Tile Amber;
|
||||
|
||||
static const Tile Obsidian {ID::OBSIDIAN, "Obsidian", Colors::Purples::DarkMagenta};
|
||||
|
||||
|
||||
static const SaplingTile OakSapling;
|
||||
static const Tile OakLog {ID::OAK_LOG, "Oak Log", Colors::Browns::BurlyWood};
|
||||
static const Tile OakPlank {ID::OAK_PLANK, "Oak Plank", Colors::Browns::BurlyWood};
|
||||
static const Tile OakPlatform;
|
||||
static const Tile OakLeaf;
|
||||
|
||||
static const Tile AshLog;
|
||||
static const Tile AshLeaf;
|
||||
static const Tile AshPlank;
|
||||
|
||||
static const Tile PalmLog;
|
||||
static const Tile PalmLeaf;
|
||||
static const Tile PalmPlank;
|
||||
|
||||
static const Tile PineLog;
|
||||
static const Tile PinePlank;
|
||||
static const Tile PinePlatform;
|
||||
static const Tile PineLeaf;
|
||||
|
||||
static const Tile RedwoodLog;
|
||||
static const Tile RedwoodPlank;
|
||||
static const Tile RedwoodPlatform;
|
||||
static const Tile RedwoodLeaf;
|
||||
|
||||
static const Tile EbonyLog;
|
||||
static const Tile EbonyPlank;
|
||||
static const Tile EbonyPlatform;
|
||||
static const Tile EbonyLeaf;
|
||||
|
||||
static const Tile MapleLog;
|
||||
static const Tile MapleLeaf;
|
||||
static const Tile MaplePlank;
|
||||
|
||||
|
||||
static const Tile BirchLog;
|
||||
static const Tile BirchLeaf;
|
||||
static const Tile BirchPlank;
|
||||
|
||||
|
||||
static const WaterTile Water {ID::WATER, "Water", Colors::Blues::DodgerBlue};
|
||||
static const LiquidTile Blood;
|
||||
static const LiquidTile Sludge;
|
||||
static const LiquidTile Lava {ID::LAVA, "Lava", Colors::Red};
|
||||
static const LiquidTile MuddyWater;
|
||||
static const LiquidTile Ectoplasm;
|
||||
static const LiquidTile Oil;
|
||||
static const LiquidTile Honey;
|
||||
static const LiquidTile Milk;
|
||||
|
||||
static const Tile Rope;
|
||||
|
||||
}*/
|
||||
|
||||
}
|
||||
|
83
Core/include/Core/TileRegistry.hpp
Normal file
@@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
#include "Singleton.hpp"
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include "Color4.hpp"
|
||||
#include "JJX/JSON.hpp"
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
#include <array>
|
||||
#include <Core/Tile.hpp>
|
||||
#include <J3ML/J3ML.hpp>
|
||||
#include <map>
|
||||
#include <fstream>
|
||||
#include <filesystem>
|
||||
|
||||
namespace CaveGame::Core {
|
||||
//class ITileMap;
|
||||
|
||||
//using ColorPallet = std::vector<Color4>;
|
||||
//using TileTiccFunc = std::function<void(const Tile& data, ITileMap* world, int x, int y)>;
|
||||
|
||||
/*std::unordered_map<std::string, TileTiccFunc> tile_functions {
|
||||
{"check-spread", [](const Tile& data, ITileMap* world, int x, int y){
|
||||
|
||||
}},
|
||||
{"check-decay"},
|
||||
{"check-decay-spread"},
|
||||
{"sand-ticc"},
|
||||
{"water-ticc"},
|
||||
};*/
|
||||
|
||||
//using TileID = uint16_t;
|
||||
|
||||
/// A Singleton that stores tile data.
|
||||
class TileRegistry : public Singleton<TileRegistry> {
|
||||
|
||||
public:
|
||||
static constexpr int MaxTiles = 65536;
|
||||
|
||||
///
|
||||
void Register(Tile data);
|
||||
|
||||
const Tile &Get(const std::string &mnemonic);
|
||||
|
||||
const Tile &Get(u16 id);
|
||||
|
||||
// TODO: Why convert to vector?
|
||||
std::vector<Tile> GetTileList();
|
||||
|
||||
const std::array<Tile, MaxTiles>& GetTileArray();
|
||||
bool Exists(const std::string& mnemonic);
|
||||
bool Exists(u16 id);
|
||||
const Tile& GetByMnemonicID(const std::string& mnemonic);
|
||||
const Tile& GetByNumericID(uint16_t ID);
|
||||
|
||||
uint16_t MnemonicToNumeric(const std::string& mnemonic);
|
||||
std::string NumericToMnemonic(uint16_t ID);
|
||||
const Tile& operator[](const std::string& mnemonic);
|
||||
const Tile& operator[](u16 ID);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
void Register(TileID ID, const Tile &data);
|
||||
|
||||
protected:
|
||||
|
||||
std::array<bool, MaxTiles> consumed_ids{};
|
||||
std::array<Tile, MaxTiles> registered_tiles{};
|
||||
std::unordered_map<std::string, u16> name_to_id_mapping{};
|
||||
std::unordered_map<u16, std::string> id_to_name_mapping{};
|
||||
u16 current_index = 0;
|
||||
private:
|
||||
};
|
||||
|
||||
/// Convenient access to the tile registry.
|
||||
static TileRegistry& Tiles() { return TileRegistry::Instance();}
|
||||
|
||||
bool LoadTileMetadata(const std::filesystem::path& path);
|
||||
|
||||
bool LoadTileMetadata();
|
||||
}
|