Skip to content

Commit

Permalink
Add quest system
Browse files Browse the repository at this point in the history
  • Loading branch information
zyperpl committed Dec 28, 2023
1 parent a5fb5dd commit e170252
Show file tree
Hide file tree
Showing 9 changed files with 117 additions and 35 deletions.
Binary file added screenrec002.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 17 additions & 6 deletions src/action.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,19 @@ void Game::play_action(const Action::Type &action_type, DialogEntity &entity) no

Action action;

action.on_start = [this, &entity](Action &)
action.on_start = [this, &entity](Action &action)
{
TraceLog(LOG_INFO, "Playing dialog %p(%s)", (void *)(&entity), entity.get_dialog_id().c_str());

freeze_entities = true;

dialog = entity.dialog();
assert(dialog);
if (dialog->actor_name == Dialog::END_DIALOG_ID)
{
TraceLog(LOG_INFO, "Dialog ended");
action.is_done = true;
return;
}

if (!dialog->responses.empty())
selected_dialog_response_index = 0;
Expand Down Expand Up @@ -170,15 +175,21 @@ void Game::play_action(const Action::Type &action_type, DialogEntity &entity) no
assert(selected_dialog_response_index.value() < dialog->responses.size());
const auto &response = dialog->responses[selected_dialog_response_index.value()];
const auto &next_dialog_id = response.next_dialog_id;

if (response.func)
response.func();

if (next_dialog_id.starts_with('_'))
{
TraceLog(LOG_WARNING, "Unknown dialog id: %s", next_dialog_id.c_str());
if (next_dialog_id == "_end")
{
TraceLog(LOG_INFO, "Dialog ended");
}
else
TraceLog(LOG_WARNING, "Unknown dialog id: %s", next_dialog_id.c_str());
}
else
{
if (response.func)
response.func();

action.data = DialogId{ next_dialog_id };
}
}
Expand Down
72 changes: 45 additions & 27 deletions src/dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@
#include <unordered_map>
#include <vector>

#include "game.hpp"

const std::string Dialog::START_DIALOG_ID = "_start";
const std::string Dialog::END_DIALOG_ID = "_end";

const Dialog Dialog::END_DIALOG{ Dialog::END_DIALOG_ID, "End of the conversation", {} };

static std::unordered_map<std::string, bool> introduced;
static std::unordered_map<std::string, bool> quest_accepted;

[[nodiscard]] bool is_introduced(const std::string &name)
{
Expand All @@ -19,13 +23,6 @@ static std::unordered_map<std::string, bool> quest_accepted;
return introduced[name];
}

[[nodiscard]] bool is_quest_accepted(const std::string &name)
{
if (!quest_accepted.contains(name))
return false;
return quest_accepted[name];
}

std::unordered_map<DialogId, Dialog> Dialog::load_dialogs(const std::string &name)
{
static std::unordered_map<std::string, DialogMap> dialogs;
Expand All @@ -47,27 +44,26 @@ std::unordered_map<DialogId, Dialog> Dialog::load_dialogs(const std::string &nam
return std::nullopt;
} });

captain_dialogs.emplace(
"name",
Dialog{ "Captain",
"Nice to meet you, James. I'm glad to see you.\n"
"We have a problem. Our ship is damaged and we\n"
"need to repair it. Can you help us?",
{
{ "Yes", "help", [name]() { quest_accepted.insert_or_assign("captain1", true); } },
{ "No", "no_help" },
},
[name]() -> std::optional<DialogId>
{
if (is_quest_accepted("captain1"))
return "help2";
return std::nullopt;
} });
captain_dialogs.emplace("name",
Dialog{ "Captain",
"Nice to meet you, James. I'm glad to see you.\n"
"We have a problem. Our ship is damaged and we\n"
"need to repair it. Can you help us?",
{
{ "Yes", "help", []() { QUEST("captain1").accept(); } },
{ "No", "no_help" },
},
[]() -> std::optional<DialogId>
{
if (QUEST("captain1").is_accepted())
return "help2";
return std::nullopt;
} });

