Skip to content

Commit

Permalink
Add quests and make game beatable
Browse files Browse the repository at this point in the history
  • Loading branch information
zyperpl committed Jan 7, 2024
1 parent 8010260 commit 60db7fb
Show file tree
Hide file tree
Showing 14 changed files with 2,331 additions and 238 deletions.
1,893 changes: 1,782 additions & 111 deletions resources/station.ldtk

Large diffs are not rendered by default.

15 changes: 8 additions & 7 deletions src/action.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -500,13 +500,14 @@ void Game::schedule_action_mission_select(const Interactable *interactable) noex
continue;
}

ship_items->push_back(ShopItem{ .name = mparams.name,
.description = mparams.description,
.price = 0,
.on_accept = [this, interactable, mnumber]
{ schedule_action_change_level(Level::Asteroids, mnumber, interactable); },
.on_has_item = [](const ShopItem &) { return false; },
.on_is_available = [](const ShopItem &) { return true; } });
ship_items->push_back(
ShopItem{ .name = mparams.name,
.description = mparams.description,
.price = 0,
.on_accept = [this, interactable, mnumber]
{ schedule_action_change_level(Level::Asteroids, mnumber, interactable); },
.on_has_item = [](const ShopItem &) { return false; },
.on_is_available = [unlocked = mparams.is_unlocked()](const ShopItem &) { return unlocked; } });
}

if (!ship_items->empty())
Expand Down
20 changes: 20 additions & 0 deletions src/asteroid.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,26 @@ void Asteroid::die()
GAME.pickables->push(Pickable::create_ore(pos, Vector2Scale(velocity, 0.5f)));
}
}

if (GAME.current_mission == 4 && QUEST("scientist1").is_accepted() && GAME.artifacts.empty())
{
if (GetRandomValue(0, 100) < 10)
{
bool found = false;
GAME.pickables->for_each(
[&](Pickable &pickable)
{
if (pickable.type == Pickable::Type::Artifact)
{
found = true;
return;
}
});

if (!found)
GAME.pickables->push(Pickable::create_artifact(position, Vector2Scale(velocity, 0.9f)));
}
}
}
else if (type == Type::Crystal)
{
Expand Down
262 changes: 252 additions & 10 deletions src/dialog.cpp

Large diffs are not rendered by default.

141 changes: 87 additions & 54 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@

void MissionParameters::unlock() noexcept
{
if (unlocked)
return;

unlocked = true;
GAME.gui->show_message("Mission unlocked: " + name);

if (!name.starts_with('_'))
GAME.gui->show_message("Mission unlocked: " + name);
}

Config Game::config{};
Expand All @@ -37,54 +42,57 @@ void Game::init()

asteroid_bg_sprite = std::make_unique<Sprite>("resources/asteroid.aseprite");

missions = {
{ 0, { .name = "_tutorial", .description = "Ship tutorial", .number_of_asteroids = 3 } },
{ 1, { .name = "Orbital Perimeter", .description = "Destroy all asteroids", .number_of_asteroids = 8 } },
{ 2,
{ .name = "Nearfield Zone",
.description = "Retrieve the crystals",
.number_of_asteroids = 1,
.number_of_asteroid_crystals = 5 } },
{ 3,
{
.name = "Inner asteroid belt",
.description = "Survive asteroids",
.number_of_asteroids = 12,
.number_of_asteroid_crystals = 1,
} },
{ 4,
{
.name = "Close Quarters Space",
.description = "Find artifact",
.number_of_asteroids = 14,
} },
{ 5,
{
.name = "Outer asteroid belt",
.description = "Destroy all asteroids",
.number_of_asteroids = 16,
.number_of_asteroid_crystals = 1,
} },
{ 6,
{
.name = "Trans-Neptunian Region",
.description = "Destroy all enemies",
.number_of_asteroids = 18,
} },
{ 7,
{
.name = "Interstellar Space",
.description = "Survive enemy attack",
.number_of_asteroids = 20,
} },
{ 8,
{
.name = "Galactic Core",
.description = "Destroy all asteroids and enemies",
.number_of_asteroids = 22,
} },
{ 9, { .name = "Intergalactic Space", .description = "Survive", .number_of_asteroids = 24 } },
};
missions = { { 0, { .name = "_tutorial", .description = "Ship tutorial", .number_of_asteroids = 3 } },
{ 1,
{ .name = "Orbital Perimeter",
.description = "Destroy all asteroids",
.number_of_asteroids = 4,
.unlocked = true } },
{ 2,
{ .name = "Nearfield Zone",
.description = "Retrieve the crystals",
.number_of_asteroids = 1,
.number_of_asteroid_crystals = 5 } },
{ 3,
{
.name = "Inner asteroid belt",
.description = "Survive asteroids",
.number_of_asteroids = 12,
.number_of_asteroid_crystals = 1,
} },
{ 4,
{
.name = "Close Quarters Space",
.description = "Find artifact",
.number_of_asteroids = 12,
} },
{ 5,
{
.name = "Outer asteroid belt",
.description = "Destroy all asteroids",
.number_of_asteroids = 13,
.number_of_asteroid_crystals = 1,
} },
{ 6,
{
.name = "Trans-Neptunian Region",
.description = "Destroy all enemies",
.number_of_asteroids = 18,
} },
{ 7,
{
.name = "Interstellar Space",
.description = "Survive enemy attack",
.number_of_asteroids = 20,
} },
{ 8,
{
.name = "Galactic Core",
.description = "Destroy all asteroids and enemies",
.number_of_asteroids = 22,
} },
{ 9, { .name = "Intergalactic Space", .description = "Survive", .number_of_asteroids = 24 } },
{ 10, { .name = "_the_end", .description = "The End", .number_of_asteroids = 0 } } };

Room::load();
room = Room::get(Room::Type::DockingBay);
Expand All @@ -110,7 +118,12 @@ void Game::init()
Quest{ .description = "Collect 10 crystals",
.progress = []() { return GAME.crystals; },
.max_progress = []() { return 10; },
.on_report = []() { GAME.crystals -= 10; } });
.on_report =
[]()
{
GAME.crystals -= 10;
MISSION(3).unlock();
} });

