After some prototyping I have what I think I want for the map. Just a simple piece of paper you take out that has the ASCII map on it.

master
Zed A. Shaw 1 week ago
parent acbf384e2a
commit 6c9016eb0f
  1. 2
      Makefile
  2. 3
      assets/config.json
  3. BIN
      assets/paper_ui_background.png
  4. 6
      assets/tiles.json
  5. 1
      constants.hpp
  6. 17
      guecs.cpp
  7. 77
      guecs.hpp
  8. 32
      gui_fsm.cpp
  9. 3
      gui_fsm.hpp
  10. 2
      main.cpp
  11. 67
      map_view.cpp
  12. 5
      map_view.hpp
  13. 2
      meson.build
  14. 1
      raycaster.cpp
  15. 2
      raycaster.hpp
  16. 4
      ritual_ui.cpp
  17. 1020
      scripts/gcovr_patched_coverage.py
  18. 11
      status_ui.cpp
  19. 3
      status_ui.hpp
  20. 17
      tilemap.cpp
  21. 1
      tilemap.hpp

@ -22,7 +22,7 @@ tracy_build:
meson compile -j 10 -C builddir
test: build
./builddir/runtests "[animation]"
./builddir/runtests
run: build test
powershell "cp ./builddir/zedcaster.exe ."

@ -53,7 +53,8 @@
"tunnel_with_rocks": "assets/tunnel_with_rocks.png",
"tunnel_with_rocks_stage": "assets/tunnel_with_rocks_stage.png",
"ritual_crafting_area": "assets/ritual_crafting_area.png",
"the_ritual_circle": "assets/the_ritual_circle.png"
"the_ritual_circle": "assets/the_ritual_circle.png",
"paper_ui_background": "assets/paper_ui_background.png"
},
"worldgen": {
"enemy_probability": 50,

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

@ -4,20 +4,20 @@
"foreground": [40, 15, 125],
"background": [200, 15, 75],
"collision": false,
"display":"\u289e"
"display":"."
},
"WALL_PLAIN": {
"texture": "assets/wall_texture_test-256.png",
"foreground": [230, 20, 30],
"background": [230, 20, 120],
"collision": true,
"display": "\ua5b8"
"display": "#"
},
"WALL_VINES": {
"texture": "assets/wall_with_vines-256.png",
"foreground": [230, 20, 30],
"background": [230, 20, 120],
"collision": false,
"display":"\u0799"
"display":"|"
}
}

@ -26,6 +26,7 @@ constexpr const int GUECS_PADDING = 3;
constexpr const int GUECS_BORDER_PX = 1;
constexpr const int GUECS_FONT_SIZE = 30;
const sf::Color GUECS_FILL_COLOR = ColorValue::DARK_MID;
const sf::Color GUECS_TEXT_COLOR = ColorValue::LIGHT_LIGHT;
const sf::Color GUECS_BG_COLOR = ColorValue::MID;
const sf::Color GUECS_BORDER_COLOR = ColorValue::MID;

@ -64,9 +64,18 @@ namespace guecs {
text.init(cell, $font);
});
$world.query<lel::Cell, WideText>([this](auto, auto& cell, auto& text) {
text.init(cell, $font);
});
$world.query<lel::Cell, WideLabel>([this](auto, auto& cell, auto& text) {
text.init(cell, $font);
});
$world.query<lel::Cell, Sprite>([&](auto, auto &cell, auto &sprite) {
sprite.init(cell);
});
}
void UI::render(sf::RenderWindow& window) {
@ -94,6 +103,14 @@ namespace guecs {
window.draw(*text.text);
});
$world.query<WideLabel>([&](auto, auto& text) {
window.draw(*text.text);
});
$world.query<WideText>([&](auto, auto& text) {
window.draw(*text.text);
});
$world.query<Textual>([&](auto, auto& text) {
window.draw(*text.text);
});

