Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/BoolGrid.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ class BoolGrid
*/
bool SetData(const container_type& data, int width, int height)
{
if (width < 0 || height < 0)
return false;
if (data.size() != static_cast<std::size_t>(width) * static_cast<std::size_t>(height))
return false;
m_Width = width;
Expand Down
39 changes: 39 additions & 0 deletions src/ConsoleCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,37 @@ bool Cmd_TimeSet(std::span<const std::string_view> args, CommandContext& ctx)
return true;
}

bool Cmd_TimeAdd(std::span<const std::string_view> args, CommandContext& ctx)
{
if (ctx.time == nullptr)
{
ctx.out.PrintError("time.add: time manager unavailable");
return false;
}
if (args.size() != 1)
{
ctx.out.PrintError("time.add: usage 'time.add <hours>'");
return false;
}
float delta = 0.0f;
if (!ParseFloat(args[0], delta))
{
ctx.out.PrintError("time.add: hours must be a finite number");
return false;
}

ctx.time->AdvanceTime(delta);

char line[96];
std::snprintf(line,
sizeof(line),
"time.add: %+.2fh -> %.2fh",
static_cast<double>(delta),
static_cast<double>(ctx.time->GetTimeOfDay()));
ctx.out.Print(line);
return true;
}

bool Cmd_TimeFreeze(std::span<const std::string_view> args, CommandContext& ctx)
{
if (ctx.time == nullptr)
Expand Down Expand Up @@ -3258,6 +3289,14 @@ void Console::RegisterDefaultCommands()
},
{"ts"});

m_Registry.Register("time.add",
"time.add <hours> - offset time of day (signed, wraps 0..24)",
[makeContext](auto args, Console&)
{
CommandContext ctx = makeContext();
(void)Cmd_TimeAdd(args, ctx);
});

m_Registry.Register("time.freeze",
"[on|off|toggle] - pause/resume day-night cycle",
[makeContext](auto args, Console&)
Expand Down
1 change: 1 addition & 0 deletions src/ConsoleCommands.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ bool Cmd_Teleport(std::span<const std::string_view> args, CommandContext& ctx);
bool Cmd_FlagSet(std::span<const std::string_view> args, CommandContext& ctx);
bool Cmd_FlagGet(std::span<const std::string_view> args, CommandContext& ctx);
bool Cmd_TimeSet(std::span<const std::string_view> args, CommandContext& ctx);
bool Cmd_TimeAdd(std::span<const std::string_view> args, CommandContext& ctx);
bool Cmd_TimeFreeze(std::span<const std::string_view> args, CommandContext& ctx);
bool Cmd_MapLoad(std::span<const std::string_view> args, CommandContext& ctx);
bool Cmd_StateDump(std::span<const std::string_view> args, CommandContext& ctx);
Expand Down
6 changes: 2 additions & 4 deletions src/GameStateManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ std::vector<std::string> GameStateManager::GetActiveQuests() const

for (const auto& [key, value] : m_Flags)
{
// Match keys that start with "accepted_" and end with "_quest".
// Using ends_with() avoids false-matching keys like "accepted_quest_status".
if (key.starts_with(kAcceptedPrefix) && key.ends_with("_quest") && !value.empty() &&
value != "false" && value != "0")
if (key.starts_with(kAcceptedPrefix) && !value.empty() && value != "false" &&
value != "0")
{
// Extract quest name (remove "accepted_" prefix)
std::string questName = key.substr(kAcceptedPrefix.size());
Expand Down
9 changes: 9 additions & 0 deletions tests/CollisionMapTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,15 @@ TEST_F(CollisionMapTest, SetData_InvalidSize_Rejected)
EXPECT_EQ(map.GetHeight(), 10);
}

TEST_F(CollisionMapTest, SetData_NegativeDimensions_Rejected)
{
std::vector<bool> data;
EXPECT_FALSE(map.SetData(data, -1, 0));
EXPECT_FALSE(map.SetData(data, 0, -1));
EXPECT_EQ(map.GetWidth(), 10);
EXPECT_EQ(map.GetHeight(), 10);
}

// --- Copy/Move ---

TEST_F(CollisionMapTest, CopyConstructor)
Expand Down
49 changes: 47 additions & 2 deletions tests/ConsoleCommandsTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,40 @@ TEST(ConsoleCommandsTests, TimeNextRejectsArgs)
EXPECT_FALSE(Cmd_TimeNext(ArgPack({"now"}).span(), ctx));
}

TEST(ConsoleCommandsTests, TimeAddOffsetsCurrentTime)
{
TimeManager time;
time.SetTime(14.0f);
ConsoleBuffer buf;
CommandContext ctx{buf};
ctx.time = &time;

EXPECT_TRUE(Cmd_TimeAdd(ArgPack({"0.5"}).span(), ctx));

EXPECT_FLOAT_EQ(time.GetTimeOfDay(), 14.5f);
EXPECT_TRUE(BufferContains(buf, "time.add: +0.50h -> 14.50h"));
}

TEST(ConsoleCommandsTests, TimeAddRejectsInvalidInputs)
{
ConsoleBuffer buf;
CommandContext ctx{buf};

EXPECT_FALSE(Cmd_TimeAdd(ArgPack({"1"}).span(), ctx));
EXPECT_TRUE(BufferContains(buf, "time.add: time manager unavailable"));

TimeManager time;
ctx.time = &time;
buf.Clear();

EXPECT_FALSE(Cmd_TimeAdd(ArgPack({}).span(), ctx));
EXPECT_TRUE(BufferContains(buf, "time.add: usage 'time.add <hours>'"));

buf.Clear();
EXPECT_FALSE(Cmd_TimeAdd(ArgPack({"soon"}).span(), ctx));
EXPECT_TRUE(BufferContains(buf, "time.add: hours must be a finite number"));
}

// ---------------------------------------------------------------------------
// character.set / character.next (parse paths only - sprite assets may be
// missing in the test working dir, so SwitchCharacter success is best-effort)
Expand Down Expand Up @@ -1710,8 +1744,6 @@ TEST(ConsoleCommandsTests, QuestCompleteSetsFlag)

TEST(ConsoleCommandsTests, QuestListShowsActiveAndCompleted)
{
// GetActiveQuests requires the "accepted_<name>_quest" naming convention,
// so quest names must end in "_quest" for this code path to recognise them.
GameStateManager gs;
gs.AcceptQuest("a_quest", "do A");
gs.AcceptQuest("b_quest", "do B");
Expand All @@ -1725,6 +1757,19 @@ TEST(ConsoleCommandsTests, QuestListShowsActiveAndCompleted)
EXPECT_TRUE(BufferContains(buf, "[DONE]"));
}

TEST(ConsoleCommandsTests, QuestListShowsQuestGiveWithoutQuestSuffix)
{
GameStateManager gs;
ConsoleBuffer buf;
CommandContext ctx{buf};
ctx.gameState = &gs;

EXPECT_TRUE(Cmd_QuestGive(ArgPack({"ufo", "investigate"}).span(), ctx));
EXPECT_TRUE(Cmd_QuestList(ArgPack({}).span(), ctx));

EXPECT_TRUE(BufferContains(buf, "[ACTIVE] ufo"));
}

// ---------------------------------------------------------------------------
// version / renderer.info / mem.stats / config.dump
// ---------------------------------------------------------------------------
Expand Down
Loading