quests.emplace("meet_captain",
Quest{ .description = "Meet the captain",
Expand All @@ -124,6 +137,24 @@ void Game::init()
.max_progress = []() { return 1; },
.on_report = []() { GAME.score += 2000; } });

quests.emplace("scientist1",
Quest{ .description = "Find an alien artifact",
.progress = []() { return GAME.artifacts.size(); },
.max_progress = []() { return 1; },
.on_report = []() { GAME.score += 1000; } });

quests.emplace("meet_captain2",
Quest{ .description = "Talk to the captain",
.progress = []() { return MISSION(6).is_unlocked(); },
.max_progress = []() { return true; },
.on_report = []() { GAME.score += 1000; } });

quests.emplace("all_missions",
Quest{ .description = "Complete all missions",
.progress = []() { return MISSION(10).is_unlocked(); },
.max_progress = []() { return true; },
.on_report = []() { GAME.score += 90000; } });

TraceLog(LOG_TRACE, "Game initialized");
}

Expand Down Expand Up @@ -178,11 +209,13 @@ void Game::update()

update_game();

if (gui && !gui->message_timer.is_done())
if (gui)
{
gui->message_timer.update();
if (gui->message_timer.is_done())
gui->message.clear();
for (auto &message : gui->messages)
message.timer.update();

while (!gui->messages.empty() && gui->messages.front().timer.is_done())
gui->messages.pop_front();
}

frame++;
Expand Down
7 changes: 4 additions & 3 deletions src/game.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@
#include "quest.hpp"
#include "room.hpp"

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

class Sprite;
class Player;
Expand Down
97 changes: 62 additions & 35 deletions src/gui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ void GUI::draw() const noexcept
ui_crystal->position.y = text_position.y + text_size.y * 0.5f;
ui_crystal->draw();

text_position.y += font_size + 5.0f;

if (!game.artifacts.empty())
{
const char *artifacts_text = TextFormat("Artifacts: %i", game.artifacts.size());
DrawTextEx(font, artifacts_text, text_position, font_size, 1.0f, WHITE);
}

// draw quests
{
const float quest_right_margin = 10.0f;
Expand All @@ -109,35 +117,42 @@ void GUI::draw() const noexcept
}
}

