mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-03 12:03:50 +00:00
Merge pull request #37 from ldmonster/chore/application-extract-appearance-controller
Some checks are pending
Build / Build (arm64) (push) Waiting to run
Build / Build (x86-64) (push) Waiting to run
Build / Build (macOS arm64) (push) Waiting to run
Build / Build (windows-arm64) (push) Waiting to run
Build / Build (windows-x86-64) (push) Waiting to run
Security / CodeQL (C/C++) (push) Waiting to run
Security / Semgrep (push) Waiting to run
Security / Sanitizer Build (ASan/UBSan) (push) Waiting to run
Some checks are pending
Build / Build (arm64) (push) Waiting to run
Build / Build (x86-64) (push) Waiting to run
Build / Build (macOS arm64) (push) Waiting to run
Build / Build (windows-arm64) (push) Waiting to run
Build / Build (windows-x86-64) (push) Waiting to run
Security / CodeQL (C/C++) (push) Waiting to run
Security / Semgrep (push) Waiting to run
Security / Sanitizer Build (ASan/UBSan) (push) Waiting to run
[chore] refactor: application extract appearance controller
This commit is contained in:
commit
6a938b1181
32 changed files with 2423 additions and 1714 deletions
|
|
@ -487,6 +487,8 @@ set(WOWEE_SOURCES
|
||||||
# Core
|
# Core
|
||||||
src/core/application.cpp
|
src/core/application.cpp
|
||||||
src/core/entity_spawner.cpp
|
src/core/entity_spawner.cpp
|
||||||
|
src/core/appearance_composer.cpp
|
||||||
|
src/core/world_loader.cpp
|
||||||
src/core/window.cpp
|
src/core/window.cpp
|
||||||
src/core/input.cpp
|
src/core/input.cpp
|
||||||
src/core/logger.cpp
|
src/core/logger.cpp
|
||||||
|
|
@ -542,6 +544,7 @@ set(WOWEE_SOURCES
|
||||||
|
|
||||||
# Audio
|
# Audio
|
||||||
src/audio/audio_engine.cpp
|
src/audio/audio_engine.cpp
|
||||||
|
src/audio/audio_coordinator.cpp
|
||||||
src/audio/music_manager.cpp
|
src/audio/music_manager.cpp
|
||||||
src/audio/footstep_manager.cpp
|
src/audio/footstep_manager.cpp
|
||||||
src/audio/activity_sound_manager.cpp
|
src/audio/activity_sound_manager.cpp
|
||||||
|
|
|
||||||
10
README.md
10
README.md
|
|
@ -347,3 +347,13 @@ This project does not include any Blizzard Entertainment proprietary data, asset
|
||||||
## Known Issues
|
## Known Issues
|
||||||
|
|
||||||
MANY issues this is actively under development
|
MANY issues this is actively under development
|
||||||
|
|
||||||
|
## Star History
|
||||||
|
|
||||||
|
<a href="https://www.star-history.com/?repos=Kelsidavis%2FWoWee&type=date&legend=top-left">
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/image?repos=Kelsidavis/WoWee&type=date&theme=dark&legend=top-left" />
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/image?repos=Kelsidavis/WoWee&type=date&legend=top-left" />
|
||||||
|
<img alt="Star History Chart" src="https://api.star-history.com/image?repos=Kelsidavis/WoWee&type=date&legend=top-left" />
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
|
|
||||||
66
include/audio/audio_coordinator.hpp
Normal file
66
include/audio/audio_coordinator.hpp
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace wowee {
|
||||||
|
namespace pipeline { class AssetManager; }
|
||||||
|
namespace audio {
|
||||||
|
|
||||||
|
class MusicManager;
|
||||||
|
class FootstepManager;
|
||||||
|
class ActivitySoundManager;
|
||||||
|
class MountSoundManager;
|
||||||
|
class NpcVoiceManager;
|
||||||
|
class AmbientSoundManager;
|
||||||
|
class UiSoundManager;
|
||||||
|
class CombatSoundManager;
|
||||||
|
class SpellSoundManager;
|
||||||
|
class MovementSoundManager;
|
||||||
|
|
||||||
|
/// Coordinates all audio subsystems.
|
||||||
|
/// Extracted from Renderer to separate audio lifecycle from rendering.
|
||||||
|
/// Owned by Application; Renderer and UI components access through Application.
|
||||||
|
class AudioCoordinator {
|
||||||
|
public:
|
||||||
|
AudioCoordinator();
|
||||||
|
~AudioCoordinator();
|
||||||
|
|
||||||
|
/// Initialize the audio engine and all managers.
|
||||||
|
/// @return true if audio is available (engine initialized successfully)
|
||||||
|
bool initialize();
|
||||||
|
|
||||||
|
/// Initialize managers that need AssetManager (music lookups, sound banks).
|
||||||
|
void initializeWithAssets(pipeline::AssetManager* assetManager);
|
||||||
|
|
||||||
|
/// Shutdown all audio managers and engine.
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
// Accessors for all audio managers (same interface as Renderer had)
|
||||||
|
MusicManager* getMusicManager() { return musicManager_.get(); }
|
||||||
|
FootstepManager* getFootstepManager() { return footstepManager_.get(); }
|
||||||
|
ActivitySoundManager* getActivitySoundManager() { return activitySoundManager_.get(); }
|
||||||
|
MountSoundManager* getMountSoundManager() { return mountSoundManager_.get(); }
|
||||||
|
NpcVoiceManager* getNpcVoiceManager() { return npcVoiceManager_.get(); }
|
||||||
|
AmbientSoundManager* getAmbientSoundManager() { return ambientSoundManager_.get(); }
|
||||||
|
UiSoundManager* getUiSoundManager() { return uiSoundManager_.get(); }
|
||||||
|
CombatSoundManager* getCombatSoundManager() { return combatSoundManager_.get(); }
|
||||||
|
SpellSoundManager* getSpellSoundManager() { return spellSoundManager_.get(); }
|
||||||
|
MovementSoundManager* getMovementSoundManager() { return movementSoundManager_.get(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<MusicManager> musicManager_;
|
||||||
|
std::unique_ptr<FootstepManager> footstepManager_;
|
||||||
|
std::unique_ptr<ActivitySoundManager> activitySoundManager_;
|
||||||
|
std::unique_ptr<MountSoundManager> mountSoundManager_;
|
||||||
|
std::unique_ptr<NpcVoiceManager> npcVoiceManager_;
|
||||||
|
std::unique_ptr<AmbientSoundManager> ambientSoundManager_;
|
||||||
|
std::unique_ptr<UiSoundManager> uiSoundManager_;
|
||||||
|
std::unique_ptr<CombatSoundManager> combatSoundManager_;
|
||||||
|
std::unique_ptr<SpellSoundManager> spellSoundManager_;
|
||||||
|
std::unique_ptr<MovementSoundManager> movementSoundManager_;
|
||||||
|
|
||||||
|
bool audioAvailable_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace audio
|
||||||
|
} // namespace wowee
|
||||||
101
include/core/appearance_composer.hpp
Normal file
101
include/core/appearance_composer.hpp
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "game/character.hpp"
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace wowee {
|
||||||
|
|
||||||
|
namespace rendering { class Renderer; }
|
||||||
|
namespace pipeline { class AssetManager; class DBCLayout; struct M2Model; }
|
||||||
|
namespace game { class GameHandler; }
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
class EntitySpawner;
|
||||||
|
|
||||||
|
// Default (bare) geoset IDs per equipment group.
|
||||||
|
// Each group's base is groupNumber * 100; variant 01 is typically bare/default.
|
||||||
|
constexpr uint16_t kGeosetDefaultConnector = 101; // Group 1: default hair connector
|
||||||
|
constexpr uint16_t kGeosetBareForearms = 401; // Group 4: no gloves
|
||||||
|
constexpr uint16_t kGeosetBareShins = 503; // Group 5: no boots
|
||||||
|
constexpr uint16_t kGeosetDefaultEars = 702; // Group 7: ears
|
||||||
|
constexpr uint16_t kGeosetBareSleeves = 801; // Group 8: no chest armor sleeves
|
||||||
|
constexpr uint16_t kGeosetDefaultKneepads = 902; // Group 9: kneepads
|
||||||
|
constexpr uint16_t kGeosetDefaultTabard = 1201; // Group 12: tabard base
|
||||||
|
constexpr uint16_t kGeosetBarePants = 1301; // Group 13: no leggings
|
||||||
|
constexpr uint16_t kGeosetNoCape = 1501; // Group 15: no cape
|
||||||
|
constexpr uint16_t kGeosetWithCape = 1502; // Group 15: with cape
|
||||||
|
constexpr uint16_t kGeosetBareFeet = 2002; // Group 20: bare feet
|
||||||
|
|
||||||
|
/// Resolved texture paths from CharSections.dbc for player character compositing.
|
||||||
|
struct PlayerTextureInfo {
|
||||||
|
std::string bodySkinPath;
|
||||||
|
std::string faceLowerPath;
|
||||||
|
std::string faceUpperPath;
|
||||||
|
std::string hairTexturePath;
|
||||||
|
std::vector<std::string> underwearPaths;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Handles player character visual appearance: skin compositing, geoset selection,
|
||||||
|
/// texture path lookups, and equipment weapon rendering.
|
||||||
|
class AppearanceComposer {
|
||||||
|
public:
|
||||||
|
AppearanceComposer(rendering::Renderer* renderer,
|
||||||
|
pipeline::AssetManager* assetManager,
|
||||||
|
game::GameHandler* gameHandler,
|
||||||
|
pipeline::DBCLayout* dbcLayout,
|
||||||
|
EntitySpawner* entitySpawner);
|
||||||
|
|
||||||
|
// Player model path resolution
|
||||||
|
std::string getPlayerModelPath(game::Race race, game::Gender gender) const;
|
||||||
|
|
||||||
|
// Phase 1: Resolve texture paths from CharSections.dbc and fill model texture slots.
|
||||||
|
// Call BEFORE charRenderer->loadModel().
|
||||||
|
PlayerTextureInfo resolvePlayerTextures(pipeline::M2Model& model,
|
||||||
|
game::Race race, game::Gender gender,
|
||||||
|
uint32_t appearanceBytes);
|
||||||
|
|
||||||
|
// Phase 2: Apply composited textures to loaded model instance.
|
||||||
|
// Call AFTER charRenderer->loadModel(). Saves skin state for re-compositing.
|
||||||
|
void compositePlayerSkin(uint32_t modelSlotId, const PlayerTextureInfo& texInfo);
|
||||||
|
|
||||||
|
// Build default active geosets for player character
|
||||||
|
std::unordered_set<uint16_t> buildDefaultPlayerGeosets(uint8_t hairStyleId, uint8_t facialId);
|
||||||
|
|
||||||
|
// Equipment weapon loading (reads inventory, attaches weapon M2 models)
|
||||||
|
void loadEquippedWeapons();
|
||||||
|
|
||||||
|
// Weapon sheathe state
|
||||||
|
void setWeaponsSheathed(bool sheathed) { weaponsSheathed_ = sheathed; }
|
||||||
|
bool isWeaponsSheathed() const { return weaponsSheathed_; }
|
||||||
|
void toggleWeaponsSheathed() { weaponsSheathed_ = !weaponsSheathed_; }
|
||||||
|
|
||||||
|
// Saved skin state accessors (used by game_screen.cpp for equipment re-compositing)
|
||||||
|
const std::string& getBodySkinPath() const { return bodySkinPath_; }
|
||||||
|
const std::vector<std::string>& getUnderwearPaths() const { return underwearPaths_; }
|
||||||
|
uint32_t getSkinTextureSlotIndex() const { return skinTextureSlotIndex_; }
|
||||||
|
uint32_t getCloakTextureSlotIndex() const { return cloakTextureSlotIndex_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool loadWeaponM2(const std::string& m2Path, pipeline::M2Model& outModel);
|
||||||
|
|
||||||
|
rendering::Renderer* renderer_;
|
||||||
|
pipeline::AssetManager* assetManager_;
|
||||||
|
game::GameHandler* gameHandler_;
|
||||||
|
pipeline::DBCLayout* dbcLayout_;
|
||||||
|
EntitySpawner* entitySpawner_;
|
||||||
|
|
||||||
|
// Saved at spawn for skin re-compositing on equipment changes
|
||||||
|
std::string bodySkinPath_;
|
||||||
|
std::vector<std::string> underwearPaths_;
|
||||||
|
uint32_t skinTextureSlotIndex_ = 0;
|
||||||
|
uint32_t cloakTextureSlotIndex_ = 0;
|
||||||
|
|
||||||
|
bool weaponsSheathed_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace core
|
||||||
|
} // namespace wowee
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
#include "core/window.hpp"
|
#include "core/window.hpp"
|
||||||
#include "core/input.hpp"
|
#include "core/input.hpp"
|
||||||
#include "core/entity_spawner.hpp"
|
#include "core/entity_spawner.hpp"
|
||||||
|
#include "core/appearance_composer.hpp"
|
||||||
|
#include "core/world_loader.hpp"
|
||||||
#include "game/character.hpp"
|
#include "game/character.hpp"
|
||||||
#include "game/game_services.hpp"
|
#include "game/game_services.hpp"
|
||||||
#include "pipeline/blp_loader.hpp"
|
#include "pipeline/blp_loader.hpp"
|
||||||
|
|
@ -27,7 +29,7 @@ namespace ui { class UIManager; }
|
||||||
namespace auth { class AuthHandler; }
|
namespace auth { class AuthHandler; }
|
||||||
namespace game { class GameHandler; class World; class ExpansionRegistry; }
|
namespace game { class GameHandler; class World; class ExpansionRegistry; }
|
||||||
namespace pipeline { class AssetManager; class DBCLayout; struct M2Model; struct WMOModel; }
|
namespace pipeline { class AssetManager; class DBCLayout; struct M2Model; struct WMOModel; }
|
||||||
namespace audio { enum class VoiceType; }
|
namespace audio { enum class VoiceType; class AudioCoordinator; }
|
||||||
namespace addons { class AddonManager; }
|
namespace addons { class AddonManager; }
|
||||||
|
|
||||||
namespace core {
|
namespace core {
|
||||||
|
|
@ -42,6 +44,8 @@ enum class AppState {
|
||||||
};
|
};
|
||||||
|
|
||||||
class Application {
|
class Application {
|
||||||
|
friend class WorldLoader;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Application();
|
Application();
|
||||||
~Application();
|
~Application();
|
||||||
|
|
@ -73,9 +77,7 @@ public:
|
||||||
// Singleton access
|
// Singleton access
|
||||||
static Application& getInstance() { return *instance; }
|
static Application& getInstance() { return *instance; }
|
||||||
|
|
||||||
// Weapon loading (called at spawn and on equipment change)
|
|
||||||
void loadEquippedWeapons();
|
|
||||||
bool loadWeaponM2(const std::string& m2Path, pipeline::M2Model& outModel);
|
|
||||||
|
|
||||||
// Logout to login screen
|
// Logout to login screen
|
||||||
void logoutToLogin();
|
void logoutToLogin();
|
||||||
|
|
@ -85,26 +87,31 @@ public:
|
||||||
bool getRenderFootZForGuid(uint64_t guid, float& outFootZ) const;
|
bool getRenderFootZForGuid(uint64_t guid, float& outFootZ) const;
|
||||||
bool getRenderPositionForGuid(uint64_t guid, glm::vec3& outPos) const;
|
bool getRenderPositionForGuid(uint64_t guid, glm::vec3& outPos) const;
|
||||||
|
|
||||||
// Character skin composite state (saved at spawn for re-compositing on equipment change)
|
// Character skin composite state — delegated to AppearanceComposer
|
||||||
const std::string& getBodySkinPath() const { return bodySkinPath_; }
|
const std::string& getBodySkinPath() const { return appearanceComposer_ ? appearanceComposer_->getBodySkinPath() : emptyString_; }
|
||||||
const std::vector<std::string>& getUnderwearPaths() const { return underwearPaths_; }
|
const std::vector<std::string>& getUnderwearPaths() const { return appearanceComposer_ ? appearanceComposer_->getUnderwearPaths() : emptyStringVec_; }
|
||||||
uint32_t getSkinTextureSlotIndex() const { return skinTextureSlotIndex_; }
|
uint32_t getSkinTextureSlotIndex() const { return appearanceComposer_ ? appearanceComposer_->getSkinTextureSlotIndex() : 0; }
|
||||||
uint32_t getCloakTextureSlotIndex() const { return cloakTextureSlotIndex_; }
|
uint32_t getCloakTextureSlotIndex() const { return appearanceComposer_ ? appearanceComposer_->getCloakTextureSlotIndex() : 0; }
|
||||||
uint32_t getGryphonDisplayId() const { return entitySpawner_ ? entitySpawner_->getGryphonDisplayId() : 0; }
|
uint32_t getGryphonDisplayId() const { return entitySpawner_ ? entitySpawner_->getGryphonDisplayId() : 0; }
|
||||||
uint32_t getWyvernDisplayId() const { return entitySpawner_ ? entitySpawner_->getWyvernDisplayId() : 0; }
|
uint32_t getWyvernDisplayId() const { return entitySpawner_ ? entitySpawner_->getWyvernDisplayId() : 0; }
|
||||||
|
|
||||||
// Entity spawner access
|
// Entity spawner access
|
||||||
EntitySpawner* getEntitySpawner() { return entitySpawner_.get(); }
|
EntitySpawner* getEntitySpawner() { return entitySpawner_.get(); }
|
||||||
|
|
||||||
|
// Appearance composer access
|
||||||
|
AppearanceComposer* getAppearanceComposer() { return appearanceComposer_.get(); }
|
||||||
|
|
||||||
|
// World loader access
|
||||||
|
WorldLoader* getWorldLoader() { return worldLoader_.get(); }
|
||||||
|
|
||||||
|
// Audio coordinator access (Section 4.1: extracted audio subsystem)
|
||||||
|
audio::AudioCoordinator* getAudioCoordinator() { return audioCoordinator_.get(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void update(float deltaTime);
|
void update(float deltaTime);
|
||||||
void render();
|
void render();
|
||||||
void setupUICallbacks();
|
void setupUICallbacks();
|
||||||
void spawnPlayerCharacter();
|
void spawnPlayerCharacter();
|
||||||
std::string getPlayerModelPath() const;
|
|
||||||
static const char* mapIdToName(uint32_t mapId);
|
|
||||||
static const char* mapDisplayName(uint32_t mapId);
|
|
||||||
void loadOnlineWorldTerrain(uint32_t mapId, float x, float y, float z);
|
|
||||||
void buildFactionHostilityMap(uint8_t playerRace);
|
void buildFactionHostilityMap(uint8_t playerRace);
|
||||||
void setupTestTransport(); // Test transport boat for development
|
void setupTestTransport(); // Test transport boat for development
|
||||||
|
|
||||||
|
|
@ -123,6 +130,9 @@ private:
|
||||||
std::unique_ptr<game::ExpansionRegistry> expansionRegistry_;
|
std::unique_ptr<game::ExpansionRegistry> expansionRegistry_;
|
||||||
std::unique_ptr<pipeline::DBCLayout> dbcLayout_;
|
std::unique_ptr<pipeline::DBCLayout> dbcLayout_;
|
||||||
std::unique_ptr<EntitySpawner> entitySpawner_;
|
std::unique_ptr<EntitySpawner> entitySpawner_;
|
||||||
|
std::unique_ptr<AppearanceComposer> appearanceComposer_;
|
||||||
|
std::unique_ptr<WorldLoader> worldLoader_;
|
||||||
|
std::unique_ptr<audio::AudioCoordinator> audioCoordinator_;
|
||||||
|
|
||||||
AppState state = AppState::AUTHENTICATION;
|
AppState state = AppState::AUTHENTICATION;
|
||||||
bool running = false;
|
bool running = false;
|
||||||
|
|
@ -140,20 +150,11 @@ private:
|
||||||
uint32_t spawnedAppearanceBytes_ = 0;
|
uint32_t spawnedAppearanceBytes_ = 0;
|
||||||
uint8_t spawnedFacialFeatures_ = 0;
|
uint8_t spawnedFacialFeatures_ = 0;
|
||||||
|
|
||||||
// Saved at spawn for skin re-compositing
|
// Static empty values for null-safe delegation
|
||||||
std::string bodySkinPath_;
|
static inline const std::string emptyString_;
|
||||||
std::vector<std::string> underwearPaths_;
|
static inline const std::vector<std::string> emptyStringVec_;
|
||||||
uint32_t skinTextureSlotIndex_ = 0;
|
|
||||||
uint32_t cloakTextureSlotIndex_ = 0;
|
|
||||||
|
|
||||||
bool lastTaxiFlight_ = false;
|
bool lastTaxiFlight_ = false;
|
||||||
uint32_t loadedMapId_ = 0xFFFFFFFF; // Map ID of currently loaded terrain (0xFFFFFFFF = none)
|
|
||||||
uint32_t worldLoadGeneration_ = 0; // Incremented on each world entry to detect re-entrant loads
|
|
||||||
bool loadingWorld_ = false; // True while loadOnlineWorldTerrain is running
|
|
||||||
struct PendingWorldEntry {
|
|
||||||
uint32_t mapId; float x, y, z;
|
|
||||||
};
|
|
||||||
std::optional<PendingWorldEntry> pendingWorldEntry_; // Deferred world entry during loading
|
|
||||||
float taxiLandingClampTimer_ = 0.0f;
|
float taxiLandingClampTimer_ = 0.0f;
|
||||||
float worldEntryMovementGraceTimer_ = 0.0f;
|
float worldEntryMovementGraceTimer_ = 0.0f;
|
||||||
|
|
||||||
|
|
@ -174,31 +175,11 @@ private:
|
||||||
glm::vec3 chargeEndPos_{0.0f}; // Render coordinates
|
glm::vec3 chargeEndPos_{0.0f}; // Render coordinates
|
||||||
uint64_t chargeTargetGuid_ = 0;
|
uint64_t chargeTargetGuid_ = 0;
|
||||||
|
|
||||||
bool weaponsSheathed_ = false;
|
|
||||||
bool wasAutoAttacking_ = false;
|
bool wasAutoAttacking_ = false;
|
||||||
bool mapNameCacheLoaded_ = false;
|
|
||||||
std::unordered_map<uint32_t, std::string> mapNameById_;
|
|
||||||
|
|
||||||
// Quest marker billboard sprites (above NPCs)
|
// Quest marker billboard sprites (above NPCs)
|
||||||
void loadQuestMarkerModels(); // Now loads BLP textures
|
void loadQuestMarkerModels(); // Now loads BLP textures
|
||||||
void updateQuestMarkers(); // Updates billboard positions
|
void updateQuestMarkers(); // Updates billboard positions
|
||||||
|
|
||||||
// Background world preloader — warms AssetManager file cache for the
|
|
||||||
// expected world before the user clicks Enter World.
|
|
||||||
struct WorldPreload {
|
|
||||||
uint32_t mapId = 0;
|
|
||||||
std::string mapName;
|
|
||||||
int centerTileX = 0;
|
|
||||||
int centerTileY = 0;
|
|
||||||
std::atomic<bool> cancel{false};
|
|
||||||
std::vector<std::thread> workers;
|
|
||||||
};
|
|
||||||
std::unique_ptr<WorldPreload> worldPreload_;
|
|
||||||
void startWorldPreload(uint32_t mapId, const std::string& mapName, float serverX, float serverY);
|
|
||||||
void cancelWorldPreload();
|
|
||||||
void saveLastWorldInfo(uint32_t mapId, const std::string& mapName, float serverX, float serverY);
|
|
||||||
struct LastWorldInfo { uint32_t mapId = 0; std::string mapName; float x = 0, y = 0; bool valid = false; };
|
|
||||||
LastWorldInfo loadLastWorldInfo() const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace core
|
} // namespace core
|
||||||
|
|
|
||||||
125
include/core/world_loader.hpp
Normal file
125
include/core/world_loader.hpp
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <optional>
|
||||||
|
#include <memory>
|
||||||
|
#include <atomic>
|
||||||
|
#include <thread>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace wowee {
|
||||||
|
|
||||||
|
namespace rendering { class Renderer; }
|
||||||
|
namespace pipeline { class AssetManager; class DBCLayout; }
|
||||||
|
namespace game { class GameHandler; class World; }
|
||||||
|
namespace addons { class AddonManager; }
|
||||||
|
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
class Application;
|
||||||
|
class EntitySpawner;
|
||||||
|
class AppearanceComposer;
|
||||||
|
class Window;
|
||||||
|
|
||||||
|
/// Handles terrain streaming, map transitions, world preloading,
|
||||||
|
/// and coordinate-aware tile management for online world entry.
|
||||||
|
class WorldLoader {
|
||||||
|
public:
|
||||||
|
WorldLoader(Application& app,
|
||||||
|
rendering::Renderer* renderer,
|
||||||
|
pipeline::AssetManager* assetManager,
|
||||||
|
game::GameHandler* gameHandler,
|
||||||
|
EntitySpawner* entitySpawner,
|
||||||
|
AppearanceComposer* appearanceComposer,
|
||||||
|
Window* window,
|
||||||
|
game::World* world,
|
||||||
|
addons::AddonManager* addonManager);
|
||||||
|
~WorldLoader();
|
||||||
|
|
||||||
|
// Main terrain loading — drives loading screen, WMO/ADT detection, player spawn
|
||||||
|
void loadOnlineWorldTerrain(uint32_t mapId, float x, float y, float z);
|
||||||
|
|
||||||
|
// Process deferred world entry (called from Application::update each frame)
|
||||||
|
void processPendingEntry();
|
||||||
|
|
||||||
|
// Map name utilities
|
||||||
|
static const char* mapIdToName(uint32_t mapId);
|
||||||
|
static const char* mapDisplayName(uint32_t mapId);
|
||||||
|
|
||||||
|
// Background preloading — warms AssetManager file cache
|
||||||
|
void startWorldPreload(uint32_t mapId, const std::string& mapName,
|
||||||
|
float serverX, float serverY);
|
||||||
|
void cancelWorldPreload();
|
||||||
|
|
||||||
|
// Persistent world info for session-to-session preloading
|
||||||
|
void saveLastWorldInfo(uint32_t mapId, const std::string& mapName,
|
||||||
|
float serverX, float serverY);
|
||||||
|
struct LastWorldInfo {
|
||||||
|
uint32_t mapId = 0;
|
||||||
|
std::string mapName;
|
||||||
|
float x = 0, y = 0;
|
||||||
|
bool valid = false;
|
||||||
|
};
|
||||||
|
LastWorldInfo loadLastWorldInfo() const;
|
||||||
|
|
||||||
|
// State accessors
|
||||||
|
uint32_t getLoadedMapId() const { return loadedMapId_; }
|
||||||
|
bool isLoadingWorld() const { return loadingWorld_; }
|
||||||
|
bool hasPendingEntry() const { return pendingWorldEntry_.has_value(); }
|
||||||
|
|
||||||
|
// Get cached map name by ID (returns empty string if not found)
|
||||||
|
std::string getMapNameById(uint32_t mapId) const {
|
||||||
|
auto it = mapNameById_.find(mapId);
|
||||||
|
return (it != mapNameById_.end()) ? it->second : std::string{};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set pending world entry for deferred processing via processPendingEntry()
|
||||||
|
void setPendingEntry(uint32_t mapId, float x, float y, float z) {
|
||||||
|
pendingWorldEntry_ = PendingWorldEntry{mapId, x, y, z};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset methods (for logout / character switch)
|
||||||
|
void resetLoadedMap() { loadedMapId_ = 0xFFFFFFFF; }
|
||||||
|
void resetMapNameCache() { mapNameCacheLoaded_ = false; mapNameById_.clear(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Application& app_;
|
||||||
|
rendering::Renderer* renderer_;
|
||||||
|
pipeline::AssetManager* assetManager_;
|
||||||
|
game::GameHandler* gameHandler_;
|
||||||
|
EntitySpawner* entitySpawner_;
|
||||||
|
AppearanceComposer* appearanceComposer_;
|
||||||
|
Window* window_;
|
||||||
|
game::World* world_;
|
||||||
|
addons::AddonManager* addonManager_;
|
||||||
|
|
||||||
|
uint32_t loadedMapId_ = 0xFFFFFFFF; // Map ID of currently loaded terrain (0xFFFFFFFF = none)
|
||||||
|
uint32_t worldLoadGeneration_ = 0; // Incremented on each world entry to detect re-entrant loads
|
||||||
|
bool loadingWorld_ = false; // True while loadOnlineWorldTerrain is running
|
||||||
|
|
||||||
|
struct PendingWorldEntry {
|
||||||
|
uint32_t mapId; float x, y, z;
|
||||||
|
};
|
||||||
|
std::optional<PendingWorldEntry> pendingWorldEntry_;
|
||||||
|
|
||||||
|
// Map.dbc name cache (loaded once per session)
|
||||||
|
bool mapNameCacheLoaded_ = false;
|
||||||
|
std::unordered_map<uint32_t, std::string> mapNameById_;
|
||||||
|
|
||||||
|
// Background world preloader — warms AssetManager file cache for the
|
||||||
|
// expected world before the user clicks Enter World.
|
||||||
|
struct WorldPreload {
|
||||||
|
uint32_t mapId = 0;
|
||||||
|
std::string mapName;
|
||||||
|
int centerTileX = 0;
|
||||||
|
int centerTileY = 0;
|
||||||
|
std::atomic<bool> cancel{false};
|
||||||
|
std::vector<std::thread> workers;
|
||||||
|
};
|
||||||
|
std::unique_ptr<WorldPreload> worldPreload_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace core
|
||||||
|
} // namespace wowee
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
// XP bar, reputation bar, macro resolution.
|
// XP bar, reputation bar, macro resolution.
|
||||||
// ============================================================
|
// ============================================================
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "ui/ui_services.hpp"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
@ -70,7 +71,11 @@ public:
|
||||||
std::unordered_map<uint32_t, uint32_t> macroPrimarySpellCache_;
|
std::unordered_map<uint32_t, uint32_t> macroPrimarySpellCache_;
|
||||||
size_t macroCacheSpellCount_ = 0;
|
size_t macroCacheSpellCount_ = 0;
|
||||||
|
|
||||||
|
// Section 3.5: UIServices injection (Phase B singleton breaking)
|
||||||
|
void setServices(const UIServices& services) { services_ = services; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
UIServices services_;
|
||||||
uint32_t resolveMacroPrimarySpellId(uint32_t macroId, game::GameHandler& gameHandler);
|
uint32_t resolveMacroPrimarySpellId(uint32_t macroId, game::GameHandler& gameHandler);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/ui_services.hpp"
|
||||||
#include "auth/auth_handler.hpp"
|
#include "auth/auth_handler.hpp"
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -30,6 +31,9 @@ public:
|
||||||
*/
|
*/
|
||||||
void setOnSuccess(std::function<void()> callback) { onSuccess = callback; }
|
void setOnSuccess(std::function<void()> callback) { onSuccess = callback; }
|
||||||
|
|
||||||
|
/// Set services (dependency injection)
|
||||||
|
void setServices(const UIServices& services) { services_ = services; }
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if authentication is in progress
|
* Check if authentication is in progress
|
||||||
|
|
@ -44,6 +48,8 @@ public:
|
||||||
const std::string& getStatusMessage() const { return statusMessage; }
|
const std::string& getStatusMessage() const { return statusMessage; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
UIServices services_; // Injected service references
|
||||||
|
|
||||||
struct ServerProfile {
|
struct ServerProfile {
|
||||||
std::string hostname;
|
std::string hostname;
|
||||||
int port = 3724;
|
int port = 3724;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/ui_services.hpp"
|
||||||
#include "game/game_handler.hpp"
|
#include "game/game_handler.hpp"
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -48,6 +49,9 @@ public:
|
||||||
void setOnBack(std::function<void()> cb) { onBack = std::move(cb); }
|
void setOnBack(std::function<void()> cb) { onBack = std::move(cb); }
|
||||||
void setOnDeleteCharacter(std::function<void(uint64_t)> cb) { onDeleteCharacter = std::move(cb); }
|
void setOnDeleteCharacter(std::function<void(uint64_t)> cb) { onDeleteCharacter = std::move(cb); }
|
||||||
|
|
||||||
|
/// Set services (dependency injection)
|
||||||
|
void setServices(const UIServices& services) { services_ = services; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset selection state (e.g., when switching servers)
|
* Reset selection state (e.g., when switching servers)
|
||||||
*/
|
*/
|
||||||
|
|
@ -89,6 +93,8 @@ public:
|
||||||
void selectCharacterByName(const std::string& name);
|
void selectCharacterByName(const std::string& name);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
UIServices services_; // Injected service references
|
||||||
|
|
||||||
// UI state
|
// UI state
|
||||||
int selectedCharacterIndex = -1;
|
int selectedCharacterIndex = -1;
|
||||||
bool characterSelected = false;
|
bool characterSelected = false;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "game/game_handler.hpp"
|
#include "game/game_handler.hpp"
|
||||||
|
#include "ui/ui_services.hpp"
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -109,10 +110,16 @@ public:
|
||||||
/** Reset all chat settings to defaults. */
|
/** Reset all chat settings to defaults. */
|
||||||
void restoreDefaults();
|
void restoreDefaults();
|
||||||
|
|
||||||
|
// Section 3.5: UIServices injection (Phase B singleton breaking)
|
||||||
|
void setServices(const UIServices& services) { services_ = services; }
|
||||||
|
|
||||||
/** Replace $g/$G and $n/$N gender/name placeholders in quest/chat text. */
|
/** Replace $g/$G and $n/$N gender/name placeholders in quest/chat text. */
|
||||||
std::string replaceGenderPlaceholders(const std::string& text, game::GameHandler& gameHandler);
|
std::string replaceGenderPlaceholders(const std::string& text, game::GameHandler& gameHandler);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Section 3.5: Injected UI services (Phase B singleton breaking)
|
||||||
|
UIServices services_;
|
||||||
|
|
||||||
// ---- Chat input state ----
|
// ---- Chat input state ----
|
||||||
char chatInputBuffer_[512] = "";
|
char chatInputBuffer_[512] = "";
|
||||||
char whisperTargetBuffer_[256] = "";
|
char whisperTargetBuffer_[256] = "";
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/ui_services.hpp"
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -70,6 +71,12 @@ public:
|
||||||
SpellbookScreen& spellbookScreen);
|
SpellbookScreen& spellbookScreen);
|
||||||
void renderThreatWindow(game::GameHandler& gameHandler);
|
void renderThreatWindow(game::GameHandler& gameHandler);
|
||||||
void renderBgScoreboard(game::GameHandler& gameHandler);
|
void renderBgScoreboard(game::GameHandler& gameHandler);
|
||||||
|
|
||||||
|
// Section 3.5: UIServices injection (Phase B singleton breaking)
|
||||||
|
void setServices(const UIServices& services) { services_ = services; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
UIServices services_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/ui_services.hpp"
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
@ -34,7 +35,12 @@ public:
|
||||||
/// called in render() after reclaim corpse button
|
/// called in render() after reclaim corpse button
|
||||||
void renderLateDialogs(game::GameHandler& gameHandler);
|
void renderLateDialogs(game::GameHandler& gameHandler);
|
||||||
|
|
||||||
|
// Section 3.5: UIServices injection (Phase B singleton breaking)
|
||||||
|
void setServices(const UIServices& services) { services_ = services; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Section 3.5: Injected UI services
|
||||||
|
UIServices services_;
|
||||||
// Common ImGui window flags for popup dialogs
|
// Common ImGui window flags for popup dialogs
|
||||||
static constexpr ImGuiWindowFlags kDialogFlags =
|
static constexpr ImGuiWindowFlags kDialogFlags =
|
||||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize;
|
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize;
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,14 @@
|
||||||
#include "ui/social_panel.hpp"
|
#include "ui/social_panel.hpp"
|
||||||
#include "ui/action_bar_panel.hpp"
|
#include "ui/action_bar_panel.hpp"
|
||||||
#include "ui/window_manager.hpp"
|
#include "ui/window_manager.hpp"
|
||||||
|
#include "ui/ui_services.hpp"
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace wowee {
|
namespace wowee {
|
||||||
|
namespace core { class AppearanceComposer; }
|
||||||
namespace pipeline { class AssetManager; }
|
namespace pipeline { class AssetManager; }
|
||||||
namespace rendering { class Renderer; }
|
namespace rendering { class Renderer; }
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
@ -50,7 +52,17 @@ public:
|
||||||
void saveSettings();
|
void saveSettings();
|
||||||
void loadSettings();
|
void loadSettings();
|
||||||
|
|
||||||
|
// Dependency injection for extracted classes (Phase A singleton breaking)
|
||||||
|
void setAppearanceComposer(core::AppearanceComposer* ac) { appearanceComposer_ = ac; }
|
||||||
|
|
||||||
|
// Section 3.5: UIServices injection (Phase B singleton breaking)
|
||||||
|
void setServices(const UIServices& services);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Injected UI services (Section 3.5 Phase B - replaces getInstance() calls)
|
||||||
|
UIServices services_;
|
||||||
|
// Legacy pointer for Phase A compatibility (will be removed when all callsites migrate)
|
||||||
|
core::AppearanceComposer* appearanceComposer_ = nullptr;
|
||||||
// Chat panel (extracted from GameScreen — owns all chat state and rendering)
|
// Chat panel (extracted from GameScreen — owns all chat state and rendering)
|
||||||
ChatPanel chatPanel_;
|
ChatPanel chatPanel_;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/ui_services.hpp"
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
@ -149,7 +150,12 @@ public:
|
||||||
/// Return the platform-specific settings file path
|
/// Return the platform-specific settings file path
|
||||||
static std::string getSettingsPath();
|
static std::string getSettingsPath();
|
||||||
|
|
||||||
|
/// Set services (dependency injection)
|
||||||
|
void setServices(const UIServices& services) { services_ = services; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
UIServices services_; // Injected service references
|
||||||
|
|
||||||
// Keybinding customization (private — only used in Controls tab)
|
// Keybinding customization (private — only used in Controls tab)
|
||||||
int pendingRebindAction_ = -1; // -1 = not rebinding, otherwise action index
|
int pendingRebindAction_ = -1; // -1 = not rebinding, otherwise action index
|
||||||
bool awaitingKeyPress_ = false;
|
bool awaitingKeyPress_ = false;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/ui_services.hpp"
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -71,6 +72,12 @@ public:
|
||||||
ChatPanel& chatPanel);
|
ChatPanel& chatPanel);
|
||||||
void renderInspectWindow(game::GameHandler& gameHandler,
|
void renderInspectWindow(game::GameHandler& gameHandler,
|
||||||
InventoryScreen& inventoryScreen);
|
InventoryScreen& inventoryScreen);
|
||||||
|
|
||||||
|
// Section 3.5: UIServices injection (Phase B singleton breaking)
|
||||||
|
void setServices(const UIServices& services) { services_ = services; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
UIServices services_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/ui_services.hpp"
|
||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -40,11 +41,17 @@ public:
|
||||||
/// Fire achievement earned toast + sound
|
/// Fire achievement earned toast + sound
|
||||||
void triggerAchievementToast(uint32_t achievementId, std::string name = {});
|
void triggerAchievementToast(uint32_t achievementId, std::string name = {});
|
||||||
|
|
||||||
|
// Section 3.5: UIServices injection (Phase B singleton breaking)
|
||||||
|
void setServices(const UIServices& services) { services_ = services; }
|
||||||
|
|
||||||
// --- public state consumed by GameScreen for the golden burst overlay ---
|
// --- public state consumed by GameScreen for the golden burst overlay ---
|
||||||
float levelUpFlashAlpha = 0.0f;
|
float levelUpFlashAlpha = 0.0f;
|
||||||
uint32_t levelUpDisplayLevel = 0;
|
uint32_t levelUpDisplayLevel = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Section 3.5: Injected UI services
|
||||||
|
UIServices services_;
|
||||||
|
|
||||||
// ---- Ding effect (own level-up) ----
|
// ---- Ding effect (own level-up) ----
|
||||||
static constexpr float DING_DURATION = 4.0f;
|
static constexpr float DING_DURATION = 4.0f;
|
||||||
float dingTimer_ = 0.0f;
|
float dingTimer_ = 0.0f;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include "ui/character_create_screen.hpp"
|
#include "ui/character_create_screen.hpp"
|
||||||
#include "ui/character_screen.hpp"
|
#include "ui/character_screen.hpp"
|
||||||
#include "ui/game_screen.hpp"
|
#include "ui/game_screen.hpp"
|
||||||
|
#include "ui/ui_services.hpp"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
// Forward declare SDL_Event
|
// Forward declare SDL_Event
|
||||||
|
|
@ -13,7 +14,7 @@ union SDL_Event;
|
||||||
namespace wowee {
|
namespace wowee {
|
||||||
|
|
||||||
// Forward declarations
|
// Forward declarations
|
||||||
namespace core { class Window; enum class AppState; }
|
namespace core { class Window; class AppearanceComposer; enum class AppState; }
|
||||||
namespace auth { class AuthHandler; }
|
namespace auth { class AuthHandler; }
|
||||||
namespace game { class GameHandler; }
|
namespace game { class GameHandler; }
|
||||||
|
|
||||||
|
|
@ -69,8 +70,23 @@ public:
|
||||||
CharacterScreen& getCharacterScreen() { return *characterScreen; }
|
CharacterScreen& getCharacterScreen() { return *characterScreen; }
|
||||||
GameScreen& getGameScreen() { return *gameScreen; }
|
GameScreen& getGameScreen() { return *gameScreen; }
|
||||||
|
|
||||||
|
// Dependency injection forwarding (Phase A singleton breaking)
|
||||||
|
void setAppearanceComposer(core::AppearanceComposer* ac) {
|
||||||
|
if (gameScreen) gameScreen->setAppearanceComposer(ac);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section 3.5: UIServices injection (Phase B singleton breaking)
|
||||||
|
void setServices(const UIServices& services) {
|
||||||
|
services_ = services;
|
||||||
|
if (gameScreen) gameScreen->setServices(services);
|
||||||
|
if (authScreen) authScreen->setServices(services);
|
||||||
|
if (characterScreen) characterScreen->setServices(services);
|
||||||
|
}
|
||||||
|
const UIServices& getServices() const { return services_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
core::Window* window = nullptr;
|
core::Window* window = nullptr;
|
||||||
|
UIServices services_; // Section 3.5: Injected services
|
||||||
|
|
||||||
// UI Screens
|
// UI Screens
|
||||||
std::unique_ptr<AuthScreen> authScreen;
|
std::unique_ptr<AuthScreen> authScreen;
|
||||||
|
|
|
||||||
55
include/ui/ui_services.hpp
Normal file
55
include/ui/ui_services.hpp
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace wowee {
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
namespace core {
|
||||||
|
class Window;
|
||||||
|
class EntitySpawner;
|
||||||
|
class AppearanceComposer;
|
||||||
|
class WorldLoader;
|
||||||
|
}
|
||||||
|
namespace rendering { class Renderer; }
|
||||||
|
namespace pipeline { class AssetManager; }
|
||||||
|
namespace game {
|
||||||
|
class GameHandler;
|
||||||
|
class ExpansionRegistry;
|
||||||
|
}
|
||||||
|
namespace addons { class AddonManager; }
|
||||||
|
namespace audio { class AudioCoordinator; }
|
||||||
|
|
||||||
|
namespace ui {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UI Services - Dependency injection container for UI components.
|
||||||
|
*
|
||||||
|
* Section 3.5: Break the singleton Phase B
|
||||||
|
*
|
||||||
|
* Replaces Application::getInstance() calls throughout UI code.
|
||||||
|
* Application creates this struct and injects it into UIManager,
|
||||||
|
* which propagates it to GameScreen and all child UI components.
|
||||||
|
*
|
||||||
|
* Owned by Application, shared as const pointers (non-owning).
|
||||||
|
*/
|
||||||
|
struct UIServices {
|
||||||
|
core::Window* window = nullptr;
|
||||||
|
rendering::Renderer* renderer = nullptr;
|
||||||
|
pipeline::AssetManager* assetManager = nullptr;
|
||||||
|
game::GameHandler* gameHandler = nullptr;
|
||||||
|
game::ExpansionRegistry* expansionRegistry = nullptr;
|
||||||
|
addons::AddonManager* addonManager = nullptr;
|
||||||
|
audio::AudioCoordinator* audioCoordinator = nullptr;
|
||||||
|
|
||||||
|
// Extracted classes (also available individually for Phase A compatibility)
|
||||||
|
core::EntitySpawner* entitySpawner = nullptr;
|
||||||
|
core::AppearanceComposer* appearanceComposer = nullptr;
|
||||||
|
core::WorldLoader* worldLoader = nullptr;
|
||||||
|
|
||||||
|
// Helper to check if core services are wired
|
||||||
|
bool isValid() const {
|
||||||
|
return window && renderer && assetManager && gameHandler;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ui
|
||||||
|
} // namespace wowee
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
// equipment sets, skills.
|
// equipment sets, skills.
|
||||||
// ============================================================
|
// ============================================================
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "ui/ui_services.hpp"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
@ -173,7 +174,11 @@ public:
|
||||||
std::unordered_map<uint32_t, ExtendedCostEntry> extendedCostCache_;
|
std::unordered_map<uint32_t, ExtendedCostEntry> extendedCostCache_;
|
||||||
bool extendedCostDbLoaded_ = false;
|
bool extendedCostDbLoaded_ = false;
|
||||||
|
|
||||||
|
// Section 3.5: UIServices injection (Phase B singleton breaking)
|
||||||
|
void setServices(const UIServices& services) { services_ = services; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
UIServices services_;
|
||||||
void loadExtendedCostDBC();
|
void loadExtendedCostDBC();
|
||||||
std::string formatExtendedCost(uint32_t extendedCostId, game::GameHandler& gameHandler);
|
std::string formatExtendedCost(uint32_t extendedCostId, game::GameHandler& gameHandler);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
87
src/audio/audio_coordinator.cpp
Normal file
87
src/audio/audio_coordinator.cpp
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
#include "audio/audio_coordinator.hpp"
|
||||||
|
#include "audio/audio_engine.hpp"
|
||||||
|
#include "audio/music_manager.hpp"
|
||||||
|
#include "audio/footstep_manager.hpp"
|
||||||
|
#include "audio/activity_sound_manager.hpp"
|
||||||
|
#include "audio/mount_sound_manager.hpp"
|
||||||
|
#include "audio/npc_voice_manager.hpp"
|
||||||
|
#include "audio/ambient_sound_manager.hpp"
|
||||||
|
#include "audio/ui_sound_manager.hpp"
|
||||||
|
#include "audio/combat_sound_manager.hpp"
|
||||||
|
#include "audio/spell_sound_manager.hpp"
|
||||||
|
#include "audio/movement_sound_manager.hpp"
|
||||||
|
#include "pipeline/asset_manager.hpp"
|
||||||
|
#include "core/logger.hpp"
|
||||||
|
|
||||||
|
namespace wowee {
|
||||||
|
namespace audio {
|
||||||
|
|
||||||
|
AudioCoordinator::AudioCoordinator() = default;
|
||||||
|
|
||||||
|
AudioCoordinator::~AudioCoordinator() {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AudioCoordinator::initialize() {
|
||||||
|
// Initialize AudioEngine (singleton)
|
||||||
|
if (!AudioEngine::instance().initialize()) {
|
||||||
|
LOG_WARNING("Failed to initialize AudioEngine - audio will be disabled");
|
||||||
|
audioAvailable_ = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
audioAvailable_ = true;
|
||||||
|
|
||||||
|
// Create all audio managers (initialized later with asset manager)
|
||||||
|
musicManager_ = std::make_unique<MusicManager>();
|
||||||
|
footstepManager_ = std::make_unique<FootstepManager>();
|
||||||
|
activitySoundManager_ = std::make_unique<ActivitySoundManager>();
|
||||||
|
mountSoundManager_ = std::make_unique<MountSoundManager>();
|
||||||
|
npcVoiceManager_ = std::make_unique<NpcVoiceManager>();
|
||||||
|
ambientSoundManager_ = std::make_unique<AmbientSoundManager>();
|
||||||
|
uiSoundManager_ = std::make_unique<UiSoundManager>();
|
||||||
|
combatSoundManager_ = std::make_unique<CombatSoundManager>();
|
||||||
|
spellSoundManager_ = std::make_unique<SpellSoundManager>();
|
||||||
|
movementSoundManager_ = std::make_unique<MovementSoundManager>();
|
||||||
|
|
||||||
|
LOG_INFO("AudioCoordinator initialized with ", 10, " audio managers");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioCoordinator::initializeWithAssets(pipeline::AssetManager* assetManager) {
|
||||||
|
if (!audioAvailable_ || !assetManager) return;
|
||||||
|
|
||||||
|
// MusicManager needs asset manager for zone music lookups
|
||||||
|
if (musicManager_) {
|
||||||
|
musicManager_->initialize(assetManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other managers may need asset manager for sound bank loading
|
||||||
|
// (Add similar calls as needed for other managers)
|
||||||
|
|
||||||
|
LOG_INFO("AudioCoordinator initialized with asset manager");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioCoordinator::shutdown() {
|
||||||
|
// Reset all managers first (they may reference AudioEngine)
|
||||||
|
movementSoundManager_.reset();
|
||||||
|
spellSoundManager_.reset();
|
||||||
|
combatSoundManager_.reset();
|
||||||
|
uiSoundManager_.reset();
|
||||||
|
ambientSoundManager_.reset();
|
||||||
|
npcVoiceManager_.reset();
|
||||||
|
mountSoundManager_.reset();
|
||||||
|
activitySoundManager_.reset();
|
||||||
|
footstepManager_.reset();
|
||||||
|
musicManager_.reset();
|
||||||
|
|
||||||
|
// Shutdown audio engine last
|
||||||
|
if (audioAvailable_) {
|
||||||
|
AudioEngine::instance().shutdown();
|
||||||
|
audioAvailable_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO("AudioCoordinator shutdown complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace audio
|
||||||
|
} // namespace wowee
|
||||||
383
src/core/appearance_composer.cpp
Normal file
383
src/core/appearance_composer.cpp
Normal file
|
|
@ -0,0 +1,383 @@
|
||||||
|
#include "core/appearance_composer.hpp"
|
||||||
|
#include "core/entity_spawner.hpp"
|
||||||
|
#include "core/logger.hpp"
|
||||||
|
#include "rendering/renderer.hpp"
|
||||||
|
#include "rendering/character_renderer.hpp"
|
||||||
|
#include "pipeline/asset_manager.hpp"
|
||||||
|
#include "pipeline/m2_loader.hpp"
|
||||||
|
#include "pipeline/dbc_loader.hpp"
|
||||||
|
#include "pipeline/dbc_layout.hpp"
|
||||||
|
#include "game/game_handler.hpp"
|
||||||
|
|
||||||
|
namespace wowee {
|
||||||
|
namespace core {
|
||||||
|
|
||||||
|
AppearanceComposer::AppearanceComposer(rendering::Renderer* renderer,
|
||||||
|
pipeline::AssetManager* assetManager,
|
||||||
|
game::GameHandler* gameHandler,
|
||||||
|
pipeline::DBCLayout* dbcLayout,
|
||||||
|
EntitySpawner* entitySpawner)
|
||||||
|
: renderer_(renderer)
|
||||||
|
, assetManager_(assetManager)
|
||||||
|
, gameHandler_(gameHandler)
|
||||||
|
, dbcLayout_(dbcLayout)
|
||||||
|
, entitySpawner_(entitySpawner)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AppearanceComposer::getPlayerModelPath(game::Race race, game::Gender gender) const {
|
||||||
|
return game::getPlayerModelPath(race, gender);
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerTextureInfo AppearanceComposer::resolvePlayerTextures(pipeline::M2Model& model,
|
||||||
|
game::Race race, game::Gender gender,
|
||||||
|
uint32_t appearanceBytes) {
|
||||||
|
PlayerTextureInfo result;
|
||||||
|
|
||||||
|
uint32_t targetRaceId = static_cast<uint32_t>(race);
|
||||||
|
uint32_t targetSexId = (gender == game::Gender::FEMALE) ? 1u : 0u;
|
||||||
|
|
||||||
|
// Race name for fallback texture paths
|
||||||
|
const char* raceFolderName = "Human";
|
||||||
|
switch (race) {
|
||||||
|
case game::Race::HUMAN: raceFolderName = "Human"; break;
|
||||||
|
case game::Race::ORC: raceFolderName = "Orc"; break;
|
||||||
|
case game::Race::DWARF: raceFolderName = "Dwarf"; break;
|
||||||
|
case game::Race::NIGHT_ELF: raceFolderName = "NightElf"; break;
|
||||||
|
case game::Race::UNDEAD: raceFolderName = "Scourge"; break;
|
||||||
|
case game::Race::TAUREN: raceFolderName = "Tauren"; break;
|
||||||
|
case game::Race::GNOME: raceFolderName = "Gnome"; break;
|
||||||
|
case game::Race::TROLL: raceFolderName = "Troll"; break;
|
||||||
|
case game::Race::BLOOD_ELF: raceFolderName = "BloodElf"; break;
|
||||||
|
case game::Race::DRAENEI: raceFolderName = "Draenei"; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
const char* genderFolder = (gender == game::Gender::FEMALE) ? "Female" : "Male";
|
||||||
|
std::string raceGender = std::string(raceFolderName) + genderFolder;
|
||||||
|
result.bodySkinPath = std::string("Character\\") + raceFolderName + "\\" + genderFolder + "\\" + raceGender + "Skin00_00.blp";
|
||||||
|
std::string pelvisPath = std::string("Character\\") + raceFolderName + "\\" + genderFolder + "\\" + raceGender + "NakedPelvisSkin00_00.blp";
|
||||||
|
|
||||||
|
// Extract appearance bytes for texture lookups
|
||||||
|
uint8_t charSkinId = appearanceBytes & 0xFF;
|
||||||
|
uint8_t charFaceId = (appearanceBytes >> 8) & 0xFF;
|
||||||
|
uint8_t charHairStyleId = (appearanceBytes >> 16) & 0xFF;
|
||||||
|
uint8_t charHairColorId = (appearanceBytes >> 24) & 0xFF;
|
||||||
|
LOG_INFO("Appearance: skin=", static_cast<int>(charSkinId), " face=", static_cast<int>(charFaceId),
|
||||||
|
" hairStyle=", static_cast<int>(charHairStyleId), " hairColor=", static_cast<int>(charHairColorId));
|
||||||
|
|
||||||
|
// Parse CharSections.dbc for skin/face/hair/underwear texture paths
|
||||||
|
auto charSectionsDbc = assetManager_->loadDBC("CharSections.dbc");
|
||||||
|
if (charSectionsDbc) {
|
||||||
|
LOG_INFO("CharSections.dbc loaded: ", charSectionsDbc->getRecordCount(), " records");
|
||||||
|
const auto* csL = pipeline::getActiveDBCLayout() ? pipeline::getActiveDBCLayout()->getLayout("CharSections") : nullptr;
|
||||||
|
auto csF = pipeline::detectCharSectionsFields(charSectionsDbc.get(), csL);
|
||||||
|
bool foundSkin = false;
|
||||||
|
bool foundUnderwear = false;
|
||||||
|
bool foundFaceLower = false;
|
||||||
|
bool foundHair = false;
|
||||||
|
for (uint32_t r = 0; r < charSectionsDbc->getRecordCount(); r++) {
|
||||||
|
uint32_t raceId = charSectionsDbc->getUInt32(r, csF.raceId);
|
||||||
|
uint32_t sexId = charSectionsDbc->getUInt32(r, csF.sexId);
|
||||||
|
uint32_t baseSection = charSectionsDbc->getUInt32(r, csF.baseSection);
|
||||||
|
uint32_t variationIndex = charSectionsDbc->getUInt32(r, csF.variationIndex);
|
||||||
|
uint32_t colorIndex = charSectionsDbc->getUInt32(r, csF.colorIndex);
|
||||||
|
|
||||||
|
if (raceId != targetRaceId || sexId != targetSexId) continue;
|
||||||
|
|
||||||
|
// Section 0 = skin: match by colorIndex = skin byte
|
||||||
|
if (baseSection == 0 && !foundSkin && colorIndex == charSkinId) {
|
||||||
|
std::string tex1 = charSectionsDbc->getString(r, csF.texture1);
|
||||||
|
if (!tex1.empty()) {
|
||||||
|
result.bodySkinPath = tex1;
|
||||||
|
foundSkin = true;
|
||||||
|
LOG_INFO(" DBC body skin: ", result.bodySkinPath, " (skin=", static_cast<int>(charSkinId), ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Section 3 = hair: match variation=hairStyle, color=hairColor
|
||||||
|
else if (baseSection == 3 && !foundHair &&
|
||||||
|
variationIndex == charHairStyleId && colorIndex == charHairColorId) {
|
||||||
|
result.hairTexturePath = charSectionsDbc->getString(r, csF.texture1);
|
||||||
|
if (!result.hairTexturePath.empty()) {
|
||||||
|
foundHair = true;
|
||||||
|
LOG_INFO(" DBC hair texture: ", result.hairTexturePath,
|
||||||
|
" (style=", static_cast<int>(charHairStyleId), " color=", static_cast<int>(charHairColorId), ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Section 1 = face: match variation=faceId, colorIndex=skinId
|
||||||
|
// Texture1 = face lower, Texture2 = face upper
|
||||||
|
else if (baseSection == 1 && !foundFaceLower &&
|
||||||
|
variationIndex == charFaceId && colorIndex == charSkinId) {
|
||||||
|
std::string tex1 = charSectionsDbc->getString(r, csF.texture1);
|
||||||
|
std::string tex2 = charSectionsDbc->getString(r, csF.texture2);
|
||||||
|
if (!tex1.empty()) {
|
||||||
|
result.faceLowerPath = tex1;
|
||||||
|
LOG_INFO(" DBC face lower: ", result.faceLowerPath);
|
||||||
|
}
|
||||||
|
if (!tex2.empty()) {
|
||||||
|
result.faceUpperPath = tex2;
|
||||||
|
LOG_INFO(" DBC face upper: ", result.faceUpperPath);
|
||||||
|
}
|
||||||
|
foundFaceLower = true;
|
||||||
|
}
|
||||||
|
// Section 4 = underwear
|
||||||
|
else if (baseSection == 4 && !foundUnderwear && colorIndex == charSkinId) {
|
||||||
|
for (uint32_t f = csF.texture1; f <= csF.texture1 + 2; f++) {
|
||||||
|
std::string tex = charSectionsDbc->getString(r, f);
|
||||||
|
if (!tex.empty()) {
|
||||||
|
result.underwearPaths.push_back(tex);
|
||||||
|
LOG_INFO(" DBC underwear texture: ", tex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foundUnderwear = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundSkin && foundHair && foundFaceLower && foundUnderwear) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundHair) {
|
||||||
|
LOG_WARNING("No DBC hair match for style=", static_cast<int>(charHairStyleId),
|
||||||
|
" color=", static_cast<int>(charHairColorId),
|
||||||
|
" race=", targetRaceId, " sex=", targetSexId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG_WARNING("Failed to load CharSections.dbc, using hardcoded textures");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill model texture slots with resolved paths
|
||||||
|
for (auto& tex : model.textures) {
|
||||||
|
if (tex.type == 1 && tex.filename.empty()) {
|
||||||
|
tex.filename = result.bodySkinPath;
|
||||||
|
} else if (tex.type == 6) {
|
||||||
|
if (!result.hairTexturePath.empty()) {
|
||||||
|
tex.filename = result.hairTexturePath;
|
||||||
|
} else if (tex.filename.empty()) {
|
||||||
|
tex.filename = std::string("Character\\") + raceFolderName + "\\Hair00_00.blp";
|
||||||
|
}
|
||||||
|
} else if (tex.type == 8 && tex.filename.empty()) {
|
||||||
|
if (!result.underwearPaths.empty()) {
|
||||||
|
tex.filename = result.underwearPaths[0];
|
||||||
|
} else {
|
||||||
|
tex.filename = pelvisPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppearanceComposer::compositePlayerSkin(uint32_t modelSlotId, const PlayerTextureInfo& texInfo) {
|
||||||
|
if (!renderer_) return;
|
||||||
|
auto* charRenderer = renderer_->getCharacterRenderer();
|
||||||
|
if (!charRenderer) return;
|
||||||
|
|
||||||
|
// Save skin composite state for re-compositing on equipment changes
|
||||||
|
// Include face textures so compositeWithRegions can rebuild the full base
|
||||||
|
bodySkinPath_ = texInfo.bodySkinPath;
|
||||||
|
underwearPaths_.clear();
|
||||||
|
if (!texInfo.faceLowerPath.empty()) underwearPaths_.push_back(texInfo.faceLowerPath);
|
||||||
|
if (!texInfo.faceUpperPath.empty()) underwearPaths_.push_back(texInfo.faceUpperPath);
|
||||||
|
for (const auto& up : texInfo.underwearPaths) underwearPaths_.push_back(up);
|
||||||
|
|
||||||
|
// Composite body skin + face + underwear overlays
|
||||||
|
{
|
||||||
|
std::vector<std::string> layers;
|
||||||
|
layers.push_back(texInfo.bodySkinPath);
|
||||||
|
if (!texInfo.faceLowerPath.empty()) layers.push_back(texInfo.faceLowerPath);
|
||||||
|
if (!texInfo.faceUpperPath.empty()) layers.push_back(texInfo.faceUpperPath);
|
||||||
|
for (const auto& up : texInfo.underwearPaths) {
|
||||||
|
layers.push_back(up);
|
||||||
|
}
|
||||||
|
if (layers.size() > 1) {
|
||||||
|
rendering::VkTexture* compositeTex = charRenderer->compositeTextures(layers);
|
||||||
|
if (compositeTex != 0) {
|
||||||
|
// Find type-1 (skin) texture slot and replace with composite
|
||||||
|
// We need model texture info — walk slots via charRenderer
|
||||||
|
// Use the model slot ID to find the right texture index
|
||||||
|
auto* modelData = charRenderer->getModelData(modelSlotId);
|
||||||
|
if (modelData) {
|
||||||
|
for (size_t ti = 0; ti < modelData->textures.size(); ti++) {
|
||||||
|
if (modelData->textures[ti].type == 1) {
|
||||||
|
charRenderer->setModelTexture(modelSlotId, static_cast<uint32_t>(ti), compositeTex);
|
||||||
|
skinTextureSlotIndex_ = static_cast<uint32_t>(ti);
|
||||||
|
LOG_INFO("Replaced type-1 texture slot ", ti, " with composited body+face+underwear");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override hair texture on GPU (type-6 slot) after model load
|
||||||
|
if (!texInfo.hairTexturePath.empty()) {
|
||||||
|
rendering::VkTexture* hairTex = charRenderer->loadTexture(texInfo.hairTexturePath);
|
||||||
|
if (hairTex) {
|
||||||
|
auto* modelData = charRenderer->getModelData(modelSlotId);
|
||||||
|
if (modelData) {
|
||||||
|
for (size_t ti = 0; ti < modelData->textures.size(); ti++) {
|
||||||
|
if (modelData->textures[ti].type == 6) {
|
||||||
|
charRenderer->setModelTexture(modelSlotId, static_cast<uint32_t>(ti), hairTex);
|
||||||
|
LOG_INFO("Applied DBC hair texture to slot ", ti, ": ", texInfo.hairTexturePath);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find cloak (type-2, Object Skin) texture slot index
|
||||||
|
{
|
||||||
|
auto* modelData = charRenderer->getModelData(modelSlotId);
|
||||||
|
if (modelData) {
|
||||||
|
for (size_t ti = 0; ti < modelData->textures.size(); ti++) {
|
||||||
|
if (modelData->textures[ti].type == 2) {
|
||||||
|
cloakTextureSlotIndex_ = static_cast<uint32_t>(ti);
|
||||||
|
LOG_INFO("Cloak texture slot: ", ti);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_set<uint16_t> AppearanceComposer::buildDefaultPlayerGeosets(uint8_t hairStyleId, uint8_t facialId) {
|
||||||
|
std::unordered_set<uint16_t> activeGeosets;
|
||||||
|
// Body parts (group 0: IDs 0-99, some models use up to 27)
|
||||||
|
for (uint16_t i = 0; i <= 99; i++) activeGeosets.insert(i);
|
||||||
|
|
||||||
|
// Hair style geoset: group 1 = 100 + variation + 1
|
||||||
|
activeGeosets.insert(static_cast<uint16_t>(100 + hairStyleId + 1));
|
||||||
|
// Facial hair geoset: group 2 = 200 + variation + 1
|
||||||
|
activeGeosets.insert(static_cast<uint16_t>(200 + facialId + 1));
|
||||||
|
activeGeosets.insert(kGeosetBareForearms);
|
||||||
|
activeGeosets.insert(kGeosetBareShins);
|
||||||
|
activeGeosets.insert(kGeosetDefaultEars);
|
||||||
|
activeGeosets.insert(kGeosetBareSleeves);
|
||||||
|
activeGeosets.insert(kGeosetDefaultKneepads);
|
||||||
|
activeGeosets.insert(kGeosetBarePants);
|
||||||
|
activeGeosets.insert(kGeosetWithCape);
|
||||||
|
activeGeosets.insert(kGeosetBareFeet);
|
||||||
|
// 1703 = DK eye glow mesh — skip for normal characters
|
||||||
|
// Normal eyes are part of the face texture on the body mesh
|
||||||
|
return activeGeosets;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppearanceComposer::loadWeaponM2(const std::string& m2Path, pipeline::M2Model& outModel) {
|
||||||
|
auto m2Data = assetManager_->readFile(m2Path);
|
||||||
|
if (m2Data.empty()) return false;
|
||||||
|
outModel = pipeline::M2Loader::load(m2Data);
|
||||||
|
// Load skin (WotLK+ M2 format): strip .m2, append 00.skin
|
||||||
|
std::string skinPath = m2Path;
|
||||||
|
size_t dotPos = skinPath.rfind('.');
|
||||||
|
if (dotPos != std::string::npos) skinPath = skinPath.substr(0, dotPos);
|
||||||
|
skinPath += "00.skin";
|
||||||
|
auto skinData = assetManager_->readFile(skinPath);
|
||||||
|
if (!skinData.empty() && outModel.version >= 264)
|
||||||
|
pipeline::M2Loader::loadSkin(skinData, outModel);
|
||||||
|
return outModel.isValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AppearanceComposer::loadEquippedWeapons() {
|
||||||
|
if (!renderer_ || !renderer_->getCharacterRenderer() || !assetManager_ || !assetManager_->isInitialized())
|
||||||
|
return;
|
||||||
|
if (!gameHandler_) return;
|
||||||
|
|
||||||
|
auto* charRenderer = renderer_->getCharacterRenderer();
|
||||||
|
uint32_t charInstanceId = renderer_->getCharacterInstanceId();
|
||||||
|
if (charInstanceId == 0) return;
|
||||||
|
|
||||||
|
auto& inventory = gameHandler_->getInventory();
|
||||||
|
|
||||||
|
// Load ItemDisplayInfo.dbc
|
||||||
|
auto displayInfoDbc = assetManager_->loadDBC("ItemDisplayInfo.dbc");
|
||||||
|
if (!displayInfoDbc) {
|
||||||
|
LOG_WARNING("loadEquippedWeapons: failed to load ItemDisplayInfo.dbc");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Mapping: EquipSlot → attachment ID (1=RightHand, 2=LeftHand)
|
||||||
|
struct WeaponSlot {
|
||||||
|
game::EquipSlot slot;
|
||||||
|
uint32_t attachmentId;
|
||||||
|
};
|
||||||
|
WeaponSlot weaponSlots[] = {
|
||||||
|
{ game::EquipSlot::MAIN_HAND, 1 },
|
||||||
|
{ game::EquipSlot::OFF_HAND, 2 },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (weaponsSheathed_) {
|
||||||
|
for (const auto& ws : weaponSlots) {
|
||||||
|
charRenderer->detachWeapon(charInstanceId, ws.attachmentId);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& ws : weaponSlots) {
|
||||||
|
const auto& equipSlot = inventory.getEquipSlot(ws.slot);
|
||||||
|
|
||||||
|
// If slot is empty or has no displayInfoId, detach any existing weapon
|
||||||
|
if (equipSlot.empty() || equipSlot.item.displayInfoId == 0) {
|
||||||
|
charRenderer->detachWeapon(charInstanceId, ws.attachmentId);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t displayInfoId = equipSlot.item.displayInfoId;
|
||||||
|
int32_t recIdx = displayInfoDbc->findRecordById(displayInfoId);
|
||||||
|
if (recIdx < 0) {
|
||||||
|
LOG_WARNING("loadEquippedWeapons: displayInfoId ", displayInfoId, " not found in DBC");
|
||||||
|
charRenderer->detachWeapon(charInstanceId, ws.attachmentId);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* idiL = pipeline::getActiveDBCLayout() ? pipeline::getActiveDBCLayout()->getLayout("ItemDisplayInfo") : nullptr;
|
||||||
|
std::string modelName = displayInfoDbc->getString(static_cast<uint32_t>(recIdx), idiL ? (*idiL)["LeftModel"] : 1);
|
||||||
|
std::string textureName = displayInfoDbc->getString(static_cast<uint32_t>(recIdx), idiL ? (*idiL)["LeftModelTexture"] : 3);
|
||||||
|
|
||||||
|
if (modelName.empty()) {
|
||||||
|
LOG_WARNING("loadEquippedWeapons: empty model name for displayInfoId ", displayInfoId);
|
||||||
|
charRenderer->detachWeapon(charInstanceId, ws.attachmentId);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert .mdx → .m2
|
||||||
|
std::string modelFile = modelName;
|
||||||
|
{
|
||||||
|
size_t dotPos = modelFile.rfind('.');
|
||||||
|
if (dotPos != std::string::npos) {
|
||||||
|
modelFile = modelFile.substr(0, dotPos) + ".m2";
|
||||||
|
} else {
|
||||||
|
modelFile += ".m2";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try Weapon directory first, then Shield
|
||||||
|
std::string m2Path = "Item\\ObjectComponents\\Weapon\\" + modelFile;
|
||||||
|
pipeline::M2Model weaponModel;
|
||||||
|
if (!loadWeaponM2(m2Path, weaponModel)) {
|
||||||
|
m2Path = "Item\\ObjectComponents\\Shield\\" + modelFile;
|
||||||
|
if (!loadWeaponM2(m2Path, weaponModel)) {
|
||||||
|
LOG_WARNING("loadEquippedWeapons: failed to load ", modelFile);
|
||||||
|
charRenderer->detachWeapon(charInstanceId, ws.attachmentId);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build texture path
|
||||||
|
std::string texturePath;
|
||||||
|
if (!textureName.empty()) {
|
||||||
|
texturePath = "Item\\ObjectComponents\\Weapon\\" + textureName + ".blp";
|
||||||
|
if (!assetManager_->fileExists(texturePath)) {
|
||||||
|
texturePath = "Item\\ObjectComponents\\Shield\\" + textureName + ".blp";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t weaponModelId = entitySpawner_->allocateWeaponModelId();
|
||||||
|
bool ok = charRenderer->attachWeapon(charInstanceId, ws.attachmentId,
|
||||||
|
weaponModel, weaponModelId, texturePath);
|
||||||
|
if (ok) {
|
||||||
|
LOG_INFO("Equipped weapon: ", m2Path, " at attachment ", ws.attachmentId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace core
|
||||||
|
} // namespace wowee
|
||||||
File diff suppressed because it is too large
Load diff
1217
src/core/world_loader.cpp
Normal file
1217
src/core/world_loader.cpp
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -172,7 +172,7 @@ void ActionBarPanel::renderActionBar(game::GameHandler& gameHandler,
|
||||||
ImVec2 displaySize = ImGui::GetIO().DisplaySize;
|
ImVec2 displaySize = ImGui::GetIO().DisplaySize;
|
||||||
float screenW = displaySize.x > 0.0f ? displaySize.x : 1280.0f;
|
float screenW = displaySize.x > 0.0f ? displaySize.x : 1280.0f;
|
||||||
float screenH = displaySize.y > 0.0f ? displaySize.y : 720.0f;
|
float screenH = displaySize.y > 0.0f ? displaySize.y : 720.0f;
|
||||||
auto* assetMgr = core::Application::getInstance().getAssetManager();
|
auto* assetMgr = services_.assetManager;
|
||||||
|
|
||||||
float slotSize = 48.0f * settingsPanel.pendingActionBarScale;
|
float slotSize = 48.0f * settingsPanel.pendingActionBarScale;
|
||||||
float spacing = 4.0f;
|
float spacing = 4.0f;
|
||||||
|
|
@ -1107,7 +1107,7 @@ void ActionBarPanel::renderStanceBar(game::GameHandler& gameHandler,
|
||||||
ImVec2 displaySize = ImGui::GetIO().DisplaySize;
|
ImVec2 displaySize = ImGui::GetIO().DisplaySize;
|
||||||
float screenW = displaySize.x > 0.0f ? displaySize.x : 1280.0f;
|
float screenW = displaySize.x > 0.0f ? displaySize.x : 1280.0f;
|
||||||
float screenH = displaySize.y > 0.0f ? displaySize.y : 720.0f;
|
float screenH = displaySize.y > 0.0f ? displaySize.y : 720.0f;
|
||||||
auto* assetMgr = core::Application::getInstance().getAssetManager();
|
auto* assetMgr = services_.assetManager;
|
||||||
|
|
||||||
// Match the action bar slot size so they align neatly
|
// Match the action bar slot size so they align neatly
|
||||||
float slotSize = 38.0f;
|
float slotSize = 38.0f;
|
||||||
|
|
@ -1196,7 +1196,7 @@ void ActionBarPanel::renderBagBar(game::GameHandler& gameHandler,
|
||||||
ImVec2 displaySize = ImGui::GetIO().DisplaySize;
|
ImVec2 displaySize = ImGui::GetIO().DisplaySize;
|
||||||
float screenW = displaySize.x > 0.0f ? displaySize.x : 1280.0f;
|
float screenW = displaySize.x > 0.0f ? displaySize.x : 1280.0f;
|
||||||
float screenH = displaySize.y > 0.0f ? displaySize.y : 720.0f;
|
float screenH = displaySize.y > 0.0f ? displaySize.y : 720.0f;
|
||||||
auto* assetMgr = core::Application::getInstance().getAssetManager();
|
auto* assetMgr = services_.assetManager;
|
||||||
|
|
||||||
float slotSize = 42.0f;
|
float slotSize = 42.0f;
|
||||||
float spacing = 4.0f;
|
float spacing = 4.0f;
|
||||||
|
|
@ -1232,7 +1232,7 @@ void ActionBarPanel::renderBagBar(game::GameHandler& gameHandler,
|
||||||
if (!blpData.empty()) {
|
if (!blpData.empty()) {
|
||||||
auto image = pipeline::BLPLoader::load(blpData);
|
auto image = pipeline::BLPLoader::load(blpData);
|
||||||
if (image.isValid()) {
|
if (image.isValid()) {
|
||||||
auto* w = core::Application::getInstance().getWindow();
|
auto* w = services_.window;
|
||||||
auto* vkCtx = w ? w->getVkContext() : nullptr;
|
auto* vkCtx = w ? w->getVkContext() : nullptr;
|
||||||
if (vkCtx)
|
if (vkCtx)
|
||||||
backpackIconTexture_ = vkCtx->uploadImGuiTexture(image.data.data(), image.width, image.height);
|
backpackIconTexture_ = vkCtx->uploadImGuiTexture(image.data.data(), image.width, image.height);
|
||||||
|
|
@ -1483,7 +1483,7 @@ void ActionBarPanel::renderXpBar(game::GameHandler& gameHandler,
|
||||||
ImVec2 displaySize = ImGui::GetIO().DisplaySize;
|
ImVec2 displaySize = ImGui::GetIO().DisplaySize;
|
||||||
float screenW = displaySize.x > 0.0f ? displaySize.x : 1280.0f;
|
float screenW = displaySize.x > 0.0f ? displaySize.x : 1280.0f;
|
||||||
float screenH = displaySize.y > 0.0f ? displaySize.y : 720.0f;
|
float screenH = displaySize.y > 0.0f ? displaySize.y : 720.0f;
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
(void)window; // Not used for positioning; kept for AssetManager if needed
|
(void)window; // Not used for positioning; kept for AssetManager if needed
|
||||||
|
|
||||||
// Position just above both action bars (bar1 at screenH-barH, bar2 above that)
|
// Position just above both action bars (bar1 at screenH-barH, bar2 above that)
|
||||||
|
|
|
||||||
|
|
@ -197,8 +197,8 @@ void ChatPanel::render(game::GameHandler& gameHandler,
|
||||||
InventoryScreen& inventoryScreen,
|
InventoryScreen& inventoryScreen,
|
||||||
SpellbookScreen& spellbookScreen,
|
SpellbookScreen& spellbookScreen,
|
||||||
QuestLogScreen& questLogScreen) {
|
QuestLogScreen& questLogScreen) {
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
auto* assetMgr = core::Application::getInstance().getAssetManager();
|
auto* assetMgr = services_.assetManager;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
float chatW = std::min(500.0f, screenW * 0.4f);
|
float chatW = std::min(500.0f, screenW * 0.4f);
|
||||||
|
|
@ -1109,7 +1109,7 @@ void ChatPanel::render(game::GameHandler& gameHandler,
|
||||||
std::string bodyLower = mMsg.message;
|
std::string bodyLower = mMsg.message;
|
||||||
for (auto& c : bodyLower) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
|
for (auto& c : bodyLower) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
|
||||||
if (bodyLower.find(selfNameLower) != std::string::npos) {
|
if (bodyLower.find(selfNameLower) != std::string::npos) {
|
||||||
if (auto* renderer = core::Application::getInstance().getRenderer()) {
|
if (auto* renderer = services_.renderer) {
|
||||||
if (auto* ui = renderer->getUiSoundManager())
|
if (auto* ui = renderer->getUiSoundManager())
|
||||||
ui->playWhisperReceived();
|
ui->playWhisperReceived();
|
||||||
}
|
}
|
||||||
|
|
@ -2151,7 +2151,7 @@ void ChatPanel::sendChatMessage(game::GameHandler& gameHandler,
|
||||||
// /run <lua code> — execute Lua script via addon system
|
// /run <lua code> — execute Lua script via addon system
|
||||||
if ((cmdLower == "run" || cmdLower == "script") && spacePos != std::string::npos) {
|
if ((cmdLower == "run" || cmdLower == "script") && spacePos != std::string::npos) {
|
||||||
std::string luaCode = command.substr(spacePos + 1);
|
std::string luaCode = command.substr(spacePos + 1);
|
||||||
auto* am = core::Application::getInstance().getAddonManager();
|
auto* am = services_.addonManager;
|
||||||
if (am) {
|
if (am) {
|
||||||
am->runScript(luaCode);
|
am->runScript(luaCode);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -2164,7 +2164,7 @@ void ChatPanel::sendChatMessage(game::GameHandler& gameHandler,
|
||||||
// /dump <expression> — evaluate Lua expression and print result
|
// /dump <expression> — evaluate Lua expression and print result
|
||||||
if ((cmdLower == "dump" || cmdLower == "print") && spacePos != std::string::npos) {
|
if ((cmdLower == "dump" || cmdLower == "print") && spacePos != std::string::npos) {
|
||||||
std::string expr = command.substr(spacePos + 1);
|
std::string expr = command.substr(spacePos + 1);
|
||||||
auto* am = core::Application::getInstance().getAddonManager();
|
auto* am = services_.addonManager;
|
||||||
if (am && am->isInitialized()) {
|
if (am && am->isInitialized()) {
|
||||||
// Wrap expression in print(tostring(...)) to display the value
|
// Wrap expression in print(tostring(...)) to display the value
|
||||||
std::string wrapped = "local __v = " + expr +
|
std::string wrapped = "local __v = " + expr +
|
||||||
|
|
@ -2187,7 +2187,7 @@ void ChatPanel::sendChatMessage(game::GameHandler& gameHandler,
|
||||||
|
|
||||||
// Check addon slash commands (SlashCmdList) before built-in commands
|
// Check addon slash commands (SlashCmdList) before built-in commands
|
||||||
{
|
{
|
||||||
auto* am = core::Application::getInstance().getAddonManager();
|
auto* am = services_.addonManager;
|
||||||
if (am && am->isInitialized()) {
|
if (am && am->isInitialized()) {
|
||||||
std::string slashCmd = "/" + cmdLower;
|
std::string slashCmd = "/" + cmdLower;
|
||||||
std::string slashArgs;
|
std::string slashArgs;
|
||||||
|
|
@ -2214,7 +2214,7 @@ void ChatPanel::sendChatMessage(game::GameHandler& gameHandler,
|
||||||
|
|
||||||
// /reload or /reloadui — reload all addons (save variables, re-init Lua, re-scan .toc files)
|
// /reload or /reloadui — reload all addons (save variables, re-init Lua, re-scan .toc files)
|
||||||
if (cmdLower == "reload" || cmdLower == "reloadui" || cmdLower == "rl") {
|
if (cmdLower == "reload" || cmdLower == "reloadui" || cmdLower == "rl") {
|
||||||
auto* am = core::Application::getInstance().getAddonManager();
|
auto* am = services_.addonManager;
|
||||||
if (am) {
|
if (am) {
|
||||||
am->reload();
|
am->reload();
|
||||||
am->fireEvent("VARIABLES_LOADED");
|
am->fireEvent("VARIABLES_LOADED");
|
||||||
|
|
@ -2301,7 +2301,7 @@ void ChatPanel::sendChatMessage(game::GameHandler& gameHandler,
|
||||||
if (cmdLower == "loc" || cmdLower == "coords" || cmdLower == "whereami") {
|
if (cmdLower == "loc" || cmdLower == "coords" || cmdLower == "whereami") {
|
||||||
const auto& pmi = gameHandler.getMovementInfo();
|
const auto& pmi = gameHandler.getMovementInfo();
|
||||||
std::string zoneName;
|
std::string zoneName;
|
||||||
if (auto* rend = core::Application::getInstance().getRenderer())
|
if (auto* rend = services_.renderer)
|
||||||
zoneName = rend->getCurrentZoneName();
|
zoneName = rend->getCurrentZoneName();
|
||||||
char buf[256];
|
char buf[256];
|
||||||
snprintf(buf, sizeof(buf), "%.1f, %.1f, %.1f%s%s",
|
snprintf(buf, sizeof(buf), "%.1f, %.1f, %.1f%s%s",
|
||||||
|
|
@ -2327,7 +2327,7 @@ void ChatPanel::sendChatMessage(game::GameHandler& gameHandler,
|
||||||
// /zone command — print current zone name
|
// /zone command — print current zone name
|
||||||
if (cmdLower == "zone") {
|
if (cmdLower == "zone") {
|
||||||
std::string zoneName;
|
std::string zoneName;
|
||||||
if (auto* rend = core::Application::getInstance().getRenderer())
|
if (auto* rend = services_.renderer)
|
||||||
zoneName = rend->getCurrentZoneName();
|
zoneName = rend->getCurrentZoneName();
|
||||||
game::MessageChatData sysMsg;
|
game::MessageChatData sysMsg;
|
||||||
sysMsg.type = game::ChatType::SYSTEM;
|
sysMsg.type = game::ChatType::SYSTEM;
|
||||||
|
|
@ -4323,7 +4323,7 @@ void ChatPanel::sendChatMessage(game::GameHandler& gameHandler,
|
||||||
std::string emoteText = rendering::Renderer::getEmoteText(cmdLower, targetNamePtr);
|
std::string emoteText = rendering::Renderer::getEmoteText(cmdLower, targetNamePtr);
|
||||||
if (!emoteText.empty()) {
|
if (!emoteText.empty()) {
|
||||||
// Play the emote animation
|
// Play the emote animation
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
if (renderer) {
|
if (renderer) {
|
||||||
renderer->playEmote(cmdLower);
|
renderer->playEmote(cmdLower);
|
||||||
}
|
}
|
||||||
|
|
@ -4697,11 +4697,11 @@ std::string ChatPanel::replaceGenderPlaceholders(const std::string& text, game::
|
||||||
void ChatPanel::renderBubbles(game::GameHandler& gameHandler) {
|
void ChatPanel::renderBubbles(game::GameHandler& gameHandler) {
|
||||||
if (chatBubbles_.empty()) return;
|
if (chatBubbles_.empty()) return;
|
||||||
|
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
auto* camera = renderer ? renderer->getCamera() : nullptr;
|
auto* camera = renderer ? renderer->getCamera() : nullptr;
|
||||||
if (!camera) return;
|
if (!camera) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ namespace ui {
|
||||||
void CombatUI::renderCastBar(game::GameHandler& gameHandler, SpellIconFn getSpellIcon) {
|
void CombatUI::renderCastBar(game::GameHandler& gameHandler, SpellIconFn getSpellIcon) {
|
||||||
if (!gameHandler.isCasting()) return;
|
if (!gameHandler.isCasting()) return;
|
||||||
|
|
||||||
auto* assetMgr = core::Application::getInstance().getAssetManager();
|
auto* assetMgr = services_.assetManager;
|
||||||
|
|
||||||
ImVec2 displaySize = ImGui::GetIO().DisplaySize;
|
ImVec2 displaySize = ImGui::GetIO().DisplaySize;
|
||||||
float screenW = displaySize.x > 0.0f ? displaySize.x : 1280.0f;
|
float screenW = displaySize.x > 0.0f ? displaySize.x : 1280.0f;
|
||||||
|
|
@ -187,8 +187,8 @@ void CombatUI::renderCooldownTracker(game::GameHandler& gameHandler,
|
||||||
return a.remaining > b.remaining;
|
return a.remaining > b.remaining;
|
||||||
});
|
});
|
||||||
|
|
||||||
auto* assetMgr = core::Application::getInstance().getAssetManager();
|
auto* assetMgr = services_.assetManager;
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -268,7 +268,7 @@ void CombatUI::renderRaidWarningOverlay(game::GameHandler& gameHandler) {
|
||||||
// Walk only the new messages (deque — iterate from back by skipping old ones)
|
// Walk only the new messages (deque — iterate from back by skipping old ones)
|
||||||
size_t toScan = newCount - raidWarnChatSeenCount_;
|
size_t toScan = newCount - raidWarnChatSeenCount_;
|
||||||
size_t startIdx = newCount > toScan ? newCount - toScan : 0;
|
size_t startIdx = newCount > toScan ? newCount - toScan : 0;
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
for (size_t i = startIdx; i < newCount; ++i) {
|
for (size_t i = startIdx; i < newCount; ++i) {
|
||||||
const auto& msg = chatHistory[i];
|
const auto& msg = chatHistory[i];
|
||||||
if (msg.type == game::ChatType::RAID_WARNING ||
|
if (msg.type == game::ChatType::RAID_WARNING ||
|
||||||
|
|
@ -361,13 +361,13 @@ void CombatUI::renderCombatText(game::GameHandler& gameHandler) {
|
||||||
const auto& entries = gameHandler.getCombatText();
|
const auto& entries = gameHandler.getCombatText();
|
||||||
if (entries.empty()) return;
|
if (entries.empty()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
if (!window) return;
|
if (!window) return;
|
||||||
const float screenW = static_cast<float>(window->getWidth());
|
const float screenW = static_cast<float>(window->getWidth());
|
||||||
const float screenH = static_cast<float>(window->getHeight());
|
const float screenH = static_cast<float>(window->getHeight());
|
||||||
|
|
||||||
// Camera for world-space projection
|
// Camera for world-space projection
|
||||||
auto* appRenderer = core::Application::getInstance().getRenderer();
|
auto* appRenderer = services_.renderer;
|
||||||
rendering::Camera* camera = appRenderer ? appRenderer->getCamera() : nullptr;
|
rendering::Camera* camera = appRenderer ? appRenderer->getCamera() : nullptr;
|
||||||
glm::mat4 viewProj;
|
glm::mat4 viewProj;
|
||||||
if (camera) viewProj = camera->getProjectionMatrix() * camera->getViewMatrix();
|
if (camera) viewProj = camera->getProjectionMatrix() * camera->getViewMatrix();
|
||||||
|
|
@ -785,7 +785,7 @@ void CombatUI::renderDPSMeter(game::GameHandler& gameHandler,
|
||||||
fmtNum(hps, hpsBuf, sizeof(hpsBuf));
|
fmtNum(hps, hpsBuf, sizeof(hpsBuf));
|
||||||
|
|
||||||
// Position: small floating label just above the action bar, right of center
|
// Position: small floating label just above the action bar, right of center
|
||||||
auto* appWin = core::Application::getInstance().getWindow();
|
auto* appWin = services_.window;
|
||||||
float screenW = appWin ? static_cast<float>(appWin->getWidth()) : 1280.0f;
|
float screenW = appWin ? static_cast<float>(appWin->getWidth()) : 1280.0f;
|
||||||
float screenH = appWin ? static_cast<float>(appWin->getHeight()) : 720.0f;
|
float screenH = appWin ? static_cast<float>(appWin->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -866,7 +866,7 @@ void CombatUI::renderBuffBar(game::GameHandler& gameHandler,
|
||||||
}
|
}
|
||||||
if (activeCount == 0 && !gameHandler.hasPet()) return;
|
if (activeCount == 0 && !gameHandler.hasPet()) return;
|
||||||
|
|
||||||
auto* assetMgr = core::Application::getInstance().getAssetManager();
|
auto* assetMgr = services_.assetManager;
|
||||||
|
|
||||||
// Position below the minimap (minimap: 200x200 at top-right, bottom edge at Y≈210)
|
// Position below the minimap (minimap: 200x200 at top-right, bottom edge at Y≈210)
|
||||||
// Anchored to the right side to stay away from party frames on the left
|
// Anchored to the right side to stay away from party frames on the left
|
||||||
|
|
@ -1201,7 +1201,7 @@ void CombatUI::renderBattlegroundScore(game::GameHandler& gameHandler) {
|
||||||
if (auto mv = gameHandler.getWorldState(def->maxKey)) maxScore = *mv;
|
if (auto mv = gameHandler.getWorldState(def->maxKey)) maxScore = *mv;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
|
|
||||||
// Width scales with screen but stays reasonable
|
// Width scales with screen but stays reasonable
|
||||||
|
|
@ -1598,7 +1598,7 @@ void CombatUI::renderCombatLog(game::GameHandler& gameHandler,
|
||||||
ImGui::TextColored(color, "%s", desc);
|
ImGui::TextColored(color, "%s", desc);
|
||||||
// Hover tooltip: show rich spell info for entries with a known spell
|
// Hover tooltip: show rich spell info for entries with a known spell
|
||||||
if (e.spellId != 0 && ImGui::IsItemHovered()) {
|
if (e.spellId != 0 && ImGui::IsItemHovered()) {
|
||||||
auto* assetMgrLog = core::Application::getInstance().getAssetManager();
|
auto* assetMgrLog = services_.assetManager;
|
||||||
ImGui::BeginTooltip();
|
ImGui::BeginTooltip();
|
||||||
bool richOk = spellbookScreen.renderSpellInfoTooltip(e.spellId, gameHandler, assetMgrLog);
|
bool richOk = spellbookScreen.renderSpellInfoTooltip(e.spellId, gameHandler, assetMgrLog);
|
||||||
if (!richOk) {
|
if (!richOk) {
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ void DialogManager::renderLateDialogs(game::GameHandler& gameHandler) {
|
||||||
void DialogManager::renderGroupInvitePopup(game::GameHandler& gameHandler) {
|
void DialogManager::renderGroupInvitePopup(game::GameHandler& gameHandler) {
|
||||||
if (!gameHandler.hasPendingGroupInvite()) return;
|
if (!gameHandler.hasPendingGroupInvite()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 150, 200), ImGuiCond_Always);
|
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 150, 200), ImGuiCond_Always);
|
||||||
|
|
@ -93,7 +93,7 @@ void DialogManager::renderGroupInvitePopup(game::GameHandler& gameHandler) {
|
||||||
void DialogManager::renderDuelRequestPopup(game::GameHandler& gameHandler) {
|
void DialogManager::renderDuelRequestPopup(game::GameHandler& gameHandler) {
|
||||||
if (!gameHandler.hasPendingDuelRequest()) return;
|
if (!gameHandler.hasPendingDuelRequest()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 150, 250), ImGuiCond_Always);
|
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 150, 250), ImGuiCond_Always);
|
||||||
|
|
@ -158,7 +158,7 @@ void DialogManager::renderDuelCountdown(game::GameHandler& gameHandler) {
|
||||||
void DialogManager::renderItemTextWindow(game::GameHandler& gameHandler) {
|
void DialogManager::renderItemTextWindow(game::GameHandler& gameHandler) {
|
||||||
if (!gameHandler.isItemTextOpen()) return;
|
if (!gameHandler.isItemTextOpen()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -194,7 +194,7 @@ void DialogManager::renderItemTextWindow(game::GameHandler& gameHandler) {
|
||||||
void DialogManager::renderSharedQuestPopup(game::GameHandler& gameHandler) {
|
void DialogManager::renderSharedQuestPopup(game::GameHandler& gameHandler) {
|
||||||
if (!gameHandler.hasPendingSharedQuest()) return;
|
if (!gameHandler.hasPendingSharedQuest()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 175, 490), ImGuiCond_Always);
|
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 175, 490), ImGuiCond_Always);
|
||||||
|
|
@ -224,7 +224,7 @@ void DialogManager::renderSummonRequestPopup(game::GameHandler& gameHandler) {
|
||||||
gameHandler.tickSummonTimeout(dt);
|
gameHandler.tickSummonTimeout(dt);
|
||||||
if (!gameHandler.hasPendingSummonRequest()) return; // expired
|
if (!gameHandler.hasPendingSummonRequest()) return; // expired
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 175, 430), ImGuiCond_Always);
|
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 175, 430), ImGuiCond_Always);
|
||||||
|
|
@ -252,7 +252,7 @@ void DialogManager::renderSummonRequestPopup(game::GameHandler& gameHandler) {
|
||||||
void DialogManager::renderTradeRequestPopup(game::GameHandler& gameHandler) {
|
void DialogManager::renderTradeRequestPopup(game::GameHandler& gameHandler) {
|
||||||
if (!gameHandler.hasPendingTradeRequest()) return;
|
if (!gameHandler.hasPendingTradeRequest()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 150, 370), ImGuiCond_Always);
|
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 150, 370), ImGuiCond_Always);
|
||||||
|
|
@ -284,7 +284,7 @@ void DialogManager::renderTradeWindow(game::GameHandler& gameHandler,
|
||||||
const uint64_t peerGold = gameHandler.getPeerTradeGold();
|
const uint64_t peerGold = gameHandler.getPeerTradeGold();
|
||||||
const auto& peerName = gameHandler.getTradePeerName();
|
const auto& peerName = gameHandler.getTradePeerName();
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -443,7 +443,7 @@ void DialogManager::renderLootRollPopup(game::GameHandler& gameHandler,
|
||||||
|
|
||||||
const auto& roll = gameHandler.getPendingLootRoll();
|
const auto& roll = gameHandler.getPendingLootRoll();
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 175, 310), ImGuiCond_Always);
|
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 175, 310), ImGuiCond_Always);
|
||||||
|
|
@ -584,7 +584,7 @@ void DialogManager::renderLootRollPopup(game::GameHandler& gameHandler,
|
||||||
void DialogManager::renderGuildInvitePopup(game::GameHandler& gameHandler) {
|
void DialogManager::renderGuildInvitePopup(game::GameHandler& gameHandler) {
|
||||||
if (!gameHandler.hasPendingGuildInvite()) return;
|
if (!gameHandler.hasPendingGuildInvite()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 175, 250), ImGuiCond_Always);
|
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 175, 250), ImGuiCond_Always);
|
||||||
|
|
@ -610,7 +610,7 @@ void DialogManager::renderGuildInvitePopup(game::GameHandler& gameHandler) {
|
||||||
void DialogManager::renderReadyCheckPopup(game::GameHandler& gameHandler) {
|
void DialogManager::renderReadyCheckPopup(game::GameHandler& gameHandler) {
|
||||||
if (!gameHandler.hasPendingReadyCheck()) return;
|
if (!gameHandler.hasPendingReadyCheck()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -684,7 +684,7 @@ void DialogManager::renderBgInvitePopup(game::GameHandler& gameHandler) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -745,7 +745,7 @@ void DialogManager::renderBfMgrInvitePopup(game::GameHandler& gameHandler) {
|
||||||
// Only shown on WotLK servers (outdoor battlefields like Wintergrasp use the BF Manager)
|
// Only shown on WotLK servers (outdoor battlefields like Wintergrasp use the BF Manager)
|
||||||
if (!gameHandler.hasBfMgrInvite()) return;
|
if (!gameHandler.hasBfMgrInvite()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -802,7 +802,7 @@ void DialogManager::renderLfgProposalPopup(game::GameHandler& gameHandler) {
|
||||||
using LfgState = game::GameHandler::LfgState;
|
using LfgState = game::GameHandler::LfgState;
|
||||||
if (gameHandler.getLfgState() != LfgState::Proposal) return;
|
if (gameHandler.getLfgState() != LfgState::Proposal) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -851,7 +851,7 @@ void DialogManager::renderLfgRoleCheckPopup(game::GameHandler& gameHandler) {
|
||||||
using LfgState = game::GameHandler::LfgState;
|
using LfgState = game::GameHandler::LfgState;
|
||||||
if (gameHandler.getLfgState() != LfgState::RoleCheck) return;
|
if (gameHandler.getLfgState() != LfgState::RoleCheck) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -915,7 +915,7 @@ void DialogManager::renderLfgRoleCheckPopup(game::GameHandler& gameHandler) {
|
||||||
void DialogManager::renderResurrectDialog(game::GameHandler& gameHandler) {
|
void DialogManager::renderResurrectDialog(game::GameHandler& gameHandler) {
|
||||||
if (!gameHandler.showResurrectDialog()) return;
|
if (!gameHandler.showResurrectDialog()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -976,7 +976,7 @@ void DialogManager::renderResurrectDialog(game::GameHandler& gameHandler) {
|
||||||
void DialogManager::renderTalentWipeConfirmDialog(game::GameHandler& gameHandler) {
|
void DialogManager::renderTalentWipeConfirmDialog(game::GameHandler& gameHandler) {
|
||||||
if (!gameHandler.showTalentWipeConfirmDialog()) return;
|
if (!gameHandler.showTalentWipeConfirmDialog()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -1046,7 +1046,7 @@ void DialogManager::renderTalentWipeConfirmDialog(game::GameHandler& gameHandler
|
||||||
void DialogManager::renderPetUnlearnConfirmDialog(game::GameHandler& gameHandler) {
|
void DialogManager::renderPetUnlearnConfirmDialog(game::GameHandler& gameHandler) {
|
||||||
if (!gameHandler.showPetUnlearnDialog()) return;
|
if (!gameHandler.showPetUnlearnDialog()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include "ui/ui_colors.hpp"
|
#include "ui/ui_colors.hpp"
|
||||||
#include "rendering/vk_context.hpp"
|
#include "rendering/vk_context.hpp"
|
||||||
#include "core/application.hpp"
|
#include "core/application.hpp"
|
||||||
|
#include "core/appearance_composer.hpp"
|
||||||
#include "addons/addon_manager.hpp"
|
#include "addons/addon_manager.hpp"
|
||||||
#include "core/coordinates.hpp"
|
#include "core/coordinates.hpp"
|
||||||
#include "core/input.hpp"
|
#include "core/input.hpp"
|
||||||
|
|
@ -249,6 +250,22 @@ GameScreen::GameScreen() {
|
||||||
loadSettings();
|
loadSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Section 3.5: Set UI services and propagate to child components
|
||||||
|
void GameScreen::setServices(const UIServices& services) {
|
||||||
|
services_ = services;
|
||||||
|
// Update legacy pointer for Phase A compatibility
|
||||||
|
appearanceComposer_ = services.appearanceComposer;
|
||||||
|
// Propagate to child panels
|
||||||
|
chatPanel_.setServices(services);
|
||||||
|
toastManager_.setServices(services);
|
||||||
|
dialogManager_.setServices(services);
|
||||||
|
settingsPanel_.setServices(services);
|
||||||
|
combatUI_.setServices(services);
|
||||||
|
socialPanel_.setServices(services);
|
||||||
|
actionBarPanel_.setServices(services);
|
||||||
|
windowManager_.setServices(services);
|
||||||
|
}
|
||||||
|
|
||||||
void GameScreen::render(game::GameHandler& gameHandler) {
|
void GameScreen::render(game::GameHandler& gameHandler) {
|
||||||
// Set up chat bubble callback (once) and cache game handler in ChatPanel
|
// Set up chat bubble callback (once) and cache game handler in ChatPanel
|
||||||
chatPanel_.setupCallbacks(gameHandler);
|
chatPanel_.setupCallbacks(gameHandler);
|
||||||
|
|
@ -268,7 +285,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
||||||
uiErrors_.push_back({msg, 0.0f});
|
uiErrors_.push_back({msg, 0.0f});
|
||||||
if (uiErrors_.size() > 5) uiErrors_.erase(uiErrors_.begin());
|
if (uiErrors_.size() > 5) uiErrors_.erase(uiErrors_.begin());
|
||||||
// Play error sound for each new error (rate-limited by deque cap of 5)
|
// Play error sound for each new error (rate-limited by deque cap of 5)
|
||||||
if (auto* r = core::Application::getInstance().getRenderer()) {
|
if (auto* r = services_.renderer) {
|
||||||
if (auto* sfx = r->getUiSoundManager()) sfx->playError();
|
if (auto* sfx = r->getUiSoundManager()) sfx->playError();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -291,7 +308,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
||||||
|
|
||||||
// Sync minimap opacity with UI opacity
|
// Sync minimap opacity with UI opacity
|
||||||
{
|
{
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
if (renderer) {
|
if (renderer) {
|
||||||
if (auto* minimap = renderer->getMinimap()) {
|
if (auto* minimap = renderer->getMinimap()) {
|
||||||
minimap->setOpacity(settingsPanel_.uiOpacity_);
|
minimap->setOpacity(settingsPanel_.uiOpacity_);
|
||||||
|
|
@ -301,7 +318,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
||||||
|
|
||||||
// Apply initial settings when renderer becomes available
|
// Apply initial settings when renderer becomes available
|
||||||
if (!settingsPanel_.minimapSettingsApplied_) {
|
if (!settingsPanel_.minimapSettingsApplied_) {
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
if (renderer) {
|
if (renderer) {
|
||||||
if (auto* minimap = renderer->getMinimap()) {
|
if (auto* minimap = renderer->getMinimap()) {
|
||||||
settingsPanel_.minimapRotate_ = false;
|
settingsPanel_.minimapRotate_ = false;
|
||||||
|
|
@ -328,7 +345,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
||||||
|
|
||||||
// Apply saved volume settings once when audio managers first become available
|
// Apply saved volume settings once when audio managers first become available
|
||||||
if (!settingsPanel_.volumeSettingsApplied_) {
|
if (!settingsPanel_.volumeSettingsApplied_) {
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
if (renderer && renderer->getUiSoundManager()) {
|
if (renderer && renderer->getUiSoundManager()) {
|
||||||
settingsPanel_.applyAudioVolumes(renderer);
|
settingsPanel_.applyAudioVolumes(renderer);
|
||||||
settingsPanel_.volumeSettingsApplied_ = true;
|
settingsPanel_.volumeSettingsApplied_ = true;
|
||||||
|
|
@ -337,7 +354,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
||||||
|
|
||||||
// Apply saved MSAA setting once when renderer is available
|
// Apply saved MSAA setting once when renderer is available
|
||||||
if (!settingsPanel_.msaaSettingsApplied_ && settingsPanel_.pendingAntiAliasing > 0) {
|
if (!settingsPanel_.msaaSettingsApplied_ && settingsPanel_.pendingAntiAliasing > 0) {
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
if (renderer) {
|
if (renderer) {
|
||||||
static const VkSampleCountFlagBits aaSamples[] = {
|
static const VkSampleCountFlagBits aaSamples[] = {
|
||||||
VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_2_BIT,
|
VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_2_BIT,
|
||||||
|
|
@ -352,7 +369,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
||||||
|
|
||||||
// Apply saved FXAA setting once when renderer is available
|
// Apply saved FXAA setting once when renderer is available
|
||||||
if (!settingsPanel_.fxaaSettingsApplied_) {
|
if (!settingsPanel_.fxaaSettingsApplied_) {
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
if (renderer) {
|
if (renderer) {
|
||||||
renderer->setFXAAEnabled(settingsPanel_.pendingFXAA);
|
renderer->setFXAAEnabled(settingsPanel_.pendingFXAA);
|
||||||
settingsPanel_.fxaaSettingsApplied_ = true;
|
settingsPanel_.fxaaSettingsApplied_ = true;
|
||||||
|
|
@ -361,7 +378,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
||||||
|
|
||||||
// Apply saved water refraction setting once when renderer is available
|
// Apply saved water refraction setting once when renderer is available
|
||||||
if (!settingsPanel_.waterRefractionApplied_) {
|
if (!settingsPanel_.waterRefractionApplied_) {
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
if (renderer) {
|
if (renderer) {
|
||||||
renderer->setWaterRefractionEnabled(settingsPanel_.pendingWaterRefraction);
|
renderer->setWaterRefractionEnabled(settingsPanel_.pendingWaterRefraction);
|
||||||
settingsPanel_.waterRefractionApplied_ = true;
|
settingsPanel_.waterRefractionApplied_ = true;
|
||||||
|
|
@ -370,7 +387,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
||||||
|
|
||||||
// Apply saved normal mapping / POM settings once when WMO renderer is available
|
// Apply saved normal mapping / POM settings once when WMO renderer is available
|
||||||
if (!settingsPanel_.normalMapSettingsApplied_) {
|
if (!settingsPanel_.normalMapSettingsApplied_) {
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
if (renderer) {
|
if (renderer) {
|
||||||
if (auto* wr = renderer->getWMORenderer()) {
|
if (auto* wr = renderer->getWMORenderer()) {
|
||||||
wr->setNormalMappingEnabled(settingsPanel_.pendingNormalMapping);
|
wr->setNormalMappingEnabled(settingsPanel_.pendingNormalMapping);
|
||||||
|
|
@ -390,7 +407,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
||||||
|
|
||||||
// Apply saved upscaling setting once when renderer is available
|
// Apply saved upscaling setting once when renderer is available
|
||||||
if (!settingsPanel_.fsrSettingsApplied_) {
|
if (!settingsPanel_.fsrSettingsApplied_) {
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
if (renderer) {
|
if (renderer) {
|
||||||
static constexpr float fsrScales[] = { 0.77f, 0.67f, 0.59f, 1.00f };
|
static constexpr float fsrScales[] = { 0.77f, 0.67f, 0.59f, 1.00f };
|
||||||
settingsPanel_.pendingFSRQuality = std::clamp(settingsPanel_.pendingFSRQuality, 0, 3);
|
settingsPanel_.pendingFSRQuality = std::clamp(settingsPanel_.pendingFSRQuality, 0, 3);
|
||||||
|
|
@ -561,7 +578,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
||||||
questLogScreen.render(gameHandler, inventoryScreen);
|
questLogScreen.render(gameHandler, inventoryScreen);
|
||||||
|
|
||||||
// Spellbook (P key toggle handled inside)
|
// Spellbook (P key toggle handled inside)
|
||||||
spellbookScreen.render(gameHandler, core::Application::getInstance().getAssetManager());
|
spellbookScreen.render(gameHandler, services_.assetManager);
|
||||||
|
|
||||||
// Insert spell link into chat if player shift-clicked a spellbook entry
|
// Insert spell link into chat if player shift-clicked a spellbook entry
|
||||||
{
|
{
|
||||||
|
|
@ -578,7 +595,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
||||||
{
|
{
|
||||||
uint64_t activeGuid = gameHandler.getActiveCharacterGuid();
|
uint64_t activeGuid = gameHandler.getActiveCharacterGuid();
|
||||||
if (activeGuid != 0 && activeGuid != inventoryScreenCharGuid_) {
|
if (activeGuid != 0 && activeGuid != inventoryScreenCharGuid_) {
|
||||||
auto* am = core::Application::getInstance().getAssetManager();
|
auto* am = services_.assetManager;
|
||||||
if (am) {
|
if (am) {
|
||||||
inventoryScreen.setAssetManager(am);
|
inventoryScreen.setAssetManager(am);
|
||||||
const auto* ch = gameHandler.getActiveCharacter();
|
const auto* ch = gameHandler.getActiveCharacter();
|
||||||
|
|
@ -631,10 +648,10 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
||||||
if (inventoryScreen.consumeEquipmentDirty() || gameHandler.consumeOnlineEquipmentDirty()) {
|
if (inventoryScreen.consumeEquipmentDirty() || gameHandler.consumeOnlineEquipmentDirty()) {
|
||||||
updateCharacterGeosets(gameHandler.getInventory());
|
updateCharacterGeosets(gameHandler.getInventory());
|
||||||
updateCharacterTextures(gameHandler.getInventory());
|
updateCharacterTextures(gameHandler.getInventory());
|
||||||
core::Application::getInstance().loadEquippedWeapons();
|
if (appearanceComposer_) appearanceComposer_->loadEquippedWeapons();
|
||||||
inventoryScreen.markPreviewDirty();
|
inventoryScreen.markPreviewDirty();
|
||||||
// Update renderer weapon type for animation selection
|
// Update renderer weapon type for animation selection
|
||||||
auto* r = core::Application::getInstance().getRenderer();
|
auto* r = services_.renderer;
|
||||||
if (r) {
|
if (r) {
|
||||||
const auto& mh = gameHandler.getInventory().getEquipSlot(game::EquipSlot::MAIN_HAND);
|
const auto& mh = gameHandler.getInventory().getEquipSlot(game::EquipSlot::MAIN_HAND);
|
||||||
r->setEquippedWeaponType(mh.empty() ? 0 : mh.item.inventoryType);
|
r->setEquippedWeaponType(mh.empty() ? 0 : mh.item.inventoryType);
|
||||||
|
|
@ -642,7 +659,7 @@ void GameScreen::render(game::GameHandler& gameHandler) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update renderer face-target position and selection circle
|
// Update renderer face-target position and selection circle
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
if (renderer) {
|
if (renderer) {
|
||||||
renderer->setInCombat(gameHandler.isInCombat() &&
|
renderer->setInCombat(gameHandler.isInCombat() &&
|
||||||
!gameHandler.isPlayerDead() &&
|
!gameHandler.isPlayerDead() &&
|
||||||
|
|
@ -1201,9 +1218,9 @@ void GameScreen::processTargetInput(game::GameHandler& gameHandler) {
|
||||||
|
|
||||||
// Cursor affordance: show hand cursor over interactable entities.
|
// Cursor affordance: show hand cursor over interactable entities.
|
||||||
if (!io.WantCaptureMouse) {
|
if (!io.WantCaptureMouse) {
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
auto* camera = renderer ? renderer->getCamera() : nullptr;
|
auto* camera = renderer ? renderer->getCamera() : nullptr;
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
if (camera && window) {
|
if (camera && window) {
|
||||||
glm::vec2 mousePos = input.getMousePosition();
|
glm::vec2 mousePos = input.getMousePosition();
|
||||||
float screenW = static_cast<float>(window->getWidth());
|
float screenW = static_cast<float>(window->getWidth());
|
||||||
|
|
@ -1257,9 +1274,9 @@ void GameScreen::processTargetInput(game::GameHandler& gameHandler) {
|
||||||
constexpr float CLICK_THRESHOLD = 5.0f; // pixels
|
constexpr float CLICK_THRESHOLD = 5.0f; // pixels
|
||||||
|
|
||||||
if (dragDistSq < CLICK_THRESHOLD * CLICK_THRESHOLD) {
|
if (dragDistSq < CLICK_THRESHOLD * CLICK_THRESHOLD) {
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
auto* camera = renderer ? renderer->getCamera() : nullptr;
|
auto* camera = renderer ? renderer->getCamera() : nullptr;
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
|
|
||||||
if (camera && window) {
|
if (camera && window) {
|
||||||
float screenW = static_cast<float>(window->getWidth());
|
float screenW = static_cast<float>(window->getWidth());
|
||||||
|
|
@ -1354,9 +1371,9 @@ void GameScreen::processTargetInput(game::GameHandler& gameHandler) {
|
||||||
|
|
||||||
// If no target or right-clicking in world, try to pick one under cursor
|
// If no target or right-clicking in world, try to pick one under cursor
|
||||||
{
|
{
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
auto* camera = renderer ? renderer->getCamera() : nullptr;
|
auto* camera = renderer ? renderer->getCamera() : nullptr;
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
if (camera && window) {
|
if (camera && window) {
|
||||||
// If a quest objective gameobject is under the cursor, prefer it over
|
// If a quest objective gameobject is under the cursor, prefer it over
|
||||||
// hostile units so quest pickups (e.g. "Bundle of Wood") are reliable.
|
// hostile units so quest pickups (e.g. "Bundle of Wood") are reliable.
|
||||||
|
|
@ -1647,7 +1664,7 @@ void GameScreen::renderPlayerFrame(game::GameHandler& gameHandler) {
|
||||||
ImGui::TextColored(ImVec4(0.9f, 0.5f, 0.2f, 1.0f), "<DND>");
|
ImGui::TextColored(ImVec4(0.9f, 0.5f, 0.2f, 1.0f), "<DND>");
|
||||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Do not disturb — /dnd to cancel");
|
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Do not disturb — /dnd to cancel");
|
||||||
}
|
}
|
||||||
if (auto* ren = core::Application::getInstance().getRenderer()) {
|
if (auto* ren = services_.renderer) {
|
||||||
if (auto* cam = ren->getCameraController()) {
|
if (auto* cam = ren->getCameraController()) {
|
||||||
if (cam->isAutoRunning()) {
|
if (cam->isAutoRunning()) {
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
@ -2175,7 +2192,7 @@ void GameScreen::renderPetFrame(game::GameHandler& gameHandler) {
|
||||||
// Raw slot value layout (WotLK 3.3.5): low 24 bits = spell/action ID,
|
// Raw slot value layout (WotLK 3.3.5): low 24 bits = spell/action ID,
|
||||||
// high byte = flag (0x80=autocast on, 0x40=can-autocast, 0x0C=type).
|
// high byte = flag (0x80=autocast on, 0x40=can-autocast, 0x0C=type).
|
||||||
// Built-in commands: id=2 follow, id=3 stay/move, id=5 attack.
|
// Built-in commands: id=2 follow, id=3 stay/move, id=5 attack.
|
||||||
auto* assetMgr = core::Application::getInstance().getAssetManager();
|
auto* assetMgr = services_.assetManager;
|
||||||
const float iconSz = 20.0f;
|
const float iconSz = 20.0f;
|
||||||
const float spacing = 2.0f;
|
const float spacing = 2.0f;
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
@ -2278,7 +2295,7 @@ void GameScreen::renderPetFrame(game::GameHandler& gameHandler) {
|
||||||
else if (actionId == 6) tip = "Aggressive";
|
else if (actionId == 6) tip = "Aggressive";
|
||||||
if (tip) ImGui::SetTooltip("%s", tip);
|
if (tip) ImGui::SetTooltip("%s", tip);
|
||||||
} else if (actionId > 6) {
|
} else if (actionId > 6) {
|
||||||
auto* spellAsset = core::Application::getInstance().getAssetManager();
|
auto* spellAsset = services_.assetManager;
|
||||||
ImGui::BeginTooltip();
|
ImGui::BeginTooltip();
|
||||||
bool richOk = spellbookScreen.renderSpellInfoTooltip(actionId, gameHandler, spellAsset);
|
bool richOk = spellbookScreen.renderSpellInfoTooltip(actionId, gameHandler, spellAsset);
|
||||||
if (!richOk) {
|
if (!richOk) {
|
||||||
|
|
@ -2399,7 +2416,7 @@ void GameScreen::renderTargetFrame(game::GameHandler& gameHandler) {
|
||||||
auto target = gameHandler.getTarget();
|
auto target = gameHandler.getTarget();
|
||||||
if (!target) return;
|
if (!target) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
|
|
||||||
float frameW = 250.0f;
|
float frameW = 250.0f;
|
||||||
|
|
@ -2838,7 +2855,7 @@ void GameScreen::renderTargetFrame(game::GameHandler& gameHandler) {
|
||||||
else
|
else
|
||||||
snprintf(castLabel, sizeof(castLabel), "Casting... (%.1fs)", castLeft);
|
snprintf(castLabel, sizeof(castLabel), "Casting... (%.1fs)", castLeft);
|
||||||
{
|
{
|
||||||
auto* tcastAsset = core::Application::getInstance().getAssetManager();
|
auto* tcastAsset = services_.assetManager;
|
||||||
VkDescriptorSet tIcon = (tspell != 0 && tcastAsset)
|
VkDescriptorSet tIcon = (tspell != 0 && tcastAsset)
|
||||||
? getSpellIcon(tspell, tcastAsset) : VK_NULL_HANDLE;
|
? getSpellIcon(tspell, tcastAsset) : VK_NULL_HANDLE;
|
||||||
if (tIcon) {
|
if (tIcon) {
|
||||||
|
|
@ -2935,7 +2952,7 @@ void GameScreen::renderTargetFrame(game::GameHandler& gameHandler) {
|
||||||
if (!a.isEmpty()) activeAuras++;
|
if (!a.isEmpty()) activeAuras++;
|
||||||
}
|
}
|
||||||
if (activeAuras > 0) {
|
if (activeAuras > 0) {
|
||||||
auto* assetMgr = core::Application::getInstance().getAssetManager();
|
auto* assetMgr = services_.assetManager;
|
||||||
constexpr float ICON_SIZE = 24.0f;
|
constexpr float ICON_SIZE = 24.0f;
|
||||||
constexpr int ICONS_PER_ROW = 8;
|
constexpr int ICONS_PER_ROW = 8;
|
||||||
|
|
||||||
|
|
@ -3233,7 +3250,7 @@ void GameScreen::renderTargetFrame(game::GameHandler& gameHandler) {
|
||||||
int totActive = 0;
|
int totActive = 0;
|
||||||
for (const auto& a : *totAuras) if (!a.isEmpty()) totActive++;
|
for (const auto& a : *totAuras) if (!a.isEmpty()) totActive++;
|
||||||
if (totActive > 0) {
|
if (totActive > 0) {
|
||||||
auto* totAsset = core::Application::getInstance().getAssetManager();
|
auto* totAsset = services_.assetManager;
|
||||||
constexpr float TA_ICON = 16.0f;
|
constexpr float TA_ICON = 16.0f;
|
||||||
constexpr int TA_PER_ROW = 8;
|
constexpr int TA_PER_ROW = 8;
|
||||||
|
|
||||||
|
|
@ -3349,7 +3366,7 @@ void GameScreen::renderFocusFrame(game::GameHandler& gameHandler) {
|
||||||
auto focus = gameHandler.getFocus();
|
auto focus = gameHandler.getFocus();
|
||||||
if (!focus) return;
|
if (!focus) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
|
|
||||||
// Position: right side of screen, mirroring the target frame on the opposite side
|
// Position: right side of screen, mirroring the target frame on the opposite side
|
||||||
|
|
@ -3651,7 +3668,7 @@ void GameScreen::renderFocusFrame(game::GameHandler& gameHandler) {
|
||||||
else
|
else
|
||||||
snprintf(castBuf, sizeof(castBuf), "Casting... (%.1fs)", rem);
|
snprintf(castBuf, sizeof(castBuf), "Casting... (%.1fs)", rem);
|
||||||
{
|
{
|
||||||
auto* fcAsset = core::Application::getInstance().getAssetManager();
|
auto* fcAsset = services_.assetManager;
|
||||||
VkDescriptorSet fcIcon = (focusCast->spellId != 0 && fcAsset)
|
VkDescriptorSet fcIcon = (focusCast->spellId != 0 && fcAsset)
|
||||||
? getSpellIcon(focusCast->spellId, fcAsset) : VK_NULL_HANDLE;
|
? getSpellIcon(focusCast->spellId, fcAsset) : VK_NULL_HANDLE;
|
||||||
if (fcIcon) {
|
if (fcIcon) {
|
||||||
|
|
@ -3677,7 +3694,7 @@ void GameScreen::renderFocusFrame(game::GameHandler& gameHandler) {
|
||||||
int activeCount = 0;
|
int activeCount = 0;
|
||||||
for (const auto& a : *focusAuras) if (!a.isEmpty()) activeCount++;
|
for (const auto& a : *focusAuras) if (!a.isEmpty()) activeCount++;
|
||||||
if (activeCount > 0) {
|
if (activeCount > 0) {
|
||||||
auto* focusAsset = core::Application::getInstance().getAssetManager();
|
auto* focusAsset = services_.assetManager;
|
||||||
constexpr float FA_ICON = 20.0f;
|
constexpr float FA_ICON = 20.0f;
|
||||||
constexpr int FA_PER_ROW = 10;
|
constexpr int FA_PER_ROW = 10;
|
||||||
|
|
||||||
|
|
@ -4348,7 +4365,7 @@ VkDescriptorSet GameScreen::getSpellIcon(uint32_t spellId, pipeline::AssetManage
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload to Vulkan via VkContext
|
// Upload to Vulkan via VkContext
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
auto* vkCtx = window ? window->getVkContext() : nullptr;
|
auto* vkCtx = window ? window->getVkContext() : nullptr;
|
||||||
if (!vkCtx) {
|
if (!vkCtx) {
|
||||||
spellIconCache_[spellId] = VK_NULL_HANDLE;
|
spellIconCache_[spellId] = VK_NULL_HANDLE;
|
||||||
|
|
@ -4455,7 +4472,7 @@ void GameScreen::renderQuestObjectiveTracker(game::GameHandler& gameHandler) {
|
||||||
const auto& questLog = gameHandler.getQuestLog();
|
const auto& questLog = gameHandler.getQuestLog();
|
||||||
if (questLog.empty()) return;
|
if (questLog.empty()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
|
|
||||||
constexpr float TRACKER_W = 220.0f;
|
constexpr float TRACKER_W = 220.0f;
|
||||||
|
|
@ -4693,12 +4710,12 @@ void GameScreen::renderNameplates(game::GameHandler& gameHandler) {
|
||||||
// Reset mouseover each frame; we'll set it below when the cursor is over a nameplate
|
// Reset mouseover each frame; we'll set it below when the cursor is over a nameplate
|
||||||
gameHandler.setMouseoverGuid(0);
|
gameHandler.setMouseoverGuid(0);
|
||||||
|
|
||||||
auto* appRenderer = core::Application::getInstance().getRenderer();
|
auto* appRenderer = services_.renderer;
|
||||||
if (!appRenderer) return;
|
if (!appRenderer) return;
|
||||||
rendering::Camera* camera = appRenderer->getCamera();
|
rendering::Camera* camera = appRenderer->getCamera();
|
||||||
if (!camera) return;
|
if (!camera) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
if (!window) return;
|
if (!window) return;
|
||||||
const float screenW = static_cast<float>(window->getWidth());
|
const float screenW = static_cast<float>(window->getWidth());
|
||||||
const float screenH = static_cast<float>(window->getHeight());
|
const float screenH = static_cast<float>(window->getHeight());
|
||||||
|
|
@ -4907,7 +4924,7 @@ void GameScreen::renderNameplates(game::GameHandler& gameHandler) {
|
||||||
// Spell icon + name above the cast bar
|
// Spell icon + name above the cast bar
|
||||||
const std::string& spellName = gameHandler.getSpellName(cs->spellId);
|
const std::string& spellName = gameHandler.getSpellName(cs->spellId);
|
||||||
{
|
{
|
||||||
auto* castAm = core::Application::getInstance().getAssetManager();
|
auto* castAm = services_.assetManager;
|
||||||
VkDescriptorSet castIcon = (cs->spellId && castAm)
|
VkDescriptorSet castIcon = (cs->spellId && castAm)
|
||||||
? getSpellIcon(cs->spellId, castAm) : VK_NULL_HANDLE;
|
? getSpellIcon(cs->spellId, castAm) : VK_NULL_HANDLE;
|
||||||
float iconSz = cbH + 8.0f;
|
float iconSz = cbH + 8.0f;
|
||||||
|
|
@ -5300,7 +5317,7 @@ void GameScreen::renderNameplates(game::GameHandler& gameHandler) {
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
void GameScreen::takeScreenshot(game::GameHandler& /*gameHandler*/) {
|
void GameScreen::takeScreenshot(game::GameHandler& /*gameHandler*/) {
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
if (!renderer) return;
|
if (!renderer) return;
|
||||||
|
|
||||||
// Build path: ~/.wowee/screenshots/WoWee_YYYYMMDD_HHMMSS.png
|
// Build path: ~/.wowee/screenshots/WoWee_YYYYMMDD_HHMMSS.png
|
||||||
|
|
@ -5331,7 +5348,7 @@ void GameScreen::takeScreenshot(game::GameHandler& /*gameHandler*/) {
|
||||||
sysMsg.type = game::ChatType::SYSTEM;
|
sysMsg.type = game::ChatType::SYSTEM;
|
||||||
sysMsg.language = game::ChatLanguage::UNIVERSAL;
|
sysMsg.language = game::ChatLanguage::UNIVERSAL;
|
||||||
sysMsg.message = "Screenshot saved: " + path;
|
sysMsg.message = "Screenshot saved: " + path;
|
||||||
core::Application::getInstance().getGameHandler()->addLocalChatMessage(sysMsg);
|
services_.gameHandler->addLocalChatMessage(sysMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5411,7 +5428,7 @@ void GameScreen::renderUIErrors(game::GameHandler& /*gameHandler*/, float deltaT
|
||||||
|
|
||||||
if (uiErrors_.empty()) return;
|
if (uiErrors_.empty()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -5550,9 +5567,9 @@ void GameScreen::renderQuestMarkers(game::GameHandler& gameHandler) {
|
||||||
const auto& statuses = gameHandler.getNpcQuestStatuses();
|
const auto& statuses = gameHandler.getNpcQuestStatuses();
|
||||||
if (statuses.empty()) return;
|
if (statuses.empty()) return;
|
||||||
|
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
auto* camera = renderer ? renderer->getCamera() : nullptr;
|
auto* camera = renderer ? renderer->getCamera() : nullptr;
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
if (!camera || !window) return;
|
if (!camera || !window) return;
|
||||||
|
|
||||||
float screenW = static_cast<float>(window->getWidth());
|
float screenW = static_cast<float>(window->getWidth());
|
||||||
|
|
@ -5628,10 +5645,10 @@ void GameScreen::renderQuestMarkers(game::GameHandler& gameHandler) {
|
||||||
|
|
||||||
void GameScreen::renderMinimapMarkers(game::GameHandler& gameHandler) {
|
void GameScreen::renderMinimapMarkers(game::GameHandler& gameHandler) {
|
||||||
const auto& statuses = gameHandler.getNpcQuestStatuses();
|
const auto& statuses = gameHandler.getNpcQuestStatuses();
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
auto* camera = renderer ? renderer->getCamera() : nullptr;
|
auto* camera = renderer ? renderer->getCamera() : nullptr;
|
||||||
auto* minimap = renderer ? renderer->getMinimap() : nullptr;
|
auto* minimap = renderer ? renderer->getMinimap() : nullptr;
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
if (!camera || !minimap || !window) return;
|
if (!camera || !minimap || !window) return;
|
||||||
|
|
||||||
float screenW = static_cast<float>(window->getWidth());
|
float screenW = static_cast<float>(window->getWidth());
|
||||||
|
|
@ -6508,7 +6525,7 @@ void GameScreen::renderMinimapMarkers(game::GameHandler& gameHandler) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto applyMuteState = [&]() {
|
auto applyMuteState = [&]() {
|
||||||
auto* activeRenderer = core::Application::getInstance().getRenderer();
|
auto* activeRenderer = services_.renderer;
|
||||||
float masterScale = settingsPanel_.soundMuted_ ? 0.0f : static_cast<float>(settingsPanel_.pendingMasterVolume) / 100.0f;
|
float masterScale = settingsPanel_.soundMuted_ ? 0.0f : static_cast<float>(settingsPanel_.pendingMasterVolume) / 100.0f;
|
||||||
audio::AudioEngine::instance().setMasterVolume(masterScale);
|
audio::AudioEngine::instance().setMasterVolume(masterScale);
|
||||||
if (!activeRenderer) return;
|
if (!activeRenderer) return;
|
||||||
|
|
@ -6842,7 +6859,7 @@ void GameScreen::renderMinimapMarkers(game::GameHandler& gameHandler) {
|
||||||
|
|
||||||
// Calendar pending invites indicator (WotLK only)
|
// Calendar pending invites indicator (WotLK only)
|
||||||
{
|
{
|
||||||
auto* expReg = core::Application::getInstance().getExpansionRegistry();
|
auto* expReg = services_.expansionRegistry;
|
||||||
bool isWotLK = expReg && expReg->getActive() && expReg->getActive()->id == "wotlk";
|
bool isWotLK = expReg && expReg->getActive() && expReg->getActive()->id == "wotlk";
|
||||||
if (isWotLK) {
|
if (isWotLK) {
|
||||||
uint32_t calPending = gameHandler.getCalendarPendingInvites();
|
uint32_t calPending = gameHandler.getCalendarPendingInvites();
|
||||||
|
|
@ -7196,7 +7213,7 @@ void GameScreen::loadSettings() {
|
||||||
else if (key == "shadow_distance") settingsPanel_.pendingShadowDistance = std::clamp(std::stof(val), 40.0f, 500.0f);
|
else if (key == "shadow_distance") settingsPanel_.pendingShadowDistance = std::clamp(std::stof(val), 40.0f, 500.0f);
|
||||||
else if (key == "brightness") {
|
else if (key == "brightness") {
|
||||||
settingsPanel_.pendingBrightness = std::clamp(std::stoi(val), 0, 100);
|
settingsPanel_.pendingBrightness = std::clamp(std::stoi(val), 0, 100);
|
||||||
if (auto* r = core::Application::getInstance().getRenderer())
|
if (auto* r = services_.renderer)
|
||||||
r->setBrightness(static_cast<float>(settingsPanel_.pendingBrightness) / 50.0f);
|
r->setBrightness(static_cast<float>(settingsPanel_.pendingBrightness) / 50.0f);
|
||||||
}
|
}
|
||||||
else if (key == "water_refraction") settingsPanel_.pendingWaterRefraction = (std::stoi(val) != 0);
|
else if (key == "water_refraction") settingsPanel_.pendingWaterRefraction = (std::stoi(val) != 0);
|
||||||
|
|
@ -7228,7 +7245,7 @@ void GameScreen::loadSettings() {
|
||||||
else if (key == "camera_pivot_height") settingsPanel_.pendingPivotHeight = std::clamp(std::stof(val), 0.0f, 3.0f);
|
else if (key == "camera_pivot_height") settingsPanel_.pendingPivotHeight = std::clamp(std::stof(val), 0.0f, 3.0f);
|
||||||
else if (key == "fov") {
|
else if (key == "fov") {
|
||||||
settingsPanel_.pendingFov = std::clamp(std::stof(val), 45.0f, 110.0f);
|
settingsPanel_.pendingFov = std::clamp(std::stof(val), 45.0f, 110.0f);
|
||||||
if (auto* renderer = core::Application::getInstance().getRenderer()) {
|
if (auto* renderer = services_.renderer) {
|
||||||
if (auto* camera = renderer->getCamera()) camera->setFov(settingsPanel_.pendingFov);
|
if (auto* camera = renderer->getCamera()) camera->setFov(settingsPanel_.pendingFov);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ ImGui::EndChild();
|
||||||
|
|
||||||
void SettingsPanel::renderSettingsGameplayTab(InventoryScreen& inventoryScreen,
|
void SettingsPanel::renderSettingsGameplayTab(InventoryScreen& inventoryScreen,
|
||||||
std::function<void()> saveCallback) {
|
std::function<void()> saveCallback) {
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
ImGui::Text("Controls");
|
ImGui::Text("Controls");
|
||||||
|
|
@ -433,7 +433,7 @@ if (ImGui::Button("Reset to Defaults", ImVec2(-1, 0))) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsPanel::renderSettingsAudioTab(std::function<void()> saveCallback) {
|
void SettingsPanel::renderSettingsAudioTab(std::function<void()> saveCallback) {
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::BeginChild("AudioSettings", ImVec2(0, 360), true);
|
ImGui::BeginChild("AudioSettings", ImVec2(0, 360), true);
|
||||||
|
|
||||||
|
|
@ -599,8 +599,8 @@ void SettingsPanel::renderSettingsWindow(InventoryScreen& inventoryScreen, ChatP
|
||||||
std::function<void()> saveCallback) {
|
std::function<void()> saveCallback) {
|
||||||
if (!showSettingsWindow) return;
|
if (!showSettingsWindow) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
if (!window) return;
|
if (!window) return;
|
||||||
|
|
||||||
static constexpr int kResolutions[][2] = {
|
static constexpr int kResolutions[][2] = {
|
||||||
|
|
@ -1045,7 +1045,7 @@ void SettingsPanel::renderSettingsWindow(InventoryScreen& inventoryScreen, ChatP
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsPanel::applyGraphicsPreset(GraphicsPreset preset) {
|
void SettingsPanel::applyGraphicsPreset(GraphicsPreset preset) {
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
|
|
||||||
// Define preset values based on quality level
|
// Define preset values based on quality level
|
||||||
switch (preset) {
|
switch (preset) {
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ void SocialPanel::renderPartyFrames(game::GameHandler& gameHandler,
|
||||||
SpellIconFn getSpellIcon) {
|
SpellIconFn getSpellIcon) {
|
||||||
if (!gameHandler.isInGroup()) return;
|
if (!gameHandler.isInGroup()) return;
|
||||||
|
|
||||||
auto* assetMgr = core::Application::getInstance().getAssetManager();
|
auto* assetMgr = services_.assetManager;
|
||||||
const auto& partyData = gameHandler.getPartyData();
|
const auto& partyData = gameHandler.getPartyData();
|
||||||
const bool isRaid = (partyData.groupType == 1);
|
const bool isRaid = (partyData.groupType == 1);
|
||||||
float frameY = 120.0f;
|
float frameY = 120.0f;
|
||||||
|
|
@ -117,7 +117,7 @@ void SocialPanel::renderPartyFrames(game::GameHandler& gameHandler,
|
||||||
float winW = activeSgs * (CELL_W + CELL_PAD) + CELL_PAD + 8.0f;
|
float winW = activeSgs * (CELL_W + CELL_PAD) + CELL_PAD + 8.0f;
|
||||||
float winH = MAX_PER_GROUP * (CELL_H + CELL_PAD) + CELL_PAD + 20.0f;
|
float winH = MAX_PER_GROUP * (CELL_H + CELL_PAD) + CELL_PAD + 20.0f;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
float raidX = (screenW - winW) / 2.0f;
|
float raidX = (screenW - winW) / 2.0f;
|
||||||
|
|
@ -757,7 +757,7 @@ void SocialPanel::renderPartyFrames(game::GameHandler& gameHandler,
|
||||||
void SocialPanel::renderBossFrames(game::GameHandler& gameHandler,
|
void SocialPanel::renderBossFrames(game::GameHandler& gameHandler,
|
||||||
SpellbookScreen& spellbookScreen,
|
SpellbookScreen& spellbookScreen,
|
||||||
SpellIconFn getSpellIcon) {
|
SpellIconFn getSpellIcon) {
|
||||||
auto* assetMgr = core::Application::getInstance().getAssetManager();
|
auto* assetMgr = services_.assetManager;
|
||||||
|
|
||||||
// Collect active boss unit slots
|
// Collect active boss unit slots
|
||||||
struct BossSlot { uint32_t slot; uint64_t guid; };
|
struct BossSlot { uint32_t slot; uint64_t guid; };
|
||||||
|
|
@ -1143,11 +1143,11 @@ void SocialPanel::renderGuildRoster(game::GameHandler& gameHandler,
|
||||||
|
|
||||||
// Get zone manager for name lookup
|
// Get zone manager for name lookup
|
||||||
game::ZoneManager* zoneManager = nullptr;
|
game::ZoneManager* zoneManager = nullptr;
|
||||||
if (auto* renderer = core::Application::getInstance().getRenderer()) {
|
if (auto* renderer = services_.renderer) {
|
||||||
zoneManager = renderer->getZoneManager();
|
zoneManager = renderer->getZoneManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -1683,7 +1683,7 @@ void SocialPanel::renderSocialFrame(game::GameHandler& gameHandler,
|
||||||
for (const auto& c : contacts)
|
for (const auto& c : contacts)
|
||||||
if (c.isFriend() && c.isOnline()) ++onlineCount;
|
if (c.isFriend() && c.isOnline()) ++onlineCount;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(screenW - 230.0f, 240.0f), ImGuiCond_Once);
|
ImGui::SetNextWindowPos(ImVec2(screenW - 230.0f, 240.0f), ImGuiCond_Once);
|
||||||
|
|
@ -1705,7 +1705,7 @@ void SocialPanel::renderSocialFrame(game::GameHandler& gameHandler,
|
||||||
|
|
||||||
// Get zone manager for area name lookups
|
// Get zone manager for area name lookups
|
||||||
game::ZoneManager* socialZoneMgr = nullptr;
|
game::ZoneManager* socialZoneMgr = nullptr;
|
||||||
if (auto* rend = core::Application::getInstance().getRenderer())
|
if (auto* rend = services_.renderer)
|
||||||
socialZoneMgr = rend->getZoneManager();
|
socialZoneMgr = rend->getZoneManager();
|
||||||
|
|
||||||
if (ImGui::BeginTabBar("##SocialTabs")) {
|
if (ImGui::BeginTabBar("##SocialTabs")) {
|
||||||
|
|
@ -2048,7 +2048,7 @@ void SocialPanel::renderDungeonFinderWindow(game::GameHandler& gameHandler,
|
||||||
|
|
||||||
if (!showDungeonFinder_) return;
|
if (!showDungeonFinder_) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -2423,7 +2423,7 @@ void SocialPanel::renderInspectWindow(game::GameHandler& gameHandler,
|
||||||
// Lazy-load SpellItemEnchantment.dbc for enchant name lookup
|
// Lazy-load SpellItemEnchantment.dbc for enchant name lookup
|
||||||
static std::unordered_map<uint32_t, std::string> s_enchantNames;
|
static std::unordered_map<uint32_t, std::string> s_enchantNames;
|
||||||
static bool s_enchantDbLoaded = false;
|
static bool s_enchantDbLoaded = false;
|
||||||
auto* assetMgrEnchant = core::Application::getInstance().getAssetManager();
|
auto* assetMgrEnchant = services_.assetManager;
|
||||||
if (!s_enchantDbLoaded && assetMgrEnchant && assetMgrEnchant->isInitialized()) {
|
if (!s_enchantDbLoaded && assetMgrEnchant && assetMgrEnchant->isInitialized()) {
|
||||||
s_enchantDbLoaded = true;
|
s_enchantDbLoaded = true;
|
||||||
auto dbc = assetMgrEnchant->loadDBC("SpellItemEnchantment.dbc");
|
auto dbc = assetMgrEnchant->loadDBC("SpellItemEnchantment.dbc");
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,7 @@ void ToastManager::setupCallbacks(game::GameHandler& gameHandler) {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
void ToastManager::renderEarlyToasts(float deltaTime, game::GameHandler& gameHandler) {
|
void ToastManager::renderEarlyToasts(float deltaTime, game::GameHandler& gameHandler) {
|
||||||
// Zone entry detection — fire a toast when the renderer's zone name changes
|
// Zone entry detection — fire a toast when the renderer's zone name changes
|
||||||
if (auto* rend = core::Application::getInstance().getRenderer()) {
|
if (auto* rend = services_.renderer) {
|
||||||
const std::string& curZone = rend->getCurrentZoneName();
|
const std::string& curZone = rend->getCurrentZoneName();
|
||||||
if (!curZone.empty() && curZone != lastKnownZone_) {
|
if (!curZone.empty() && curZone != lastKnownZone_) {
|
||||||
if (!lastKnownZone_.empty()) {
|
if (!lastKnownZone_.empty()) {
|
||||||
|
|
@ -175,7 +175,7 @@ void ToastManager::renderRepToasts(float deltaTime) {
|
||||||
|
|
||||||
if (repToasts_.empty()) return;
|
if (repToasts_.empty()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -254,7 +254,7 @@ void ToastManager::renderQuestCompleteToasts(float deltaTime) {
|
||||||
|
|
||||||
if (questCompleteToasts_.empty()) return;
|
if (questCompleteToasts_.empty()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -329,7 +329,7 @@ void ToastManager::renderZoneToasts(float deltaTime) {
|
||||||
|
|
||||||
if (zoneToasts_.empty()) return;
|
if (zoneToasts_.empty()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
|
|
||||||
ImDrawList* draw = ImGui::GetForegroundDrawList();
|
ImDrawList* draw = ImGui::GetForegroundDrawList();
|
||||||
|
|
@ -395,7 +395,7 @@ void ToastManager::renderAreaTriggerToasts(float deltaTime, game::GameHandler& g
|
||||||
areaTriggerToasts_.end());
|
areaTriggerToasts_.end());
|
||||||
if (areaTriggerToasts_.empty()) return;
|
if (areaTriggerToasts_.empty()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -461,7 +461,7 @@ void ToastManager::triggerDing(uint32_t newLevel, uint32_t hpDelta, uint32_t man
|
||||||
dingStats_[3] = intel;
|
dingStats_[3] = intel;
|
||||||
dingStats_[4] = spi;
|
dingStats_[4] = spi;
|
||||||
|
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
if (renderer) {
|
if (renderer) {
|
||||||
if (auto* sfx = renderer->getUiSoundManager()) {
|
if (auto* sfx = renderer->getUiSoundManager()) {
|
||||||
sfx->playLevelUp();
|
sfx->playLevelUp();
|
||||||
|
|
@ -550,7 +550,7 @@ void ToastManager::triggerAchievementToast(uint32_t achievementId, std::string n
|
||||||
achievementToastTimer_ = ACHIEVEMENT_TOAST_DURATION;
|
achievementToastTimer_ = ACHIEVEMENT_TOAST_DURATION;
|
||||||
|
|
||||||
// Play a UI sound if available
|
// Play a UI sound if available
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
if (renderer) {
|
if (renderer) {
|
||||||
if (auto* sfx = renderer->getUiSoundManager()) {
|
if (auto* sfx = renderer->getUiSoundManager()) {
|
||||||
sfx->playAchievementAlert();
|
sfx->playAchievementAlert();
|
||||||
|
|
@ -565,7 +565,7 @@ void ToastManager::renderAchievementToast() {
|
||||||
achievementToastTimer_ -= dt;
|
achievementToastTimer_ -= dt;
|
||||||
if (achievementToastTimer_ < 0.0f) achievementToastTimer_ = 0.0f;
|
if (achievementToastTimer_ < 0.0f) achievementToastTimer_ = 0.0f;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -641,7 +641,7 @@ void ToastManager::renderDiscoveryToast() {
|
||||||
alpha = 1.0f;
|
alpha = 1.0f;
|
||||||
alpha = std::clamp(alpha, 0.0f, 1.0f);
|
alpha = std::clamp(alpha, 0.0f, 1.0f);
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -1183,7 +1183,7 @@ void ToastManager::renderZoneText(game::GameHandler& gameHandler) {
|
||||||
|
|
||||||
// Also poll the renderer for zone name changes (covers map-level transitions
|
// Also poll the renderer for zone name changes (covers map-level transitions
|
||||||
// where worldStateZoneId may not change immediately).
|
// where worldStateZoneId may not change immediately).
|
||||||
auto* appRenderer = core::Application::getInstance().getRenderer();
|
auto* appRenderer = services_.renderer;
|
||||||
if (appRenderer) {
|
if (appRenderer) {
|
||||||
const std::string& zoneName = appRenderer->getCurrentZoneName();
|
const std::string& zoneName = appRenderer->getCurrentZoneName();
|
||||||
if (!zoneName.empty() && zoneName != lastKnownZoneName_) {
|
if (!zoneName.empty() && zoneName != lastKnownZoneName_) {
|
||||||
|
|
@ -1202,7 +1202,7 @@ void ToastManager::renderZoneText(game::GameHandler& gameHandler) {
|
||||||
zoneTextTimer_ -= dt;
|
zoneTextTimer_ -= dt;
|
||||||
if (zoneTextTimer_ < 0.0f) zoneTextTimer_ = 0.0f;
|
if (zoneTextTimer_ < 0.0f) zoneTextTimer_ = 0.0f;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ void WindowManager::renderLootWindow(game::GameHandler& gameHandler,
|
||||||
ChatPanel& chatPanel) {
|
ChatPanel& chatPanel) {
|
||||||
if (!gameHandler.isLootWindowOpen()) return;
|
if (!gameHandler.isLootWindowOpen()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 150, 200), ImGuiCond_Appearing);
|
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 150, 200), ImGuiCond_Appearing);
|
||||||
|
|
@ -273,7 +273,7 @@ void WindowManager::renderGossipWindow(game::GameHandler& gameHandler,
|
||||||
ChatPanel& chatPanel) {
|
ChatPanel& chatPanel) {
|
||||||
if (!gameHandler.isGossipWindowOpen()) return;
|
if (!gameHandler.isGossipWindowOpen()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 200, 150), ImGuiCond_Appearing);
|
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 200, 150), ImGuiCond_Appearing);
|
||||||
|
|
@ -445,7 +445,7 @@ void WindowManager::renderQuestDetailsWindow(game::GameHandler& gameHandler,
|
||||||
InventoryScreen& inventoryScreen) {
|
InventoryScreen& inventoryScreen) {
|
||||||
if (!gameHandler.isQuestDetailsOpen()) return;
|
if (!gameHandler.isQuestDetailsOpen()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -569,7 +569,7 @@ void WindowManager::renderQuestRequestItemsWindow(game::GameHandler& gameHandler
|
||||||
InventoryScreen& inventoryScreen) {
|
InventoryScreen& inventoryScreen) {
|
||||||
if (!gameHandler.isQuestRequestItemsOpen()) return;
|
if (!gameHandler.isQuestRequestItemsOpen()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -672,7 +672,7 @@ void WindowManager::renderQuestOfferRewardWindow(game::GameHandler& gameHandler,
|
||||||
InventoryScreen& inventoryScreen) {
|
InventoryScreen& inventoryScreen) {
|
||||||
if (!gameHandler.isQuestOfferRewardOpen()) return;
|
if (!gameHandler.isQuestOfferRewardOpen()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -845,7 +845,7 @@ void WindowManager::renderQuestOfferRewardWindow(game::GameHandler& gameHandler,
|
||||||
void WindowManager::loadExtendedCostDBC() {
|
void WindowManager::loadExtendedCostDBC() {
|
||||||
if (extendedCostDbLoaded_) return;
|
if (extendedCostDbLoaded_) return;
|
||||||
extendedCostDbLoaded_ = true;
|
extendedCostDbLoaded_ = true;
|
||||||
auto* am = core::Application::getInstance().getAssetManager();
|
auto* am = services_.assetManager;
|
||||||
if (!am || !am->isInitialized()) return;
|
if (!am || !am->isInitialized()) return;
|
||||||
auto dbc = am->loadDBC("ItemExtendedCost.dbc");
|
auto dbc = am->loadDBC("ItemExtendedCost.dbc");
|
||||||
if (!dbc || !dbc->isLoaded()) return;
|
if (!dbc || !dbc->isLoaded()) return;
|
||||||
|
|
@ -898,7 +898,7 @@ void WindowManager::renderVendorWindow(game::GameHandler& gameHandler,
|
||||||
ChatPanel& chatPanel) {
|
ChatPanel& chatPanel) {
|
||||||
if (!gameHandler.isVendorWindowOpen()) return;
|
if (!gameHandler.isVendorWindowOpen()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 200, 100), ImGuiCond_Appearing);
|
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 200, 100), ImGuiCond_Appearing);
|
||||||
|
|
@ -1236,9 +1236,9 @@ void WindowManager::renderTrainerWindow(game::GameHandler& gameHandler,
|
||||||
SpellIconFn getSpellIcon) {
|
SpellIconFn getSpellIcon) {
|
||||||
if (!gameHandler.isTrainerWindowOpen()) return;
|
if (!gameHandler.isTrainerWindowOpen()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
auto* assetMgr = core::Application::getInstance().getAssetManager();
|
auto* assetMgr = services_.assetManager;
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 225, 100), ImGuiCond_Appearing);
|
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 225, 100), ImGuiCond_Appearing);
|
||||||
ImGui::SetNextWindowSize(ImVec2(500, 450), ImGuiCond_Appearing);
|
ImGui::SetNextWindowSize(ImVec2(500, 450), ImGuiCond_Appearing);
|
||||||
|
|
@ -1701,7 +1701,7 @@ void WindowManager::renderEscapeMenu(SettingsPanel& settingsPanel) {
|
||||||
settingsPanel.showEscapeSettingsNotice = false;
|
settingsPanel.showEscapeSettingsNotice = false;
|
||||||
}
|
}
|
||||||
if (ImGui::Button("Quit", ImVec2(-1, 0))) {
|
if (ImGui::Button("Quit", ImVec2(-1, 0))) {
|
||||||
auto* renderer = core::Application::getInstance().getRenderer();
|
auto* renderer = services_.renderer;
|
||||||
if (renderer) {
|
if (renderer) {
|
||||||
if (auto* music = renderer->getMusicManager()) {
|
if (auto* music = renderer->getMusicManager()) {
|
||||||
music->stopMusic(0.0f);
|
music->stopMusic(0.0f);
|
||||||
|
|
@ -1763,7 +1763,7 @@ void WindowManager::renderBarberShopWindow(game::GameHandler& gameHandler) {
|
||||||
int maxHairColor = static_cast<int>(game::getMaxHairColor(raceEnum, gender));
|
int maxHairColor = static_cast<int>(game::getMaxHairColor(raceEnum, gender));
|
||||||
int maxFacialHair = static_cast<int>(game::getMaxFacialFeature(raceEnum, gender));
|
int maxFacialHair = static_cast<int>(game::getMaxFacialFeature(raceEnum, gender));
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
float winW = 300.0f;
|
float winW = 300.0f;
|
||||||
|
|
@ -1847,7 +1847,7 @@ void WindowManager::renderBarberShopWindow(game::GameHandler& gameHandler) {
|
||||||
void WindowManager::renderStableWindow(game::GameHandler& gameHandler) {
|
void WindowManager::renderStableWindow(game::GameHandler& gameHandler) {
|
||||||
if (!gameHandler.isStableWindowOpen()) return;
|
if (!gameHandler.isStableWindowOpen()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -1960,7 +1960,7 @@ void WindowManager::renderStableWindow(game::GameHandler& gameHandler) {
|
||||||
void WindowManager::renderTaxiWindow(game::GameHandler& gameHandler) {
|
void WindowManager::renderTaxiWindow(game::GameHandler& gameHandler) {
|
||||||
if (!gameHandler.isTaxiWindowOpen()) return;
|
if (!gameHandler.isTaxiWindowOpen()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 200, 150), ImGuiCond_Appearing);
|
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 200, 150), ImGuiCond_Appearing);
|
||||||
|
|
@ -2058,7 +2058,7 @@ void WindowManager::renderTaxiWindow(game::GameHandler& gameHandler) {
|
||||||
void WindowManager::renderLogoutCountdown(game::GameHandler& gameHandler) {
|
void WindowManager::renderLogoutCountdown(game::GameHandler& gameHandler) {
|
||||||
if (!gameHandler.isLoggingOut()) return;
|
if (!gameHandler.isLoggingOut()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -2127,7 +2127,7 @@ void WindowManager::renderDeathScreen(game::GameHandler& gameHandler) {
|
||||||
deathElapsed_ += dt;
|
deathElapsed_ += dt;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -2216,7 +2216,7 @@ void WindowManager::renderDeathScreen(game::GameHandler& gameHandler) {
|
||||||
void WindowManager::renderReclaimCorpseButton(game::GameHandler& gameHandler) {
|
void WindowManager::renderReclaimCorpseButton(game::GameHandler& gameHandler) {
|
||||||
if (!gameHandler.isPlayerGhost() || !gameHandler.canReclaimCorpse()) return;
|
if (!gameHandler.isPlayerGhost() || !gameHandler.canReclaimCorpse()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -2274,7 +2274,7 @@ void WindowManager::renderMailWindow(game::GameHandler& gameHandler,
|
||||||
ChatPanel& chatPanel) {
|
ChatPanel& chatPanel) {
|
||||||
if (!gameHandler.isMailboxOpen()) return;
|
if (!gameHandler.isMailboxOpen()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 250, 80), ImGuiCond_Appearing);
|
ImGui::SetNextWindowPos(ImVec2(screenW / 2 - 250, 80), ImGuiCond_Appearing);
|
||||||
|
|
@ -2553,7 +2553,7 @@ void WindowManager::renderMailComposeWindow(game::GameHandler& gameHandler,
|
||||||
InventoryScreen& inventoryScreen) {
|
InventoryScreen& inventoryScreen) {
|
||||||
if (!gameHandler.isMailComposeOpen()) return;
|
if (!gameHandler.isMailComposeOpen()) return;
|
||||||
|
|
||||||
auto* window = core::Application::getInstance().getWindow();
|
auto* window = services_.window;
|
||||||
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
float screenW = window ? static_cast<float>(window->getWidth()) : 1280.0f;
|
||||||
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
float screenH = window ? static_cast<float>(window->getHeight()) : 720.0f;
|
||||||
|
|
||||||
|
|
@ -3769,7 +3769,7 @@ void WindowManager::renderAchievementWindow(game::GameHandler& gameHandler) {
|
||||||
static bool s_criteriaDataLoaded = false;
|
static bool s_criteriaDataLoaded = false;
|
||||||
if (!s_criteriaDataLoaded) {
|
if (!s_criteriaDataLoaded) {
|
||||||
s_criteriaDataLoaded = true;
|
s_criteriaDataLoaded = true;
|
||||||
auto* am = core::Application::getInstance().getAssetManager();
|
auto* am = services_.assetManager;
|
||||||
if (am && am->isInitialized()) {
|
if (am && am->isInitialized()) {
|
||||||
auto dbc = am->loadDBC("AchievementCriteria.dbc");
|
auto dbc = am->loadDBC("AchievementCriteria.dbc");
|
||||||
if (dbc && dbc->isLoaded() && dbc->getFieldCount() >= 10) {
|
if (dbc && dbc->isLoaded() && dbc->getFieldCount() >= 10) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue