Skip to content
Draft
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
4 changes: 3 additions & 1 deletion GWToolboxdll/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ file(GLOB SOURCES CONFIGURE_DEPENDS
"Windows/Pathfinding/*.h"
"Windows/Pathfinding/*.hpp"
"Windows/Pathfinding/*.cpp"
"Windows/Splits/*.h"
"Windows/Splits/*.cpp"
)

source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${SOURCES})
target_sources(GWToolboxdll PRIVATE ${SOURCES})

Expand Down
2 changes: 2 additions & 0 deletions GWToolboxdll/Modules/ToolboxSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#include <Windows/InfoWindow.h>
#include <Windows/MaterialsWindow.h>
#include <Windows/NotePadWindow.h>
#include <Windows/SplitsWindow.h>
#include <Windows/PartyStatisticsWindow.h>
#include <Windows/TradeWindow.h>
#include <Windows/ObjectiveTimerWindow.h>
Expand Down Expand Up @@ -208,6 +209,7 @@ namespace {
TradeWindow::Instance(),
NotePadWindow::Instance(),
ObjectiveTimerWindow::Instance(),
SplitsWindow::Instance(),
FactionLeaderboardWindow::Instance(),
DailyQuests::Instance(),
FriendListWindow::Instance(),
Expand Down
32 changes: 32 additions & 0 deletions GWToolboxdll/Windows/Splits/GoalClock.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#include "stdafx.h"
#include "GoalClock.h"

void GoalClock::Start() { running_ = true; }
void GoalClock::Pause() { running_ = false; }
void GoalClock::Resume() { running_ = true; }

void GoalClock::Reset()
{
running_ = false;
real_elapsed_ = 0.0;
game_elapsed_ = 0.0;
}

void GoalClock::AddRealTime(double delta)
{
if (running_ && delta > 0.0)
real_elapsed_ += delta;
}

void GoalClock::AddGameTime(double delta)
{
if (running_ && delta > 0.0)
game_elapsed_ += delta;
}

void GoalClock::Restore(double real_elapsed, double game_elapsed)
{
real_elapsed_ = real_elapsed;
game_elapsed_ = game_elapsed;
running_ = true;
}
31 changes: 31 additions & 0 deletions GWToolboxdll/Windows/Splits/GoalClock.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

// ---------------------------------------------------------------------------
// GoalClock — two independent timers running simultaneously.
//
// real_time — wall-clock elapsed; pauses on loading screens / cinematics.
// game_time — explorable-only time accumulated via AddGameTime().
// ---------------------------------------------------------------------------
class GoalClock {
public:
void Start();
void Pause();
void Resume();
void Reset();

void AddGameTime(double delta);
void AddRealTime(double delta);

// Restore clock state (crash-protection resume).
void Restore(double real_elapsed, double game_elapsed);

[[nodiscard]] bool IsRunning() const { return running_; }
[[nodiscard]] double RealTime() const { return real_elapsed_; }
[[nodiscard]] double GameTime() const { return game_elapsed_; }
[[nodiscard]] double TownTime() const { return real_elapsed_ - game_elapsed_; }

private:
bool running_ = false;
double real_elapsed_ = 0.0;
double game_elapsed_ = 0.0;
};
164 changes: 164 additions & 0 deletions GWToolboxdll/Windows/Splits/GoalEngine.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
#include "stdafx.h"
#include "GoalEngine.h"

#include <GWCA/GameContainers/Array.h>
#include <GWCA/GameEntities/Title.h>
#include <GWCA/Managers/PartyMgr.h>
#include <GWCA/Managers/PlayerMgr.h>

void GoalEngine::Attach(GoalList* list)
{
list_ = list;
Reset();
}

void GoalEngine::Detach()
{
list_ = nullptr;
started_ = false;
}

void GoalEngine::Reset()
{
started_ = false;
prev_map_ = GW::Constants::MapID::None;
last_real_ = 0.0;
last_game_ = 0.0;
mission_complete_map_ = GW::Constants::MapID::None;
mission_bonus_map_ = GW::Constants::MapID::None;
if (list_) list_->ResetRunState();
}

void GoalEngine::NotifyMissionComplete(GW::Constants::MapID map)
{
mission_complete_map_ = map;
}

void GoalEngine::NotifyMissionBonus(GW::Constants::MapID map)
{
mission_bonus_map_ = map;
}

int GoalEngine::Update(const GoalClock& clock,
GW::Constants::MapID current_map,
bool just_entered_map,
bool came_from_explorable,
GW::Constants::InstanceType instance_type,
bool vq_complete,
int player_level)
{
const bool is_explorable = (instance_type == GW::Constants::InstanceType::Explorable);

if (!list_ || list_->goals.empty()) {
prev_map_ = current_map;
mission_complete_map_ = GW::Constants::MapID::None;
mission_bonus_map_ = GW::Constants::MapID::None;
return -1;
}

if (!started_ && just_entered_map)
started_ = true;

int fired = -1;

if (started_) {
for (int i = 0; i < static_cast<int>(list_->goals.size()); ++i) {
GoalEntry& g = list_->goals[i];
if (g.completed) continue;

bool fire = false;
const GoalTrigger& t = g.trigger;

switch (t.type) {
case GoalTrigger::Type::MapEnter:
fire = just_entered_map && (current_map == t.map_id);
break;

case GoalTrigger::Type::EnterExplorable:
fire = just_entered_map && is_explorable && (current_map == t.map_id);
break;

case GoalTrigger::Type::ExitExplorable:
fire = just_entered_map && came_from_explorable && (prev_map_ == t.map_id);
break;

case GoalTrigger::Type::VanquishComplete:
fire = vq_complete && (current_map == t.map_id);
break;

case GoalTrigger::Type::MissionComplete:
fire = (mission_complete_map_ == t.map_id) &&
(!t.hard_mode || GW::PartyMgr::GetIsPartyInHardMode());
break;

case GoalTrigger::Type::MissionBonus:
fire = (mission_bonus_map_ == t.map_id) &&
(!t.hard_mode || GW::PartyMgr::GetIsPartyInHardMode());
break;

case GoalTrigger::Type::ReachLevel:
fire = (player_level >= t.level);
break;

case GoalTrigger::Type::ExitOutpost:
fire = just_entered_map && (prev_map_ == t.map_id);
break;

case GoalTrigger::Type::ReachTitleRank: {
const GW::Title* title = GW::PlayerMgr::GetTitleTrack(t.title_id);
fire = title && title->current_title_tier_index >= static_cast<uint32_t>(t.level);
break;
}

case GoalTrigger::Type::Manual:
break;

default:
break;
}

if (fire) {
FireGoal(i, clock);
fired = i;
break;
}
}
}

mission_complete_map_ = GW::Constants::MapID::None;
mission_bonus_map_ = GW::Constants::MapID::None;
if (current_map != GW::Constants::MapID::None)
prev_map_ = current_map;

return fired;
}

void GoalEngine::ForceStarted()
{
started_ = true;
}

void GoalEngine::TriggerManual(const GoalClock& clock)
{
if (!list_) return;
for (int i = 0; i < static_cast<int>(list_->goals.size()); ++i) {
GoalEntry& g = list_->goals[i];
if (!g.completed && g.trigger.type == GoalTrigger::Type::Manual) {
if (!started_) started_ = true;
FireGoal(i, clock);
return;
}
}
}

void GoalEngine::FireGoal(int index, const GoalClock& clock)
{
GoalEntry& g = list_->goals[index];
g.completed = true;
g.split.real_time = clock.RealTime();
g.split.game_time = clock.GameTime();
g.split.segment_real = clock.RealTime() - last_real_;
g.split.segment_game = clock.GameTime() - last_game_;
last_real_ = clock.RealTime();
last_game_ = clock.GameTime();
}
51 changes: 51 additions & 0 deletions GWToolboxdll/Windows/Splits/GoalEngine.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#pragma once

#include "GoalList.h"
#include "GoalClock.h"

#include <GWCA/Constants/Maps.h>
#include <GWCA/Constants/Constants.h>

// ---------------------------------------------------------------------------
// GoalEngine — checks conditions each frame, fires splits, tracks state.
// ---------------------------------------------------------------------------
class GoalEngine {
public:
void Attach(GoalList* list);
void Detach();

// Returns the index of a goal that just completed this frame, or -1.
int Update(const GoalClock& clock,
GW::Constants::MapID current_map,
bool just_entered_map,
bool came_from_explorable,
GW::Constants::InstanceType instance_type,
bool vq_complete,
int player_level);

void TriggerManual(const GoalClock& clock);

void NotifyMissionComplete(GW::Constants::MapID map);
void NotifyMissionBonus(GW::Constants::MapID map);

void Reset();
void ForceStarted();

[[nodiscard]] bool HasList() const { return list_ != nullptr; }
[[nodiscard]] bool IsStarted() const { return started_; }
[[nodiscard]] GoalList* List() const { return list_; }

private:
void FireGoal(int index, const GoalClock& clock);

GoalList* list_ = nullptr;
bool started_ = false;

GW::Constants::MapID prev_map_ = GW::Constants::MapID::None;

double last_real_ = 0.0;
double last_game_ = 0.0;

GW::Constants::MapID mission_complete_map_ = GW::Constants::MapID::None;
GW::Constants::MapID mission_bonus_map_ = GW::Constants::MapID::None;
};
49 changes: 49 additions & 0 deletions GWToolboxdll/Windows/Splits/GoalEntry.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#pragma once

#include <string>
#include <GWCA/Constants/Maps.h>
#include <GWCA/Constants/Constants.h>

// ---------------------------------------------------------------------------
// GoalTrigger — describes the condition that completes a split.
// ---------------------------------------------------------------------------
struct GoalTrigger {
enum class Type : uint8_t {
Manual = 0,
MapEnter = 1,
EnterExplorable = 2,
ExitExplorable = 3,
VanquishComplete = 4,
MissionComplete = 5,
MissionBonus = 6,
ReachLevel = 7,
ExitOutpost = 8,
ReachTitleRank = 9
};

Type type = Type::Manual;
bool hard_mode = false; // MissionComplete/Bonus: require hard mode
GW::Constants::MapID map_id = GW::Constants::MapID::None;
int level = 0;
GW::Constants::TitleID title_id = GW::Constants::TitleID::None;
};

// ---------------------------------------------------------------------------
// CompletedSplit — time data recorded when a goal fires.
// ---------------------------------------------------------------------------
struct CompletedSplit {
double real_time = 0.0;
double game_time = 0.0;
double segment_real = 0.0;
double segment_game = 0.0;
};

// ---------------------------------------------------------------------------
// GoalEntry — one row in a split list.
// ---------------------------------------------------------------------------
struct GoalEntry {
std::string label;
GoalTrigger trigger;
bool completed = false;
CompletedSplit split = {};
};
Loading