@ -18,12 +18,34 @@ namespace guecs {
struct Label {
std::string label;
unsigned int size = GUECS_FONT_SIZE;
sf::Color color = GUECS_TEXT_COLOR;
shared_ptr<sf::Font> font = nullptr;
shared_ptr<sf::Text> text = nullptr;
void init(lel::Cell &cell, shared_ptr<sf::Font> font_ptr) {
dbc::check(font_ptr != nullptr, "you failed to initialize this Label");
if(font == nullptr) font = font_ptr;
if(text == nullptr) text = make_shared<sf::Text>(*font, label, size);
text->setFillColor(color);
auto bounds = text->getLocalBounds();
auto text_cell = lel::center(bounds.size.x, bounds.size.y, cell);
// this stupid / 2 is because SFML renders from baseline rather than from the claimed bounding box
text->setPosition({float(text_cell.x), float(text_cell.y) - text_cell.h / 2});
}
};
struct WideLabel {
std::wstring label;
unsigned int size = GUECS_FONT_SIZE;
sf::Color color = GUECS_TEXT_COLOR;
shared_ptr<sf::Font> font = nullptr;
shared_ptr<sf::Text> text = nullptr;
void init(lel::Cell &cell, shared_ptr<sf::Font> font_ptr) {
dbc::check(font_ptr != nullptr, "you failed to initialize this WideLabel");
if(font == nullptr) font = font_ptr;
if(text == nullptr) text = make_shared<sf::Text>(*font, label, size);
text->setFillColor(color);
auto bounds = text->getLocalBounds();
auto text_cell = lel::center(bounds.size.x, bounds.size.y, cell);
// this stupid / 2 is because SFML renders from baseline rather than from the claimed bounding box
@ -34,13 +56,17 @@ namespace guecs {
struct Textual {
std::string content;
unsigned int size = GUECS_FONT_SIZE;
sf::Color color = GUECS_TEXT_COLOR;
int padding = GUECS_PADDING;
shared_ptr<sf::Font> font = nullptr;
shared_ptr<sf::Text> text = nullptr;
void init(lel::Cell &cell, shared_ptr<sf::Font> font_ptr) {
dbc::check(font_ptr != nullptr, "you failed to initialize this Text");
if(font == nullptr) font = font_ptr;
if(text == nullptr) text = make_shared<sf::Text>(*font, content, size);
text->setPosition({float(cell.x + GUECS_PADDING * 2), float(cell.y + GUECS_PADDING * 2)});
text->setFillColor(color);
text->setPosition({float(cell.x + padding * 2), float(cell.y + padding * 2)});
text->setCharacterSize(size);
}
@ -50,6 +76,29 @@ namespace guecs {
}
};
struct WideText {
std::wstring content;
unsigned int size = GUECS_FONT_SIZE;
sf::Color color = GUECS_TEXT_COLOR;
int padding = GUECS_PADDING;
shared_ptr<sf::Font> font = nullptr;
shared_ptr<sf::Text> text = nullptr;
void init(lel::Cell &cell, shared_ptr<sf::Font> font_ptr) {
dbc::check(font_ptr != nullptr, "you failed to initialize this WideText");
if(font == nullptr) font = font_ptr;
if(text == nullptr) text = make_shared<sf::Text>(*font, content, size);
text->setFillColor(color);
text->setPosition({float(cell.x + padding * 2), float(cell.y + padding * 2)});
text->setCharacterSize(size);
}
void update(std::wstring& new_content) {
content = new_content;
text->setString(content);
}
};
struct Clickable {
/* This is actually called by UI::mouse and passed the entity ID of the
* button pressed so you can interact with it in the event handler.
@ -59,6 +108,7 @@ namespace guecs {
struct Sprite {
std::string name;
int padding = GUECS_PADDING;
std::shared_ptr<sf::Sprite> sprite = nullptr;
std::shared_ptr<sf::Texture> texture = nullptr;
@ -67,26 +117,30 @@ namespace guecs {
texture = sprite_texture.texture;
sprite = make_shared<sf::Sprite>(*texture);
sprite->setPosition({
float(cell.x + GUECS_PADDING),
float(cell.y + GUECS_PADDING)});
float(cell.x + padding),
float(cell.y + padding)});
auto size = texture->getSize();
sprite->setScale({
float(cell.w - GUECS_PADDING * 2) / size.x,
float(cell.h - GUECS_PADDING * 2) / size.y});
float(cell.w - padding * 2) / size.x,
float(cell.h - padding * 2) / size.y});
}
};
struct Rectangle {
int padding = GUECS_PADDING;
sf::Color color = GUECS_FILL_COLOR;
sf::Color border_color = GUECS_BORDER_COLOR;
int border_px = GUECS_BORDER_PX;
shared_ptr<sf::RectangleShape> shape = nullptr;
void init(lel::Cell& cell) {
sf::Vector2f size{float(cell.w) - GUECS_PADDING * 2, float(cell.h) - GUECS_PADDING * 2};
sf::Vector2f size{float(cell.w) - padding * 2, float(cell.h) - padding * 2};
if(shape == nullptr) shape = make_shared<sf::RectangleShape>(size);
shape->setPosition({float(cell.x + GUECS_PADDING), float(cell.y + GUECS_PADDING)});
shape->setFillColor(GUECS_FILL_COLOR);
shape->setOutlineColor(GUECS_BORDER_COLOR);
shape->setOutlineThickness(GUECS_BORDER_PX);
shape->setPosition({float(cell.x + padding), float(cell.y + padding)});
shape->setFillColor(color);
shape->setOutlineColor(border_color);
shape->setOutlineThickness(border_px);
}
};
@ -112,6 +166,7 @@ namespace guecs {
float y = 0.0f;
float w = 0.0f;
float h = 0.0f;
sf::Color color = GUECS_BG_COLOR;
shared_ptr<sf::RectangleShape> shape = nullptr;
@ -128,7 +183,7 @@ namespace guecs {
sf::Vector2f size{float(w), float(h)};
if(shape == nullptr) shape = make_shared<sf::RectangleShape>(size);
shape->setPosition({float(x), float(y)});
shape->setFillColor(GUECS_BG_COLOR);
shape->setFillColor(color);
}
};

@ -16,7 +16,6 @@ namespace gui {
$window(sf::VideoMode({SCREEN_WIDTH, SCREEN_HEIGHT}), "Zed's Raycaster Thing"),
$main_ui($window),
$level($levels.current()),
$map_ui($level),
$combat_ui($level),
$status_ui($level),
$font{FONT_FILE_NAME}
@ -28,7 +27,6 @@ namespace gui {
FSM_STATE(State, START, ev);
FSM_STATE(State, MOVING, ev);
FSM_STATE(State, ATTACKING, ev);
FSM_STATE(State, MAPPING, ev);
FSM_STATE(State, ROTATING, ev);
FSM_STATE(State, IDLE, ev);
FSM_STATE(State, IN_COMBAT, ev);
@ -41,10 +39,7 @@ namespace gui {
void FSM::START(Event ) {
$main_ui.update_level($level);
$level.world->set_the<Debug>({});
$main_ui.init();
$map_ui.init();
$combat_ui.init();
$status_ui.init();
$status_ui.log("Welcome to the game!");
@ -57,23 +52,6 @@ namespace gui {
state(State::IDLE);
}
void FSM::MAPPING(Event ev) {
using enum Event;
// BUG: can't close window when in mapping
switch(ev) {
case MAP_OPEN:
state(State::IDLE);
break;
case CLOSE:
state(State::IDLE);
break;
case TICK:
break;
default:
dbc::log("invalid event sent to MAPPING");
}
}
void FSM::MOVING(Event ) {
// this should be an optional that returns a point
if(auto move_to = $main_ui.play_move()) {
@ -148,7 +126,7 @@ namespace gui {
state(State::ROTATING);
break;
case MAP_OPEN:
state(State::MAPPING);
$status_ui.map_open = !$status_ui.map_open;
break;
case ATTACK:
state(State::ATTACKING);
@ -313,16 +291,15 @@ namespace gui {
$boss_fight_ui->render($window);
} else {
$main_ui.render();
$status_ui.render($window);
$combat_ui.render($window);
}
}
void FSM::render() {
if(in_state(State::MAPPING)) {
$window.clear();
$map_ui.render($window);
} else if(in_state(State::NEXT_LEVEL)) {
if(in_state(State::NEXT_LEVEL)) {
$window.clear();
$boss_fight_ui->render($window);
} else {
@ -416,7 +393,6 @@ namespace gui {
$status_ui.update_level($level);
$combat_ui.update_level($level);
$map_ui.update_level($level);
$main_ui.update_level($level);
$boss_fight_ui = $levels.create_bossfight($level.world);
$boss_fight_ui->init();

@ -3,7 +3,6 @@
#include "stats.hpp"
#include "levelmanager.hpp"
#include "fsm.hpp"
#include "map_view.hpp"
#include "main_ui.hpp"
#include "combat_ui.hpp"
#include "status_ui.hpp"
@ -16,7 +15,6 @@ namespace gui {
IN_COMBAT,
COMBAT_ROTATE,
ATTACKING,
MAPPING,
ROTATING,
NEXT_LEVEL,
IDLE,
@ -50,7 +48,6 @@ namespace gui {
MainUI $main_ui;
GameLevel $level;
shared_ptr<BossFightUI> $boss_fight_ui = nullptr;
MapViewUI $map_ui;
CombatUI $combat_ui;
StatusUI $status_ui;
sf::Font $font;

@ -13,7 +13,6 @@ int main(int argc, char* argv[]) {
ai::init("assets/ai.json");
animation::init();
sound::mute(true);
gui::FSM main;
main.event(gui::Event::STARTED);
@ -31,7 +30,6 @@ int main(int argc, char* argv[]) {
// ZED: need to sort out how to deal with this in the FSM
if(main.in_state(gui::State::IDLE)
|| main.in_state(gui::State::NEXT_LEVEL)
|| main.in_state(gui::State::MAPPING)
|| main.in_state(gui::State::IN_COMBAT))
{
if(main.autowalking) {

@ -6,64 +6,63 @@
#include "rand.hpp"
#include "animation.hpp"
#include "rand.hpp"
#include <codecvt>
#include <iostream>
namespace gui {
using namespace components;
MapViewUI::MapViewUI(GameLevel &level) :
$level(level)
$level(level), $tiles(level.map->width(), level.map->height())
{
$gui.position(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
$gui.layout(
"[*%(100,900)left|*%(200,900)map_grid| _]"
"[_ | _ | _]"
"[_ | _ | _]"
"[_ | _ | _]"
"[_ | _ | _]"
"[_ | _ | _]"
"[_ | _ | _]"
"[_ | _ | _]"
"[_ | _ | _]"
"[bottom_status_left | bottom_status_right ]");
auto cell = $gui.cell_for($gui.entity("map_grid"));
$grid.position(cell.x, cell.y, cell.w, cell.h);
$grid.layout(
"[cell_11|cell_12|cell_13|cell_14|cell_15|cell_16|cell_17]"
"[cell_21|cell_22|cell_23|cell_24|cell_25|cell_26|cell_27]"
"[cell_31|cell_32|cell_33|cell_34|cell_35|cell_36|cell_37]"
"[cell_41|cell_42|cell_43|cell_44|cell_45|cell_46|cell_47]"
"[cell_51|cell_52|cell_53|cell_54|cell_55|cell_56|cell_57]");
}
void MapViewUI::update_level(GameLevel &level) {
$level = level;
}
void MapViewUI::init() {
$gui.world().set_the<guecs::Background>({$gui.$parser});
void MapViewUI::init(int x, int y, int w, int h) {
$gui.position(x, y, w, h);
$gui.layout(
"[*%(100,900)map_grid]"
"[_ ]"
"[_ ]"
"[_ ]"
"[_ ]"
"[_ ]"
"[_ ]"
"[_ ]"
"[_ ]");
for(auto& [name, cell] : $gui.cells()) {
auto box = $gui.entity(name);
if(name != "map_grid") {
if(name == "status") {
$gui.set<guecs::Sprite>(box, {"paper_ui_background"});
} else if(name != "map_grid") {
$gui.set<guecs::Rectangle>(box, {});
$gui.set<guecs::Label>(box, {name});
}
}
$gui.init();
auto grid = $gui.entity("map_grid");
$gui.set<guecs::WideText>(grid, {L"Loading...", 25, ColorValue::DARK_LIGHT, 20});
$gui.set<guecs::Sprite>(grid, {"paper_ui_background"});
for(auto& [name, cell] : $grid.cells()) {
auto box = $grid.entity(name);
$grid.set<guecs::Rectangle>(box, {});
$grid.set<guecs::Label>(box, {name});
}
$gui.init();
$grid.init();
}
void MapViewUI::render(sf::RenderWindow &window) {
$tiles = $level.map->tiles();
auto grid = $gui.entity("map_grid");
auto player_pos = $level.world->get<Position>($level.player);
std::string map_out = $tiles.to_string(player_pos.location.x, player_pos.location.y);
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
std::wstring map_wstr = converter.from_bytes(map_out);
auto& map_text = $gui.get<guecs::WideText>(grid);
map_text.update(map_wstr);
$gui.render(window);
$grid.render(window);
}
}

@ -2,16 +2,17 @@
#include "levelmanager.hpp"
#include "textures.hpp"
#include "guecs.hpp"
#include "tilemap.hpp"
namespace gui {
class MapViewUI {
public:
guecs::UI $gui;
guecs::UI $grid;
GameLevel $level;
TileMap $tiles;
MapViewUI(GameLevel &level);
void init();
void init(int x, int y, int w, int h);
void render(sf::RenderWindow &window);
void update_level(GameLevel &level);
};

@ -1,3 +1,5 @@
# clang might need _LIBCPP_ENABLE_CXX26_REMOVED_CODECVT
project('raycaster', 'cpp',
version: '0.1.0',
default_options: [

@ -379,6 +379,7 @@ void Raycaster::update_level(GameLevel level) {
$sprites.clear();
$level = level;
// BUG: this is way too complex, please make it easier, the issue is that I need to convert the maps to visible tiles and that involves wstring convert, but this is many steps done probably over and over
auto& tiles = $level.map->tiles();
$map = textures::convert_char_to_texture(tiles.$tile_ids);

@ -21,7 +21,7 @@ struct Raycaster {
double $dir_y = 0;
// the 2d raycaster version of camera plane
double $plane_x = 0;
double $plane_x = 0.0;
double $plane_y = 0.66;
sf::Texture $view_texture;
sf::Sprite $view_sprite;

@ -124,7 +124,9 @@ namespace gui {
}
}
/* WARNING: This is really not the greatest way to do this. */
/* WARNING: This is really not the greatest way to do this.
* look in status_ui.update_level()
* */
void RitualUI::update() {
dbc::log("RITUAL UPDATE NOT IMPLEMENTED");
}

File diff suppressed because it is too large Load Diff

@ -10,7 +10,8 @@ namespace gui {
using std::any, std::any_cast, std::string, std::make_any;
StatusUI::StatusUI(GameLevel level) :
$level(level), $ritual_ui(level)
$level(level), $ritual_ui(level),
$map_ui($level)
{
$gui.position(STATUS_UI_X, STATUS_UI_Y, STATUS_UI_WIDTH, STATUS_UI_HEIGHT);
$gui.layout(
@ -37,6 +38,8 @@ namespace gui {
$log_to = $gui.entity("log_view");
$gui.set<Rectangle>($log_to, {});
$gui.set<Textual>($log_to, {"Welcome to the Game!", 20});
$map_ui.init(cell.x, cell.y, cell.w, cell.h);
} else {
auto button = $gui.entity(name);
$gui.set<Rectangle>(button, {});
@ -134,6 +137,11 @@ namespace gui {
void StatusUI::render(sf::RenderWindow &window) {
$gui.render(window);
if(map_open) {
$map_ui.render(window);
}
$ritual_ui.render(window);
}
@ -146,6 +154,7 @@ namespace gui {
void StatusUI::update_level(GameLevel &level) {
$level = level;
$map_ui.update_level($level);
init();
}
}

@ -5,16 +5,19 @@
#include "textures.hpp"
#include "guecs.hpp"
#include "ritual_ui.hpp"
#include "map_view.hpp"
namespace gui {
class StatusUI {
public:
bool map_open = false;
guecs::UI $gui;
DinkyECS::Entity $log_to;
std::map<std::string, size_t> $slots;
std::deque<std::string> $messages;
GameLevel $level;
RitualUI $ritual_ui;
MapViewUI $map_ui;
StatusUI(GameLevel level);
void select_slot(DinkyECS::Entity ent, std::any data);

@ -1,6 +1,7 @@
#include "tilemap.hpp"
#include "dbc.hpp"
#include "constants.hpp"
#include <iostream>
using nlohmann::json;
using components::Tile;
@ -14,18 +15,26 @@ TileMap::TileMap(size_t width, size_t height) :
{
}
void TileMap::dump(int show_x, int show_y) {
std::string TileMap::to_string(int show_x, int show_y) {
std::string result;
for(matrix::each_row it{$tile_ids}; it.next();) {
const Tile &cell = $display[it.y][it.x];
if(int(it.x) == show_x && int(it.y) == show_y) {
fmt::print("{}<", cell.display);
result += "@";
} else {
fmt::print("{} ", cell.display);
result += cell.display;
}
if(it.row) fmt::print("\n");
if(it.row) result += "\n";
}
return result;
}
void TileMap::dump(int show_x, int show_y) {
std::cout << to_string(show_x, show_y) << std::endl;
}
void TileMap::set_tile(size_t x, size_t y, string tile_name) {

@ -32,6 +32,7 @@ public:
void set_tile(size_t x, size_t y, std::string tile_name);
std::vector<std::string> tile_names(bool collision);
std::string to_string(int show_x, int show_y);
void dump(int show_x=-1, int show_y=-1);
bool INVARIANT();
};

Loading…
Cancel
Save