if (!message.empty())
if (!messages.empty())
{
if (!message_timer.is_done())
float total_y = Game::height * 0.1f;
for (const auto &message : messages)
{
const float letter_spacing = 0.0f;
const Color color = selection_color();

const Vector2 text_size = MeasureTextEx(font, message.c_str(), font_size, letter_spacing);
const float message_x = std::roundf(Game::width * 0.5f - text_size.x * 0.5f);
const float r = message_timer.get_ratio() * PI;
const float y_offset = std::clamp(std::sin(r), 0.0f, 0.8f) * 1.25f;
const float message_y = std::roundf(-text_size.y - 8.0f + y_offset * Game::height * 0.16f);

const float margin_w = 6.0f;
const float margin_h = 4.0f;
const Rectangle bg_rectangle{
message_x - margin_w, message_y - margin_h, text_size.x + margin_w * 2.0f, text_size.y + margin_h * 2.0f
};
DrawRectangleRounded(bg_rectangle, 0.5f, 12, Color{ 16, 16, 32, 220 });
DrawRectangleRoundedLines(bg_rectangle, 0.5f, 12, 2.0f, Color{ 16, 16, color.g, 250 });

for (int y = -1; y <= 1; y++)
if (!message.timer.is_done())
{
for (int x = -1; x <= 1; x++)
const float letter_spacing = 0.0f;
const Color color = selection_color();

const Vector2 text_size = MeasureTextEx(font, message.text.c_str(), font_size, letter_spacing);
const float message_x = std::roundf(Game::width * 0.5f - text_size.x * 0.5f);
const float r = message.timer.get_ratio() * PI;
const float y_offset = std::clamp(std::sin(r), 0.0f, 0.8f) * 1.25f;
const float message_y = std::roundf(-text_size.y - 8.0f + y_offset * total_y);

const float margin_w = 6.0f;
const float margin_h = 4.0f;
const Rectangle bg_rectangle{
message_x - margin_w, message_y - margin_h, text_size.x + margin_w * 2.0f, text_size.y + margin_h * 2.0f
};
DrawRectangleRounded(bg_rectangle, 0.5f, 12, Color{ 16, 16, 32, 220 });
DrawRectangleRoundedLines(bg_rectangle, 0.5f, 12, 2.0f, Color{ 16, 16, color.g, 250 });

for (int y = -1; y <= 1; y++)
{
DrawTextEx(font, message.c_str(), Vector2{ message_x + x, message_y + y }, font_size, letter_spacing, BLACK);
for (int x = -1; x <= 1; x++)
{
DrawTextEx(
font, message.text.c_str(), Vector2{ message_x + x, message_y + y }, font_size, letter_spacing, BLACK);
}
}
DrawTextEx(font, message.text.c_str(), Vector2{ message_x, message_y }, font_size, letter_spacing, color);

total_y += text_size.y * 2.0f;
}
DrawTextEx(font, message.c_str(), Vector2{ message_x, message_y }, font_size, letter_spacing, color);
}
}
}
Expand Down Expand Up @@ -200,7 +215,8 @@ void GUI::draw_dialog() const noexcept
{ '2', GOLD },
};

if (t.find('$') != std::string::npos)
const bool advanced_text = t.find('$') != std::string::npos || t.find('&') != std::string::npos;
if (advanced_text)
{
const float text_x = dialog_x + 10.0f;
const float text_y = dialog_y + 10.0f + font_size + 5.0f;
Expand All @@ -221,7 +237,25 @@ void GUI::draw_dialog() const noexcept
continue;
}
}
if (c == '\n')
else if (c == '&' && i + 1 < t.size())
{
const char &next = t[i + 1];
if (next == 'U')
{
const float triangle_size = 8.0f;
const float triangle_x = x;
const float triangle_y = y + triangle_size * 0.1f;
DrawTriangle(Vector2{ triangle_x + triangle_size, triangle_y + triangle_size },
Vector2{ triangle_x + triangle_size * 0.5f, triangle_y },
Vector2{ triangle_x, triangle_y + triangle_size },
color);

x += triangle_size + letter_spacing + 1.0f;
i++;
continue;
}
}
else if (c == '\n')
{
x = text_x;
y += line_spacing;
Expand Down Expand Up @@ -578,14 +612,7 @@ void GUI::handle_accepting_index(std::optional<size_t> &index, std::function<voi

void GUI::show_message(const std::string &new_message)
{
if (!message_timer.is_done())
{
message = message + "\n" + new_message;
TraceLog(LOG_WARNING, "GUI::show_message: message is already shown, appending new message");
}
else
{
message = new_message;
message_timer.start();
}
Message message{ new_message };
message.timer.start();
messages.push_back(message);
}
9 changes: 7 additions & 2 deletions src/gui.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <list>
#include <memory>
#include <optional>

Expand Down Expand Up @@ -51,8 +52,12 @@ class GUI
const std::vector<ShopItem> &items,
const std::unordered_map<ShopItem::AvailabilityReason, std::string> &buy_text_map) const noexcept;

std::string message{};
Timer message_timer{ FRAMES(240), 0.0f };
struct Message
{
std::string text;
Timer timer{ FRAMES(240), 0.0f };
};
std::list<Message> messages;

friend class Game;
};
Loading

0 comments on commit 60db7fb

Please sign in to comment.