Merge branch 'master' of https://git.redacted.cc/Josh/ReCaveGame
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,4 +3,5 @@
|
||||
/.ccls-cache
|
||||
/compile_commands.json
|
||||
/cmake-build-debug
|
||||
/cmake-build-release
|
||||
/build
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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/
|
||||
};
|
||||
|
@@ -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:
|
||||
};
|
||||
|
@@ -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:
|
||||
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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) {
|
||||
|
@@ -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) {
|
||||
|
6
Client/src/Client/ContainerWindow.cpp
Normal file
6
Client/src/Client/ContainerWindow.cpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#include <Client/ContainerWindow.hpp>
|
||||
|
||||
|
||||
namespace CaveGame::Client {
|
||||
|
||||
}
|
@@ -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) {
|
||||
|
@@ -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});
|
||||
}
|
@@ -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) {
|
||||
|
11
ClientApp/data/items/base_items.json
Normal file
11
ClientApp/data/items/base_items.json
Normal file
@@ -0,0 +1,11 @@
|
||||
[
|
||||
{
|
||||
|
||||
},
|
||||
{
|
||||
|
||||
},
|
||||
{
|
||||
|
||||
}
|
||||
]
|
23
ClientApp/data/tiles/base_tiles.json
Normal file
23
ClientApp/data/tiles/base_tiles.json
Normal 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
|
||||
}
|
||||
]
|
@@ -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");
|
||||
}
|
||||
};
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@@ -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)
|
@@ -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:
|
||||
};
|
||||
}
|
@@ -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;
|
||||
|
@@ -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}};
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -3,5 +3,15 @@
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
class ItemStack : PhysicsEntity {};
|
||||
class ItemStack
|
||||
{
|
||||
public:
|
||||
protected:
|
||||
private:
|
||||
};
|
||||
class ItemStackEntity : public PhysicsEntity, public ItemStack {
|
||||
public:
|
||||
protected:
|
||||
private:
|
||||
};
|
||||
}
|
@@ -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);
|
||||
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
59
Core/include/Core/Sprite.hpp
Normal file
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:
|
||||
};
|
||||
}
|
@@ -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;}
|
||||
|
@@ -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;
|
||||
|
@@ -1,6 +0,0 @@
|
||||
#include <Core/AnimatedSprite.hpp>
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
|
||||
}
|
33
Core/src/Core/Item.cpp
Normal file
33
Core/src/Core/Item.cpp
Normal 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);
|
||||
}
|
||||
}
|
@@ -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
6
Core/src/Core/Sprite.cpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#include <Core/Sprite.hpp>
|
||||
|
||||
namespace CaveGame::Core
|
||||
{
|
||||
|
||||
}
|
@@ -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() {}
|
||||
|
||||
|
Reference in New Issue
Block a user