captain_dialogs.emplace("help",
Dialog{ "Captain",
"Thank you, James. I'm glad to hear that.\n"
"We need to $1collect 10 crystals$0 to repair the ship.\n"
"We need to $1collect 10 crystals$0 to repair the station.\n"
"You can find them in the asteroids.\n"
"Be careful, there are many dangers in space.",
{
Expand All @@ -76,16 +72,22 @@ std::unordered_map<DialogId, Dialog> Dialog::load_dialogs(const std::string &nam

captain_dialogs.emplace("help2",
Dialog{ "Captain",
"We need to $1collect 10 crystals$0 to repair the ship.\n"
"We need to $1collect 10 crystals$0 to repair the station.\n"
"You can find them in the asteroids.\n"
"Be careful, there are many dangers in space.",
{
{ "Ok", "help_ok" },
},
[&]() -> std::optional<DialogId>
{
if (QUEST("captain1").is_completed())
return "completed1";
return std::nullopt;
} });

captain_dialogs.emplace("help_ok",
Dialog{ "Captain",
"Good luck, James. I'll be waiting for you on the ship.",
"Good luck, James. I'll be waiting for you on the station.",
{
{ "Goodbye", "_end" },
} });
Expand All @@ -97,6 +99,22 @@ std::unordered_map<DialogId, Dialog> Dialog::load_dialogs(const std::string &nam
{ "Goodbye", "_end" },
} });

captain_dialogs.emplace("completed1",
Dialog{ "Captain",
"Thank you for collecting $1the crystals$0!\n"
"Now we can repair the station.",
{
{ "Goodbye", "_end", []() { QUEST("captain1").report(); } },
},
[]() -> std::optional<DialogId>
{
if (QUEST("captain1").is_reported())
return "hello";
return std::nullopt;
} });

captain_dialogs.emplace("hello", Dialog{ "Captain", "Hello, James.", { { "Goodbye", "_end" } } });

dialogs.emplace("captain", captain_dialogs);
}

Expand Down
3 changes: 3 additions & 0 deletions src/dialog.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ struct Dialog

static DialogMap load_dialogs(const std::string &name);
static const std::string START_DIALOG_ID;
static const std::string END_DIALOG_ID;

static const Dialog END_DIALOG;

private:
};
5 changes: 5 additions & 0 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ void Game::init()
for (size_t i = 0; i < stars.size(); i++)
stars[i] = Vector2{ static_cast<float>(GetRandomValue(0, width)), static_cast<float>(GetRandomValue(0, height)) };

quests.emplace("captain1",
Quest{ .description = "Collect 10 crystals",
.progress = []() { return GAME.coins; },
.max_progress = []() { return 10; } });

TraceLog(LOG_TRACE, "Game initialized");
}

Expand Down
8 changes: 6 additions & 2 deletions src/game.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
#include <raymath.h>

#include "dialog.hpp"
#include "quest.hpp"

#define CONFIG(Option) Game::config.Option
#define GAME Game::get()
#define CONFIG(Option) Game::config.Option
#define GAME Game::get()
#define QUEST(quest_name) Game::get().quests.at(quest_name)

class Sprite;
class Player;
Expand Down Expand Up @@ -137,6 +139,8 @@ class Game
Font font;
Font dialog_font;

std::unordered_map<std::string, Quest> quests;

private:
[[nodiscard]] Game() noexcept = default;

Expand Down
3 changes: 3 additions & 0 deletions src/interactable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ class DialogEntity final : public Interactable
private:
[[nodiscard]] const Dialog &get_dialog(const DialogId &dialog_id) const
{
if (dialog_id == Dialog::END_DIALOG_ID)
return Dialog::END_DIALOG;

assert(dialogs.contains(dialog_id));

auto &dialog = dialogs.at(dialog_id);
Expand Down
16 changes: 16 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,22 @@ void update_draw_frame()
}
}
}

{
const float quest_x = Game::width - 200.0f;
float quest_y = 10.0f;
for (const auto &[quest_name, quest] : game.quests)
{
if (!quest.is_accepted() || quest.is_reported())
continue;

const auto &quest_text =
TextFormat("%s: %i/%i", quest.description.c_str(), quest.progress(), quest.max_progress());
const Color color = quest.is_completed() ? GREEN : WHITE;
DrawTextEx(GAME.font, quest_text, Vector2{ quest_x, quest_y }, font_size, 1.0f, color);
quest_y += font_size + 5.0f;
}
}
};

const float screen_width_float = static_cast<float>(GetScreenWidth());
Expand Down
22 changes: 22 additions & 0 deletions src/quest.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#include <functional>
#include <string>

struct Quest
{
std::string description{};
std::function<size_t()> progress;
std::function<size_t()> max_progress;

[[nodiscard]] bool is_accepted() const noexcept { return accepted; }
void accept() noexcept { accepted = true; }

[[nodiscard]] bool is_completed() const noexcept { return progress() >= max_progress(); }

[[nodiscard]] bool is_reported() const noexcept { return reported; }
void report() noexcept { reported = true; }

bool accepted{ false };
bool reported{ false };
};

0 comments on commit e170252

Please sign in to comment.