First kind of working FTXUI for the game.

master
Zed A. Shaw 3 months ago
parent 6fc74ca199
commit 6d4aa9390a
  1. 153
      escape_turings_tarpit.cpp
  2. 29
      ftxtest.cpp
  3. 3
      game_engine.hpp

@ -1,17 +1,33 @@
#include <iostream>
#include <fstream>
#include <fmt/core.h>
#include <fmt/color.h>
#include <fmt/chrono.h>
#include <string>
#include "dbc.hpp" #include "dbc.hpp"
#include "game_engine.hpp" #include "game_engine.hpp"
#include <unistd.h> #include <chrono> // for milliseconds
#include <stdio.h>
#include <git2.h>
#include <efsw/efsw.hpp> #include <efsw/efsw.hpp>
#include <regex>
#include <filesystem> #include <filesystem>
#include <fmt/chrono.h>
#include <fmt/color.h>
#include <fmt/core.h>
#include <fmt/core.h>
#include <fstream>
#include <ftxui/component/event.hpp> // for Event
#include <ftxui/component/mouse.hpp> // for ftxui
#include <ftxui/dom/elements.hpp> // for text, separator, Element, operator|, vbox, border
#include <git2.h>
#include <iostream>
#include <memory> // for allocator, shared_ptr
#include <regex>
#include <stdio.h>
#include <stdlib.h> // for EXIT_SUCCESS
#include <string> // for operator+, to_string
#include <thread> // for sleep_for
#include <unistd.h>
#include <vector>
#include "ftxui/component/component.hpp" // for CatchEvent, Renderer, operator|=
#include "ftxui/component/loop.hpp" // for Loop
#include "ftxui/component/screen_interactive.hpp" // for ScreenInteractive
using namespace ftxui;
using namespace std;
using namespace std; using namespace std;
using namespace fmt; using namespace fmt;
@ -19,8 +35,14 @@ namespace fs = std::filesystem;
const auto ERROR = fmt::emphasis::bold | fg(fmt::color::red); const auto ERROR = fmt::emphasis::bold | fg(fmt::color::red);
vector<string> lines;
#define BUF_MAX 1024 #define BUF_MAX 1024
void output(const string &msg) {
lines.push_back(msg);
}
class UpdateListener : public efsw::FileWatchListener { class UpdateListener : public efsw::FileWatchListener {
public: public:
bool changes = false; bool changes = false;
@ -49,11 +71,9 @@ class UpdateListener : public efsw::FileWatchListener {
dbc::check(rc == 0, "git ignored failed."); dbc::check(rc == 0, "git ignored failed.");
if(!ignored) { if(!ignored) {
println("\nCHANGE: filename={} .gitignored?={}", full_path.c_str(), ignored); output(format("\nCHANGE: filename={} .gitignored?={}", full_path.c_str(), ignored));
changes = changes || !ignored; changes = changes || !ignored;
} else {
print(".");
} }
} }
@ -95,13 +115,13 @@ void run_build(GameEngine &game, const char* command) {
lnumber, col, type, message); lnumber, col, type, message);
stats_out << result; stats_out << result;
print(ERROR, "\nHIT WITH {} @ {}:{}:{} {}", type, file_name, lnumber, col, message); output(format("\nHIT WITH {} @ {}:{}:{} {}", type, file_name, lnumber, col, message));
game.hit(type); game.hit(type);
// refactor this // refactor this
if(game.is_dead()) { if(game.is_dead()) {
print(ERROR, "YOU DIED!\n"); output(format("YOU DIED!\n"));
} }
} }
} }
@ -110,6 +130,89 @@ void run_build(GameEngine &game, const char* command) {
dbc::post("a post test", [&]() { return !stats_out.is_open(); }); dbc::post("a post test", [&]() { return !stats_out.is_open(); });
} }
ButtonOption Style() {
auto option = ButtonOption::Animated();
option.transform = [](const EntryState& s) {
auto element = paragraph(s.label);
if (s.focused) {
element |= bold;
}
return element | center | borderEmpty | flex;
};
return option;
}
int main_loop(UpdateListener* listener, efsw::FileWatcher* fileWatcher, GameEngine &game, const char *build_cmd) {
auto screen = ScreenInteractive::Fullscreen();
screen.TrackMouse(true);
// Create a component counting the number of frames drawn and event handled.
float scroll_x = 0.1;
float scroll_y = 1.0;
auto status = Renderer([&] {
return vbox({
paragraph(fmt::format("HP {}", game.hit_points)),
separator(),
hbox({
text("HP"),
gauge(game.hit_points / 100.0f),
}),
});
});
auto content = Renderer([&] {
vector<Element> output;
for(const auto line : lines) {
output.push_back(text(line));
}
return vbox(output);
});
auto scrollable_content = Renderer(content,
[&, content] {
return content->Render()
| focusPositionRelative(scroll_x, scroll_y)
| frame | flex;
});
auto component = Renderer(scrollable_content, [&] {
return vbox({
status->Render(),
separator(),
scrollable_content->Render() | vscroll_indicator | yframe | size(HEIGHT, LESS_THAN, 20),
}) |
border;
});
component |= CatchEvent([&](Event) -> bool {
return false;
});
Loop loop(&screen, component);
while (!loop.HasQuitted()) {
fileWatcher->watch();
if(listener->changes) {
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
output(format("CHANGES! Running build {}", build_cmd));
run_build(game, build_cmd);
listener->reset_state();
}
loop.RunOnce();
screen.Post(Event::Custom);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return EXIT_SUCCESS;
}
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
git_repository* repo = nullptr; git_repository* repo = nullptr;
@ -119,7 +222,7 @@ int main(int argc, char *argv[])
const char *git_path = argv[1]; const char *git_path = argv[1];
const char *build_cmd = argv[2]; const char *build_cmd = argv[2];
println("Using build command: {}", build_cmd); output(format("Using build command: {}", build_cmd));
efsw::FileWatcher* fileWatcher = new efsw::FileWatcher(); efsw::FileWatcher* fileWatcher = new efsw::FileWatcher();
dbc::check(fileWatcher != nullptr, "Failed to create filewatcher."); dbc::check(fileWatcher != nullptr, "Failed to create filewatcher.");
@ -131,27 +234,17 @@ int main(int argc, char *argv[])
UpdateListener* listener = new UpdateListener(repo); UpdateListener* listener = new UpdateListener(repo);
dbc::check(listener != nullptr, "Failed to create listener."); dbc::check(listener != nullptr, "Failed to create listener.");
print("Watching directory {} for changes...\n", git_path); output(format("Watching directory {} for changes...\n", git_path));
efsw::WatchID wid = fileWatcher->addWatch(git_path, listener, true); efsw::WatchID wid = fileWatcher->addWatch(git_path, listener, true);
GameEngine game{100}; GameEngine game{100};
while(true) { int rc = main_loop(listener, fileWatcher, game, build_cmd);
print("WAITING...\r"); dbc::check(rc == 0, "Invalid return from main_loop.");
fileWatcher->watch();
if(listener->changes) {
sleep(1);
println("CHANGES! Running build {}", build_cmd);
run_build(game, build_cmd);
listener->reset_state();
}
sleep(1);
}
git_libgit2_shutdown(); git_libgit2_shutdown();
} catch(dbc::Error &err) { } catch(dbc::Error &err) {
print("ERROR: {}\n", err.message); output(format("ERROR: {}\n", err.message));
if(repo != nullptr) git_repository_free(repo); if(repo != nullptr) git_repository_free(repo);
git_libgit2_shutdown(); git_libgit2_shutdown();
return 1; return 1;

@ -37,33 +37,13 @@ int main() {
vector<string> lines; vector<string> lines;
// Create a component counting the number of frames drawn and event handled. // Create a component counting the number of frames drawn and event handled.
int custom_loop_count = 0;
int frame_count = 0;
int event_count = 0;
float scroll_x = 0.1; float scroll_x = 0.1;
float scroll_y = 1.0; float scroll_y = 1.0;
int hp = 100; int hp = 100;
int row = 0;
auto hit_button = Button("Hit", [&] {
hp -= 1;
lines.push_back(fmt::format("You were hit! HP now {}", hp));
}, Style());
auto hard_button = Button("Hard", [&] {
hp -= 10;
lines.push_back(fmt::format("You were hit HARD! HP now {}", hp));
}, Style());
auto heal_button = Button("Heal", [&] {
hp += 10;
lines.push_back(fmt::format("You healed! HP now {}", hp));
}, Style());
auto buttons = Container::Horizontal({
hit_button, hard_button, heal_button}, &row);
auto status = Renderer([&] { auto status = Renderer([&] {
return vbox({ return vbox({
paragraph(fmt::format("HP {} frames {} events {} custom {}", paragraph(fmt::format("HP {}", hp)),
hp, frame_count, event_count, custom_loop_count)),
separator(), separator(),
hbox({ hbox({
text("HP"), text("HP"),
@ -89,27 +69,22 @@ int main() {
| frame | flex; | frame | flex;
}); });
auto component = Renderer(buttons, [&] { auto component = Renderer(scrollable_content, [&] {
frame_count++;
return vbox({ return vbox({
status->Render(), status->Render(),
separator(), separator(),
buttons->Render(),
scrollable_content->Render() | vscroll_indicator | yframe | size(HEIGHT, LESS_THAN, 20), scrollable_content->Render() | vscroll_indicator | yframe | size(HEIGHT, LESS_THAN, 20),
}) | }) |
border; border;
}); });
component |= CatchEvent([&](Event) -> bool { component |= CatchEvent([&](Event) -> bool {
event_count++;
return false; return false;
}); });
Loop loop(&screen, component); Loop loop(&screen, component);
while (!loop.HasQuitted()) { while (!loop.HasQuitted()) {
custom_loop_count++;
loop.RunOnce(); loop.RunOnce();
screen.Post(Event::Custom); screen.Post(Event::Custom);
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));

@ -26,7 +26,6 @@ class Brainfucker {
}; };
class GameEngine { class GameEngine {
int hit_points = 0;
map<string, int> damage_types{ map<string, int> damage_types{
{"error", 4}, {"error", 4},
{"warning", 1}, {"warning", 1},
@ -34,6 +33,8 @@ class GameEngine {
}; };
public: public:
int hit_points = 0;
GameEngine(int hp); GameEngine(int hp);
int determine_damage(string &type); int determine_damage(string &type);

Loading…
Cancel
Save