This commit is contained in:
2025-03-19 11:47:26 -04:00
34 changed files with 547 additions and 112 deletions

1
.gitignore vendored
View File

@@ -3,4 +3,5 @@
/.ccls-cache
/compile_commands.json
/cmake-build-debug
/cmake-build-release
/build

View File

@@ -37,6 +37,11 @@ CPMAddPackage(
URL https://git.redacted.cc/maxine/mcolor/archive/Prerelease-6.3.zip
)
CPMAddPackage(
NAME jjx
URL https://git.redacted.cc/josh/jjx/archive/Prerelease-2.zip
)
CPMAddPackage(
NAME jlog
URL https://git.redacted.cc/josh/jlog/archive/Prerelease-17.zip
@@ -47,6 +52,11 @@ CPMAddPackage(
URL https://git.redacted.cc/josh/j3ml/archive/3.4.5.zip
)
CPMAddPackage(
NAME jstick
URL https://git.redacted.cc/josh/jstick/archive/Prerelease-2.zip
)
CPMAddPackage(
NAME ReWindow
URL https://git.redacted.cc/Redacted/ReWindow/archive/Prerelease-32.zip

View File

@@ -13,6 +13,7 @@ endif()
target_include_directories(CaveClient PUBLIC
${CaveCore_SOURCE_DIR}/include
# ${jstick_SOURCE_DIR}/include
${J3ML_SOURCE_DIR}/include
${JGL_SOURCE_DIR}/include
${JUI_SOURCE_DIR}/include

View File

@@ -86,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.
@@ -134,6 +135,8 @@ namespace CaveGame::Client
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/
};

View File

@@ -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 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>
@@ -5,10 +16,34 @@
namespace CaveGame
{
using namespace JUI::UDimLiterals;
/// An inventory container window widget.
class ContainerWindow : public JUI::Window
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->SetTitle(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:
private:
};

View File

@@ -23,6 +23,7 @@
#include <JUI/Widgets/TextRect.hpp>
#include <Core/Item.hpp>
#include "TileTool.hpp"
#include "ContainerWindow.hpp"
namespace CaveGame::Client {
using namespace Core;
@@ -74,6 +75,7 @@ namespace CaveGame::Client {
hud->ObserveMouseInput((JUI::MouseButton)a, b);
}
void PassMouseMovement(const J3ML::LinearAlgebra::Vector2 &pos) override {
mouse_pos = pos;
world->mouse_pos = pos;
hud->ObserveMouseMovement(pos);
@@ -110,8 +112,9 @@ namespace CaveGame::Client {
LocalWorld* world = nullptr;
TileHotbar hotbar;
JUI::Scene* hud = nullptr;
Vector2 mouse_pos;
Vector2 mouse_pos = {0,0};
RNG tool_rng;
ContainerWindow* test_container = nullptr;
private:

View File

@@ -28,8 +28,11 @@ namespace CaveGame::Client {
X
};
class TileTool : public JUI::Window
{
static const bool WorldEditorEnabledByDefault = false;
public:
Event<float> BrushSizeChanged;
Event<float> BrushPercentChanged;

View File

@@ -1,5 +1,6 @@
#include <Client/Camera2D.hpp>
#include <ReWindow/InputService.h>
#include <jstick.hpp>
namespace CaveGame::Client
{
@@ -11,6 +12,12 @@ namespace CaveGame::Client
{
// TODO: implement freecam panning via mouse.
Vector2 m = jstick::GetLeftThumbstickAxisNormalized();
if (m.Magnitude() > 0.1f)
Move(m*elapsed);
if (InputService::IsKeyDown(Keys::LeftArrow))
MoveLeft(elapsed);
if (InputService::IsKeyDown(Keys::RightArrow))
@@ -25,14 +32,19 @@ namespace CaveGame::Client
if (InputService::IsKeyDown(Keys::RightBracket))
Rotate(5.f*elapsed);
if (InputService::IsKeyDown(Keys::Minus))
if (InputService::IsKeyDown(Keys::Minus) || jstick::IsButtonDown(jstick::XBoxButton::Back))
ZoomOut(elapsed);
if (InputService::IsKeyDown(Keys::Equals))
if (InputService::IsKeyDown(Keys::Equals) || jstick::IsButtonDown(jstick::XBoxButton::Start))
ZoomIn(elapsed);
}
void Camera2D::Update(float elapsed) {
last_free_move += elapsed;
float t = 1 - move_smoothing;
float step = 1.f - Math::Pow(t, elapsed);
//float step = Math::Exp(- (move_smoothing*elapsed) );
@@ -40,10 +52,16 @@ namespace CaveGame::Client
if (freecam)
UpdateFreecamControls(elapsed);
if (lerp_to_target)
position = Vector2::Lerp(position, target, step);
else
position = target;
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-7f) {
@@ -99,19 +117,28 @@ namespace CaveGame::Client
Vector2 Camera2D::Position() const { return position; }
void Camera2D::MoveLeft(float rate) {
target -= Vector2(move_speed * rate, 0);
position -= Vector2(move_speed * rate, 0);
}
void Camera2D::Move(const Vector2& velocity)
{
position += velocity * move_speed;
last_free_move = 0;
}
void Camera2D::MoveRight(float rate) {
target += Vector2(move_speed * rate, 0);
position += Vector2(move_speed * rate, 0);
last_free_move = 0;
}
void Camera2D::MoveUp(float rate) {
target -= Vector2(0, move_speed * rate);
position -= Vector2(0, move_speed * rate);
last_free_move = 0;
}
void Camera2D::MoveDown(float rate) {
target += Vector2(0, move_speed * rate);
position += Vector2(0, move_speed * rate);
last_free_move = 0;
}
void Camera2D::PassViewportSize(const Vector2 &size) {

View File

@@ -29,6 +29,7 @@ CaveGame::Client::Console::Console(JUI::Widget *parent) : JUI::Window(parent)
input_box->SetTextSize(16);
input_box->SetAutoCompleteText("");
input_box->SetTextColor(Colors::White);
input_box->SetAutocompleteTextColor(Colors::White);
// TODO: input_box->ClearTextOnReturn(true);
input_box->OnReturn += [this] (const std::string& msg) {

View File

@@ -0,0 +1,6 @@
#include <Client/ContainerWindow.hpp>
namespace CaveGame::Client {
}

View File

@@ -10,6 +10,8 @@
#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();
@@ -27,6 +29,21 @@ void CaveGame::Client::GameSession::Load() {
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) {
@@ -37,6 +54,8 @@ void CaveGame::Client::GameSession::Load() {
for (int i = 0; i < steps; i++)
World()->DoTileTiccs(0.f);
};
test_container = new ContainerWindow(hud, 5, 5, "Test Container");
}
void CaveGame::Client::GameSession::Draw() {
@@ -113,7 +132,7 @@ void CaveGame::Client::GameSession::WorldEditToolDrawTiles(TileID tile) {
}
void CaveGame::Client::GameSession::WorldEditToolControlsUpdate(float elapsed){
if (InputService::IsMouseButtonDown(MouseButtons::Left)) {
if (InputService::IsMouseButtonDown(MouseButtons::Left) || jstick::GetLeftTriggerNormalized() > 0.1f) {
if (!following)
{
last = MouseWorldPos();
@@ -129,7 +148,7 @@ void CaveGame::Client::GameSession::WorldEditToolControlsUpdate(float elapsed){
}
if (InputService::IsMouseButtonDown(MouseButtons::Right)) {
if (InputService::IsMouseButtonDown(MouseButtons::Right) || jstick::GetRightTriggerNormalized() > 0.1f) {
if (!following)
{
last = MouseWorldPos();
@@ -146,6 +165,10 @@ void CaveGame::Client::GameSession::WorldEditToolControlsUpdate(float elapsed){
}
bool lp = false;
bool rp = false;
void CaveGame::Client::GameSession::Update(float elapsed) {
world->Update(elapsed);
hud->Update(elapsed);
@@ -153,6 +176,38 @@ void CaveGame::Client::GameSession::Update(float 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());
}
}
auto rstick = jstick::GetRightThumbstickAxisNormalized();
if (rstick.Magnitude() > 0.1f)
{
mouse_pos += rstick*elapsed*100;
}
// FIXME: Temporary hack, we poll the bumper button state each frame, cause the jstick event doesn't propagate for some reason.
bool lprn = jstick::IsButtonDown(jstick::XBoxButton::BumperL);
bool rprn = jstick::IsButtonDown(jstick::XBoxButton::BumperR);
if (lprn && !lp)
hotbar.PrevSlot();
if (rprn && !rp)
hotbar.NextSlot();
lp = lprn;
rp = rprn;
/*
for (int i = 0; i < 10; i++)
{
@@ -205,7 +260,13 @@ void CaveGame::Client::GameSession::WorldEditToolDrawOverlay()
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();
}
void CaveGame::Client::GameSession::PassWindowSize(const Vector2 &size) {

View File

@@ -3,7 +3,7 @@
#include <Core/Entity.hpp>
#include <JGL/JGL.h>
#include <ReWindow/InputService.h>
#include <jstick.hpp>
// TODO: Move this shit to Client/Player.cpp
@@ -17,7 +17,8 @@ void CaveGame::Core::Player::Draw() {
AABB2D quad = Frame_Idle;
if (!on_ground && Math::Abs(velocity.y) > 5.f)
// 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;
@@ -52,15 +53,21 @@ void CaveGame::Core::Player::Update(float elapsed) {
anim_timer += elapsed;
walking = false;
if (InputService::IsKeyDown(Keys::A))
Vector2 dpad = jstick::GetDPadAxisNormalized();
if (InputService::IsKeyDown(Keys::A) || dpad.x <= -0.5f)
WalkLeft(elapsed);
if (InputService::IsKeyDown(Keys::D))
if (InputService::IsKeyDown(Keys::D) || dpad.x >= +0.5f)
WalkRight(elapsed);
if (InputService::IsKeyDown(Keys::W))
if (InputService::IsKeyDown(Keys::W) || dpad.y <= -0.5f)
Jump(elapsed);
//if (InputService::IsKeyDown(Keys::S))
//Accelerate({0, -1});
}

View File

@@ -51,6 +51,7 @@ CaveGame::Client::TileTool::TileTool(JUI::Widget *parent) : JUI::Window(parent)
BrushSizeChanged(newval);
};
brush_size_slider->CurrentValue(0.5f);
brush_size_slider->SetClicked(false);
brush_percent_slider = new Slider(right_row_layout);
@@ -65,7 +66,7 @@ CaveGame::Client::TileTool::TileTool(JUI::Widget *parent) : JUI::Window(parent)
BrushPercentChanged(newval);
};
brush_percent_slider->CurrentValue(1.f);
brush_percent_slider->SetClicked(false);
auto* tile_sim_checkbox = new Checkbox(right_row_layout);
@@ -107,6 +108,9 @@ CaveGame::Client::TileTool::TileTool(JUI::Widget *parent) : JUI::Window(parent)
BrushPercentChanged += [this] (float value) {
brush_density = value;
};
Enable(WorldEditorEnabledByDefault);
}
void CaveGame::Client::TileTool::BrushRadius(float size) {

View File

@@ -0,0 +1,11 @@
[
{
},
{
},
{
}
]

View File

@@ -0,0 +1,23 @@
[
{
"mnemonic-id" : "stone",
"display-name" : "",
"solid": true,
"does-random-ticc": false,
"does-forced-ticc": false,
"color": "#FFFFFF",
"pallet": []
},
{
"mnemonic-id" : "stone",
"display-name" : "",
"solid": true,
"does-random-ticc": false,
"does-forced-ticc": false,
"color": "#FFFFFF",
"pallet": [],
"random-ticc-func": "zzyyzz",
"forced-ticc-func": "zzyyzz",
"drops" : null
}
]

View File

@@ -125,6 +125,8 @@ namespace CaveGame::ClientApp {
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);
/// Toggles tile simulation if we are
bool ToggleTileSim(const CommandArgs& args);
@@ -179,15 +181,15 @@ namespace CaveGame::ClientApp {
{"freecam", {"q"}, [this](const CommandArgs& args) {}},
{"noclip", {"q"}, [this](const CommandArgs& args) {}},
{"god", {"q"}, [this](const CommandArgs& args) {}},
{"fullbright", {"q"}, [this](const auto& args) {}},
{"connect", {"q"}, [this](const auto& args) {}},
{"disconnect", {"q"}, [this](const auto& args) {}},
{"server", {"q"}, [this](const auto& args) {}},
{"sandbox", {"q"}, [this](const auto& args) {}},
{"crash", {"q"}, [this](const auto& args) {}},
{"time", {"q"}, [this](const auto& args) {}},
{"juidebug", {"q"}, [this](const auto& args) {}},
{"fpslimit", {"fps"}, [this](const auto& 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) {
if (args.size() < 2)
return Log(std::format("Current FPS Limit: {}fps", max_fps));
@@ -195,29 +197,34 @@ namespace CaveGame::ClientApp {
std::cout << val << std::endl;
max_fps = val;
}},
{"grid", {}, [this](const auto& 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 auto& args) {
{"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 auto& args) {
{"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) {
}}
};
Command InvalidCommand {
"n/a", {}, [this](const auto& args) {
"n/a", {}, [this](const CommandArgs& args) {
Log("Nope");
}
};

View File

@@ -22,9 +22,33 @@
#include <Core/Tile.hpp>
#include <JGL/logger/logger.h>
std::string read_file(const std::string& file_path)
{
std::ifstream file(file_path, std::ios::binary);
if (!file)
throw std::runtime_error("We couldn't find the file: " + file_path);
std::streamsize file_size;
file.seekg(0, std::ios::end);
file_size = file.tellg();
file.seekg(0, std::ios::beg);
std::string file_content(file_size, '\0');
file.read(&file_content[0], file_size);
file.close();
return file_content;
}
int main(int argc, char** argv) {
std::string content = read_file("data/tiles/base_tiles.json");
auto [text, parse_error] = json::parse(content);
// Hide logs from engine components so we can focus on CaveGame.
JGL::Logger::Warning.EnableConsole(false);

View File

@@ -7,6 +7,7 @@
#include <Core/Player.hpp>
#include <ReWindow/InputService.h>
#include <Core/Macros.hpp>
#include <jstick.hpp>
namespace CaveGame::ClientApp
{
@@ -90,6 +91,8 @@ namespace CaveGame::ClientApp
void CaveGameWindow::Run()
{
int js = jstick::Connect();
//this->SetRenderer(RenderingAPI::OPENGL);
bool success = this->Open();
@@ -223,6 +226,8 @@ namespace CaveGame::ClientApp
void CaveGameWindow::Update(float elapsed)
{
jstick::ReadEventLoop();
// Update floating windows.
wm->Update(elapsed);
@@ -562,6 +567,28 @@ namespace CaveGame::ClientApp
void CaveGameWindow::MarkReadyToClose(bool close) { wanna_die = close;}
bool CaveGameWindow::TileListCmd(const CommandArgs &args) {
auto tile_list = Core::GetAllTiles();
for (Core::Tile* tile : tile_list) {
if (tile != nullptr)
Log(std::format("ID: {}, Mnemonic: {}, DisplayName: {}", (int)tile->NumericID(), tile->MnemonicID(), tile->Name()));
}
return true;
}
bool CaveGameWindow::ItemListCmd(const CommandArgs &args) {
auto item_list = Core::GetAllItems();
for (auto [name, item] : item_list) {
if (item != nullptr)
Log(std::format("Mnemonic: {}", name));
}
return true;
}
}

View File

@@ -19,5 +19,6 @@ 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 JGL jjx mcolor Sockets)
target_link_libraries(CaveCore PUBLIC J3ML JGL jjx mcolor Sockets jstick)

View File

@@ -1,39 +0,0 @@
#pragma once
#include <J3ML/LinearAlgebra/Vector2.hpp>
namespace CaveGame::Core
{
struct Frame {
Vector2 origin;
Vector2 offset;
Vector2 size;
//AABB2D quad;
};
/* class KeyframeAnimation
{
Event<> Began;
Event<> Complete;
Event<> Paused;
Event<> Resumed;
Event<> Cancelled;
std::vector<Frame> keyframes;
float timer;
int keyframe_index;
};*/
class AnimatedSprite {
public:
AnimatedSprite() { }
void AddFrame(const std::string& frame_id, const Frame& frame);
void SetCurrentFrame();
void DrawFrame();
void DrawCurrentFrame();
//void PlayAnimation(const KeyframeAnimation& anim);
protected:
private:
};
}

View File

@@ -61,6 +61,7 @@ namespace CaveGame::Core {
std::vector<StatusEffect> active_effects;
Color4 color;
bool on_ground;
float airtime = 0;
float climbing_skill = 55;
bool facing_left = true;
bool walking = false;

View File

@@ -1,12 +1,12 @@
#pragma once
#include "AnimatedSprite.hpp"
#include "Sprite.hpp"
#include "Entity.hpp"
#include <JGL/types/RenderTarget.h>
namespace CaveGame::Core
{
class Explosion : public Entity, public AnimatedSprite {
class Explosion : public Entity, public Sprite {
public:
const AABB2D SP_EXPLOSION0 {{0, 0}, {32, 32}};

View File

@@ -1,12 +1,31 @@
#pragma once
#include <set>
#include <string>
#include <J3ML/Math.hpp>
#include <map>
//#include <Core/Registry.hpp>
#pragma once
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_ITEM, ORE, TOOL, WEAPON, ARMOR, CLOTHING, FOOD, POTION, INGREDIENT, MEME, PLANT, METAL, ORGANIC, BUILDING_BLOCK,
FLUID, SOIL, GAS, LIGHT_SOURCE, CRAFTING_STATION,
@@ -15,7 +34,7 @@ namespace CaveGame::Core
/// 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,19 +64,49 @@ namespace CaveGame::Core
ANCIENT, VICTORIOUS, CONDEMNED, CLEAN, LUCKY, POWERFUL, SUPER,
};
class ItemComponent
{
};
class UseCooldown {};
class Consumable {};
class Drinkable {};
class Eatable {};
class Item
{
public:
std::string mnemonic;
std::string display_name;
std::string tooltip;
std::string author;
unsigned int max_stack;
int value = 1;
Item() {}
explicit Item(const std::string& mnemonic);
protected:
private:
};
class EmptyBottle : public Item
{
//REGISTER("EmptyBottle", Item);
class TileItem : public Item {
};
void RegisterItem(const std::string& mnemonic, Item* item);
void RegisterNewItem(const std::string& mnemonic, const Item& item);
Item* GetItem(const std::string& mnemonic);
std::map<std::string, Item*> GetAllItems();
class ItemFilter
{
@@ -76,10 +125,10 @@ namespace CaveGame::Core
static const Item Straw;
static const Item Gel;
//static const Item Gel {"gel", "Gel", };
static const Item Glowstick;
static const Item RoastChicken;
//static const Item RoastChicken {"roast-chicken", "Roast Chicken"};
static const Item PumpkinPie;
static const Item ShepherdsPie;

View File

@@ -3,5 +3,15 @@
namespace CaveGame::Core
{
class ItemStack : PhysicsEntity {};
class ItemStack
{
public:
protected:
private:
};
class ItemStackEntity : public PhysicsEntity, public ItemStack {
public:
protected:
private:
};
}

View File

@@ -19,6 +19,7 @@ namespace CaveGame::Core
{
class PhysicsEntity : public Entity {
public:
bool freeze_in_void = true;
explicit PhysicsEntity(const Vector2& spawnPoint);
[[nodiscard]] Vector2 Velocity() const;
@@ -45,7 +46,18 @@ namespace CaveGame::Core
TileID id = map->GetTile(tileBoxPos.x, tileBoxPos.y);
if (id == TileID::VOID || id == TileID::AIR)
if (id == TileID::VOID) {
if (freeze_in_void)
{
velocity = {0,0};
next_position = position;
return;
}
continue;
}
if (id == TileID::AIR)
continue;
coll_tests++;
@@ -117,6 +129,11 @@ namespace CaveGame::Core
void Update(float elapsed) override {
if (!on_ground)
airtime += elapsed;
else
airtime = 0;
// TODO: Sophisticated mechanism to maintain locked-timestep, multiple-iteration physics steps.
PhysicsUpdate(elapsed);

View File

@@ -25,9 +25,19 @@ namespace CaveGame::Core {
void PhysicsUpdate(float elapsed) override;
void Jump(float elapsed) {
// TODO: Make it so the player falls **slightly** slower
if (on_ground)
{
Accelerate({0, -16000*elapsed});
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, -16000*elapsed);
Accelerate(projection);
on_ground = false;
} else
Accelerate({0, -100*elapsed});
@@ -46,7 +56,7 @@ namespace CaveGame::Core {
walking = true;
}
protected:
// TODO: Duplicated in Explosion.hpp. Refactor into AnimatedSprite class?
// TODO: Duplicated in Explosion.hpp. Refactor into Sprite class?
float anim_timer = 0;
};

View 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:
};
}

View File

@@ -201,9 +201,11 @@ namespace CaveGame::Core {
class Tile;
///
void RegisterTile(Tile* data);
std::vector<Tile*> GetAllTiles();
Tile* GetByNumeric(TileID id);
Tile* GetByName(const std::string& name);
@@ -215,6 +217,7 @@ namespace CaveGame::Core {
protected:
TileID numeric_id = TileID::VOID;
std::string mnemonic_id;
std::string display_name = "";
public:
Color4 base_color;
std::vector<Color4> color_pallet;
@@ -235,7 +238,7 @@ namespace CaveGame::Core {
[[nodiscard]] TileID NumericID() const;
[[nodiscard]] std::string MnemonicID() const;
std::string Name() const { return mnemonic_id; }
std::string Name() const;
virtual bool DoesRandomTicc() const { return false; }
virtual bool DoesForcedTicc() const { return false;}
virtual bool Solid() const { return false;}

View File

@@ -151,6 +151,8 @@ namespace CaveGame::Core {
/// Adds the entity pointer to the world, via std::move, this may leave the original entity pointer empty.
void AddEntity(Entity* e);
std::vector<Entity*> GetEntities() { return entities;}
/// Returns the number of extant entities in this world right now.
unsigned int GetCurrentEntityCount() const;

View File

@@ -1,6 +0,0 @@
#include <Core/AnimatedSprite.hpp>
namespace CaveGame::Core
{
}

33
Core/src/Core/Item.cpp Normal file
View File

@@ -0,0 +1,33 @@
#include <Core/Item.hpp>
#include <string>
#include <map>
namespace CaveGame::Core {
std::map<std::string, Item*> item_registry{};
void RegisterItem(const std::string& mnemonic, Item* item)
{
item_registry.insert({mnemonic, item});
}
void RegisterNewItem(const std::string& mnemonic, const Item& item)
{
//item_registry.emplace(mnemonic, new Item(item));
}
Item* GetItem(const std::string& mnemonic)
{
return item_registry.at(mnemonic);
}
std::map<std::string, Item*> GetAllItems()
{
return item_registry;
}
Item::Item(const std::string& mnemonic) {
this->mnemonic = mnemonic;
RegisterItem(mnemonic, this);
}
}

View File

@@ -18,14 +18,20 @@ namespace CaveGame::Core
void PhysicsEntity::PhysicsUpdate(float elapsed) {
next_position += velocity * elapsed;
float air_resistance = 6.45f;
float air_resistance = 2.45f;
float base_friction_coefficient = 4.f;
float gravity = 9.81f;
float mass = 40.f;
if (Math::Abs(velocity.y) > 1e-2f)
on_ground = false;
//
// Simulate friction.
if (on_ground)
velocity.x *= (1 - (elapsed * base_friction_coefficient));
// Air resistance.
velocity *= (1 - (elapsed * air_resistance));
velocity.y += (elapsed*gravity*mass);

6
Core/src/Core/Sprite.cpp Normal file
View File

@@ -0,0 +1,6 @@
#include <Core/Sprite.hpp>
namespace CaveGame::Core
{
}

View File

@@ -1,11 +1,46 @@
#include <Core/Loggers.hpp>
#include <Core/Tile.hpp>
#include <array>
#include "Core/Item.hpp"
namespace CaveGame::Core
{
// TODO: Use unique_ptr?
// TODO: Create singleton TileRegistry class
std::array<Tile*, 65536> registered_tiles{};
std::vector<Tile*> GetAllTiles() {
return {std::begin(registered_tiles), std::end(registered_tiles)};
}
Tile *GetByNumeric(TileID id) {
// TODO: Optimize with additional mapping!!
//if (registered_tiles.at((uint16_t)id))
return registered_tiles[(uint16_t)id];
//for(auto& tile : registered_tiles)
//if (tile->numeric_id == id)
//return tile;
//throw std::runtime_error("ID not in tile registry!");
return nullptr;
}
void RegisterTile(Tile *data) {
if (data == nullptr)
return;
uint16_t index = (uint16_t)data->NumericID();
registered_tiles[index] = data;
//std::string name = std::format("{}_tile", data->MnemonicID());
//Core::Item item_for_this_tile = Core::Item(name);
//item_for_this_tile->mnemonic = name;
//item_for_this_tile->display_name = std::format("{} Tile", data->MnemonicID());
//RegisterItem(name, item_for_this_tile);
}
void Tile::DecayTo(ITileMap *world, TileState state, int x, int y, TileID TDecaysTo) {
world->SetTile(x, y, TDecaysTo);
@@ -274,21 +309,15 @@ namespace CaveGame::Core
std::string Tile::MnemonicID() const { return mnemonic_id; }
Tile *GetByNumeric(TileID id) {
// TODO: Optimize with additional mapping!!
//if (registered_tiles.at((uint16_t)id))
return registered_tiles[(uint16_t)id];
//for(auto& tile : registered_tiles)
//if (tile->numeric_id == id)
//return tile;
//throw std::runtime_error("ID not in tile registry!");
return nullptr;
std::string Tile::Name() const {
// Use the mnemonic ID if no display name is set.
if (display_name.empty())
return mnemonic_id;
return display_name;
}
void RegisterTile(Tile *data) {
uint16_t index = (uint16_t)data->NumericID();
registered_tiles[index] = data;
}
WaterTile::WaterTile() : LiquidTile() {}