diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000000..ff3770db8d --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,544 @@ +# mod-playerbots Service Architecture Refactoring + +## Document Overview + +This document describes the architectural refactoring of the mod-playerbots module from a monolithic design to a service-oriented architecture. This refactoring was implemented in the `refactor/total-architecture` branch. + +--- + +## 1. Purpose + +### 1.1 Problem Statement + +The mod-playerbots codebase had grown organically into a **monolithic architecture** centered around a single "god object" - the `PlayerbotAI` class. This class contained: + +- ~2,500+ methods handling all bot functionality +- 22 static role-checking methods (IsTank, IsHeal, IsDps, etc.) +- Direct coupling to global singletons (TravelMgr, RandomPlayerbotMgr, etc.) +- Tight dependencies between AI logic and implementation details +- **No unit testing infrastructure** - the codebase was effectively untestable + +### 1.2 Goals + +1. **Enable Unit Testing** - Create interfaces that can be mocked for isolated testing +2. **Reduce Coupling** - Separate concerns into distinct, focused services +3. **Improve Maintainability** - Make the codebase easier to understand and modify +4. **Support Gradual Migration** - Allow incremental adoption without breaking existing code +5. **Establish Clean Architecture** - Define clear boundaries between components + +### 1.3 Non-Goals + +- Complete rewrite of PlayerbotAI (too risky) +- Performance optimization (focus is on architecture) +- Adding new features (refactoring only) + +--- + +## 2. Scope + +### 2.1 Summary Statistics + +| Metric | Value | +|--------|-------| +| Files Changed | 333 | +| Insertions | +18,112 | +| Deletions | -2,516 | +| Net Change | +15,596 lines | +| Commits | 28 | + +### 2.2 Components Changed + +**New Infrastructure (Created):** +- 6 service interface files (`src/Bot/Interface/`) - *actively used* +- 6 service implementation files (`src/Bot/Service/`) - *actively used* +- 35 test files (`test/`) + +**Manager Infrastructure (Now Active):** +- 3 manager interface files (ITravelManager, IRandomBotManager, IBotRepository) +- 3 adapter files (TravelManagerAdapter, RandomBotManagerAdapter, BotRepositoryAdapter) +- ManagerRegistry (`src/Bot/Core/ManagerRegistry.h`) - initialized at startup +- ~125 call sites migrated to use `sManagerRegistry` instead of direct singleton access + +**AI Components Updated (Modified):** +- 130+ Action files updated to use services +- 50+ Trigger files updated +- 40+ Value files updated +- All class-specific AI (Paladin, Priest, Mage, etc.) +- All raid AI (ICC, Ulduar, Karazhan) +- All dungeon AI (Oculus, FoS, ToC) + +**Core Files Refactored:** +- `PlayerbotAI.cpp` - Major restructuring (~1,054 lines changed) +- `PlayerbotAI.h` - Removed 22 static methods, added service container + +### 2.3 What Was NOT Changed + +- Core game logic (spell effects, combat calculations) +- Database schema +- Configuration file format +- External interfaces (commands, chat handlers) +- PlayerbotMgr class +- Engine execution model + +--- + +## 3. Design + +### 3.1 Architecture Overview + +**What's Actually Active:** +``` +┌─────────────────────────────────────────────────────────────────┐ +│ PlayerbotAI │ +│ ┌─────────────────────────────────────────────────────────┐ │ +│ │ BotServiceContainer (DI) │ │ +│ │ ┌──────────┬──────────┬──────────┬──────────┬────────┐ │ │ +│ │ │ IBotCtx │ISpellSvc │IChatSvc │IRoleSvc │IItemSvc│ │ │ +│ │ └────┬─────┴────┬─────┴────┬─────┴────┬─────┴───┬────┘ │ │ +│ └───────┼──────────┼──────────┼──────────┼─────────┼──────┘ │ +│ ▼ ▼ ▼ ▼ ▼ │ +│ BotContext BotSpellSvc BotChatSvc BotRoleSvc BotItemSvc │ +│ │ │ │ │ │ │ +│ │ (standalone) (standalone) (standalone)(standalone)│ +│ │ │ │ │ │ │ +│ └──────────┴──────────┴──────────┴─────────┘ │ +│ │ │ +│ All services fully extracted with static methods │ +└─────────────────────────────────────────────────────────────────┘ +``` + +**Global Manager Abstraction (Now Active):** +``` +┌─────────────────────────────────────────────────────────────────┐ +│ ManagerRegistry (ACTIVE) │ +│ ┌────────────────┬─────────────────┬──────────────────┐ │ +│ │ITravelManager │IRandomBotManager│IBotRepository │ │ +│ └───────┬────────┴────────┬────────┴─────────┬────────┘ │ +│ ▼ ▼ ▼ │ +│ TravelMgrAdapter RandomBotMgrAdapter BotRepoAdapter │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ sTravelMgr sRandomPlayerbotMgr sPlayerbotRepository │ +│ (underlying singletons) │ +└─────────────────────────────────────────────────────────────────┘ + +Initialized at startup in Playerbots.cpp:OnBeforeWorldInitialized() +~125 call sites migrated to use sManagerRegistry.Get*() methods +``` + +### 3.2 Design Patterns Used + +| Pattern | Usage | Location | +|---------|-------|----------| +| **Dependency Injection** | BotServiceContainer holds all services | `Bot/Core/BotServiceContainer.h` | +| **Interface Segregation** | Each service has a focused interface | `Bot/Interface/*.h` | +| **Static Factory** | Services provide static methods for direct access | `Bot/Service/Bot*.cpp` | + +**Global Manager Patterns (Now Active):** +- **Service Locator** (ManagerRegistry) - initialized at startup, provides mockable access to global managers +- **Adapter Pattern** (*Adapter files) - wrap existing singletons behind interfaces for testability + +### 3.3 Service Interfaces + +All interfaces are pure virtual classes in `src/Bot/Interface/`: + +**IBotContext** (~50 methods) - Bot state and object access: +- `GetBot()`, `GetMaster()` - Player pointers +- `GetState()`, `IsInCombat()` - State queries +- `IsOpposing()`, `IsFriendly()` - Faction checks + +**ISpellService** (~15 methods) - Spell operations: +- `CanCastSpell()`, `CastSpell()` - Spell casting +- `HasAura()`, `GetAuraCount()` - Aura queries +- `InterruptSpell()`, `IsInterruptableSpellCasting()` - Interrupts + +**IChatService** (~10 methods) - Communication: +- `TellMaster()`, `TellError()` - Master messages +- `Say()`, `Whisper()`, `Yell()` - Chat functions +- `SayToGuild()`, `SayToParty()` - Group messaging + +**IRoleService** (~15 methods) - Role detection: +- `IsTank()`, `IsHeal()`, `IsDps()` - Primary roles +- `IsRanged()`, `IsMelee()`, `IsCaster()` - Combat style +- `GetGroupTankNum()`, `IsMainTank()` - Group positions + +**IItemService** (~10 methods) - Inventory: +- `FindPoison()`, `FindAmmo()` - Class items +- `GetInventoryAndEquippedItems()` - Item queries +- `HasItemInInventory()`, `GetItemCount()` - Item checks + +**IConfigProvider** (~40 methods) - Configuration: +- `GetSightDistance()`, `GetGlobalCooldown()` - Numeric configs +- `IsEnabled()`, `ShouldAutoEquip()` - Boolean configs + +### 3.4 Service Implementations + +All four services now use the **Full Implementation** pattern with static methods for direct access and instance methods that delegate to static methods. This provides both backward compatibility and full testability. + +**Pattern: Static Methods + Instance Delegation** +```cpp +class BotRoleService : public IRoleService { + // Static methods for direct access (no instance needed) + static bool IsTankStatic(Player* player, bool bySpec = false); + + // Interface implementation delegates to static + bool IsTank(Player* player, bool bySpec = false) const override { + return IsTankStatic(player_, bySpec); + } +}; +``` + +**Service Extraction Summary:** + +| Service | Lines | Static Methods | Context Struct | +|---------|-------|----------------|----------------| +| BotRoleService | ~850 | 22 methods | N/A | +| BotChatService | ~480 | 12 methods | ChatContext | +| BotItemService | ~780 | 17 methods | N/A | +| BotSpellService | ~590 | 12 methods | SpellContext | + +**Context Structs for Complex Dependencies:** + +Some services require context from PlayerbotAI for their static methods. Context structs bundle these dependencies: + +```cpp +// ChatContext bundles chat-related dependencies +struct ChatContext { + Player* bot; + std::function getMaster; + PlayerbotSecurity* security; + std::map* whispers; + std::pair* currentChat; + std::function hasStrategy; + std::function hasRealPlayerMaster; +}; + +// SpellContext bundles spell-related dependencies +struct SpellContext { + Player* bot; + AiObjectContext* aiObjectContext; + ChatHelper* chatHelper; + std::function setNextCheckDelay; + std::function hasStrategy; + std::function hasRealPlayerMaster; +}; +``` + +**Benefits of Full Extraction:** +- No dependency on PlayerbotAI at runtime +- Fully standalone and testable +- Static methods callable without service instance +- Instance methods provide interface compliance for mocking + +### 3.5 BotServiceContainer + +Central dependency injection container (`Bot/Core/BotServiceContainer.h`): + +```cpp +class BotServiceContainer { + std::unique_ptr context_; + std::unique_ptr spellService_; + std::unique_ptr chatService_; + std::unique_ptr roleService_; + std::unique_ptr itemService_; + std::unique_ptr config_; +public: + // Getters for production code + IBotContext& GetContext(); + ISpellService& GetSpellService(); + // ... + + // Setters for test injection + void SetContext(std::unique_ptr context); + void SetSpellService(std::unique_ptr service); + // ... +}; +``` + +**Design Decisions:** +- `unique_ptr` for single ownership +- No copying allowed (deleted copy constructor) +- Move semantics supported +- Both const and non-const accessors + +### 3.6 ManagerRegistry (Now Active) + +A singleton registry provides mockable access to global managers: + +```cpp +class ManagerRegistry { + static ManagerRegistry& Instance(); + ITravelManager& GetTravelManager(); + IRandomBotManager& GetRandomBotManager(); + IBotRepository& GetBotRepository(); +}; +#define sManagerRegistry ManagerRegistry::Instance() +``` + +**Initialization (Playerbots.cpp):** +```cpp +void OnBeforeWorldInitialized() override +{ + // ... config loading ... + + // Initialize ManagerRegistry with production adapters + sManagerRegistry.SetTravelManager(std::make_shared()); + sManagerRegistry.SetRandomBotManager(std::make_shared()); + sManagerRegistry.SetBotRepository(std::make_shared()); +} +``` + +**Usage Pattern:** +```cpp +// Before (direct singleton access) +sRandomPlayerbotMgr->IsRandomBot(bot) + +// After (through registry) +sManagerRegistry.GetRandomBotManager().IsRandomBot(bot) +``` + +**Migration Status:** +- `IRandomBotManager`: ~65 `IsRandomBot` calls + ~60 other interface-compatible calls migrated +- `IBotRepository`: All `Save`/`Load`/`Reset` calls migrated (~8 call sites) +- `ITravelManager`: Interface has limited methods; most `sTravelMgr` calls still use direct access + +**Note:** Some `sRandomPlayerbotMgr` methods are not yet in `IRandomBotManager` (e.g., `BattlegroundData`, `GetPlayerBotsBegin/End`, `IsAddclassBot`). These still use direct singleton access pending interface expansion. + +### 3.7 Migration Approach + +The refactoring used a **direct migration** approach rather than feature flags: + +1. **Services created alongside existing code** - New service classes wrap existing PlayerbotAI methods +2. **Callers updated in bulk** - Each service migration commit updated all callers at once +3. **Tests validate behavior** - Unit tests ensure services work correctly before integration + +**Note:** A `RefactorFlags.h` file was created in the foundation commit as a potential mechanism for gradual rollout, but was ultimately **not used**. The direct migration approach was chosen instead because: +- Services delegate to existing PlayerbotAI methods (minimal risk) +- Comprehensive test coverage validates the changes +- Bulk migration is cleaner than maintaining two code paths + +### 3.8 Call Pattern Transformation + +**Before Refactoring:** +```cpp +// Direct method calls on PlayerbotAI +if (botAI->IsTank(player)) { + botAI->CastSpell(TAUNT_SPELL_ID, target); + botAI->TellMaster("Taunting target!"); +} +``` + +**After Refactoring:** +```cpp +// Service-based calls via container +if (botAI->GetServices().GetRoleService().IsTank(player)) { + botAI->GetServices().GetSpellService().CastSpell(TAUNT_SPELL_ID, target); + botAI->GetServices().GetChatService().TellMaster("Taunting target!"); +} +``` + +### 3.9 Testing Infrastructure + +**Test Directory Structure:** +``` +test/ +├── CMakeLists.txt # Build configuration +├── fixtures/ # Test utilities +│ ├── AcoreTypes.h # AzerothCore type definitions +│ ├── TriggerTestFixture.h +│ └── PlayerbotSecurity.h +├── mocks/ # Mock implementations +│ ├── MockBotServices.h # All service mocks (GMock) +│ ├── MockManagers.h # Manager mocks +│ └── MockPlayerbotAI.h +└── unit/ # 24 test files + ├── Bot/Core/ # Core infrastructure tests + │ └── ManagerRegistryTest.cpp + ├── Bot/Service/ # Service tests + │ ├── RoleServiceTest.cpp + │ ├── ChatServiceTest.cpp + │ ├── ItemServiceTest.cpp + │ └── SpellServiceTest.cpp + ├── Ai/Action/ # Action logic tests + ├── Ai/Combat/ # Combat behavior tests + └── ... +``` + +**Mock Example:** +```cpp +class MockRoleService : public IRoleService { + MOCK_METHOD(bool, IsTank, (Player*, bool), (const, override)); + MOCK_METHOD(bool, IsHeal, (Player*, bool), (const, override)); + // All interface methods mocked +}; +``` + +**Test Example:** +```cpp +TEST_F(RoleServiceTest, CanMockTankRole) { + MockRoleService mockRoleService; + EXPECT_CALL(mockRoleService, IsTank(_, false)) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockRoleService.IsTank(nullptr, false)); +} +``` + +**Test Coverage:** +- 344+ unit tests passing +- Combat behavior tests (5 suites) +- Engine tests (4 suites) +- AI integration tests +- Resource management tests +- ManagerRegistry tests (mock injection patterns) + +--- + +## 4. Implementation Timeline + +| Commit | Phase | Description | +|--------|-------|-------------| +| 1f8cc0ce | 0-2 | Foundation: interfaces, container, test infra | +| 8ff4c776 | 3 | Extract service implementations | +| 682dde0c | 3 | Extract handler implementations | +| 0a68a631 | 3 | Integrate BotServiceContainer into PlayerbotAI | +| 10fdb6db | 3 | Add manager interfaces and registry | +| 3a709117 | 3 | Add unit tests for services | +| 88174144 | 3 | Add unit tests for AI pattern logic | +| c0e2ee7e | 3 | Add comprehensive tests for all subsystems | +| 832b1ac5 | 4 | Migrate role service callers | +| 28def1a4 | 4 | Migrate spell service callers (~460 callers) | +| 51a9e807 | 4 | Migrate chat service callers (~477 callers) | +| 3d407ea5 | 4 | Migrate item service callers (~35 callers) | +| f7b5a69e | 5 | Fix test infrastructure | +| 884ff940 | 5 | Fix all unit test algorithm issues | +| 69f75c13 | 5 | Resolve build failures | +| 66e1e7ab | 5 | Add PlayerbotSecurity.h fixture | +| b6593be8 | 5 | Delete migration progress doc (cleanup) | +| 2f473996 | 5 | Merge upstream with service adaptation | +| ba7a1d58 | 6 | Wire up ManagerRegistry with production adapters | +| a1e0ae74 | 6 | Expand service layer with item, spell, and chat functionality | + +--- + +## 5. Key Files Reference + +**Active - Core Architecture:** +- `src/Bot/Core/BotServiceContainer.h` - DI container (actively used) +- `src/Bot/PlayerbotAI.h` - Contains BotServiceContainer +- `src/Bot/PlayerbotAI.cpp` - Service initialization + +**Active - Service Interfaces:** +- `src/Bot/Interface/IBotContext.h` +- `src/Bot/Interface/ISpellService.h` +- `src/Bot/Interface/IChatService.h` +- `src/Bot/Interface/IRoleService.h` +- `src/Bot/Interface/IItemService.h` +- `src/Bot/Interface/IConfigProvider.h` + +**Active - Service Implementations (all fully extracted with static methods):** +- `src/Bot/Service/BotContext.h/cpp` +- `src/Bot/Service/BotRoleService.h/cpp` (~850 lines) +- `src/Bot/Service/BotSpellService.h/cpp` (~590 lines) +- `src/Bot/Service/BotChatService.h/cpp` (~480 lines) +- `src/Bot/Service/BotItemService.h/cpp` (~400 lines) +- `src/Bot/Service/ConfigProvider.h` + +**Active - Manager Infrastructure:** +- `src/Bot/Core/ManagerRegistry.h` - Global manager registry (initialized at startup) +- `src/Bot/Interface/ITravelManager.h` - Travel manager interface +- `src/Bot/Interface/IRandomBotManager.h` - Random bot manager interface +- `src/Bot/Interface/IBotRepository.h` - Bot repository interface +- `src/Bot/Service/TravelManagerAdapter.h/cpp` - Wraps sTravelMgr +- `src/Bot/Service/RandomBotManagerAdapter.h/cpp` - Wraps sRandomPlayerbotMgr +- `src/Bot/Service/BotRepositoryAdapter.h/cpp` - Wraps sPlayerbotRepository + +**Unused:** +- `src/RefactorFlags.h` - Feature flags (not used) + +**Test Infrastructure:** +- `test/mocks/MockBotServices.h` - All service mocks +- `test/mocks/MockManagers.h` - Manager mocks (ITravelManager, IRandomBotManager, IBotRepository) +- `test/unit/Bot/Core/ManagerRegistryTest.cpp` - ManagerRegistry mock injection tests +- `test/unit/Bot/Service/RoleServiceTest.cpp` - Role service tests +- `test/unit/Bot/Service/ChatServiceTest.cpp` - Chat service tests +- `test/unit/Bot/Service/ItemServiceTest.cpp` - Item service tests +- `test/unit/Bot/Service/SpellServiceTest.cpp` - Spell service tests + +--- + +## 6. Benefits Achieved + +1. **Testability** - Services are independently testable via interfaces +2. **Loose Coupling** - Components depend on interfaces, not implementations +3. **Clean Boundaries** - Clear separation between service responsibilities +4. **Mock Support** - Full GMock infrastructure for testing +5. **Type Safety** - Template-based registry with compile-time checking +6. **Backward Compatibility** - Services delegate to existing code, minimizing risk + +--- + +## 7. Code Quality Improvements + +During the service extraction, several compiler warnings were fixed: + +**Deprecated Copy Warnings (-Wdeprecated-copy-with-user-provided-copy):** +Added copy assignment operators to classes with user-provided copy constructors: +- `NextAction` in `Action.h` +- `PositionInfo` in `PositionValue.h` +- `CraftData` in `CraftValue.h` +- `UnitPosition` in `Arrow.h` + +**Initialization Order Warnings (-Wreorder-ctor):** +Fixed member initialization order to match declaration order: +- `ArrowFormation` in `Arrow.h` +- `LastMovement` in `LastMovementValue.cpp` +- Various predicate classes in `PartyMember*.cpp` + +**Sign Comparison Warnings (-Wsign-compare):** +Fixed integer signedness mismatches: +- `MovementActions.h` - loop counter type +- `ChatShortcutActions.cpp` - map ID comparison +- `FollowActions.cpp` - map ID comparison + +**Pessimizing Move Warnings (-Wpessimizing-move):** +Removed unnecessary `std::move()` on return statements: +- `TravelNode.cpp` lines 367, 1166 + +**Unused Parameter Warnings (-Wunused-parameter):** +Added `[[maybe_unused]]` attribute to intentionally unused parameters in action Execute() methods. + +**Current Status:** The mod-playerbots module now compiles **warning-free** with standard compiler warning flags. + +--- + +## 8. Future Considerations + +**Completed (Phase 2 - Services):** +- ✅ All four services fully extracted with static methods (BotRoleService, BotChatService, BotItemService, BotSpellService) +- ✅ Context structs added for complex dependencies (ChatContext, SpellContext) +- ✅ Unit tests added for all services + +**Completed (Phase 3 - ManagerRegistry):** +- ✅ ManagerRegistry initialized at startup with production adapters +- ✅ ~125 call sites migrated from direct singleton access to `sManagerRegistry` +- ✅ RandomBotManagerAdapter fully implemented (all interface methods) +- ✅ BotRepositoryAdapter core methods (Save/Load/Reset) implemented +- ✅ Unit tests added for ManagerRegistry mock injection patterns + +**Remaining Work:** + +*Interface Expansion:* +- Extend `IRandomBotManager` to cover more methods (BattlegroundData, GetPlayerBotsBegin/End, IsAddclassBot, etc.) +- Extend `ITravelManager` to cover more TravelMgr methods (getQuestTravelDestinations, getRpgTravelDestinations, etc.) +- Migrate remaining direct singleton calls once interfaces are expanded + +*Service Architecture:* +- Migrate remaining PlayerbotAI methods to services (CanCastSpell, CastSpell - the largest methods) +- Consider extracting more granular services (e.g., AuraService, MovementService) +- Add integration tests alongside unit tests +- Document API contracts for each service +- Add deprecation warnings to PlayerbotAI methods that have been extracted + +*Clean Up:* +- Remove `src/RefactorFlags.h` (unused) diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..63d8f2397f --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,127 @@ +# Pull Request + +Describe what this change does and why it is needed... + +--- + +## Design Philosophy + +We prioritize **stability, performance, and predictability** over behavioral realism. +Complex player-mimicking logic is intentionally limited due to its negative impact on scalability, maintainability, and +long-term robustness. + +Excessive processing overhead can lead to server hiccups, increased CPU usage, and degraded performance for all +participants. Because every action and +decision tree is executed **per bot and per trigger**, even small increases in logic complexity can scale poorly and +negatively affect both players and +world (random) bots. Bots are not expected to behave perfectly, and perfect simulation of human decision-making is not a +project goal. Increased behavioral +realism often introduces disproportionate cost, reduced predictability, and significantly higher maintenance overhead. + +Every additional branch of logic increases long-term responsibility. All decision paths must be tested, validated, and +maintained continuously as the system evolves. +If advanced or AI-intensive behavior is introduced, the **default configuration must remain the lightweight decision +model**. More complex behavior should only be +available as an **explicit opt-in option**, clearly documented as having a measurable performance cost. + +Principles: + +- **Stability before intelligence** + A stable system is always preferred over a smarter one. + +- **Performance is a shared resource** + Any increase in bot cost affects all players and all bots. + +- **Simple logic scales better than smart logic** + Predictable behavior under load is more valuable than perfect decisions. + +- **Complexity must justify itself** + If a feature cannot clearly explain its cost, it should not exist. + +- **Defaults must be cheap** + Expensive behavior must always be optional and clearly communicated. + +- **Bots should look reasonable, not perfect** + The goal is believable behavior, not human simulation. + +Before submitting, confirm that this change aligns with those principles. + +--- + +## Feature Evaluation + +Please answer the following: + +- Describe the **minimum logic** required to achieve the intended behavior? +- Describe the **cheapest implementation** that produces an acceptable result? +- Describe the **runtime cost** when this logic executes across many bots? + +--- + +## How to Test the Changes + +- Step-by-step instructions to test the change +- Any required setup (e.g. multiple players, bots, specific configuration) +- Expected behavior and how to verify it + +## Complexity & Impact + +- Does this change add new decision branches? + - [ ] No + - [ ] Yes (**explain below**) + +- Does this change increase per-bot or per-tick processing? + - [ ] No + - [ ] Yes (**describe and justify impact**) + +- Could this logic scale poorly under load? + - [ ] No + - [ ] Yes (**explain why**) + +--- + +## Defaults & Configuration + +- Does this change modify default bot behavior? + - [ ] No + - [ ] Yes (**explain why**) + +If this introduces more advanced or AI-heavy logic: + +- [ ] Lightweight mode remains the default +- [ ] More complex behavior is optional and thereby configurable + +--- + +## AI Assistance + +- Was AI assistance (e.g. ChatGPT or similar tools) used while working on this change? + - [ ] No + - [ ] Yes (**explain below**) + +If yes, please specify: + +- AI tool or model used (e.g. ChatGPT, GPT-4, Claude, etc.) +- Purpose of usage (e.g. brainstorming, refactoring, documentation, code generation) +- Which parts of the change were influenced or generated +- Whether the result was manually reviewed and adapted + +AI assistance is allowed, but all submitted code must be fully understood, reviewed, and owned by the contributor. +Any AI-influenced changes must be verified against existing CORE and PB logic. We expect contributors to be honest +about what they do and do not understand. + +--- + +## Final Checklist + +- [ ] Stability is not compromised +- [ ] Performance impact is understood, tested, and acceptable +- [ ] Added logic complexity is justified and explained +- [ ] Documentation updated if needed + +--- + +## Notes for Reviewers + +Anything that significantly improves realism at the cost of stability or performance should be carefully discussed +before merging. diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 81668a9559..cf0ea08085 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -185,7 +185,7 @@ AiPlayerbot.SummonWhenGroup = 1 AiPlayerbot.SelfBotLevel = 1 # Non-GM player can only use init=auto to initialize bots based on their own level and gearscore -# Default: 0 (non-GM player can use any intialization commands) +# Default: 0 (non-GM player can use any initialization commands) AiPlayerbot.AutoInitOnly = 0 # The upper limit ratio of bot equipment level for init=auto diff --git a/src/Ai/Base/Actions/AcceptBattlegroundInvitationAction.cpp b/src/Ai/Base/Actions/AcceptBattlegroundInvitationAction.cpp index 96d61d4ba3..cc606a8d62 100644 --- a/src/Ai/Base/Actions/AcceptBattlegroundInvitationAction.cpp +++ b/src/Ai/Base/Actions/AcceptBattlegroundInvitationAction.cpp @@ -8,7 +8,7 @@ #include "Event.h" #include "Playerbots.h" -bool AcceptBgInvitationAction::Execute(Event event) +bool AcceptBgInvitationAction::Execute(Event /*event*/) { uint8 type = 0; // arenatype if arena uint8 unk2 = 0; // unk, can be 0x0 (may be if was invited?) and 0x1 diff --git a/src/Ai/Base/Actions/AcceptInvitationAction.cpp b/src/Ai/Base/Actions/AcceptInvitationAction.cpp index 5e0bffc47f..076d2ca695 100644 --- a/src/Ai/Base/Actions/AcceptInvitationAction.cpp +++ b/src/Ai/Base/Actions/AcceptInvitationAction.cpp @@ -3,8 +3,10 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "AcceptInvitationAction.h" +#include "Bot/Core/ManagerRegistry.h" #include "Event.h" #include "ObjectAccessor.h" #include "PlayerbotAIConfig.h" @@ -46,7 +48,7 @@ bool AcceptInvitationAction::Execute(Event event) if (!bot->GetGroup() || !bot->GetGroup()->IsMember(inviter->GetGUID())) return false; - if (sRandomPlayerbotMgr->IsRandomBot(bot)) + if (sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) botAI->SetMaster(inviter); // else // sPlayerbotRepository->Save(botAI); @@ -55,7 +57,7 @@ bool AcceptInvitationAction::Execute(Event event) botAI->ChangeStrategy("+follow,-lfg,-bg", BOT_STATE_NON_COMBAT); botAI->Reset(); - botAI->TellMaster("Hello"); + botAI->GetServices().GetChatService().TellMaster("Hello"); if (sPlayerbotAIConfig->summonWhenGroup && bot->GetDistance(inviter) > sPlayerbotAIConfig->sightDistance) { diff --git a/src/Ai/Base/Actions/AcceptQuestAction.cpp b/src/Ai/Base/Actions/AcceptQuestAction.cpp index 005cfb08a9..4ae21e93e0 100644 --- a/src/Ai/Base/Actions/AcceptQuestAction.cpp +++ b/src/Ai/Base/Actions/AcceptQuestAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "AcceptQuestAction.h" #include "Event.h" @@ -88,7 +89,7 @@ bool AcceptQuestAction::Execute(Event event) std::stringstream ss; ss << "AcceptQuestAction [" << qInfo->GetTitle() << "] - [" << std::to_string(qInfo->GetQuestId()) << "]"; LOG_DEBUG("playerbots", "{}", ss.str().c_str()); - // botAI->TellMaster(ss.str()); + // botAI->GetServices().GetChatService().TellMaster(ss.str()); } return hasAccept; @@ -113,7 +114,7 @@ bool AcceptQuestShareAction::Execute(Event event) if (bot->HasQuest(quest)) { bot->SetDivider(ObjectGuid::Empty); - botAI->TellError("I have this quest"); + botAI->GetServices().GetChatService().TellError("I have this quest"); return false; } @@ -121,7 +122,7 @@ bool AcceptQuestShareAction::Execute(Event event) { // can't take quest bot->SetDivider(ObjectGuid::Empty); - botAI->TellError("I can't take this quest"); + botAI->GetServices().GetChatService().TellError("I can't take this quest"); return false; } @@ -149,7 +150,7 @@ bool AcceptQuestShareAction::Execute(Event event) bot->CastSpell(bot, qInfo->GetSrcSpell(), true); } - botAI->TellMaster("Quest accepted"); + botAI->GetServices().GetChatService().TellMaster("Quest accepted"); return true; } @@ -174,7 +175,7 @@ bool ConfirmQuestAction::Execute(Event event) if (!bot->CanTakeQuest(qInfo, false)) { // can't take quest - // botAI->TellError("quest_cant_take"); + // botAI->GetServices().GetChatService().TellError("quest_cant_take"); return false; } @@ -190,7 +191,7 @@ bool ConfirmQuestAction::Execute(Event event) bot->CastSpell(bot, qInfo->GetSrcSpell(), true); } - // botAI->TellMaster("quest_accept"); + // botAI->GetServices().GetChatService().TellMaster("quest_accept"); return true; } diff --git a/src/Ai/Base/Actions/AddLootAction.cpp b/src/Ai/Base/Actions/AddLootAction.cpp index 9e16ee2d3a..40cda0e6e1 100644 --- a/src/Ai/Base/Actions/AddLootAction.cpp +++ b/src/Ai/Base/Actions/AddLootAction.cpp @@ -22,7 +22,7 @@ bool AddLootAction::Execute(Event event) return AI_VALUE(LootObjectStack*, "available loot")->Add(guid); } -bool AddAllLootAction::Execute(Event event) +bool AddAllLootAction::Execute(Event /*event*/) { bool added = false; diff --git a/src/Ai/Base/Actions/AreaTriggerAction.cpp b/src/Ai/Base/Actions/AreaTriggerAction.cpp index f480277825..0ebc374685 100644 --- a/src/Ai/Base/Actions/AreaTriggerAction.cpp +++ b/src/Ai/Base/Actions/AreaTriggerAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "AreaTriggerAction.h" #include "Event.h" @@ -36,7 +37,7 @@ bool ReachAreaTriggerAction::Execute(Event event) if (bot->GetMapId() != at->map) { - botAI->TellError("I won't follow: too far away"); + botAI->GetServices().GetChatService().TellError("I won't follow: too far away"); return true; } @@ -51,14 +52,14 @@ bool ReachAreaTriggerAction::Execute(Event event) float distance = bot->GetDistance(at->x, at->y, at->z); float delay = 1000.0f * distance / bot->GetSpeed(MOVE_RUN) + sPlayerbotAIConfig->reactDelay; - botAI->TellError("Wait for me"); + botAI->GetServices().GetChatService().TellError("Wait for me"); botAI->SetNextCheckDelay(delay); context->GetValue("last area trigger")->Get().lastAreaTrigger = triggerId; return true; } -bool AreaTriggerAction::Execute(Event event) +bool AreaTriggerAction::Execute(Event /*event*/) { LastMovement& movement = context->GetValue("last area trigger")->Get(); @@ -76,6 +77,6 @@ bool AreaTriggerAction::Execute(Event event) p.rpos(0); bot->GetSession()->HandleAreaTriggerOpcode(p); - botAI->TellMaster("Hello"); + botAI->GetServices().GetChatService().TellMaster("Hello"); return true; } diff --git a/src/Ai/Base/Actions/AttackAction.cpp b/src/Ai/Base/Actions/AttackAction.cpp index 2b8c486f8b..502716d013 100644 --- a/src/Ai/Base/Actions/AttackAction.cpp +++ b/src/Ai/Base/Actions/AttackAction.cpp @@ -4,6 +4,8 @@ */ #include "AttackAction.h" +#include "BotChatService.h" +#include "BotRoleService.h" #include "CreatureAI.h" #include "Event.h" @@ -37,7 +39,7 @@ bool AttackMyTargetAction::Execute(Event /*event*/) if (!guid) { if (verbose) - botAI->TellError("You have no target"); + botAI->GetServices().GetChatService().TellError("You have no target"); return false; } @@ -53,7 +55,7 @@ bool AttackMyTargetAction::Execute(Event /*event*/) bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) { Unit* oldTarget = context->GetValue("current target")->Get(); - bool shouldMelee = bot->IsWithinMeleeRange(target) || botAI->IsMelee(bot); + bool shouldMelee = bot->IsWithinMeleeRange(target) || BotRoleService::IsMeleeStatic(bot); bool sameTarget = oldTarget == target && bot->GetVictim() == target; bool inCombat = botAI->GetState() == BOT_STATE_COMBAT; @@ -63,7 +65,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) bot->HasUnitState(UNIT_STATE_IN_FLIGHT)) { if (verbose) - botAI->TellError("I cannot attack in flight"); + botAI->GetServices().GetChatService().TellError("I cannot attack in flight"); return false; } @@ -71,7 +73,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) if (!target) { if (verbose) - botAI->TellError("I have no target"); + botAI->GetServices().GetChatService().TellError("I have no target"); return false; } @@ -79,7 +81,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) if (!target->IsInWorld()) { if (verbose) - botAI->TellError(std::string(target->GetName()) + " is no longer in the world."); + botAI->GetServices().GetChatService().TellError(std::string(target->GetName()) + " is no longer in the world."); return false; } @@ -91,7 +93,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) sPlayerbotAIConfig->IsPvpProhibited(target->GetZoneId(), target->GetAreaId()))) { if (verbose) - botAI->TellError("I cannot attack other players in PvP prohibited areas."); + botAI->GetServices().GetChatService().TellError("I cannot attack other players in PvP prohibited areas."); return false; } @@ -99,7 +101,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) if (bot->IsFriendlyTo(target)) { if (verbose) - botAI->TellError(std::string(target->GetName()) + " is friendly to me."); + botAI->GetServices().GetChatService().TellError(std::string(target->GetName()) + " is friendly to me."); return false; } @@ -107,7 +109,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) if (target->isDead()) { if (verbose) - botAI->TellError(std::string(target->GetName()) + " is dead."); + botAI->GetServices().GetChatService().TellError(std::string(target->GetName()) + " is dead."); return false; } @@ -115,7 +117,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) if (!bot->IsWithinLOSInMap(target)) { if (verbose) - botAI->TellError(std::string(target->GetName()) + " is not in my sight."); + botAI->GetServices().GetChatService().TellError(std::string(target->GetName()) + " is not in my sight."); return false; } @@ -123,7 +125,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) if (sameTarget && inCombat && sameAttackMode) { if (verbose) - botAI->TellError("I am already attacking " + std::string(target->GetName()) + "."); + botAI->GetServices().GetChatService().TellError("I am already attacking " + std::string(target->GetName()) + "."); return false; } @@ -131,7 +133,7 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) if (!bot->IsValidAttackTarget(target)) { if (verbose) - botAI->TellError("I cannot attack an invalid target."); + botAI->GetServices().GetChatService().TellError("I cannot attack an invalid target."); return false; } diff --git a/src/Ai/Base/Actions/AutoMaintenanceOnLevelupAction.cpp b/src/Ai/Base/Actions/AutoMaintenanceOnLevelupAction.cpp index 6939e5c925..c9a8a3c85f 100644 --- a/src/Ai/Base/Actions/AutoMaintenanceOnLevelupAction.cpp +++ b/src/Ai/Base/Actions/AutoMaintenanceOnLevelupAction.cpp @@ -1,5 +1,7 @@ +#include "BotChatService.h" #include "AutoMaintenanceOnLevelupAction.h" +#include "Bot/Core/ManagerRegistry.h" #include "GuildMgr.h" #include "PlayerbotAIConfig.h" #include "PlayerbotFactory.h" @@ -8,7 +10,7 @@ #include "SharedDefines.h" #include "BroadcastHelper.h" -bool AutoMaintenanceOnLevelupAction::Execute(Event event) +bool AutoMaintenanceOnLevelupAction::Execute(Event /*event*/) { AutoPickTalents(); AutoLearnSpell(); @@ -19,7 +21,7 @@ bool AutoMaintenanceOnLevelupAction::Execute(Event event) void AutoMaintenanceOnLevelupAction::AutoTeleportForLevel() { - if (!sPlayerbotAIConfig->autoTeleportForLevel || !sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sPlayerbotAIConfig->autoTeleportForLevel || !sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) { return; } @@ -33,7 +35,7 @@ void AutoMaintenanceOnLevelupAction::AutoTeleportForLevel() void AutoMaintenanceOnLevelupAction::AutoPickTalents() { - if (!sPlayerbotAIConfig->autoPickTalents || !sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sPlayerbotAIConfig->autoPickTalents || !sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) return; if (bot->GetFreeTalentPoints() <= 0) @@ -57,7 +59,7 @@ void AutoMaintenanceOnLevelupAction::AutoLearnSpell() out << temp; out.seekp(-2, out.cur); out << "."; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } return; } @@ -65,14 +67,14 @@ void AutoMaintenanceOnLevelupAction::AutoLearnSpell() void AutoMaintenanceOnLevelupAction::LearnSpells(std::ostringstream* out) { BroadcastHelper::BroadcastLevelup(botAI, bot); - if (sPlayerbotAIConfig->autoLearnTrainerSpells && sRandomPlayerbotMgr->IsRandomBot(bot)) + if (sPlayerbotAIConfig->autoLearnTrainerSpells && sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) LearnTrainerSpells(out); - if (sPlayerbotAIConfig->autoLearnQuestSpells && sRandomPlayerbotMgr->IsRandomBot(bot)) + if (sPlayerbotAIConfig->autoLearnQuestSpells && sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) LearnQuestSpells(out); } -void AutoMaintenanceOnLevelupAction::LearnTrainerSpells(std::ostringstream* out) +void AutoMaintenanceOnLevelupAction::LearnTrainerSpells(std::ostringstream* /*out*/) { PlayerbotFactory factory(bot, bot->GetLevel()); factory.InitSkills(); @@ -166,7 +168,7 @@ std::string const AutoMaintenanceOnLevelupAction::FormatSpell(SpellInfo const* s void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip() { - if (!sPlayerbotAIConfig->autoUpgradeEquip || !sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sPlayerbotAIConfig->autoUpgradeEquip || !sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) return; PlayerbotFactory factory(bot, bot->GetLevel()); diff --git a/src/Ai/Base/Actions/BankAction.cpp b/src/Ai/Base/Actions/BankAction.cpp index 4d8d6c4d8c..4fa7807373 100644 --- a/src/Ai/Base/Actions/BankAction.cpp +++ b/src/Ai/Base/Actions/BankAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "BankAction.h" #include "Event.h" @@ -23,11 +24,11 @@ bool BankAction::Execute(Event event) return ExecuteBank(text, npc); } - botAI->TellError("Cannot find banker nearby"); + botAI->GetServices().GetChatService().TellError("Cannot find banker nearby"); return false; } -bool BankAction::ExecuteBank(std::string const text, Unit* bank) +bool BankAction::ExecuteBank(std::string const text, Unit* /*bank*/) { if (text.empty() || text == "?") { @@ -83,7 +84,7 @@ bool BankAction::Withdraw(uint32 itemid) std::ostringstream out; out << "got " << chat->FormatItem(pItem->GetTemplate(), pItem->GetCount()) << " from bank"; - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); return true; } @@ -103,13 +104,13 @@ bool BankAction::Deposit(Item* pItem) bot->BankItem(dest, pItem, true); out << "put " << chat->FormatItem(pItem->GetTemplate(), pItem->GetCount()) << " to bank"; - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); return true; } void BankAction::ListItems() { - botAI->TellMaster("=== Bank ==="); + botAI->GetServices().GetChatService().TellMaster("=== Bank ==="); std::map items; std::map soulbound; diff --git a/src/Ai/Base/Actions/BattleGroundJoinAction.cpp b/src/Ai/Base/Actions/BattleGroundJoinAction.cpp index 542c7566ff..ddb852381c 100644 --- a/src/Ai/Base/Actions/BattleGroundJoinAction.cpp +++ b/src/Ai/Base/Actions/BattleGroundJoinAction.cpp @@ -15,7 +15,7 @@ #include "PositionValue.h" #include "UpdateTime.h" -bool BGJoinAction::Execute(Event event) +bool BGJoinAction::Execute(Event /*event*/) { uint32 queueType = AI_VALUE(uint32, "bg type"); if (!queueType) // force join to fill bg @@ -25,8 +25,6 @@ bool BGJoinAction::Execute(Event event) BattlegroundQueueTypeId queueTypeId = (BattlegroundQueueTypeId)bgList[urand(0, bgList.size() - 1)]; BattlegroundTypeId bgTypeId = BattlegroundMgr::BGTemplateId(queueTypeId); - BattlegroundBracketId bracketId; - bool isArena = false; bool isRated = false; Battleground* bg = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId); @@ -38,12 +36,8 @@ bool BGJoinAction::Execute(Event event) if (!pvpDiff) return false; - bracketId = pvpDiff->GetBracketId(); - if (ArenaType type = ArenaType(BattlegroundMgr::BGArenaType(queueTypeId))) { - isArena = true; - std::vector::iterator i = find(ratedList.begin(), ratedList.end(), queueTypeId); if (i != ratedList.end()) isRated = true; @@ -409,10 +403,6 @@ bool BGJoinAction::JoinQueue(uint32 type) bracketId = pvpDiff->GetBracketId(); - uint32 BracketSize = bg->GetMaxPlayersPerTeam() * 2; - uint32 TeamSize = bg->GetMaxPlayersPerTeam(); - TeamId teamId = bot->GetTeamId(); - // check if already in queue if (bot->InBattlegroundQueueForBattlegroundQueueType(queueTypeId)) return false; @@ -487,8 +477,6 @@ bool BGJoinAction::JoinQueue(uint32 type) if (isArena) { isArena = true; - BracketSize = type * 2; - TeamSize = type; isRated = botAI->GetAiObjectContext()->GetValue("arena type")->Get(); if (joinAsGroup) @@ -528,14 +516,14 @@ bool BGJoinAction::JoinQueue(uint32 type) } else if (!joinAsGroup) { - if (teamId == TEAM_ALLIANCE) + if (bot->GetTeamId() == TEAM_ALLIANCE) sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].bgAllianceBotCount++; else sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].bgHordeBotCount++; } else { - if (teamId == TEAM_ALLIANCE) + if (bot->GetTeamId() == TEAM_ALLIANCE) sRandomPlayerbotMgr->BattlegroundData[queueTypeId][bracketId].bgAllianceBotCount += bot->GetGroup()->GetMembersCount(); else @@ -653,7 +641,7 @@ bool FreeBGJoinAction::shouldJoinBg(BattlegroundQueueTypeId queueTypeId, Battleg return false; } -bool BGLeaveAction::Execute(Event event) +bool BGLeaveAction::Execute(Event /*event*/) { if (!(bot->InBattlegroundQueue() || bot->InBattleground())) return false; @@ -1064,7 +1052,7 @@ bool BGStatusAction::Execute(Event event) return true; } -bool BGStatusCheckAction::Execute(Event event) +bool BGStatusCheckAction::Execute(Event /*event*/) { if (bot->IsBeingTeleported()) return false; @@ -1080,7 +1068,7 @@ bool BGStatusCheckAction::Execute(Event event) bool BGStatusCheckAction::isUseful() { return bot->InBattlegroundQueue(); } -bool BGStrategyCheckAction::Execute(Event event) +bool BGStrategyCheckAction::Execute(Event /*event*/) { bool inside_bg = bot->InBattleground() && bot->GetBattleground(); ; diff --git a/src/Ai/Base/Actions/BattleGroundTactics.cpp b/src/Ai/Base/Actions/BattleGroundTactics.cpp index 827ab01970..17f58bc98a 100644 --- a/src/Ai/Base/Actions/BattleGroundTactics.cpp +++ b/src/Ai/Base/Actions/BattleGroundTactics.cpp @@ -4,6 +4,9 @@ */ #include "BattleGroundTactics.h" +#include "Bot/Core/ManagerRegistry.h" +#include "BotRoleService.h" +#include "BotSpellService.h" #include @@ -32,6 +35,7 @@ #include "PvpTriggers.h" #include "ServerFacade.h" #include "Vehicle.h" +#include "BotChatService.h" // common bg positions Position const WS_WAITING_POS_HORDE_1 = {944.981f, 1423.478f, 345.434f, 6.18f}; @@ -49,7 +53,7 @@ Position const WS_FLAG_HIDE_ALLIANCE_1 = {1529.249f, 1456.470f, 353.04f, 1.25f}; Position const WS_FLAG_HIDE_ALLIANCE_2 = {1540.286f, 1476.026f, 352.692f, 2.91f}; Position const WS_FLAG_HIDE_ALLIANCE_3 = {1495.807f, 1466.774f, 352.350f, 1.50f}; Position const WS_ROAM_POS = {1227.446f, 1476.235f, 307.484f, 1.50f}; -Position const WS_GY_CAMPING_HORDE = {1039.819, 1388.759f, 340.703f, 0.0f}; +Position const WS_GY_CAMPING_HORDE = {1039.819f, 1388.759f, 340.703f, 0.0f}; Position const WS_GY_CAMPING_ALLIANCE = {1422.320f, 1551.978f, 342.834f, 0.0f}; std::vector const WS_FLAG_HIDE_HORDE = {WS_FLAG_HIDE_HORDE_1, WS_FLAG_HIDE_HORDE_2, WS_FLAG_HIDE_HORDE_3}; std::vector const WS_FLAG_HIDE_ALLIANCE = {WS_FLAG_HIDE_ALLIANCE_1, WS_FLAG_HIDE_ALLIANCE_2, @@ -1368,7 +1372,7 @@ std::string const BGTactics::HandleConsoleCommandPrivate(WorldSession* session, uint32 max = vPaths->size() - 1; if (num >= 0) // num specified or found { - if (num > max) + if (static_cast(num) > max) return fmt::format("Path {} of range of 0 - {}", num, max); min = num; max = num; @@ -1557,7 +1561,7 @@ bool BGTactics::eyJumpDown() // // actual bg tactics below // -bool BGTactics::Execute(Event event) +bool BGTactics::Execute(Event /*event*/) { Battleground* bg = bot->GetBattleground(); if (!bg) @@ -1929,11 +1933,11 @@ bool BGTactics::selectObjective(bool reset) { uint32 bossEntry = (team == TEAM_HORDE) ? AV_CPLACE_MINE_S_3 : AV_CPLACE_MINE_N_3; Creature* mBossNeutral = bg->GetBGCreature(bossEntry); - const Position* minePositions[] = {(team == TEAM_HORDE) ? &AV_MINE_SOUTH_1 : &AV_MINE_NORTH_1, + Position const* minePositions[] = {(team == TEAM_HORDE) ? &AV_MINE_SOUTH_1 : &AV_MINE_NORTH_1, (team == TEAM_HORDE) ? &AV_MINE_SOUTH_2 : &AV_MINE_NORTH_2, (team == TEAM_HORDE) ? &AV_MINE_SOUTH_3 : &AV_MINE_NORTH_3}; - const Position* chosen = minePositions[urand(0, 2)]; + Position const* chosen = minePositions[urand(0, 2)]; pos.Set(chosen->GetPositionX(), chosen->GetPositionY(), chosen->GetPositionZ(), bot->GetMapId()); posMap["bg objective"] = pos; BgObjective = mBossNeutral; @@ -2080,7 +2084,7 @@ bool BGTactics::selectObjective(bool reset) else { // Fallback: move to boss wait position - const Position& waitPos = (team == TEAM_HORDE) ? AV_BOSS_WAIT_H : AV_BOSS_WAIT_A; + Position const& waitPos = (team == TEAM_HORDE) ? AV_BOSS_WAIT_H : AV_BOSS_WAIT_A; float rx, ry, rz; bot->GetRandomPoint(waitPos, 5.0f, rx, ry, rz); @@ -2122,7 +2126,7 @@ bool BGTactics::selectObjective(bool reset) float rx, ry, rz; if (linkedNodeId && AVNodeMovementTargets.count(*linkedNodeId)) { - const AVNodePositionData& data = AVNodeMovementTargets[*linkedNodeId]; + AVNodePositionData const& data = AVNodeMovementTargets[*linkedNodeId]; bot->GetRandomPoint(data.pos, frand(-data.maxRadius, data.maxRadius), rx, ry, rz); } else @@ -2176,26 +2180,10 @@ bool BGTactics::selectObjective(bool reset) WSBotStrategy enemyStrategy = (team == TEAM_ALLIANCE) ? strategyHorde : strategyAlliance; uint8 defendersProhab = 3; // Default balanced - - switch (strategy) - { - case 0: - case 1: - case 2: - case 3: // Balanced - defendersProhab = 3; - break; - case 4: - case 5: - case 6: - case 7: // Heavy Offense - defendersProhab = 1; - break; - case 8: - case 9: // Heavy Defense - defendersProhab = 6; - break; - } + if (strategy == WS_STRATEGY_OFFENSIVE) + defendersProhab = 1; + else if (strategy == WS_STRATEGY_DEFENSIVE) + defendersProhab = 6; if (enemyStrategy == WS_STRATEGY_DEFENSIVE) defendersProhab = 2; @@ -2226,8 +2214,8 @@ bool BGTactics::selectObjective(bool reset) target.Relocate(enemyFC->GetPositionX(), enemyFC->GetPositionY(), enemyFC->GetPositionZ()); } // Graveyard Camping if in lead - else if (!hasFlag && role < 8 && - (team == TEAM_ALLIANCE && allianceScore == 2 && hordeScore == 0) || + else if ((!hasFlag && role < 8 && + (team == TEAM_ALLIANCE && allianceScore == 2 && hordeScore == 0)) || (team == TEAM_HORDE && hordeScore == 2 && allianceScore == 0)) { if (team == TEAM_ALLIANCE) @@ -2497,7 +2485,6 @@ bool BGTactics::selectObjective(bool reset) EYBotStrategy strategyHorde = static_cast(GetBotStrategyForTeam(bg, TEAM_HORDE)); EYBotStrategy strategyAlliance = static_cast(GetBotStrategyForTeam(bg, TEAM_ALLIANCE)); EYBotStrategy strategy = (team == TEAM_ALLIANCE) ? strategyAlliance : strategyHorde; - EYBotStrategy enemyStrategy = (team == TEAM_ALLIANCE) ? strategyHorde : strategyAlliance; auto IsOwned = [&](uint32 nodeId) -> bool { return eyeOfTheStormBG->GetCapturePointInfo(nodeId)._ownerTeamId == team; }; @@ -2566,7 +2553,7 @@ bool BGTactics::selectObjective(bool reset) if (bestNodeId != 0 && EY_NodePositions.contains(bestNodeId)) { - const Position& targetPos = EY_NodePositions[bestNodeId]; + Position const& targetPos = EY_NodePositions[bestNodeId]; float rx, ry, rz; bot->GetRandomPoint(targetPos, 5.0f, rx, ry, rz); @@ -2592,7 +2579,7 @@ bool BGTactics::selectObjective(bool reset) } else { - const Position& fallback = (team == TEAM_ALLIANCE) ? EY_FLAG_RETURN_POS_RETREAT_ALLIANCE + Position const& fallback = (team == TEAM_ALLIANCE) ? EY_FLAG_RETURN_POS_RETREAT_ALLIANCE : EY_FLAG_RETURN_POS_RETREAT_HORDE; float rx, ry, rz; @@ -2622,7 +2609,7 @@ bool BGTactics::selectObjective(bool reset) if (it == EY_NodePositions.end()) continue; - const Position& p = it->second; + Position const& p = it->second; if (bot->IsWithinDist2d(p.GetPositionX(), p.GetPositionY(), 125.0f)) { float rx, ry, rz; @@ -2699,7 +2686,7 @@ bool BGTactics::selectObjective(bool reset) uint32 chosenId = owned[urand(0, owned.size() - 1)]; if (EY_NodePositions.contains(chosenId)) { - const Position& p = EY_NodePositions[chosenId]; + Position const& p = EY_NodePositions[chosenId]; float rx, ry, rz; bot->GetRandomPoint(p, 5.0f, rx, ry, rz); rz = bot->GetMap()->GetHeight(rx, ry, rz); @@ -2761,7 +2748,7 @@ bool BGTactics::selectObjective(bool reset) if (bestNode && EY_NodePositions.contains(*bestNode)) { - const Position& p = EY_NodePositions[*bestNode]; + Position const& p = EY_NodePositions[*bestNode]; float rx, ry, rz; bot->GetRandomPoint(p, 5.0f, rx, ry, rz); rz = bot->GetMap()->GetHeight(rx, ry, rz); @@ -2816,7 +2803,7 @@ bool BGTactics::selectObjective(bool reset) uint32 chosen = candidates[urand(0, candidates.size() - 1)]; if (EY_NodePositions.contains(chosen)) { - const Position& p = EY_NodePositions[chosen]; + Position const& p = EY_NodePositions[chosen]; pos.Set(p.GetPositionX(), p.GetPositionY(), p.GetPositionZ(), bot->GetMapId()); foundObjective = true; } @@ -2847,8 +2834,8 @@ bool BGTactics::selectObjective(bool reset) BattlegroundIC* isleOfConquestBG = (BattlegroundIC*)bg; uint32 role = context->GetValue("bg role")->Get(); - bool inVehicle = botAI->IsInVehicle(); - bool controlsVehicle = botAI->IsInVehicle(true); + bool inVehicle = botAI->GetServices().GetSpellService().IsInVehicle(); + bool controlsVehicle = botAI->GetServices().GetSpellService().IsInVehicle(true); uint32 vehicleId = inVehicle ? bot->GetVehicleBase()->GetEntry() : 0; // skip if not the driver @@ -3215,7 +3202,7 @@ bool BGTactics::moveToObjective(bool ignoreDist) // std::ostringstream out; out << "Moving to objective " << pos.x << ", " << pos.y << ", Distance: " << // sServerFacade->GetDistance2d(bot, pos.x, pos.y); bot->Say(out.str(), LANG_UNIVERSAL); - // dont increase from 1.5 will cause bugs with horde capping AV towers + // dont increase from 1.5f will cause bugs with horde capping AV towers return MoveNear(bot->GetMapId(), pos.x, pos.y, pos.z, 1.5f); } return false; @@ -3231,7 +3218,6 @@ bool BGTactics::selectObjectiveWp(std::vector const& vPaths) if (bgType == BATTLEGROUND_RB) bgType = bg->GetBgTypeID(true); - PositionMap& posMap = context->GetValue("position")->Get(); PositionInfo pos = context->GetValue("position")->Get()["bg objective"]; if (!pos.isSet()) return false; @@ -3326,7 +3312,7 @@ bool BGTactics::selectObjectiveWp(std::vector const& vPaths) // don't pick path where bot is already closest to the paths closest point to target (it means path cant lead it // anywhere) don't pick path where closest point is too far away - if (closestPointIndex == (reverse ? 0 : path->size() - 1) || closestPointDistToBot > botDistanceLimit) + if (closestPointIndex == static_cast(reverse ? 0 : path->size() - 1) || closestPointDistToBot > botDistanceLimit) continue; // creates a score based on dist-to-bot and dist-to-destination, where lower is better, and dist-to-bot is more @@ -3863,7 +3849,7 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vectorm_targets.SetGOTarget(go); spell->prepare(&spell->m_targets); - botAI->WaitForSpellCast(spell); + botAI->GetServices().GetSpellService().WaitForSpellCast(spell); resetObjective(); return true; @@ -3953,7 +3939,7 @@ bool BGTactics::atFlag(std::vector const& vPaths, std::vectorStopMoving(); spell->prepare(&spell->m_targets); - botAI->WaitForSpellCast(spell); + botAI->GetServices().GetSpellService().WaitForSpellCast(spell); resetObjective(); return true; } @@ -4070,7 +4056,7 @@ bool BGTactics::useBuff() // do not move to Berserk buff if bot is healer or has flag if (!(bot->HasAura(BG_WS_SPELL_WARSONG_FLAG) || bot->HasAura(BG_WS_SPELL_SILVERWING_FLAG) || bot->HasAura(BG_EY_NETHERSTORM_FLAG_SPELL)) && - !botAI->IsHeal(bot) && go->GetEntry() == Buff_Entries[2]) + !BotRoleService::IsHealStatic(bot) && go->GetEntry() == Buff_Entries[2]) foundBuff = true; if (foundBuff) @@ -4249,11 +4235,11 @@ bool BGTactics::IsLockedInsideKeep() return false; } -bool ArenaTactics::Execute(Event event) +bool ArenaTactics::Execute(Event /*event*/) { if (!bot->InBattleground()) { - bool IsRandomBot = sRandomPlayerbotMgr->IsRandomBot(bot->GetGUID().GetCounter()); + bool IsRandomBot = sManagerRegistry.GetRandomBotManager().IsRandomBot(bot->GetGUID().GetCounter()); botAI->ChangeStrategy("-arena", BOT_STATE_COMBAT); botAI->ChangeStrategy("-arena", BOT_STATE_NON_COMBAT); botAI->ResetStrategies(!IsRandomBot); @@ -4304,7 +4290,7 @@ bool ArenaTactics::Execute(Event event) float x, y, z; target->GetPosition(x, y, z); - botAI->TellMasterNoFacing("Repositioning to exit the LoS target!"); + botAI->GetServices().GetChatService().TellMasterNoFacing("Repositioning to exit the LoS target!"); return MoveTo(target->GetMapId(), x + frand(-1, +1), y + frand(-1, +1), z, false, true); } } @@ -4343,6 +4329,8 @@ bool ArenaTactics::moveToCenter(Battleground* bg) case CLASS_WARLOCK: Preference = 9; break; + default: + break; } switch (bg->GetBgTypeID()) diff --git a/src/Ai/Base/Actions/BattleGroundTactics.h b/src/Ai/Base/Actions/BattleGroundTactics.h index b6d065aebf..8391d0a7f6 100644 --- a/src/Ai/Base/Actions/BattleGroundTactics.h +++ b/src/Ai/Base/Actions/BattleGroundTactics.h @@ -14,7 +14,7 @@ class Battleground; class PlayerbotAI; struct Position; -#define SPELL_CAPTURE_BANNER 21651 +constexpr uint32 SPELL_CAPTURE_BANNER = 21651; enum WSBotStrategy : uint8 { diff --git a/src/Ai/Base/Actions/BossAuraActions.cpp b/src/Ai/Base/Actions/BossAuraActions.cpp index d711559ee5..422f4f69fe 100644 --- a/src/Ai/Base/Actions/BossAuraActions.cpp +++ b/src/Ai/Base/Actions/BossAuraActions.cpp @@ -10,7 +10,7 @@ #include "BossAuraActions.h" #include "BossAuraTriggers.h" -const std::string ADD_STRATEGY_CHAR = "+"; +std::string const ADD_STRATEGY_CHAR = "+"; bool BossFireResistanceAction::isUseful() { @@ -18,7 +18,7 @@ bool BossFireResistanceAction::isUseful() return bossFireResistanceTrigger.IsActive(); } -bool BossFireResistanceAction::Execute(Event event) +bool BossFireResistanceAction::Execute(Event /*event*/) { PaladinFireResistanceStrategy paladinFireResistanceStrategy(botAI); botAI->ChangeStrategy(ADD_STRATEGY_CHAR + paladinFireResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT); @@ -32,7 +32,7 @@ bool BossFrostResistanceAction::isUseful() return bossFrostResistanceTrigger.IsActive(); } -bool BossFrostResistanceAction::Execute(Event event) +bool BossFrostResistanceAction::Execute(Event /*event*/) { PaladinFrostResistanceStrategy paladinFrostResistanceStrategy(botAI); botAI->ChangeStrategy(ADD_STRATEGY_CHAR + paladinFrostResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT); @@ -46,7 +46,7 @@ bool BossNatureResistanceAction::isUseful() return bossNatureResistanceTrigger.IsActive(); } -bool BossNatureResistanceAction::Execute(Event event) +bool BossNatureResistanceAction::Execute(Event /*event*/) { HunterNatureResistanceStrategy hunterNatureResistanceStrategy(botAI); botAI->ChangeStrategy(ADD_STRATEGY_CHAR + hunterNatureResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT); @@ -60,7 +60,7 @@ bool BossShadowResistanceAction::isUseful() return bossShadowResistanceTrigger.IsActive(); } -bool BossShadowResistanceAction::Execute(Event event) +bool BossShadowResistanceAction::Execute(Event /*event*/) { PaladinShadowResistanceStrategy paladinShadowResistanceStrategy(botAI); botAI->ChangeStrategy(ADD_STRATEGY_CHAR + paladinShadowResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT); diff --git a/src/Ai/Base/Actions/BuffAction.cpp b/src/Ai/Base/Actions/BuffAction.cpp index d15dcf65ee..c29cf8f99d 100644 --- a/src/Ai/Base/Actions/BuffAction.cpp +++ b/src/Ai/Base/Actions/BuffAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "BuffAction.h" #include "Event.h" @@ -64,20 +65,22 @@ void BuffAction::TellHeader(uint32 subClass) switch (subClass) { case ITEM_SUBCLASS_ELIXIR: - botAI->TellMaster("--- Elixir ---"); + botAI->GetServices().GetChatService().TellMaster("--- Elixir ---"); return; case ITEM_SUBCLASS_FLASK: - botAI->TellMaster("--- Flask ---"); + botAI->GetServices().GetChatService().TellMaster("--- Flask ---"); return; case ITEM_SUBCLASS_SCROLL: - botAI->TellMaster("--- Scroll ---"); + botAI->GetServices().GetChatService().TellMaster("--- Scroll ---"); return; case ITEM_SUBCLASS_FOOD: - botAI->TellMaster("--- Food ---"); + botAI->GetServices().GetChatService().TellMaster("--- Food ---"); return; case ITEM_SUBCLASS_ITEM_ENHANCEMENT: - botAI->TellMaster("--- Enchant ---"); + botAI->GetServices().GetChatService().TellMaster("--- Enchant ---"); return; + default: + break; } } @@ -107,7 +110,7 @@ bool BuffAction::Execute(Event event) Item* item = *j; std::ostringstream out; out << chat->FormatItem(item->GetTemplate(), item->GetCount()); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } } diff --git a/src/Ai/Base/Actions/BuyAction.cpp b/src/Ai/Base/Actions/BuyAction.cpp index 1e8ec7e5c4..4b7ee97191 100644 --- a/src/Ai/Base/Actions/BuyAction.cpp +++ b/src/Ai/Base/Actions/BuyAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "BuyAction.h" #include "BudgetValues.h" @@ -12,6 +13,7 @@ #include "ItemVisitors.h" #include "Playerbots.h" #include "StatsWeightCalculator.h" +#include "BotItemService.h" bool BuyAction::Execute(Event event) { @@ -118,7 +120,7 @@ bool BuyAction::Execute(Event event) } // Check the bot's currently equipped item for this slot - uint8 dstSlot = botAI->FindEquipSlot(proto, NULL_SLOT, true); + uint8 dstSlot = botAI->GetServices().GetItemService().FindEquipSlot(proto, NULL_SLOT, true); Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, dstSlot); float oldScore = 0.0f; @@ -198,7 +200,7 @@ bool BuyAction::Execute(Event event) { std::ostringstream out; out << "Nobody sells " << ChatHelper::FormatItem(proto) << " nearby"; - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); continue; } @@ -215,7 +217,7 @@ bool BuyAction::Execute(Event event) if (!vendored) { - botAI->TellError("There are no vendors nearby"); + botAI->GetServices().GetChatService().TellError("There are no vendors nearby"); return false; } @@ -249,7 +251,7 @@ bool BuyAction::BuyItem(VendorItemData const* tItems, ObjectGuid vendorguid, Ite { std::ostringstream out; out << "Buying " << ChatHelper::FormatItem(proto); - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); return true; } diff --git a/src/Ai/Base/Actions/CancelChannelAction.cpp b/src/Ai/Base/Actions/CancelChannelAction.cpp index 9f359f392b..d9dfd34dd5 100644 --- a/src/Ai/Base/Actions/CancelChannelAction.cpp +++ b/src/Ai/Base/Actions/CancelChannelAction.cpp @@ -7,7 +7,7 @@ #include "Player.h" #include "PlayerbotAI.h" -bool CancelChannelAction::Execute(Event event) +bool CancelChannelAction::Execute(Event /*event*/) { if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) { diff --git a/src/Ai/Base/Actions/CastCustomSpellAction.cpp b/src/Ai/Base/Actions/CastCustomSpellAction.cpp index 417ffb50d3..193158b03e 100644 --- a/src/Ai/Base/Actions/CastCustomSpellAction.cpp +++ b/src/Ai/Base/Actions/CastCustomSpellAction.cpp @@ -1,3 +1,4 @@ +#include "BotSpellService.h" /* * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it * and/or modify it under version 3 of the License, or (at your option), any later version. @@ -10,6 +11,7 @@ #include "ItemUsageValue.h" #include "Playerbots.h" #include "ServerFacade.h" +#include "BotChatService.h" size_t FindLastSeparator(std::string const text, std::string const sep) { @@ -33,7 +35,7 @@ static inline void ltrim(std::string& s) bool CastCustomSpellAction::Execute(Event event) { // only allow proper vehicle seats - if (botAI->IsInVehicle() && !botAI->IsInVehicle(false, false, true)) + if (botAI->GetServices().GetSpellService().IsInVehicle() && !botAI->GetServices().GetSpellService().IsInVehicle(false, false, true)) return false; Player* master = GetMaster(); @@ -118,7 +120,7 @@ bool CastCustomSpellAction::Execute(Event event) if (!spell) { msg << "Unknown spell " << text; - botAI->TellError(msg.str()); + botAI->GetServices().GetChatService().TellError(msg.str()); return false; } @@ -126,7 +128,7 @@ bool CastCustomSpellAction::Execute(Event event) if (!spellInfo) { msg << "Unknown spell " << text; - botAI->TellError(msg.str()); + botAI->GetServices().GetChatService().TellError(msg.str()); return false; } @@ -152,14 +154,14 @@ bool CastCustomSpellAction::Execute(Event event) else spellName << target->GetName(); - if (!bot->GetTrader() && !botAI->CanCastSpell(spell, target, true, itemTarget)) + if (!bot->GetTrader() && !botAI->GetServices().GetSpellService().CanCastSpell(spell, target, true, itemTarget)) { msg << "Cannot cast " << spellName.str(); - botAI->TellError(msg.str()); + botAI->GetServices().GetChatService().TellError(msg.str()); return false; } - bool result = spell ? botAI->CastSpell(spell, target, itemTarget) : botAI->CastSpell(text, target, itemTarget); + bool result = spell ? botAI->GetServices().GetSpellService().CastSpell(spell, target, itemTarget) : botAI->GetServices().GetSpellService().CastSpell(text, target, itemTarget); if (result) { msg << "Casting " << spellName.str(); @@ -172,12 +174,12 @@ bool CastCustomSpellAction::Execute(Event event) msg << "|cffffff00(x" << (castCount - 1) << " left)|r"; } - botAI->TellMasterNoFacing(msg.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(msg.str()); } else { msg << "Cast " << spellName.str() << " is failed"; - botAI->TellError(msg.str()); + botAI->GetServices().GetChatService().TellError(msg.str()); } return result; @@ -249,12 +251,12 @@ bool CastRandomSpellAction::Execute(Event event) { uint32 spellPriority = GetSpellPriority(spellInfo); - if (target && botAI->CanCastSpell(spellId, target, true)) + if (target && botAI->GetServices().GetSpellService().CanCastSpell(spellId, target, true)) spellList.push_back(std::make_pair(spellId, std::make_pair(spellPriority, target))); if (got && - botAI->CanCastSpell(spellId, got->GetPositionX(), got->GetPositionY(), got->GetPositionZ(), true)) + botAI->GetServices().GetSpellService().CanCastSpell(spellId, got->GetPositionX(), got->GetPositionY(), got->GetPositionZ(), true)) spellList.push_back(std::make_pair(spellId, std::make_pair(spellPriority, got))); - if (botAI->CanCastSpell(spellId, bot, true)) + if (botAI->GetServices().GetSpellService().CanCastSpell(spellId, bot, true)) spellList.push_back(std::make_pair(spellId, std::make_pair(spellPriority, bot))); } } @@ -329,12 +331,12 @@ uint32 CraftRandomItemAction::GetSpellPriority(SpellInfo const* spellInfo) bool CastRandomSpellAction::castSpell(uint32 spellId, WorldObject* wo) { if (wo->GetGUID().IsUnit()) - return botAI->CastSpell(spellId, (Unit*)(wo)); + return botAI->GetServices().GetSpellService().CastSpell(spellId, (Unit*)(wo)); else - return botAI->CastSpell(spellId, wo->GetPositionX(), wo->GetPositionY(), wo->GetPositionZ()); + return botAI->GetServices().GetSpellService().CastSpell(spellId, wo->GetPositionX(), wo->GetPositionY(), wo->GetPositionZ()); } -bool DisEnchantRandomItemAction::Execute(Event event) +bool DisEnchantRandomItemAction::Execute(Event /*event*/) { std::vector items = AI_VALUE2(std::vector, "inventory items", "usage " + std::to_string(ITEM_USAGE_DISENCHANT)); diff --git a/src/Ai/Base/Actions/CastCustomSpellAction.h b/src/Ai/Base/Actions/CastCustomSpellAction.h index ed53b18a5a..6cfc1e6892 100644 --- a/src/Ai/Base/Actions/CastCustomSpellAction.h +++ b/src/Ai/Base/Actions/CastCustomSpellAction.h @@ -21,7 +21,7 @@ class CastCustomSpellAction : public InventoryAction } bool Execute(Event event) override; - virtual std::string const castString(WorldObject* target) { return "cast"; } + virtual std::string const castString(WorldObject* /*target*/) { return "cast"; } protected: bool ncCast = false; @@ -49,7 +49,7 @@ class CastRandomSpellAction : public ListSpellsAction bool isUseful() override { return false; } virtual bool AcceptSpell(SpellInfo const* spellInfo); - virtual uint32 GetSpellPriority(SpellInfo const* spellInfo) { return 1; } + virtual uint32 GetSpellPriority(SpellInfo const* /*spellInfo*/) { return 1; } virtual bool castSpell(uint32 spellId, WorldObject* wo); bool Execute(Event event) override; diff --git a/src/Ai/Base/Actions/ChangeChatAction.cpp b/src/Ai/Base/Actions/ChangeChatAction.cpp index 04d692c219..2b72172bd3 100644 --- a/src/Ai/Base/Actions/ChangeChatAction.cpp +++ b/src/Ai/Base/Actions/ChangeChatAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "ChangeChatAction.h" #include "Event.h" @@ -16,7 +17,7 @@ bool ChangeChatAction::Execute(Event event) { std::ostringstream out; out << "Current chat is " << chat->FormatChat(*context->GetValue("chat")); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } else { @@ -24,7 +25,7 @@ bool ChangeChatAction::Execute(Event event) std::ostringstream out; out << "Chat set to " << chat->FormatChat(parsed); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } return true; diff --git a/src/Ai/Base/Actions/ChangeStrategyAction.cpp b/src/Ai/Base/Actions/ChangeStrategyAction.cpp index d17cd005d3..790de3417d 100644 --- a/src/Ai/Base/Actions/ChangeStrategyAction.cpp +++ b/src/Ai/Base/Actions/ChangeStrategyAction.cpp @@ -4,6 +4,8 @@ */ #include "ChangeStrategyAction.h" +#include "Bot/Core/ManagerRegistry.h" +#include "BotChatService.h" #include "Event.h" #include "PlayerbotRepository.h" @@ -24,10 +26,12 @@ bool ChangeCombatStrategyAction::Execute(Event event) case '+': case '-': case '~': - sPlayerbotRepository->Save(botAI); + sManagerRegistry.GetBotRepository().Save(botAI); break; case '?': break; + default: + break; } } } @@ -45,7 +49,7 @@ bool ChangeNonCombatStrategyAction::Execute(Event event) { if (text.find("loot") != std::string::npos || text.find("gather") != std::string::npos) { - botAI->TellError("You can change any strategy except loot and gather"); + botAI->GetServices().GetChatService().TellError("You can change any strategy except loot and gather"); return false; } } @@ -62,10 +66,12 @@ bool ChangeNonCombatStrategyAction::Execute(Event event) case '+': case '-': case '~': - sPlayerbotRepository->Save(botAI); + sManagerRegistry.GetBotRepository().Save(botAI); break; case '?': break; + default: + break; } } } diff --git a/src/Ai/Base/Actions/ChangeTalentsAction.cpp b/src/Ai/Base/Actions/ChangeTalentsAction.cpp index c50e4a9274..f830d85beb 100644 --- a/src/Ai/Base/Actions/ChangeTalentsAction.cpp +++ b/src/Ai/Base/Actions/ChangeTalentsAction.cpp @@ -3,9 +3,11 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "ChangeTalentsAction.h" #include "AiFactory.h" +#include "Bot/Core/ManagerRegistry.h" #include "ChatHelper.h" #include "Event.h" #include "PlayerbotAIConfig.h" @@ -90,7 +92,7 @@ bool ChangeTalentsAction::Execute(Event event) out << TalentsHelp(); } - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return true; } @@ -108,7 +110,7 @@ std::string ChangeTalentsAction::SpecList() int cls = bot->getClass(); int specFound = 0; std::ostringstream out; - for (int specNo = 0; specNo < MAX_SPECNO; ++specNo) + for (uint32 specNo = 0; specNo < MAX_SPECNO; ++specNo) { if (sPlayerbotAIConfig->premadeSpecName[cls][specNo].size() == 0) { @@ -125,7 +127,7 @@ std::string ChangeTalentsAction::SpecList() } out << specFound << ". " << sPlayerbotAIConfig->premadeSpecName[cls][specNo] << " ("; out << tabCount[0] << "-" << tabCount[1] << "-" << tabCount[2] << ")"; - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); } out << "Total " << specFound << " specs found"; return out.str(); @@ -135,7 +137,7 @@ std::string ChangeTalentsAction::SpecPick(std::string param) { int cls = bot->getClass(); // int specFound = 0; //not used, line marked for removal. - for (int specNo = 0; specNo < MAX_SPECNO; ++specNo) + for (uint32 specNo = 0; specNo < MAX_SPECNO; ++specNo) { if (sPlayerbotAIConfig->premadeSpecName[cls][specNo].size() == 0) { @@ -320,14 +322,14 @@ std::string ChangeTalentsAction::SpecApply(std::string param) // // specLink = ""; // } // else if (paths.size() > 1 && false/*!sPlayerbotAIConfig->autoPickTalents*/ && -// !sRandomPlayerbotMgr->IsRandomBot(bot)) +// !sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) // { // *out << "Found multiple specs: "; // listPremadePaths(paths, out); // } // else // { -// specId = PickPremadePath(paths, sRandomPlayerbotMgr->IsRandomBot(bot))->id; +// specId = PickPremadePath(paths, sManagerRegistry.GetRandomBotManager().IsRandomBot(bot))->id; // TalentSpec newSpec = *GetBestPremadeSpec(specId); // specLink = newSpec.GetTalentLink(); // newSpec.CropTalents(bot->GetLevel()); @@ -368,11 +370,11 @@ std::string ChangeTalentsAction::SpecApply(std::string param) // return nullptr; // } -bool AutoSetTalentsAction::Execute(Event event) +bool AutoSetTalentsAction::Execute(Event /*event*/) { std::ostringstream out; - if (!sPlayerbotAIConfig->autoPickTalents || !sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sPlayerbotAIConfig->autoPickTalents || !sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) return false; if (bot->GetFreeTalentPoints() <= 0) @@ -381,7 +383,7 @@ bool AutoSetTalentsAction::Execute(Event event) PlayerbotFactory factory(bot, bot->GetLevel()); factory.InitTalentsTree(true, true, true); factory.InitPetTalents(); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return true; } diff --git a/src/Ai/Base/Actions/ChatShortcutActions.cpp b/src/Ai/Base/Actions/ChatShortcutActions.cpp index 02c306b8f7..db07bfc701 100644 --- a/src/Ai/Base/Actions/ChatShortcutActions.cpp +++ b/src/Ai/Base/Actions/ChatShortcutActions.cpp @@ -3,7 +3,9 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "ChatShortcutActions.h" +#include "BotRoleService.h" #include "Event.h" #include "Formations.h" @@ -42,7 +44,7 @@ void PositionsResetAction::SetStayPosition(float x, float y, float z) posMap["stay"] = pos; } -bool FollowChatShortcutAction::Execute(Event event) +bool FollowChatShortcutAction::Execute([[maybe_unused]] Event event) { Player* master = GetMaster(); if (!master) @@ -74,7 +76,7 @@ bool FollowChatShortcutAction::Execute(Event event) else { WorldLocation loc = formation->GetLocation(); - if (Formation::IsNullLocation(loc) || loc.GetMapId() == -1) + if (Formation::IsNullLocation(loc) || loc.GetMapId() == uint32(-1)) return false; MovementPriority priority = botAI->GetState() == BOT_STATE_COMBAT ? MovementPriority::MOVEMENT_COMBAT : MovementPriority::MOVEMENT_NORMAL; @@ -89,7 +91,7 @@ bool FollowChatShortcutAction::Execute(Event event) if (moved) { - botAI->TellMaster("Following"); + botAI->GetServices().GetChatService().TellMaster("Following"); return true; } } @@ -101,10 +103,10 @@ bool FollowChatShortcutAction::Execute(Event event) if (bot->isDead()) { bot->ResurrectPlayer(1.0f, false); - botAI->TellMasterNoFacing("Back from the grave!"); + botAI->GetServices().GetChatService().TellMasterNoFacing("Back from the grave!"); } else - botAI->TellMaster("You are too far away from me! I will there soon."); + botAI->GetServices().GetChatService().TellMaster("You are too far away from me! I will there soon."); bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP); bot->TeleportTo(master->GetMapId(), master->GetPositionX(), master->GetPositionY(), master->GetPositionZ(), @@ -112,11 +114,11 @@ bool FollowChatShortcutAction::Execute(Event event) } */ - botAI->TellMaster("Following"); + botAI->GetServices().GetChatService().TellMaster("Following"); return true; } -bool StayChatShortcutAction::Execute(Event event) +bool StayChatShortcutAction::Execute([[maybe_unused]] Event event) { Player* master = GetMaster(); if (!master) @@ -129,11 +131,11 @@ bool StayChatShortcutAction::Execute(Event event) SetReturnPosition(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()); SetStayPosition(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()); - botAI->TellMaster("Staying"); + botAI->GetServices().GetChatService().TellMaster("Staying"); return true; } -bool MoveFromGroupChatShortcutAction::Execute(Event event) +bool MoveFromGroupChatShortcutAction::Execute([[maybe_unused]] Event event) { Player* master = GetMaster(); if (!master) @@ -144,11 +146,11 @@ bool MoveFromGroupChatShortcutAction::Execute(Event event) botAI->ChangeStrategy("+move from group", BOT_STATE_NON_COMBAT); botAI->ChangeStrategy("+move from group", BOT_STATE_COMBAT); - botAI->TellMaster("Moving away from group"); + botAI->GetServices().GetChatService().TellMaster("Moving away from group"); return true; } -bool FleeChatShortcutAction::Execute(Event event) +bool FleeChatShortcutAction::Execute([[maybe_unused]] Event event) { Player* master = GetMaster(); if (!master) @@ -163,15 +165,15 @@ bool FleeChatShortcutAction::Execute(Event event) if (bot->GetMapId() != master->GetMapId() || bot->GetDistance(master) > sPlayerbotAIConfig->sightDistance) { - botAI->TellError("I will not flee with you - too far away"); + botAI->GetServices().GetChatService().TellError("I will not flee with you - too far away"); return true; } - botAI->TellMaster("Fleeing"); + botAI->GetServices().GetChatService().TellMaster("Fleeing"); return true; } -bool GoawayChatShortcutAction::Execute(Event event) +bool GoawayChatShortcutAction::Execute([[maybe_unused]] Event event) { Player* master = GetMaster(); if (!master) @@ -184,11 +186,11 @@ bool GoawayChatShortcutAction::Execute(Event event) ResetReturnPosition(); ResetStayPosition(); - botAI->TellMaster("Running away"); + botAI->GetServices().GetChatService().TellMaster("Running away"); return true; } -bool GrindChatShortcutAction::Execute(Event event) +bool GrindChatShortcutAction::Execute([[maybe_unused]] Event event) { Player* master = GetMaster(); if (!master) @@ -200,17 +202,17 @@ bool GrindChatShortcutAction::Execute(Event event) ResetReturnPosition(); ResetStayPosition(); - botAI->TellMaster("Grinding"); + botAI->GetServices().GetChatService().TellMaster("Grinding"); return true; } -bool TankAttackChatShortcutAction::Execute(Event event) +bool TankAttackChatShortcutAction::Execute([[maybe_unused]] Event event) { Player* master = GetMaster(); if (!master) return false; - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) return false; botAI->Reset(); @@ -220,11 +222,11 @@ bool TankAttackChatShortcutAction::Execute(Event event) ResetReturnPosition(); ResetStayPosition(); - botAI->TellMaster("Attacking"); + botAI->GetServices().GetChatService().TellMaster("Attacking"); return true; } -bool MaxDpsChatShortcutAction::Execute(Event event) +bool MaxDpsChatShortcutAction::Execute([[maybe_unused]] Event event) { Player* master = GetMaster(); if (!master) @@ -236,12 +238,12 @@ bool MaxDpsChatShortcutAction::Execute(Event event) botAI->Reset(); botAI->ChangeStrategy("-threat,-conserve mana,-cast time,+dps debuff,+boost", BOT_STATE_COMBAT); - botAI->TellMaster("Max DPS!"); + botAI->GetServices().GetChatService().TellMaster("Max DPS!"); return true; } -bool BwlChatShortcutAction::Execute(Event event) +bool BwlChatShortcutAction::Execute([[maybe_unused]] Event event) { Player* master = GetMaster(); if (!master) @@ -250,6 +252,6 @@ bool BwlChatShortcutAction::Execute(Event event) botAI->Reset(); botAI->ChangeStrategy("+bwl", BOT_STATE_NON_COMBAT); botAI->ChangeStrategy("+bwl", BOT_STATE_COMBAT); - botAI->TellMasterNoFacing("Add Bwl Strategies!"); + botAI->GetServices().GetChatService().TellMasterNoFacing("Add Bwl Strategies!"); return true; } diff --git a/src/Ai/Base/Actions/CheatAction.cpp b/src/Ai/Base/Actions/CheatAction.cpp index b3f4ec5cf7..448ab062f5 100644 --- a/src/Ai/Base/Actions/CheatAction.cpp +++ b/src/Ai/Base/Actions/CheatAction.cpp @@ -6,6 +6,7 @@ #include "CheatAction.h" #include "Playerbots.h" +#include "BotChatService.h" bool CheatAction::Execute(Event event) { @@ -28,6 +29,8 @@ bool CheatAction::Execute(Event event) case '?': ListCheats(); return true; + default: + break; } } @@ -92,5 +95,5 @@ void CheatAction::ListCheats() out << "[" << GetCheatName(BotCheatMask(cheatMask)) << "]"; } - botAI->TellMasterNoFacing(out); + botAI->GetServices().GetChatService().TellMasterNoFacing(out); } diff --git a/src/Ai/Base/Actions/CheckMailAction.cpp b/src/Ai/Base/Actions/CheckMailAction.cpp index 85df168f58..e33baa5e58 100644 --- a/src/Ai/Base/Actions/CheckMailAction.cpp +++ b/src/Ai/Base/Actions/CheckMailAction.cpp @@ -9,7 +9,7 @@ #include "GuildTaskMgr.h" #include "Playerbots.h" -bool CheckMailAction::Execute(Event event) +bool CheckMailAction::Execute(Event /*event*/) { WorldPacket p; bot->GetSession()->HandleQueryNextMailTime(p); diff --git a/src/Ai/Base/Actions/CheckMountStateAction.cpp b/src/Ai/Base/Actions/CheckMountStateAction.cpp index bf7a3a169a..f3cfa2648d 100644 --- a/src/Ai/Base/Actions/CheckMountStateAction.cpp +++ b/src/Ai/Base/Actions/CheckMountStateAction.cpp @@ -1,3 +1,4 @@ +#include "BotSpellService.h" /* * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it * and/or modify it under version 3 of the License, or (at your option), any later version. @@ -7,6 +8,7 @@ #include "BattleGroundTactics.h" #include "BattlegroundEY.h" #include "BattlegroundWS.h" +#include "BotRoleService.h" #include "Event.h" #include "PlayerbotAI.h" #include "PlayerbotAIConfig.h" @@ -18,7 +20,7 @@ std::unordered_map CheckMountStateAction::mountCache; bool CheckMountStateAction::preferredMountTableChecked = false; -MountData CollectMountData(const Player* bot) +MountData CollectMountData(Player const* bot) { MountData data; for (auto& entry : bot->GetSpellMap()) @@ -58,7 +60,7 @@ MountData CollectMountData(const Player* bot) bool CheckMountStateAction::isUseful() { // Not useful when: - if (botAI->IsInVehicle() || bot->isDead() || bot->HasUnitState(UNIT_STATE_IN_FLIGHT) || + if (botAI->GetServices().GetSpellService().IsInVehicle() || bot->isDead() || bot->HasUnitState(UNIT_STATE_IN_FLIGHT) || !bot->IsOutdoors() || bot->InArena()) return false; @@ -142,7 +144,7 @@ bool CheckMountStateAction::Execute(Event /*event*/) (masterInShapeshiftForm != FORM_TRAVEL && botInShapeshiftForm == FORM_TRAVEL) || (masterInShapeshiftForm != FORM_FLIGHT && botInShapeshiftForm == FORM_FLIGHT && master && !master->IsMounted()) || (masterInShapeshiftForm != FORM_FLIGHT_EPIC && botInShapeshiftForm == FORM_FLIGHT_EPIC && master && !master->IsMounted())) - botAI->RemoveShapeshift(); + botAI->GetServices().GetSpellService().RemoveShapeshift(); if (shouldDismount && bot->IsMounted()) { @@ -189,8 +191,8 @@ bool CheckMountStateAction::Mount() botInShapeshiftForm != FORM_FLIGHT && botInShapeshiftForm != FORM_FLIGHT_EPIC) { - botAI->RemoveShapeshift(); - botAI->RemoveAura("tree of life"); + botAI->GetServices().GetSpellService().RemoveShapeshift(); + botAI->GetServices().GetSpellService().RemoveAura("tree of life"); } if (TryPreferredMount(master)) @@ -243,34 +245,34 @@ bool CheckMountStateAction::TryForms(Player* master, int32 masterMountType, int3 return true; // Check if master is in Travel Form and bot can do the same - if (botAI->CanCastSpell(SPELL_TRAVEL_FORM, bot, true) && + if (botAI->GetServices().GetSpellService().CanCastSpell(SPELL_TRAVEL_FORM, bot, true) && masterInShapeshiftForm == FORM_TRAVEL && botInShapeshiftForm != FORM_TRAVEL) { - botAI->CastSpell(SPELL_TRAVEL_FORM, bot); + botAI->GetServices().GetSpellService().CastSpell(SPELL_TRAVEL_FORM, bot); return true; } // Check if master is in Flight Form or has a flying mount and bot can flight form - if (botAI->CanCastSpell(SPELL_FLIGHT_FORM, bot, true) && + if (botAI->GetServices().GetSpellService().CanCastSpell(SPELL_FLIGHT_FORM, bot, true) && ((masterInShapeshiftForm == FORM_FLIGHT && botInShapeshiftForm != FORM_FLIGHT) || (masterMountType == 1 && masterSpeed == 149))) { - botAI->CastSpell(SPELL_FLIGHT_FORM, bot); + botAI->GetServices().GetSpellService().CastSpell(SPELL_FLIGHT_FORM, bot); // Compensate speedbuff - bot->SetSpeed(MOVE_RUN, 2.5, true); + bot->SetSpeed(MOVE_RUN, 2.5f, true); return true; } // Check if master is in Swift Flight Form or has an epic flying mount and bot can swift flight form - if (botAI->CanCastSpell(SPELL_SWIFT_FLIGHT_FORM, bot, true) && + if (botAI->GetServices().GetSpellService().CanCastSpell(SPELL_SWIFT_FLIGHT_FORM, bot, true) && ((masterInShapeshiftForm == FORM_FLIGHT_EPIC && botInShapeshiftForm != FORM_FLIGHT_EPIC) || (masterMountType == 1 && masterSpeed == 279))) { - botAI->CastSpell(SPELL_SWIFT_FLIGHT_FORM, bot); + botAI->GetServices().GetSpellService().CastSpell(SPELL_SWIFT_FLIGHT_FORM, bot); // Compensate speedbuff - bot->SetSpeed(MOVE_RUN, 3.8, true); + bot->SetSpeed(MOVE_RUN, 3.8f, true); return true; } @@ -358,9 +360,9 @@ bool CheckMountStateAction::TryPreferredMount(Player* master) const bot->StopMoving(); // Check if spell can be cast - for now allow all, even if the bot does not have the actual mount - //if (botAI->CanCastSpell(mountId, botAI->GetBot())) + //if (botAI->GetServices().GetSpellService().CanCastSpell(mountId, botAI->GetBot())) //{ - botAI->CastSpell(chosenMountId, botAI->GetBot()); + botAI->GetServices().GetSpellService().CastSpell(chosenMountId, botAI->GetBot()); return true; //} @@ -368,7 +370,7 @@ bool CheckMountStateAction::TryPreferredMount(Player* master) const return false; } -bool CheckMountStateAction::TryRandomMountFiltered(const std::map>& spells, int32 masterSpeed) const +bool CheckMountStateAction::TryRandomMountFiltered(std::map>& spells, int32 masterSpeed) const { for (auto const& pair : spells) { @@ -387,9 +389,9 @@ bool CheckMountStateAction::TryRandomMountFiltered(const std::mapCanCastSpell(ids[index], bot)) + if (botAI->GetServices().GetSpellService().CanCastSpell(ids[index], bot)) { - botAI->CastSpell(ids[index], bot); + botAI->GetServices().GetSpellService().CastSpell(ids[index], bot); return true; } } @@ -401,7 +403,7 @@ float CheckMountStateAction::CalculateDismountDistance() const { // Warrior bots should dismount far enough to charge (because it's important for generating some initial rage), // a real player would be riding toward enemy mashing the charge key but the bots won't cast charge while mounted. - bool isMelee = PlayerbotAI::IsMelee(bot); + bool isMelee = BotRoleService::IsMeleeStatic(bot); float dismountDistance = isMelee ? sPlayerbotAIConfig->meleeDistance + 2.0f : sPlayerbotAIConfig->spellDistance + 2.0f; return bot->getClass() == CLASS_WARRIOR ? std::max(18.0f, dismountDistance) : dismountDistance; } @@ -411,8 +413,8 @@ float CheckMountStateAction::CalculateMountDistance() const // Mount distance should be >= 21 regardless of class, because when travelling a distance < 21 it takes longer // to cast mount-spell than the time saved from the speed increase. At a distance of 21 both approaches take 3 // seconds: - // 21 / 7 = 21 / 14 + 1.5 = 3 (7 = dismounted speed 14 = epic-mount speed 1.5 = mount-spell cast time) - bool isMelee = PlayerbotAI::IsMelee(bot); + // 21 / 7 = 21 / 14 + 1.5f = 3 (7 = dismounted speed 14 = epic-mount speed 1.5f = mount-spell cast time) + bool isMelee = BotRoleService::IsMeleeStatic(bot); float baseDistance = isMelee ? sPlayerbotAIConfig->meleeDistance + 10.0f : sPlayerbotAIConfig->spellDistance + 10.0f; return std::max(21.0f, baseDistance); } @@ -434,7 +436,7 @@ bool CheckMountStateAction::ShouldDismountForMaster(Player* master) const return !isMasterMounted && bot->IsMounted(); } -int32 CheckMountStateAction::CalculateMasterMountSpeed(Player* master, const MountData& mountData) const +int32 CheckMountStateAction::CalculateMasterMountSpeed(Player* master, MountData const& mountData) const { // Check riding skill and level requirements int32 ridingSkill = bot->GetPureSkillValue(SKILL_RIDING); diff --git a/src/Ai/Base/Actions/CheckMountStateAction.h b/src/Ai/Base/Actions/CheckMountStateAction.h index 9a21838e17..cba387c677 100644 --- a/src/Ai/Base/Actions/CheckMountStateAction.h +++ b/src/Ai/Base/Actions/CheckMountStateAction.h @@ -53,13 +53,13 @@ class CheckMountStateAction : public UseItemAction void Dismount(); bool ShouldFollowMasterMountState(Player* master, bool noAttackers, bool shouldMount) const; bool ShouldDismountForMaster(Player* master) const; - int32 CalculateMasterMountSpeed(Player* master, const MountData& mountData) const; + int32 CalculateMasterMountSpeed(Player* master, MountData const& mountData) const; bool CheckForSwiftMount() const; std::map>> GetAllMountSpells() const; bool TryForms(Player* master, int32 masterMountType, int32 masterSpeed) const; bool TryPreferredMount(Player* master) const; uint32 GetMountType(Player* master) const; - bool TryRandomMountFiltered(const std::map>& spells, int32 masterSpeed) const; + bool TryRandomMountFiltered(std::map>& spells, int32 masterSpeed) const; }; #endif diff --git a/src/Ai/Base/Actions/CheckValuesAction.cpp b/src/Ai/Base/Actions/CheckValuesAction.cpp index 1fc0d4c3a1..8f5f34e4a3 100644 --- a/src/Ai/Base/Actions/CheckValuesAction.cpp +++ b/src/Ai/Base/Actions/CheckValuesAction.cpp @@ -4,6 +4,7 @@ */ #include "CheckValuesAction.h" +#include "BotChatService.h" #include "Event.h" #include "Playerbots.h" @@ -11,11 +12,11 @@ CheckValuesAction::CheckValuesAction(PlayerbotAI* botAI) : Action(botAI, "check values") {} -bool CheckValuesAction::Execute(Event event) +bool CheckValuesAction::Execute(Event /*event*/) { if (botAI->HasStrategy("debug move", BOT_STATE_NON_COMBAT)) { - botAI->Ping(bot->GetPositionX(), bot->GetPositionY()); + botAI->GetServices().GetChatService().Ping(bot->GetPositionX(), bot->GetPositionY()); } if (botAI->HasStrategy("map", BOT_STATE_NON_COMBAT) || botAI->HasStrategy("map full", BOT_STATE_NON_COMBAT)) diff --git a/src/Ai/Base/Actions/ChooseRpgTargetAction.cpp b/src/Ai/Base/Actions/ChooseRpgTargetAction.cpp index a888aa14ee..a8322013ce 100644 --- a/src/Ai/Base/Actions/ChooseRpgTargetAction.cpp +++ b/src/Ai/Base/Actions/ChooseRpgTargetAction.cpp @@ -17,6 +17,7 @@ #include "Util.h" #include "ServerFacade.h" #include "PossibleRpgTargetsValue.h" +#include "BotChatService.h" bool ChooseRpgTargetAction::HasSameTarget(ObjectGuid guid, uint32 max, GuidVector const& nearGuids) { @@ -107,22 +108,17 @@ float ChooseRpgTargetAction::getMaxRelevance(GuidPosition guidP) SET_AI_VALUE(GuidPosition, "rpg target", currentRpgTarget); if (!maxRelevance) - return 0.0; + return 0.0f; - return floor((maxRelevance - 1.0) * 1000.0f); + return floor((maxRelevance - 1.0f) * 1000.0f); } -bool ChooseRpgTargetAction::Execute(Event event) +bool ChooseRpgTargetAction::Execute(Event /*event*/) { //TravelTarget* travelTarget = AI_VALUE(TravelTarget*, "travel target"); //not used, line marked for removal. Player* master = botAI->GetMaster(); GuidPosition masterRpgTarget; - if (master && master != bot && GET_PLAYERBOT_AI(master) && master->GetMapId() == bot->GetMapId() && !master->IsBeingTeleported()) - { - Player* player = botAI->GetMaster(); - //GuidPosition masterRpgTarget = PAI_VALUE(GuidPosition, "rpg target"); //not used, line marked for removal. - } - else + if (!(master && master != bot && GET_PLAYERBOT_AI(master) && master->GetMapId() == bot->GetMapId() && !master->IsBeingTeleported())) master = nullptr; std::unordered_map targets; @@ -213,7 +209,7 @@ bool ChooseRpgTargetAction::Execute(Event event) if (it->second == 0) it = targets.erase(it); //Remove useless targets if there's any good ones - else if (hasGoodRelevance && it->second <= 1.0) + else if (hasGoodRelevance && it->second <= 1.0f) it = targets.erase(it); //Remove useless targets if it's not masters target. else if (!hasGoodRelevance && master && (!masterRpgTarget || it->first != masterRpgTarget)) @@ -264,7 +260,7 @@ bool ChooseRpgTargetAction::Execute(Event event) out << chat->FormatWorldobject(guidP.GetWorldObject()); out << " " << relevances.front(); - botAI->TellMasterNoFacing(out); + botAI->GetServices().GetChatService().TellMasterNoFacing(out); } SET_AI_VALUE(GuidPosition, "rpg target", guidP); @@ -339,7 +335,7 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos) if (!botAI->HasActivePlayerMaster() && distance < 50.0f) { Player* player = groupLeader; - if (groupLeader && !groupLeader->isMoving() || + if ((groupLeader && !groupLeader->isMoving()) || PAI_VALUE(WorldPosition, "last long move").distance(pos) < sPlayerbotAIConfig->reactDistance) return true; } diff --git a/src/Ai/Base/Actions/ChooseTargetActions.cpp b/src/Ai/Base/Actions/ChooseTargetActions.cpp index ab533e7136..653f07a797 100644 --- a/src/Ai/Base/Actions/ChooseTargetActions.cpp +++ b/src/Ai/Base/Actions/ChooseTargetActions.cpp @@ -4,6 +4,7 @@ */ #include "ChooseTargetActions.h" +#include "BotChatService.h" #include "ChooseRpgTargetAction.h" #include "Event.h" @@ -61,7 +62,7 @@ bool AttackAnythingAction::isUseful() return true; } -bool DropTargetAction::Execute(Event event) +bool DropTargetAction::Execute(Event /*event*/) { Unit* target = context->GetValue("current target")->Get(); if (target && target->isDead()) @@ -137,7 +138,7 @@ bool DpsAssistAction::isUseful() return true; } -bool AttackRtiTargetAction::Execute(Event event) +bool AttackRtiTargetAction::Execute(Event /*event*/) { Unit* rtiTarget = AI_VALUE(Unit*, "rti target"); @@ -169,7 +170,7 @@ bool AttackRtiTargetAction::Execute(Event event) } } else - botAI->TellError("I dont see my rti attack target"); + botAI->GetServices().GetChatService().TellError("I dont see my rti attack target"); return false; } diff --git a/src/Ai/Base/Actions/ChooseTravelTargetAction.cpp b/src/Ai/Base/Actions/ChooseTravelTargetAction.cpp index cf6dddd40c..72b6cd5409 100644 --- a/src/Ai/Base/Actions/ChooseTravelTargetAction.cpp +++ b/src/Ai/Base/Actions/ChooseTravelTargetAction.cpp @@ -3,13 +3,14 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "ChooseTravelTargetAction.h" #include "ChatHelper.h" #include "LootObjectStack.h" #include "Playerbots.h" -bool ChooseTravelTargetAction::Execute(Event event) +bool ChooseTravelTargetAction::Execute(Event /*event*/) { // Player* requester = event.getOwner() ? event.getOwner() : GetMaster(); //not used, line marked for removal. @@ -79,7 +80,7 @@ void ChooseTravelTargetAction::getNewTarget(TravelTarget* newTarget, TravelTarge target->setForced(true); std::ostringstream out; out << "Traveling to " << dest->getTitle(); - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); foundTarget = true; } } @@ -233,16 +234,6 @@ void ChooseTravelTargetAction::ReportTravelTarget(TravelTarget* newTarget, Trave Quest const* quest = QuestDestination->GetQuestTemplate(); WorldPosition botLocation(bot); - CreatureTemplate const* cInfo = nullptr; - GameObjectTemplate const* gInfo = nullptr; - - if (destination->getEntry() > 0) - cInfo = sObjectMgr->GetCreatureTemplate(destination->getEntry()); - else - gInfo = sObjectMgr->GetGameObjectTemplate(destination->getEntry() * -1); - - std::string Sub; - if (newTarget->isGroupCopy()) out << "Following group "; else if (oldDestination && oldDestination == destination) @@ -256,7 +247,7 @@ void ChooseTravelTargetAction::ReportTravelTarget(TravelTarget* newTarget, Trave out << " to " << QuestDestination->getTitle(); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } else if (destination->getName() == "RpgTravelDestination") { @@ -284,7 +275,7 @@ void ChooseTravelTargetAction::ReportTravelTarget(TravelTarget* newTarget, Trave out << " to " << RpgDestination->getTitle(); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } else if (destination->getName() == "ExploreTravelDestination") { @@ -305,7 +296,7 @@ void ChooseTravelTargetAction::ReportTravelTarget(TravelTarget* newTarget, Trave out << " to " << ExploreDestination->getTitle(); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } else if (destination->getName() == "GrindTravelDestination") { @@ -326,7 +317,7 @@ void ChooseTravelTargetAction::ReportTravelTarget(TravelTarget* newTarget, Trave out << " to " << GrindDestination->getTitle(); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } else if (destination->getName() == "BossTravelDestination") { @@ -347,13 +338,13 @@ void ChooseTravelTargetAction::ReportTravelTarget(TravelTarget* newTarget, Trave out << " to " << BossDestination->getTitle(); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } else if (destination->getName() == "NullTravelDestination") { if (!oldTarget->getDestination() || oldTarget->getDestination()->getName() != "NullTravelDestination") { - botAI->TellMaster("No where to travel. Idling a bit."); + botAI->GetServices().GetChatService().TellMaster("No where to travel. Idling a bit."); } } } @@ -478,7 +469,7 @@ bool ChooseTravelTargetAction::SetCurrentTarget(TravelTarget* target, TravelTarg return target->isActive(); } -bool ChooseTravelTargetAction::SetQuestTarget(TravelTarget* target, bool onlyCompleted, bool newQuests, bool activeQuests, bool completedQuests) +bool ChooseTravelTargetAction::SetQuestTarget(TravelTarget* target, bool /*onlyCompleted*/, bool newQuests, bool activeQuests, bool completedQuests) { std::vector activeDestinations; std::vector activePoints; @@ -528,7 +519,7 @@ bool ChooseTravelTargetAction::SetQuestTarget(TravelTarget* target, bool onlyCom activeDestinations = sTravelMgr->getQuestTravelDestinations(bot, -1, true, false); //If we really don't find any new quests look futher away. if (botAI->HasStrategy("debug travel", BotState::BOT_STATE_NON_COMBAT)) - botAI->TellMasterNoFacing(std::to_string(activeDestinations.size()) + " quest destinations found."); + botAI->GetServices().GetChatService().TellMasterNoFacing(std::to_string(activeDestinations.size()) + " quest destinations found."); if (!getBestDestination(&activeDestinations, &activePoints)) return false; @@ -823,10 +814,6 @@ char* strstri(char const* haystack, char const* needle); TravelDestination* ChooseTravelTargetAction::FindDestination(Player* bot, std::string const name, bool zones, bool npcs, bool quests, bool mobs, bool bosses) { - PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); - - // AiObjectContext* context = botAI->GetAiObjectContext(); //not used, line marked for removal. - std::vector dests; //Quests @@ -941,7 +928,7 @@ bool ChooseTravelTargetAction::needForQuest(Unit* target) int required = questTemplate->RequiredNpcOrGoCount[j]; int available = questStatus.CreatureOrGOCount[j]; - if (required && available < required && (target->GetEntry() == entry || justCheck)) + if (required && available < required && (target->GetEntry() == static_cast(entry) || justCheck)) return true; } @@ -976,8 +963,8 @@ bool ChooseTravelTargetAction::needForQuest(Unit* target) return false; } -bool ChooseTravelTargetAction::needItemForQuest(uint32 itemId, const Quest* questTemplate, - const QuestStatusData* questStatus) +bool ChooseTravelTargetAction::needItemForQuest(uint32 itemId, Quest const* questTemplate, + QuestStatusData const* questStatus) { for (uint32 i = 0; i < QUEST_OBJECTIVES_COUNT; i++) { diff --git a/src/Ai/Base/Actions/CombatActions.cpp b/src/Ai/Base/Actions/CombatActions.cpp index fe29a11b4f..09d40dddc1 100644 --- a/src/Ai/Base/Actions/CombatActions.cpp +++ b/src/Ai/Base/Actions/CombatActions.cpp @@ -9,10 +9,11 @@ #include "LastMovementValue.h" #include "Playerbots.h" #include "ServerFacade.h" +#include "BotChatService.h" bool SwitchToMeleeAction::Execute(Event event) { - // botAI->TellMasterNoFacing("Switching to melee!"); + // botAI->GetServices().GetChatService().TellMasterNoFacing("Switching to melee!"); return ChangeCombatStrategyAction::Execute(event); } @@ -34,7 +35,7 @@ bool SwitchToMeleeAction::isUseful() bool SwitchToRangedAction::Execute(Event event) { - // botAI->TellMasterNoFacing("Switching to ranged!"); + // botAI->GetServices().GetChatService().TellMasterNoFacing("Switching to ranged!"); return ChangeCombatStrategyAction::Execute(event); } diff --git a/src/Ai/Base/Actions/CustomStrategyEditAction.cpp b/src/Ai/Base/Actions/CustomStrategyEditAction.cpp index a8e83e87c2..0b69a91f15 100644 --- a/src/Ai/Base/Actions/CustomStrategyEditAction.cpp +++ b/src/Ai/Base/Actions/CustomStrategyEditAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "CustomStrategyEditAction.h" #include "CustomStrategy.h" @@ -31,7 +32,7 @@ bool CustomStrategyEditAction::Execute(Event event) bool CustomStrategyEditAction::PrintHelp() { - botAI->TellMaster("=== Custom strategies ==="); + botAI->GetServices().GetChatService().TellMaster("=== Custom strategies ==="); uint32 owner = botAI->GetBot()->GetGUID().GetCounter(); @@ -44,11 +45,11 @@ bool CustomStrategyEditAction::PrintHelp() { Field* fields = result->Fetch(); std::string const name = fields[0].Get(); - botAI->TellMaster(name); + botAI->GetServices().GetChatService().TellMaster(name); } while (result->NextRow()); } - botAI->TellMaster("Usage: cs "); + botAI->GetServices().GetChatService().TellMaster("Usage: cs "); return false; } @@ -56,7 +57,7 @@ bool CustomStrategyEditAction::Print(std::string const name) { std::ostringstream out; out << "=== " << name << " ==="; - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); uint32 owner = botAI->GetBot()->GetGUID().GetCounter(); @@ -139,6 +140,6 @@ bool CustomStrategyEditAction::PrintActionLine(uint32 idx, std::string const com { std::ostringstream out; out << "#" << idx << " " << command; - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); return true; } diff --git a/src/Ai/Base/Actions/DebugAction.cpp b/src/Ai/Base/Actions/DebugAction.cpp index 11a0bf26a0..6b14ca40ac 100644 --- a/src/Ai/Base/Actions/DebugAction.cpp +++ b/src/Ai/Base/Actions/DebugAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "DebugAction.h" #include "ChooseTravelTargetAction.h" @@ -82,13 +83,13 @@ bool DebugAction::Execute(Event event) out << node->getName() << ", "; } - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); return true; } else { - botAI->TellMasterNoFacing("Destination " + destination + " not found."); + botAI->GetServices().GetChatService().TellMasterNoFacing("Destination " + destination + " not found."); return true; } } @@ -100,7 +101,7 @@ bool DebugAction::Execute(Event event) if (!quest) { - botAI->TellMasterNoFacing("Quest " + text.substr(6) + " not found."); + botAI->GetServices().GetChatService().TellMasterNoFacing("Quest " + text.substr(6) + " not found."); return false; } @@ -128,7 +129,7 @@ bool DebugAction::Execute(Event event) break; } - botAI->TellMasterNoFacing(out); + botAI->GetServices().GetChatService().TellMasterNoFacing(out); return true; } @@ -153,7 +154,7 @@ bool DebugAction::Execute(Event event) out << noG << "|" << noT << "|" << noO << " bad."; - botAI->TellMasterNoFacing(out); + botAI->GetServices().GetChatService().TellMasterNoFacing(out); return true; } @@ -186,7 +187,7 @@ bool DebugAction::Execute(Event event) if (q.second->questObjectives.empty()) out << " no O"; } - botAI->TellMasterNoFacing(out); + botAI->GetServices().GetChatService().TellMasterNoFacing(out); } else if (text.find("add node") != std::string::npos) { @@ -201,7 +202,7 @@ bool DebugAction::Execute(Event event) endNode->setLinked(false); } - botAI->TellMasterNoFacing("Node " + name + " created."); + botAI->GetServices().GetChatService().TellMasterNoFacing("Node " + name + " created."); sTravelNodeMap->setHasToGen(); @@ -218,12 +219,12 @@ bool DebugAction::Execute(Event event) if (startNode->isImportant()) { - botAI->TellMasterNoFacing("Node can not be removed."); + botAI->GetServices().GetChatService().TellMasterNoFacing("Node can not be removed."); } sTravelNodeMap->m_nMapMtx.lock(); sTravelNodeMap->removeNode(startNode); - botAI->TellMasterNoFacing("Node removed."); + botAI->GetServices().GetChatService().TellMasterNoFacing("Node removed."); sTravelNodeMap->m_nMapMtx.unlock(); sTravelNodeMap->setHasToGen(); @@ -494,7 +495,7 @@ bool DebugAction::Execute(Event event) out << "effect "; out << effect; - const std::string& Cname = out.str(); + std::string const& Cname = out.str(); wpCreature->Say(Cname.c_str(), LANG_UNIVERSAL, master); } @@ -516,8 +517,8 @@ bool DebugAction::Execute(Event event) botPos.setY(botPos.getY() + (dy - 5) * 5); botPos.setZ(botPos.getHeight()); - Creature* wpCreature = bot->SummonCreature(effect, botPos.getX(), botPos.getY(), botPos.getZ(), 0, - TEMPSUMMON_TIMED_DESPAWN, 10000.0f); + bot->SummonCreature(effect, botPos.getX(), botPos.getY(), botPos.getZ(), 0, + TEMPSUMMON_TIMED_DESPAWN, 10000.0f); } } return true; @@ -752,6 +753,8 @@ bool DebugAction::Execute(Event event) case 2: case 3: break; + default: + break; } } @@ -830,6 +833,8 @@ bool DebugAction::Execute(Event event) case 2: case 3: break; + default: + break; } } @@ -912,7 +917,7 @@ bool DebugAction::Execute(Event event) } std::string const response = botAI->HandleRemoteCommand(text); - botAI->TellMaster(response); + botAI->GetServices().GetChatService().TellMaster(response); return true; } diff --git a/src/Ai/Base/Actions/DelayAction.cpp b/src/Ai/Base/Actions/DelayAction.cpp index 8d47912fe4..c9f00a692e 100644 --- a/src/Ai/Base/Actions/DelayAction.cpp +++ b/src/Ai/Base/Actions/DelayAction.cpp @@ -8,7 +8,7 @@ #include "Event.h" #include "Playerbots.h" -bool DelayAction::Execute(Event event) +bool DelayAction::Execute(Event /*event*/) { uint32 delay = sPlayerbotAIConfig->passiveDelay + sPlayerbotAIConfig->globalCoolDown; diff --git a/src/Ai/Base/Actions/DestroyItemAction.cpp b/src/Ai/Base/Actions/DestroyItemAction.cpp index 0fce4ad701..2b387327dd 100644 --- a/src/Ai/Base/Actions/DestroyItemAction.cpp +++ b/src/Ai/Base/Actions/DestroyItemAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "DestroyItemAction.h" #include "Event.h" @@ -31,7 +32,7 @@ void DestroyItemAction::DestroyItem(FindItemVisitor* visitor) { std::ostringstream out; out << chat->FormatItem(item->GetTemplate()) << " destroyed"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); bot->DestroyItem(item->GetBagSlot(), item->GetSlot(), true); } @@ -39,7 +40,7 @@ void DestroyItemAction::DestroyItem(FindItemVisitor* visitor) bool SmartDestroyItemAction::isUseful() { return !botAI->HasActivePlayerMaster(); } -bool SmartDestroyItemAction::Execute(Event event) +bool SmartDestroyItemAction::Execute(Event /*event*/) { uint8 bagSpace = AI_VALUE(uint8, "bag space"); diff --git a/src/Ai/Base/Actions/DropQuestAction.cpp b/src/Ai/Base/Actions/DropQuestAction.cpp index 48e571eb3e..9bd443b946 100644 --- a/src/Ai/Base/Actions/DropQuestAction.cpp +++ b/src/Ai/Base/Actions/DropQuestAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "DropQuestAction.h" #include "ChatHelper.h" @@ -48,13 +49,13 @@ bool DropQuestAction::Execute(Event event) if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT) || botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) { - const Quest* pQuest = sObjectMgr->GetQuestTemplate(entry); - const std::string text_quest = ChatHelper::FormatQuest(pQuest); + Quest const* pQuest = sObjectMgr->GetQuestTemplate(entry); + std::string const text_quest = ChatHelper::FormatQuest(pQuest); LOG_INFO("playerbots", "{} => Quest [ {} ] removed", bot->GetName(), pQuest->GetTitle()); bot->Say("Quest [ " + text_quest + " ] removed", LANG_UNIVERSAL); } - botAI->TellMaster("Quest removed"); + botAI->GetServices().GetChatService().TellMaster("Quest removed"); return true; } @@ -63,7 +64,7 @@ bool CleanQuestLogAction::Execute(Event event) Player* requester = event.getOwner() ? event.getOwner() : GetMaster(); if (!requester) { - botAI->TellMaster("No event owner detected"); + botAI->GetServices().GetChatService().TellMaster("No event owner detected"); return false; } @@ -75,7 +76,7 @@ bool CleanQuestLogAction::Execute(Event event) // Only output this message if "debug rpg" strategy is enabled if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) { - botAI->TellMaster("Clean Quest Log command received, removing grey/trivial quests..."); + botAI->GetServices().GetChatService().TellMaster("Clean Quest Log command received, removing grey/trivial quests..."); } uint8 botLevel = bot->GetLevel(); // Get bot's level @@ -94,7 +95,7 @@ bool CleanQuestLogAction::Execute(Event event) if (!questId) continue; - const Quest* quest = sObjectMgr->GetQuestTemplate(questId); + Quest const* quest = sObjectMgr->GetQuestTemplate(questId); if (!quest) continue; @@ -127,7 +128,7 @@ bool CleanQuestLogAction::Execute(Event event) // Output only if "debug rpg" strategy is enabled if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) { - botAI->TellMaster("Quest [ " + quest->GetTitle() + " ] will be removed because it is trivial (grey)."); + botAI->GetServices().GetChatService().TellMaster("Quest [ " + quest->GetTitle() + " ] will be removed because it is trivial (grey)."); } // Remove quest @@ -141,14 +142,14 @@ bool CleanQuestLogAction::Execute(Event event) if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) { - const std::string text_quest = ChatHelper::FormatQuest(quest); + std::string const text_quest = ChatHelper::FormatQuest(quest); LOG_INFO("playerbots", "{} => Quest [ {} ] removed", bot->GetName(), quest->GetTitle()); bot->Say("Quest [ " + text_quest + " ] removed", LANG_UNIVERSAL); } if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) { - botAI->TellMaster("Quest [ " + quest->GetTitle() + " ] has been removed."); + botAI->GetServices().GetChatService().TellMaster("Quest [ " + quest->GetTitle() + " ] has been removed."); } } else @@ -156,7 +157,7 @@ bool CleanQuestLogAction::Execute(Event event) // Only output if "debug rpg" strategy is enabled if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) { - botAI->TellMaster("Quest [ " + quest->GetTitle() + " ] is not trivial and will be kept."); + botAI->GetServices().GetChatService().TellMaster("Quest [ " + quest->GetTitle() + " ] is not trivial and will be kept."); } } } @@ -232,11 +233,11 @@ void CleanQuestLogAction::DropQuestType(uint8& numQuest, uint8 wantNum, bool isG if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT) || botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) { - const std::string text_quest = ChatHelper::FormatQuest(quest); + std::string const text_quest = ChatHelper::FormatQuest(quest); LOG_INFO("playerbots", "{} => Quest [ {} ] removed", bot->GetName(), quest->GetTitle()); bot->Say("Quest [ " + text_quest + " ] removed", LANG_UNIVERSAL); } - botAI->TellMaster("Quest removed" + chat->FormatQuest(quest)); + botAI->GetServices().GetChatService().TellMaster("Quest removed" + chat->FormatQuest(quest)); } } diff --git a/src/Ai/Base/Actions/EmoteAction.cpp b/src/Ai/Base/Actions/EmoteAction.cpp index 1770ca2b75..f1c537af0c 100644 --- a/src/Ai/Base/Actions/EmoteAction.cpp +++ b/src/Ai/Base/Actions/EmoteAction.cpp @@ -9,6 +9,7 @@ #include "PlayerbotTextMgr.h" #include "Playerbots.h" #include "ServerFacade.h" +#include "BotChatService.h" std::map EmoteActionBase::emotes; std::map EmoteActionBase::textEmotes; @@ -164,7 +165,7 @@ bool EmoteActionBase::ReceiveEmote(Player* source, uint32 emote, bool verbal) if (botAI->GetMaster() == source) { botAI->ChangeStrategy("-follow,+stay", BOT_STATE_NON_COMBAT); - botAI->TellMasterNoFacing("Fine.. I'll stay right here.."); + botAI->GetServices().GetChatService().TellMasterNoFacing("Fine.. I'll stay right here.."); } break; case TEXT_EMOTE_BECKON: @@ -172,7 +173,7 @@ bool EmoteActionBase::ReceiveEmote(Player* source, uint32 emote, bool verbal) if (botAI->GetMaster() == source) { botAI->ChangeStrategy("+follow", BOT_STATE_NON_COMBAT); - botAI->TellMasterNoFacing("Wherever you go, I'll follow.."); + botAI->GetServices().GetChatService().TellMasterNoFacing("Wherever you go, I'll follow.."); } break; case TEXT_EMOTE_WAVE: @@ -746,7 +747,7 @@ bool EmoteAction::Execute(Event event) if (param.find("sound") == 0) { - return botAI->PlaySound(atoi(param.substr(5).c_str())); + return botAI->GetServices().GetChatService().PlaySound(atoi(param.substr(5).c_str())); } if (!param.empty() && textEmotes.find(param) != textEmotes.end()) @@ -787,7 +788,7 @@ bool EmoteAction::isUseful() return time(nullptr) >= lastEmote; } -bool TalkAction::Execute(Event event) +bool TalkAction::Execute(Event /*event*/) { Unit* target = botAI->GetUnit(AI_VALUE(ObjectGuid, "talk target")); if (!target) @@ -912,6 +913,8 @@ uint32 EmoteActionBase::GetNumberOfEmoteVariants(TextEmotes emote, uint8 Race, u return 3; } + default: + break; } } else if (emote == 305) @@ -950,6 +953,8 @@ uint32 EmoteActionBase::GetNumberOfEmoteVariants(TextEmotes emote, uint8 Race, u return 3; } + default: + break; } } else if (emote == 306) @@ -982,6 +987,8 @@ uint32 EmoteActionBase::GetNumberOfEmoteVariants(TextEmotes emote, uint8 Race, u return 3; } + default: + break; } } else if (emote == TEXT_EMOTE_HELLO) @@ -1014,6 +1021,8 @@ uint32 EmoteActionBase::GetNumberOfEmoteVariants(TextEmotes emote, uint8 Race, u return 3; } + default: + break; } } else if (emote == 323) @@ -1050,6 +1059,8 @@ uint32 EmoteActionBase::GetNumberOfEmoteVariants(TextEmotes emote, uint8 Race, u return 1; } + default: + break; } } else if (emote == 325) @@ -1082,6 +1093,8 @@ uint32 EmoteActionBase::GetNumberOfEmoteVariants(TextEmotes emote, uint8 Race, u return 2; } + default: + break; } } else if (emote == 326) @@ -1108,6 +1121,8 @@ uint32 EmoteActionBase::GetNumberOfEmoteVariants(TextEmotes emote, uint8 Race, u return 3; } + default: + break; } } else if (emote == TEXT_EMOTE_CHEER) @@ -1134,6 +1149,8 @@ uint32 EmoteActionBase::GetNumberOfEmoteVariants(TextEmotes emote, uint8 Race, u return 2; } + default: + break; } } else if (emote == TEXT_EMOTE_OPENFIRE) @@ -1157,6 +1174,8 @@ uint32 EmoteActionBase::GetNumberOfEmoteVariants(TextEmotes emote, uint8 Race, u return 3; } + default: + break; } } else if (emote == TEXT_EMOTE_BYE) @@ -1183,6 +1202,8 @@ uint32 EmoteActionBase::GetNumberOfEmoteVariants(TextEmotes emote, uint8 Race, u return 4; } + default: + break; } } else if (emote == TEXT_EMOTE_NOD) @@ -1215,6 +1236,8 @@ uint32 EmoteActionBase::GetNumberOfEmoteVariants(TextEmotes emote, uint8 Race, u return 4; } + default: + break; } } else if (emote == TEXT_EMOTE_NO) @@ -1241,6 +1264,8 @@ uint32 EmoteActionBase::GetNumberOfEmoteVariants(TextEmotes emote, uint8 Race, u return 3; } + default: + break; } } else if (emote == TEXT_EMOTE_THANK) @@ -1273,6 +1298,8 @@ uint32 EmoteActionBase::GetNumberOfEmoteVariants(TextEmotes emote, uint8 Race, u return 3; } + default: + break; } } else if (emote == TEXT_EMOTE_WELCOME) @@ -1297,6 +1324,8 @@ uint32 EmoteActionBase::GetNumberOfEmoteVariants(TextEmotes emote, uint8 Race, u return 2; return 3; } + default: + break; } } else if (emote == TEXT_EMOTE_CONGRATULATE) @@ -1329,6 +1358,8 @@ uint32 EmoteActionBase::GetNumberOfEmoteVariants(TextEmotes emote, uint8 Race, u return 4; } + default: + break; } } else if (emote == TEXT_EMOTE_FLIRT) @@ -1372,6 +1403,8 @@ uint32 EmoteActionBase::GetNumberOfEmoteVariants(TextEmotes emote, uint8 Race, u return 6; } + default: + break; } } else if (emote == TEXT_EMOTE_JOKE) @@ -1434,6 +1467,8 @@ uint32 EmoteActionBase::GetNumberOfEmoteVariants(TextEmotes emote, uint8 Race, u return 7; } + default: + break; } } diff --git a/src/Ai/Base/Actions/EquipAction.cpp b/src/Ai/Base/Actions/EquipAction.cpp index 9f4a67ca90..465fc5bd65 100644 --- a/src/Ai/Base/Actions/EquipAction.cpp +++ b/src/Ai/Base/Actions/EquipAction.cpp @@ -3,9 +3,11 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "EquipAction.h" #include +#include "Bot/Core/ManagerRegistry.h" #include "Event.h" #include "ItemCountValue.h" #include "ItemUsageValue.h" @@ -13,6 +15,7 @@ #include "Playerbots.h" #include "StatsWeightCalculator.h" #include "ItemPackets.h" +#include "BotItemService.h" bool EquipAction::Execute(Event event) { @@ -38,7 +41,7 @@ uint8 EquipAction::GetSmallestBagSlot() uint32 curSlots = 0; for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag) { - const Bag* const pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag); + Bag const* const pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag); if (pBag) { if (curBag > 0 && curSlots < pBag->GetBagSize()) @@ -66,7 +69,7 @@ void EquipAction::EquipItem(Item* item) { uint8 bagIndex = item->GetBagSlot(); uint8 slot = item->GetSlot(); - const ItemTemplate* itemProto = item->GetTemplate(); + ItemTemplate const* itemProto = item->GetTemplate(); uint32 itemId = itemProto->ItemId; uint8 invType = itemProto->InventoryType; @@ -76,7 +79,7 @@ void EquipAction::EquipItem(Item* item) bot->SetAmmo(itemId); std::ostringstream out; out << "equipping " << chat->FormatItem(itemProto); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return; } @@ -84,8 +87,8 @@ void EquipAction::EquipItem(Item* item) bool equippedBag = false; if (itemProto->Class == ITEM_CLASS_CONTAINER) { - // Attempt to equip as a bag - Bag* pBag = reinterpret_cast(item); + // Attempt to equip as a bag (cast is for type safety, not used directly) + (void)reinterpret_cast(item); uint8 newBagSlot = GetSmallestBagSlot(); if (newBagSlot > 0) { @@ -113,11 +116,11 @@ void EquipAction::EquipItem(Item* item) std::ostringstream out; out << "Equipping " << chat->FormatItem(itemProto) << " in ranged slot"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return; } - uint8 dstSlot = botAI->FindEquipSlot(itemProto, NULL_SLOT, true); + uint8 dstSlot = botAI->GetServices().GetItemService().FindEquipSlot(itemProto, NULL_SLOT, true); // Check if the item is a weapon and whether the bot can dual wield or use Titan Grip bool isWeapon = (itemProto->Class == ITEM_CLASS_WEAPON); @@ -175,7 +178,7 @@ void EquipAction::EquipItem(Item* item) bool mainHandCanGoOff = false; if (mainHandItem) { - const ItemTemplate* mhProto = mainHandItem->GetTemplate(); + ItemTemplate const* mhProto = mainHandItem->GetTemplate(); bool mhIsValidTG = false; if (canTitanGrip && mhProto->InventoryType == INVTYPE_2HWEAPON) { @@ -212,7 +215,7 @@ void EquipAction::EquipItem(Item* item) // Try moving old main hand weapon to offhand if beneficial if (mainHandItem && mainHandCanGoOff && (!offHandItem || mainHandScore > offHandScore)) { - const ItemTemplate* oldMHProto = mainHandItem->GetTemplate(); + ItemTemplate const* oldMHProto = mainHandItem->GetTemplate(); WorldPacket offhandPacket(CMSG_AUTOEQUIP_ITEM_SLOT, 2); ObjectGuid oldMHGuid = mainHandItem->GetGUID(); @@ -223,12 +226,12 @@ void EquipAction::EquipItem(Item* item) std::ostringstream moveMsg; moveMsg << "Main hand upgrade found. Moving " << chat->FormatItem(oldMHProto) << " to offhand"; - botAI->TellMaster(moveMsg); + botAI->GetServices().GetChatService().TellMaster(moveMsg); } std::ostringstream out; out << "Equipping " << chat->FormatItem(itemProto) << " in main hand"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return; } @@ -245,7 +248,7 @@ void EquipAction::EquipItem(Item* item) std::ostringstream out; out << "Equipping " << chat->FormatItem(itemProto) << " in offhand"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return; } else @@ -325,12 +328,12 @@ void EquipAction::EquipItem(Item* item) std::ostringstream out; out << "Equipping " << chat->FormatItem(itemProto); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } bool EquipUpgradesAction::Execute(Event event) { - if (!sPlayerbotAIConfig->autoEquipUpgradeLoot && !sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sPlayerbotAIConfig->autoEquipUpgradeLoot && !sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) return false; if (event.GetSource() == "trade status") @@ -397,7 +400,7 @@ bool EquipUpgradesAction::Execute(Event event) return true; } -bool EquipUpgradeAction::Execute(Event event) +bool EquipUpgradeAction::Execute(Event /*event*/) { CollectItemsVisitor visitor; IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS); diff --git a/src/Ai/Base/Actions/EquipGlyphsAction.cpp b/src/Ai/Base/Actions/EquipGlyphsAction.cpp index 05c064b963..ff76c09111 100644 --- a/src/Ai/Base/Actions/EquipGlyphsAction.cpp +++ b/src/Ai/Base/Actions/EquipGlyphsAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "EquipGlyphsAction.h" #include "Playerbots.h" @@ -106,7 +107,7 @@ bool EquipGlyphsAction::Execute(Event event) std::vector glyphs; if (!CollectGlyphs(itemIds, glyphs)) { - botAI->TellMaster("Usage: glyph equip <6 glyph item IDs> (3 major, 3 minor)."); + botAI->GetServices().GetChatService().TellMaster("Usage: glyph equip <6 glyph item IDs> (3 major, 3 minor)."); return false; } @@ -144,12 +145,12 @@ bool EquipGlyphsAction::Execute(Event event) if (!placed) { - botAI->TellMaster("Not enought empty sockets for all glyphs."); + botAI->GetServices().GetChatService().TellMaster("Not enought empty sockets for all glyphs."); return false; } } - botAI->TellMaster("Glyphs updated."); + botAI->GetServices().GetChatService().TellMaster("Glyphs updated."); // Flag for custom glyphs botAI->GetAiObjectContext()->GetValue("custom_glyphs")->Set(true); diff --git a/src/Ai/Base/Actions/EquipGlyphsAction.h b/src/Ai/Base/Actions/EquipGlyphsAction.h index d2812d8e3c..e7a3202a24 100644 --- a/src/Ai/Base/Actions/EquipGlyphsAction.h +++ b/src/Ai/Base/Actions/EquipGlyphsAction.h @@ -21,7 +21,7 @@ class EquipGlyphsAction : public Action struct GlyphInfo { GlyphPropertiesEntry const* prop; ///< entrée GlyphProperties.dbc - ItemTemplate const* proto; ///< template de l’objet glyphe + ItemTemplate const* proto; ///< template de l’objet glyphe }; private: diff --git a/src/Ai/Base/Actions/FishingAction.cpp b/src/Ai/Base/Actions/FishingAction.cpp index dc431d3425..9074609dc1 100644 --- a/src/Ai/Base/Actions/FishingAction.cpp +++ b/src/Ai/Base/Actions/FishingAction.cpp @@ -4,6 +4,8 @@ */ #include "FishingAction.h" +#include "Bot/Core/ManagerRegistry.h" +#include "BotChatService.h" #include "FishValues.h" #include "Event.h" @@ -18,6 +20,7 @@ #include "PlayerbotTextMgr.h" #include "Playerbots.h" #include "Position.h" +#include "BotSpellService.h" uint32 const FISHING_SPELL = 7620; uint32 const FISHING_POLE = 6256; @@ -34,7 +37,7 @@ static bool IsFishingPole(Item* const item) { if (!item) return false; - const ItemTemplate* proto = item->GetTemplate(); + ItemTemplate const* proto = item->GetTemplate(); return proto && proto->Class == ITEM_CLASS_WEAPON && proto->SubClass == ITEM_SUBCLASS_WEAPON_FISHING_POLE; } @@ -246,7 +249,7 @@ WorldPosition FindFishingHole(PlayerbotAI* botAI) return WorldPosition(); } -bool MoveNearWaterAction::Execute(Event event) +bool MoveNearWaterAction::Execute(Event /*event*/) { WorldPosition landSpot = AI_VALUE(WorldPosition, "fishing spot"); if (landSpot.IsValid()) @@ -292,7 +295,6 @@ bool MoveNearWaterAction::isPossible() // Water spot is out of range, lets look for a spot to move to for the fishing hole. if (distance > MAX_DISTANCE_TO_WATER || distance < MIN_DISTANCE_TO_WATER) { - float angle = bot->GetAngle(fishingHole.GetPositionX(), fishingHole.GetPositionY()); WorldPosition landSpot = FindLandRadialFromPosition(botAI, fishingHole, MIN_DISTANCE_TO_WATER, MAX_DISTANCE_TO_WATER, SEARCH_INCREMENT, fishingSearchWindow, 32); if (landSpot.IsValid()) { @@ -323,7 +325,6 @@ bool MoveNearWaterAction::isPossible() if (!water.IsValid()) return false; - bool hasLOS = bot->IsWithinLOS(water.GetPositionX(), water.GetPositionY(), water.GetPositionZ()); float angle = bot->GetAngle(water.GetPositionX(), water.GetPositionY()); WorldPosition landSpot = FindLandFromPosition(botAI, 0.0f, MAX_DISTANCE_TO_WATER, 1.0f, angle, water, fishingSearchWindow, false); @@ -336,7 +337,7 @@ bool MoveNearWaterAction::isPossible() return false; } -bool EquipFishingPoleAction::Execute(Event event) +bool EquipFishingPoleAction::Execute(Event /*event*/) { if (!_pole) return false; @@ -385,7 +386,7 @@ bool EquipFishingPoleAction::isUseful() } } - if (sRandomPlayerbotMgr->IsRandomBot(bot)) + if (sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) { bot->StoreNewItemInBestSlots(FISHING_POLE, 1); // Try to get a fishing pole return true; @@ -398,7 +399,7 @@ bool EquipFishingPoleAction::isUseful() std::string masterName = master->GetName(); std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( "no_fishing_pole_error", "I don't have a Fishing Pole",{}); - botAI->Whisper(text, masterName); + botAI->GetServices().GetChatService().Whisper(text, masterName); return false; } @@ -426,7 +427,7 @@ bool FishingAction::Execute(Event event) } Position pos = target; - if (!bot->HasInArc(1.0, &pos, 1.0)) + if (!bot->HasInArc(1.0f, &pos, 1.0f)) { float angle = bot->GetAngle(pos.GetPositionX(), pos.GetPositionY()); bot->SetOrientation(angle); @@ -438,7 +439,7 @@ bool FishingAction::Execute(Event event) if (equipAction.isUseful()) return equipAction.Execute(event); - botAI->CastSpell(FISHING_SPELL, bot); + botAI->GetServices().GetSpellService().CastSpell(FISHING_SPELL, bot); botAI->ChangeStrategy("+use bobber", BOT_STATE_NON_COMBAT); return true; @@ -463,7 +464,7 @@ bool UseBobberAction::isUseful() return AI_VALUE(bool, "can use fishing bobber"); } -bool UseBobberAction::Execute(Event event) +bool UseBobberAction::Execute(Event /*event*/) { GuidVector gos = AI_VALUE(GuidVector, "nearest game objects no los"); for (auto const& guid : gos) @@ -485,7 +486,7 @@ bool UseBobberAction::Execute(Event event) return false; } -bool EndMasterFishingAction::Execute(Event event) +bool EndMasterFishingAction::Execute(Event /*event*/) { botAI->ChangeStrategy("-master fishing", BOT_STATE_NON_COMBAT); return true; @@ -503,7 +504,7 @@ bool EndMasterFishingAction::isUseful() return !nearWater.IsValid(); } -bool RemoveBobberStrategyAction::Execute(Event event) +bool RemoveBobberStrategyAction::Execute(Event /*event*/) { botAI->ChangeStrategy("-use bobber", BOT_STATE_NON_COMBAT); return true; diff --git a/src/Ai/Base/Actions/FlagAction.cpp b/src/Ai/Base/Actions/FlagAction.cpp index 023b376d67..9bfb209da9 100644 --- a/src/Ai/Base/Actions/FlagAction.cpp +++ b/src/Ai/Base/Actions/FlagAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "FlagAction.h" #include "Event.h" @@ -10,7 +11,7 @@ bool FlagAction::TellUsage() { - botAI->TellError("Usage: flag cloak/helm/pvp on/set/off/clear/toggle/?"); + botAI->GetServices().GetChatService().TellError("Usage: flag cloak/helm/pvp on/set/off/clear/toggle/?"); return false; } @@ -35,7 +36,7 @@ bool FlagAction::Execute(Event event) std::ostringstream out; out << ss[0] << " flag is " << chat->FormatBoolean(bot->IsPvP()); - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); return true; } @@ -57,6 +58,6 @@ bool FlagAction::Execute(Event event) std::ostringstream out; out << ss[0] << " flag is " << chat->FormatBoolean(!bot->HasFlag(PLAYER_FLAGS, playerFlags)); - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); return true; } diff --git a/src/Ai/Base/Actions/FollowActions.cpp b/src/Ai/Base/Actions/FollowActions.cpp index 2593ea28e5..31f2faca65 100644 --- a/src/Ai/Base/Actions/FollowActions.cpp +++ b/src/Ai/Base/Actions/FollowActions.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "FollowActions.h" #include @@ -15,7 +16,7 @@ #include "ServerFacade.h" #include "SharedDefines.h" -bool FollowAction::Execute(Event event) +bool FollowAction::Execute([[maybe_unused]] Event event) { Formation* formation = AI_VALUE(Formation*, "formation"); std::string const target = formation->GetTargetName(); @@ -28,7 +29,7 @@ bool FollowAction::Execute(Event event) else { WorldLocation loc = formation->GetLocation(); - if (Formation::IsNullLocation(loc) || loc.GetMapId() == -1) + if (Formation::IsNullLocation(loc) || loc.GetMapId() == uint32(-1)) return false; MovementPriority priority = botAI->GetState() == BOT_STATE_COMBAT ? MovementPriority::MOVEMENT_COMBAT : MovementPriority::MOVEMENT_NORMAL; @@ -116,7 +117,7 @@ bool FollowAction::CanDeadFollow(Unit* target) return true; } -bool FleeToGroupLeaderAction::Execute(Event event) +bool FleeToGroupLeaderAction::Execute([[maybe_unused]] Event event) { Unit* fTarget = AI_VALUE(Unit*, "group leader"); bool canFollow = Follow(fTarget); @@ -133,15 +134,15 @@ bool FleeToGroupLeaderAction::Execute(Event event) if (distance < sPlayerbotAIConfig->reactDistance * 3) { if (!urand(0, 3)) - botAI->TellMaster("I am close, wait for me!"); + botAI->GetServices().GetChatService().TellMaster("I am close, wait for me!"); } else if (distance < 1000) { if (!urand(0, 10)) - botAI->TellMaster("I heading to your position."); + botAI->GetServices().GetChatService().TellMaster("I heading to your position."); } else if (!urand(0, 20)) - botAI->TellMaster("I am traveling to your position."); + botAI->GetServices().GetChatService().TellMaster("I am traveling to your position."); botAI->SetNextCheckDelay(3000); diff --git a/src/Ai/Base/Actions/GenericActions.cpp b/src/Ai/Base/Actions/GenericActions.cpp index 184b17715d..87f5f8a29a 100644 --- a/src/Ai/Base/Actions/GenericActions.cpp +++ b/src/Ai/Base/Actions/GenericActions.cpp @@ -3,7 +3,9 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "GenericActions.h" +#include "BotSpellService.h" #include "PlayerbotAI.h" #include "Player.h" #include "Pet.h" @@ -48,13 +50,13 @@ static std::vector disabledPetSpells = { bool MeleeAction::isUseful() { // do not allow if can't attack from vehicle - if (botAI->IsInVehicle() && !botAI->IsInVehicle(false, false, true)) + if (botAI->GetServices().GetSpellService().IsInVehicle() && !botAI->GetServices().GetSpellService().IsInVehicle(false, false, true)) return false; return true; } -bool TogglePetSpellAutoCastAction::Execute(Event event) +bool TogglePetSpellAutoCastAction::Execute(Event /*event*/) { Pet* pet = bot->GetPet(); if (!pet) @@ -83,7 +85,7 @@ bool TogglePetSpellAutoCastAction::Execute(Event event) continue; uint32 spellId = itr->first; - const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo->IsAutocastable()) continue; @@ -114,12 +116,12 @@ bool TogglePetSpellAutoCastAction::Execute(Event event) // Debug message if pet spells have been toggled and debug is enabled if (toggled && sPlayerbotAIConfig->petChatCommandDebug == 1) - botAI->TellMaster("Pet autocast spells have been toggled."); + botAI->GetServices().GetChatService().TellMaster("Pet autocast spells have been toggled."); return toggled; } -bool PetAttackAction::Execute(Event event) +bool PetAttackAction::Execute(Event /*event*/) { Guardian* pet = bot->GetGuardianPet(); if (!pet) @@ -180,7 +182,7 @@ bool SetPetStanceAction::Execute(Event /*event*/) // If there are no controlled pets or guardians, notify the player and exit if (targets.empty()) { - botAI->TellError("You have no pet or guardian pet."); + botAI->GetServices().GetChatService().TellError("You have no pet or guardian pet."); return false; } @@ -222,7 +224,7 @@ bool SetPetStanceAction::Execute(Event /*event*/) // If debug is enabled in config, inform the master of the new stance if (sPlayerbotAIConfig->petChatCommandDebug == 1) - botAI->TellMaster("Pet stance set to " + stanceText + " (applied to all pets/guardians)."); + botAI->GetServices().GetChatService().TellMaster("Pet stance set to " + stanceText + " (applied to all pets/guardians)."); return true; } diff --git a/src/Ai/Base/Actions/GenericBuffUtils.h b/src/Ai/Base/Actions/GenericBuffUtils.h index c893de5976..7bc27b6300 100644 --- a/src/Ai/Base/Actions/GenericBuffUtils.h +++ b/src/Ai/Base/Actions/GenericBuffUtils.h @@ -3,7 +3,8 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ -#pragma once +#ifndef MOD_PLAYERBOTS_AI_BASE_ACTIONS_GENERIC_BUFF_UTILS_H +#define MOD_PLAYERBOTS_AI_BASE_ACTIONS_GENERIC_BUFF_UTILS_H #include #include @@ -61,3 +62,5 @@ namespace ai::chat { }; } } + +#endif // MOD_PLAYERBOTS_AI_BASE_ACTIONS_GENERIC_BUFF_UTILS_H diff --git a/src/Ai/Base/Actions/GenericSpellActions.cpp b/src/Ai/Base/Actions/GenericSpellActions.cpp index af06a457f0..66e5df2259 100644 --- a/src/Ai/Base/Actions/GenericSpellActions.cpp +++ b/src/Ai/Base/Actions/GenericSpellActions.cpp @@ -1,9 +1,11 @@ +#include "BotSpellService.h" /* * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it * and/or modify it under version 3 of the License, or (at your option), any later version. */ #include "GenericSpellActions.h" +#include "BotRoleService.h" #include @@ -20,16 +22,17 @@ #include "Language.h" #include "GenericBuffUtils.h" #include "PlayerbotAI.h" +#include "BotChatService.h" using ai::buff::MakeAuraQualifierForBuff; using ai::buff::UpgradeToGroupIfAppropriate; CastSpellAction::CastSpellAction(PlayerbotAI* botAI, std::string const spell) - : Action(botAI, spell), range(botAI->GetRange("spell")), spell(spell) + : Action(botAI, spell), spell(spell), range(botAI->GetRange("spell")) { } -bool CastSpellAction::Execute(Event event) +bool CastSpellAction::Execute(Event /*event*/) { if (spell == "conjure food" || spell == "conjure water") { @@ -72,15 +75,15 @@ bool CastSpellAction::Execute(Event event) castId = spellInfo->Id; } - return botAI->CastSpell(castId, bot); + return botAI->GetServices().GetSpellService().CastSpell(castId, bot); } - return botAI->CastSpell(spell, GetTarget()); + return botAI->GetServices().GetSpellService().CastSpell(spell, GetTarget()); } bool CastSpellAction::isPossible() { - if (botAI->IsInVehicle() && !botAI->IsInVehicle(false, false, true)) + if (botAI->GetServices().GetSpellService().IsInVehicle() && !botAI->GetServices().GetSpellService().IsInVehicle(false, false, true)) { if (!sPlayerbotAIConfig->logInGroupOnly || (bot->GetGroup() && botAI->HasRealPlayerMaster())) { @@ -103,12 +106,12 @@ bool CastSpellAction::isPossible() } // Spell* currentSpell = bot->GetCurrentSpell(CURRENT_GENERIC_SPELL); //not used, line marked for removal. - return botAI->CanCastSpell(spell, GetTarget()); + return botAI->GetServices().GetSpellService().CanCastSpell(spell, GetTarget()); } bool CastSpellAction::isUseful() { - if (botAI->IsInVehicle() && !botAI->IsInVehicle(false, false, true)) + if (botAI->GetServices().GetSpellService().IsInVehicle() && !botAI->GetServices().GetSpellService().IsInVehicle(false, false, true)) return false; if (spell == "mount" && !bot->IsMounted() && !bot->IsInCombat()) @@ -128,7 +131,7 @@ bool CastSpellAction::isUseful() return false; // float combatReach = bot->GetCombatReach() + spellTarget->GetCombatReach(); - // if (!botAI->IsRanged(bot)) + // if (!BotRoleService::IsRangedStatic(bot)) // combatReach += 4.0f / 3.0f; return spellTarget && @@ -174,10 +177,10 @@ bool CastAuraSpellAction::isUseful() { if (!GetTarget() || !CastSpellAction::isUseful()) return false; - Aura* aura = botAI->GetAura(spell, GetTarget(), isOwner, checkDuration); + Aura* aura = botAI->GetServices().GetSpellService().GetAura(spell, GetTarget(), isOwner, checkDuration); if (!aura) return true; - if (beforeDuration && aura->GetDuration() < beforeDuration) + if (beforeDuration && aura->GetDuration() < static_cast(beforeDuration)) return true; return false; } @@ -192,7 +195,7 @@ bool CastEnchantItemAction::isPossible() { // if (!CastSpellAction::isPossible()) // { - // botAI->TellMasterNoFacing("Impossible: " + spell); + // botAI->GetServices().GetChatService().TellMasterNoFacing("Impossible: " + spell); // return false; // } @@ -200,14 +203,14 @@ bool CastEnchantItemAction::isPossible() // bool ok = AI_VALUE2(Item*, "item for spell", spellId); // Item* item = AI_VALUE2(Item*, "item for spell", spellId); - // botAI->TellMasterNoFacing("spell: " + spell + ", spell id: " + std::to_string(spellId) + " item for spell: " + + // botAI->GetServices().GetChatService().TellMasterNoFacing("spell: " + spell + ", spell id: " + std::to_string(spellId) + " item for spell: " + // std::to_string(ok)); return spellId && AI_VALUE2(Item*, "item for spell", spellId); } CastHealingSpellAction::CastHealingSpellAction(PlayerbotAI* botAI, std::string const spell, uint8 estAmount, HealingManaEfficiency manaEfficiency, bool isOwner) - : CastAuraSpellAction(botAI, spell, isOwner), estAmount(estAmount), manaEfficiency(manaEfficiency) + : CastAuraSpellAction(botAI, spell, isOwner), manaEfficiency(manaEfficiency), estAmount(estAmount) { range = botAI->GetRange("heal"); } @@ -233,14 +236,14 @@ Value* BuffOnPartyAction::GetTargetValue() return context->GetValue("party member without aura", MakeAuraQualifierForBuff(spell)); } -bool BuffOnPartyAction::Execute(Event event) +bool BuffOnPartyAction::Execute(Event /*event*/) { std::string castName = spell; // default = mono auto SendGroupRP = ai::chat::MakeGroupAnnouncer(bot); castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, castName, /*announceOnMissing=*/true, SendGroupRP); - return botAI->CastSpell(castName, GetTarget()); + return botAI->GetServices().GetSpellService().CastSpell(castName, GetTarget()); } // End greater buff fix @@ -261,6 +264,8 @@ CastShootAction::CastShootAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "s case ITEM_SUBCLASS_WEAPON_CROSSBOW: spell += " crossbow"; break; + default: + break; } } } @@ -290,31 +295,31 @@ Value* CastSnareSpellAction::GetTargetValue() { return context->GetValue< Value* CastCrowdControlSpellAction::GetTargetValue() { return context->GetValue("cc target", getName()); } -bool CastCrowdControlSpellAction::Execute(Event event) { return botAI->CastSpell(getName(), GetTarget()); } +bool CastCrowdControlSpellAction::Execute(Event /*event*/) { return botAI->GetServices().GetSpellService().CastSpell(getName(), GetTarget()); } -bool CastCrowdControlSpellAction::isPossible() { return botAI->CanCastSpell(getName(), GetTarget()); } +bool CastCrowdControlSpellAction::isPossible() { return botAI->GetServices().GetSpellService().CanCastSpell(getName(), GetTarget()); } bool CastCrowdControlSpellAction::isUseful() { return true; } std::string const CastProtectSpellAction::GetTargetName() { return "party member to protect"; } -bool CastProtectSpellAction::isUseful() { return GetTarget() && !botAI->HasAura(spell, GetTarget()); } +bool CastProtectSpellAction::isUseful() { return GetTarget() && !botAI->GetServices().GetSpellService().HasAura(spell, GetTarget()); } bool CastVehicleSpellAction::isPossible() { uint32 spellId = AI_VALUE2(uint32, "vehicle spell id", spell); - return botAI->CanCastVehicleSpell(spellId, GetTarget()); + return botAI->GetServices().GetSpellService().CanCastVehicleSpell(spellId, GetTarget()); } -bool CastVehicleSpellAction::isUseful() { return botAI->IsInVehicle(false, true); } +bool CastVehicleSpellAction::isUseful() { return botAI->GetServices().GetSpellService().IsInVehicle(false, true); } -bool CastVehicleSpellAction::Execute(Event event) +bool CastVehicleSpellAction::Execute(Event /*event*/) { uint32 spellId = AI_VALUE2(uint32, "vehicle spell id", spell); - return botAI->CastVehicleSpell(spellId, GetTarget()); + return botAI->GetServices().GetSpellService().CastVehicleSpell(spellId, GetTarget()); } -bool UseTrinketAction::Execute(Event event) +bool UseTrinketAction::Execute(Event /*event*/) { Item* trinket1 = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_TRINKET1); @@ -350,7 +355,7 @@ bool UseTrinketAction::UseTrinket(Item* item) if (item->GetTemplate()->Spells[i].SpellId > 0 && item->GetTemplate()->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE) { spellId = item->GetTemplate()->Spells[i].SpellId; - const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo || !spellInfo->IsPositive()) return false; @@ -358,7 +363,7 @@ bool UseTrinketAction::UseTrinket(Item* item) bool applyAura = false; for (int i = 0; i < MAX_SPELL_EFFECTS; i++) { - const SpellEffectInfo& effectInfo = spellInfo->Effects[i]; + SpellEffectInfo const& effectInfo = spellInfo->Effects[i]; if (effectInfo.Effect == SPELL_EFFECT_APPLY_AURA) { applyAura = true; @@ -378,7 +383,7 @@ bool UseTrinketAction::UseTrinket(Item* item) // This will lead to multiple hundreds of entries in m_appliedAuras -> Once killing an enemy -> Big diff time spikes if (spellProcFlag != 0) return false; - if (!botAI->CanCastSpell(spellId, bot, false)) + if (!botAI->GetServices().GetSpellService().CanCastSpell(spellId, bot, false)) { return false; } diff --git a/src/Ai/Base/Actions/GiveItemAction.cpp b/src/Ai/Base/Actions/GiveItemAction.cpp index 6e2d6744ec..31c5c9d7f9 100644 --- a/src/Ai/Base/Actions/GiveItemAction.cpp +++ b/src/Ai/Base/Actions/GiveItemAction.cpp @@ -5,13 +5,14 @@ #include "GiveItemAction.h" +#include "Bot/Core/ManagerRegistry.h" #include "Event.h" #include "ItemCountValue.h" #include "Playerbots.h" std::vector split(std::string const s, char delim); -bool GiveItemAction::Execute(Event event) +bool GiveItemAction::Execute(Event /*event*/) { Unit* target = GetTarget(); if (!target) @@ -28,7 +29,6 @@ bool GiveItemAction::Execute(Event event) if (receiverAi->GetAiObjectContext()->GetValue("item count", item)->Get()) return true; - bool moved = false; std::vector items = InventoryAction::parseItems(item, ITERATE_ITEMS_IN_BAGS); for (Item* item : items) { @@ -42,7 +42,6 @@ bool GiveItemAction::Execute(Event event) bot->MoveItemFromInventory(item->GetBagSlot(), item->GetSlot(), true); item->SetOwnerGUID(target->GetGUID()); receiver->MoveItemToInventory(dest, item, true); - moved = true; std::ostringstream out; out << "Got " << chat->FormatItem(item->GetTemplate(), item->GetCount()) << " from " << bot->GetName(); @@ -74,7 +73,7 @@ bool GiveFoodAction::isUseful() if (!GetTarget()) return false; - bool isRandomBot = GetTarget()->IsPlayer() && sRandomPlayerbotMgr->IsRandomBot((Player*)GetTarget()); + bool isRandomBot = GetTarget()->IsPlayer() && sManagerRegistry.GetRandomBotManager().IsRandomBot((Player*)GetTarget()); return !isRandomBot || (isRandomBot && !botAI->HasCheat(BotCheatMask::food)); } @@ -86,7 +85,7 @@ bool GiveWaterAction::isUseful() if (!GetTarget()) return false; - bool isRandomBot = GetTarget()->IsPlayer() && sRandomPlayerbotMgr->IsRandomBot((Player*)GetTarget()); + bool isRandomBot = GetTarget()->IsPlayer() && sManagerRegistry.GetRandomBotManager().IsRandomBot((Player*)GetTarget()); return !isRandomBot || (isRandomBot && !botAI->HasCheat(BotCheatMask::food)); } diff --git a/src/Ai/Base/Actions/GoAction.cpp b/src/Ai/Base/Actions/GoAction.cpp index 1b8cd62a2a..e1567b0d5d 100644 --- a/src/Ai/Base/Actions/GoAction.cpp +++ b/src/Ai/Base/Actions/GoAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "GoAction.h" #include "ChooseTravelTargetAction.h" @@ -31,7 +32,7 @@ bool GoAction::Execute(Event event) std::ostringstream out; out << "I am at " << x << "," << y; - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); return true; } @@ -54,13 +55,13 @@ bool GoAction::Execute(Event event) std::ostringstream out; out << "Traveling to " << dest->getTitle(); - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); return true; } else { - botAI->TellMasterNoFacing("Clearing travel target"); + botAI->GetServices().GetChatService().TellMasterNoFacing("Clearing travel target"); target->setTarget(sTravelMgr->nullTravelDestination, sTravelMgr->nullWorldPosition); target->setForced(false); return true; @@ -78,13 +79,13 @@ bool GoAction::Execute(Event event) if (sServerFacade->IsDistanceGreaterThan(sServerFacade->GetDistance2d(bot, go), sPlayerbotAIConfig->reactDistance)) { - botAI->TellError("It is too far away"); + botAI->GetServices().GetChatService().TellError("It is too far away"); return false; } std::ostringstream out; out << "Moving to " << ChatHelper::FormatGameobject(go); - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); return MoveNear(bot->GetMapId(), go->GetPositionX(), go->GetPositionY(), go->GetPositionZ() + 0.5f, sPlayerbotAIConfig->followDistance); } @@ -104,7 +105,7 @@ bool GoAction::Execute(Event event) { std::ostringstream out; out << "Moving to " << unit->GetName(); - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); return MoveNear(bot->GetMapId(), unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ() + 0.5f, sPlayerbotAIConfig->followDistance); } @@ -154,7 +155,7 @@ bool GoAction::Execute(Event event) CreateWp(bot, i.x, i.y, i.z, 0.f, 11144); } - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } if (bot->IsWithinLOS(x, y, z)) @@ -179,20 +180,20 @@ bool GoAction::Execute(Event event) if (sServerFacade->IsDistanceGreaterThan(sServerFacade->GetDistance2d(bot, x, y), sPlayerbotAIConfig->reactDistance)) { - botAI->TellMaster("It is too far away"); + botAI->GetServices().GetChatService().TellMaster("It is too far away"); return false; } if (map->IsInWater(bot->GetPhaseMask(), x, y, z, bot->GetCollisionHeight())) { - botAI->TellError("It is in water"); + botAI->GetServices().GetChatService().TellError("It is in water"); return false; } float ground = map->GetHeight(x, y, z + 0.5f); if (ground <= INVALID_HEIGHT) { - botAI->TellError("I can't go there"); + botAI->GetServices().GetChatService().TellError("I can't go there"); return false; } @@ -201,7 +202,7 @@ bool GoAction::Execute(Event event) std::ostringstream out; out << "Moving to " << x1 << "," << y1; - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); return MoveNear(bot->GetMapId(), x, y, z + 0.5f, sPlayerbotAIConfig->followDistance); } @@ -212,16 +213,16 @@ bool GoAction::Execute(Event event) if (sServerFacade->IsDistanceGreaterThan(sServerFacade->GetDistance2d(bot, pos.x, pos.y), sPlayerbotAIConfig->reactDistance)) { - botAI->TellError("It is too far away"); + botAI->GetServices().GetChatService().TellError("It is too far away"); return false; } std::ostringstream out; out << "Moving to position " << param; - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); return MoveNear(bot->GetMapId(), pos.x, pos.y, pos.z + 0.5f, sPlayerbotAIConfig->followDistance); } - botAI->TellMaster("Whisper 'go x,y', 'go [game object]', 'go unit' or 'go position' and I will go there"); + botAI->GetServices().GetChatService().TellMaster("Whisper 'go x,y', 'go [game object]', 'go unit' or 'go position' and I will go there"); return false; } diff --git a/src/Ai/Base/Actions/GossipHelloAction.cpp b/src/Ai/Base/Actions/GossipHelloAction.cpp index 9c5083d803..fa704d85ca 100644 --- a/src/Ai/Base/Actions/GossipHelloAction.cpp +++ b/src/Ai/Base/Actions/GossipHelloAction.cpp @@ -8,6 +8,7 @@ #include "Event.h" #include "GossipDef.h" #include "Playerbots.h" +#include "BotChatService.h" bool GossipHelloAction::Execute(Event event) { @@ -45,11 +46,11 @@ void GossipHelloAction::TellGossipText(uint32 textId) { std::string const text0 = text->Options[i].Text_0; if (!text0.empty()) - botAI->TellMasterNoFacing(text0); + botAI->GetServices().GetChatService().TellMasterNoFacing(text0); std::string const text1 = text->Options[i].Text_1; if (!text1.empty()) - botAI->TellMasterNoFacing(text1); + botAI->GetServices().GetChatService().TellMasterNoFacing(text1); } } } @@ -73,7 +74,7 @@ void GossipHelloAction::TellGossipMenus() GossipMenuItem const* item = &(iter->second); std::ostringstream out; out << "[" << iter->first << "] " << item->Message; - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); } } @@ -83,7 +84,7 @@ bool GossipHelloAction::ProcessGossip(int32 menuToSelect, bool silent) if (menuToSelect != -1 && !menu.GetItem(menuToSelect)) { if (!silent) - botAI->TellError("Unknown gossip option"); + botAI->GetServices().GetChatService().TellError("Unknown gossip option"); return false; } @@ -131,14 +132,14 @@ bool GossipHelloAction::Execute(ObjectGuid guid, int32 menuToSelect, bool silent { std::ostringstream out; out << "--- " << pCreature->GetName() << " ---"; - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); TellGossipMenus(); } } else if (!bot->PlayerTalkClass) { if (!silent) - botAI->TellError("I need to talk first"); + botAI->GetServices().GetChatService().TellError("I need to talk first"); return false; } else diff --git a/src/Ai/Base/Actions/GreetAction.cpp b/src/Ai/Base/Actions/GreetAction.cpp index 87bf2c5ffb..e16af60da9 100644 --- a/src/Ai/Base/Actions/GreetAction.cpp +++ b/src/Ai/Base/Actions/GreetAction.cpp @@ -4,13 +4,14 @@ */ #include "GreetAction.h" +#include "BotChatService.h" #include "Event.h" #include "Playerbots.h" GreetAction::GreetAction(PlayerbotAI* botAI) : Action(botAI, "greet") {} -bool GreetAction::Execute(Event event) +bool GreetAction::Execute(Event /*event*/) { ObjectGuid guid = AI_VALUE(ObjectGuid, "new player nearby"); if (!guid || !guid.IsPlayer()) @@ -26,7 +27,7 @@ bool GreetAction::Execute(Event event) ObjectGuid oldSel = bot->GetTarget(); bot->SetTarget(guid); // bot->HandleEmote(EMOTE_ONESHOT_WAVE); - botAI->PlayEmote(TEXT_EMOTE_HELLO); + botAI->GetServices().GetChatService().PlayEmote(TEXT_EMOTE_HELLO); bot->SetTarget(oldSel); GuidSet& alreadySeenPlayers = botAI->GetAiObjectContext()->GetValue("already seen players")->Get(); diff --git a/src/Ai/Base/Actions/GuildAcceptAction.cpp b/src/Ai/Base/Actions/GuildAcceptAction.cpp index cd7635a97b..8008ecabb1 100644 --- a/src/Ai/Base/Actions/GuildAcceptAction.cpp +++ b/src/Ai/Base/Actions/GuildAcceptAction.cpp @@ -4,6 +4,7 @@ */ #include "GuildAcceptAction.h" +#include "BotChatService.h" #include "Event.h" #include "GuildPackets.h" @@ -28,17 +29,17 @@ bool GuildAcceptAction::Execute(Event event) uint32 guildId = inviter->GetGuildId(); if (!guildId) { - botAI->TellError("You are not in a guild!"); + botAI->GetServices().GetChatService().TellError("You are not in a guild!"); accept = false; } else if (bot->GetGuildId()) { - botAI->TellError("Sorry, I am in a guild already"); + botAI->GetServices().GetChatService().TellError("Sorry, I am in a guild already"); accept = false; } else if (!botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_INVITE, false, inviter, true)) { - botAI->TellError("Sorry, I don't want to join your guild :("); + botAI->GetServices().GetChatService().TellError("Sorry, I don't want to join your guild :("); accept = false; } diff --git a/src/Ai/Base/Actions/GuildBankAction.cpp b/src/Ai/Base/Actions/GuildBankAction.cpp index 2d9c74ff69..0559c15dd6 100644 --- a/src/Ai/Base/Actions/GuildBankAction.cpp +++ b/src/Ai/Base/Actions/GuildBankAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "GuildBankAction.h" #include "GuildMgr.h" @@ -16,7 +17,7 @@ bool GuildBankAction::Execute(Event event) if (!bot->GetGuildId() || (GetMaster() && GetMaster()->GetGuildId() != bot->GetGuildId())) { - botAI->TellMaster("I'm not in your guild!"); + botAI->GetServices().GetChatService().TellMaster("I'm not in your guild!"); return false; } @@ -30,7 +31,7 @@ bool GuildBankAction::Execute(Event event) return Execute(text, go); } - botAI->TellMaster("Cannot find the guild bank nearby"); + botAI->GetServices().GetChatService().TellMaster("Cannot find the guild bank nearby"); return false; } @@ -52,7 +53,7 @@ bool GuildBankAction::Execute(std::string const text, GameObject* bank) return result; } -bool GuildBankAction::MoveFromCharToBank(Item* item, GameObject* bank) +bool GuildBankAction::MoveFromCharToBank(Item* item, GameObject* /*bank*/) { uint32 playerSlot = item->GetSlot(); uint32 playerBag = item->GetBagSlot(); @@ -72,7 +73,7 @@ bool GuildBankAction::MoveFromCharToBank(Item* item, GameObject* bank) guild->SwapItemsWithInventory(bot, false, 0, 255, playerBag, playerSlot, 0); } - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return true; } diff --git a/src/Ai/Base/Actions/GuildCreateActions.cpp b/src/Ai/Base/Actions/GuildCreateActions.cpp index 075b28cac3..e446771ccd 100644 --- a/src/Ai/Base/Actions/GuildCreateActions.cpp +++ b/src/Ai/Base/Actions/GuildCreateActions.cpp @@ -14,10 +14,9 @@ #include "ServerFacade.h" #include "SharedDefines.h" // GOLD -bool BuyPetitionAction::Execute(Event event) +bool BuyPetitionAction::Execute(Event /*event*/) { GuidVector vendors = botAI->GetAiObjectContext()->GetValue("nearest npcs")->Get(); - bool vendored = false, result = false; for (GuidVector::iterator i = vendors.begin(); i != vendors.end(); ++i) { ObjectGuid vendorguid = *i; @@ -97,7 +96,6 @@ bool BuyPetitionAction::canBuyPetition(Player* bot) bool PetitionOfferAction::Execute(Event event) { - uint32 petitionEntry = 5863; // GUILD_CHARTER std::vector petitions = AI_VALUE2(std::vector, "inventory items", chat->FormatQItem(5863)); if (petitions.empty()) @@ -152,7 +150,7 @@ bool PetitionOfferAction::Execute(Event event) bool PetitionOfferAction::isUseful() { return !bot->GetGuildId(); } -bool PetitionOfferNearbyAction::Execute(Event event) +bool PetitionOfferNearbyAction::Execute(Event /*event*/) { uint32 found = 0; @@ -209,10 +207,9 @@ bool PetitionOfferNearbyAction::isUseful() AI_VALUE(uint8, "petition signs") < sWorld->getIntConfig(CONFIG_MIN_PETITION_SIGNS); } -bool PetitionTurnInAction::Execute(Event event) +bool PetitionTurnInAction::Execute(Event /*event*/) { GuidVector vendors = botAI->GetAiObjectContext()->GetValue("nearest npcs")->Get(); - bool vendored = false, result = false; std::vector petitions = AI_VALUE2(std::vector, "inventory items", chat->FormatQItem(5863)); if (petitions.empty()) @@ -297,7 +294,7 @@ bool PetitionTurnInAction::isUseful() !context->GetValue("travel target")->Get()->isTraveling(); } -bool BuyTabardAction::Execute(Event event) +bool BuyTabardAction::Execute(Event /*event*/) { bool canBuy = botAI->DoSpecificAction("buy", Event("buy tabard", "Hitem:5976:")); if (canBuy && AI_VALUE2(uint32, "item count", chat->FormatQItem(5976))) diff --git a/src/Ai/Base/Actions/GuildManagementActions.cpp b/src/Ai/Base/Actions/GuildManagementActions.cpp index f00a955e7c..728a0d103e 100644 --- a/src/Ai/Base/Actions/GuildManagementActions.cpp +++ b/src/Ai/Base/Actions/GuildManagementActions.cpp @@ -4,6 +4,8 @@ */ #include "GuildManagementActions.h" +#include "Bot/Core/ManagerRegistry.h" +#include "BotChatService.h" #include "GuildMgr.h" #include "GuildPackets.h" @@ -128,7 +130,7 @@ bool GuildRemoveAction::PlayerIsValid(Player* member) return member->GetGuildId() == bot->GetGuildId() && GetRankId(bot) < GetRankId(member); }; -bool GuildManageNearbyAction::Execute(Event event) +bool GuildManageNearbyAction::Execute(Event /*event*/) { uint32 found = 0; @@ -149,7 +151,6 @@ bool GuildManageNearbyAction::Execute(Event event) // Promote or demote nearby members based on chance. if (player->GetGuildId() && player->GetGuildId() == bot->GetGuildId()) { - Guild::Member* member = guild->GetMember(player->GetGUID()); uint32 dCount = AI_VALUE(uint32, "death count"); if (!urand(0, 30) && dCount < 2 && guild->GetRankRights(botMember->GetRankId()) & GR_RIGHT_PROMOTE) @@ -193,7 +194,7 @@ bool GuildManageNearbyAction::Execute(Event event) if (botAi->GetGuilderType() == GuilderType::SOLO && !botAi->HasRealPlayerMaster()) //Do not invite solo players. continue; - if (botAi->HasActivePlayerMaster() && !sRandomPlayerbotMgr->IsRandomBot(player)) //Do not invite alts of active players. + if (botAi->HasActivePlayerMaster() && !sManagerRegistry.GetRandomBotManager().IsRandomBot(player)) //Do not invite alts of active players. continue; } @@ -202,7 +203,7 @@ bool GuildManageNearbyAction::Execute(Event event) if (!sameGroup && sServerFacade->GetDistance2d(bot, player) > sPlayerbotAIConfig->spellDistance) continue; - if (sPlayerbotAIConfig->inviteChat && (sRandomPlayerbotMgr->IsRandomBot(bot) || !botAI->HasActivePlayerMaster())) + if (sPlayerbotAIConfig->inviteChat && (sManagerRegistry.GetRandomBotManager().IsRandomBot(bot) || !botAI->HasActivePlayerMaster())) { /* std::map placeholders; placeholders["%name"] = player->GetName(); @@ -299,7 +300,7 @@ bool GuildLeaveAction::Execute(Event event) Player* owner = event.getOwner(); if (owner && !botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_INVITE, false, owner, true)) { - botAI->TellError("Sorry, I am happy in my guild :)"); + botAI->GetServices().GetChatService().TellError("Sorry, I am happy in my guild :)"); return false; } diff --git a/src/Ai/Base/Actions/HelpAction.cpp b/src/Ai/Base/Actions/HelpAction.cpp index 3dbe1d6e4b..9685ce9d81 100644 --- a/src/Ai/Base/Actions/HelpAction.cpp +++ b/src/Ai/Base/Actions/HelpAction.cpp @@ -4,6 +4,7 @@ */ #include "HelpAction.h" +#include "BotChatService.h" #include "ChatActionContext.h" #include "Event.h" @@ -13,7 +14,7 @@ HelpAction::HelpAction(PlayerbotAI* botAI) : Action(botAI, "help") { chatContext HelpAction::~HelpAction() { delete chatContext; } -bool HelpAction::Execute(Event event) +bool HelpAction::Execute(Event /*event*/) { TellChatCommands(); TellStrategies(); @@ -26,7 +27,7 @@ void HelpAction::TellChatCommands() out << "Whisper any of: "; out << CombineSupported(chatContext->supports()); out << ", [item], [quest] or [object] link"; - botAI->TellError(out.str()); + botAI->GetServices().GetChatService().TellError(out.str()); } void HelpAction::TellStrategies() @@ -34,7 +35,7 @@ void HelpAction::TellStrategies() std::ostringstream out; out << "Possible strategies (co/nc/dead commands): "; out << CombineSupported(botAI->GetAiObjectContext()->GetSupportedStrategies()); - botAI->TellError(out.str()); + botAI->GetServices().GetChatService().TellError(out.str()); } std::string const HelpAction::CombineSupported(std::set commands) diff --git a/src/Ai/Base/Actions/HireAction.cpp b/src/Ai/Base/Actions/HireAction.cpp index f8b3653964..0f7a4fe787 100644 --- a/src/Ai/Base/Actions/HireAction.cpp +++ b/src/Ai/Base/Actions/HireAction.cpp @@ -3,18 +3,20 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "HireAction.h" +#include "Bot/Core/ManagerRegistry.h" #include "Event.h" #include "Playerbots.h" -bool HireAction::Execute(Event event) +bool HireAction::Execute(Event /*event*/) { Player* master = GetMaster(); if (!master) return false; - if (!sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) return false; uint32 account = master->GetSession()->GetAccountId(); @@ -29,13 +31,13 @@ bool HireAction::Execute(Event event) if (charCount >= 10) { - botAI->TellMaster("You already have the maximum number of characters"); + botAI->GetServices().GetChatService().TellMaster("You already have the maximum number of characters"); return false; } if (bot->GetLevel() > master->GetLevel()) { - botAI->TellMaster("You cannot hire higher level characters than you"); + botAI->GetServices().GetChatService().TellMaster("You cannot hire higher level characters than you"); return false; } @@ -47,14 +49,14 @@ bool HireAction::Execute(Event event) std::ostringstream out; out << "You cannot hire me - I barely know you. Make sure you have at least " << chat->formatMoney(moneyReq) << " as a trade discount"; - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); return false; } - botAI->TellMaster("I will join you at your next relogin"); + botAI->GetServices().GetChatService().TellMaster("I will join you at your next relogin"); bot->SetMoney(moneyReq); - sRandomPlayerbotMgr->Remove(bot); + sManagerRegistry.GetRandomBotManager().Remove(bot); CharacterDatabase.Execute("UPDATE characters SET account = {} WHERE guid = {}", account, bot->GetGUID().GetCounter()); diff --git a/src/Ai/Base/Actions/ImbueAction.cpp b/src/Ai/Base/Actions/ImbueAction.cpp index 8c151ef8a6..2f1f09269b 100644 --- a/src/Ai/Base/Actions/ImbueAction.cpp +++ b/src/Ai/Base/Actions/ImbueAction.cpp @@ -4,13 +4,15 @@ */ #include "ImbueAction.h" +#include "BotRoleService.h" #include "Event.h" #include "Playerbots.h" +#include "BotItemService.h" ImbueWithPoisonAction::ImbueWithPoisonAction(PlayerbotAI* botAI) : Action(botAI, "apply poison") {} -bool ImbueWithPoisonAction::Execute(Event event) +bool ImbueWithPoisonAction::Execute(Event /*event*/) { if (bot->IsInCombat()) return false; @@ -22,12 +24,12 @@ bool ImbueWithPoisonAction::Execute(Event event) if (bot->getStandState() != UNIT_STAND_STATE_STAND) bot->SetStandState(UNIT_STAND_STATE_STAND); - static const std::vector prioritizedInstantPoisons = { + static std::vector const prioritizedInstantPoisons = { INSTANT_POISON_IX, INSTANT_POISON_VIII, INSTANT_POISON_VII, INSTANT_POISON_VI, INSTANT_POISON_V, INSTANT_POISON_IV, INSTANT_POISON_III, INSTANT_POISON_II, INSTANT_POISON }; - static const std::vector prioritizedDeadlyPoisons = { + static std::vector const prioritizedDeadlyPoisons = { DEADLY_POISON_IX, DEADLY_POISON_VIII, DEADLY_POISON_VII, DEADLY_POISON_VI, DEADLY_POISON_V, DEADLY_POISON_IV, DEADLY_POISON_III, DEADLY_POISON_II, DEADLY_POISON }; @@ -36,14 +38,14 @@ bool ImbueWithPoisonAction::Execute(Event event) Item* deadlyPoison = nullptr; for (auto id : prioritizedDeadlyPoisons) { - deadlyPoison = botAI->FindConsumable(id); + deadlyPoison = botAI->GetServices().GetItemService().FindConsumable(id); if (deadlyPoison) break; } Item* instantPoison = nullptr; for (auto id : prioritizedInstantPoisons) { - instantPoison = botAI->FindConsumable(id); + instantPoison = botAI->GetServices().GetItemService().FindConsumable(id); if (instantPoison) break; } @@ -67,7 +69,7 @@ bool ImbueWithPoisonAction::Execute(Event event) if (poison) { - botAI->ImbueItem(poison, EQUIPMENT_SLOT_MAINHAND); + botAI->GetServices().GetItemService().ImbueItem(poison, EQUIPMENT_SLOT_MAINHAND); botAI->SetNextCheckDelay(5); } } @@ -92,7 +94,7 @@ bool ImbueWithPoisonAction::Execute(Event event) if (poison) { - botAI->ImbueItem(poison, EQUIPMENT_SLOT_OFFHAND); + botAI->GetServices().GetItemService().ImbueItem(poison, EQUIPMENT_SLOT_OFFHAND); botAI->SetNextCheckDelay(5); } } @@ -103,7 +105,7 @@ bool ImbueWithPoisonAction::Execute(Event event) // Search and apply stone to weapons ImbueWithStoneAction::ImbueWithStoneAction(PlayerbotAI* botAI) : Action(botAI, "apply stone") {} -bool ImbueWithStoneAction::Execute(Event event) +bool ImbueWithStoneAction::Execute(Event /*event*/) { if (bot->IsInCombat()) return false; @@ -122,10 +124,10 @@ bool ImbueWithStoneAction::Execute(Event event) Item* weapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); if (weapon && weapon->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) == 0) { - stone = botAI->FindStoneFor(weapon); + stone = botAI->GetServices().GetItemService().FindStoneFor(weapon); if (stone) { - botAI->ImbueItem(stone, EQUIPMENT_SLOT_MAINHAND); + botAI->GetServices().GetItemService().ImbueItem(stone, EQUIPMENT_SLOT_MAINHAND); botAI->SetNextCheckDelay(5); } } @@ -134,10 +136,10 @@ bool ImbueWithStoneAction::Execute(Event event) weapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); if (weapon && weapon->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) == 0) { - stone = botAI->FindStoneFor(weapon); + stone = botAI->GetServices().GetItemService().FindStoneFor(weapon); if (stone) { - botAI->ImbueItem(stone, EQUIPMENT_SLOT_OFFHAND); + botAI->GetServices().GetItemService().ImbueItem(stone, EQUIPMENT_SLOT_OFFHAND); botAI->SetNextCheckDelay(5); } } @@ -148,7 +150,7 @@ bool ImbueWithStoneAction::Execute(Event event) // Search and apply oil to weapons ImbueWithOilAction::ImbueWithOilAction(PlayerbotAI* botAI) : Action(botAI, "apply oil") {} -bool ImbueWithOilAction::Execute(Event event) +bool ImbueWithOilAction::Execute(Event /*event*/) { if (bot->IsInCombat()) return false; @@ -166,10 +168,10 @@ bool ImbueWithOilAction::Execute(Event event) Item* weapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); if (weapon && weapon->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) == 0) { - oil = botAI->FindOilFor(weapon); + oil = botAI->GetServices().GetItemService().FindOilFor(weapon); if (oil) { - botAI->ImbueItem(oil, EQUIPMENT_SLOT_MAINHAND); + botAI->GetServices().GetItemService().ImbueItem(oil, EQUIPMENT_SLOT_MAINHAND); botAI->SetNextCheckDelay(5); } } @@ -201,18 +203,18 @@ static const uint32 uPrioritizedHealingItemIds[19] = { TryEmergencyAction::TryEmergencyAction(PlayerbotAI* botAI) : Action(botAI, "try emergency") {} -bool TryEmergencyAction::Execute(Event event) +bool TryEmergencyAction::Execute(Event /*event*/) { // Do not use consumable if bot can heal self - if ((botAI->IsHeal(bot)) && (bot->GetPowerPct(POWER_MANA) > 20)) + if ((BotRoleService::IsHealStatic(bot)) && (bot->GetPowerPct(POWER_MANA) > 20)) return false; // If bot does not have aggro: use bandage instead of potion/stone/crystal if ((!AI_VALUE(uint8, "my attacker count")) && !bot->HasAura(11196)) // Recently bandaged { - if (Item* bandage = botAI->FindBandage()) + if (Item* bandage = botAI->GetServices().GetItemService().FindBandage()) { - botAI->ImbueItem(bandage, bot); + botAI->GetServices().GetItemService().ImbueItem(bandage, bot); botAI->SetNextCheckDelay(8); } } @@ -220,9 +222,9 @@ bool TryEmergencyAction::Execute(Event event) // Else loop over the list of health consumable to pick one for (uint8 i = 0; i < std::size(uPrioritizedHealingItemIds); ++i) { - if (Item* healthItem = botAI->FindConsumable(uPrioritizedHealingItemIds[i])) + if (Item* healthItem = botAI->GetServices().GetItemService().FindConsumable(uPrioritizedHealingItemIds[i])) { - botAI->ImbueItem(healthItem); + botAI->GetServices().GetItemService().ImbueItem(healthItem); } } diff --git a/src/Ai/Base/Actions/InventoryAction.cpp b/src/Ai/Base/Actions/InventoryAction.cpp index f7b606a91a..c2a4b612fe 100644 --- a/src/Ai/Base/Actions/InventoryAction.cpp +++ b/src/Ai/Base/Actions/InventoryAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "InventoryAction.h" #include "Event.h" @@ -130,40 +131,42 @@ void InventoryAction::TellItems(std::map itemMap, std::mapClass) { case ITEM_CLASS_CONSUMABLE: - botAI->TellMaster("--- consumable ---"); + botAI->GetServices().GetChatService().TellMaster("--- consumable ---"); break; case ITEM_CLASS_CONTAINER: - botAI->TellMaster("--- container ---"); + botAI->GetServices().GetChatService().TellMaster("--- container ---"); break; case ITEM_CLASS_WEAPON: - botAI->TellMaster("--- weapon ---"); + botAI->GetServices().GetChatService().TellMaster("--- weapon ---"); break; case ITEM_CLASS_ARMOR: - botAI->TellMaster("--- armor ---"); + botAI->GetServices().GetChatService().TellMaster("--- armor ---"); break; case ITEM_CLASS_REAGENT: - botAI->TellMaster("--- reagent ---"); + botAI->GetServices().GetChatService().TellMaster("--- reagent ---"); break; case ITEM_CLASS_PROJECTILE: - botAI->TellMaster("--- projectile ---"); + botAI->GetServices().GetChatService().TellMaster("--- projectile ---"); break; case ITEM_CLASS_TRADE_GOODS: - botAI->TellMaster("--- trade goods ---"); + botAI->GetServices().GetChatService().TellMaster("--- trade goods ---"); break; case ITEM_CLASS_RECIPE: - botAI->TellMaster("--- recipe ---"); + botAI->GetServices().GetChatService().TellMaster("--- recipe ---"); break; case ITEM_CLASS_QUIVER: - botAI->TellMaster("--- quiver ---"); + botAI->GetServices().GetChatService().TellMaster("--- quiver ---"); break; case ITEM_CLASS_QUEST: - botAI->TellMaster("--- quest items ---"); + botAI->GetServices().GetChatService().TellMaster("--- quest items ---"); break; case ITEM_CLASS_KEY: - botAI->TellMaster("--- keys ---"); + botAI->GetServices().GetChatService().TellMaster("--- keys ---"); break; case ITEM_CLASS_MISC: - botAI->TellMaster("--- other ---"); + botAI->GetServices().GetChatService().TellMaster("--- other ---"); + break; + default: break; } } @@ -179,7 +182,7 @@ void InventoryAction::TellItem(ItemTemplate const* proto, uint32 count, bool sou if (soulbound) out << " (soulbound)"; - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); } std::vector InventoryAction::parseItems(std::string const text, IterateItemsMask mask) @@ -373,8 +376,8 @@ ItemIds InventoryAction::FindOutfitItems(std::string const name) std::string const InventoryAction::parseOutfitName(std::string const outfit) { - uint32 pos = outfit.find("="); - if (pos == -1) + size_t pos = outfit.find("="); + if (pos == std::string::npos) return ""; return outfit.substr(0, pos); @@ -384,11 +387,11 @@ ItemIds InventoryAction::parseOutfitItems(std::string const text) { ItemIds itemIds; - uint8 pos = text.find("=") + 1; + size_t pos = text.find("=") + 1; while (pos < text.size()) { - uint32 endPos = text.find(',', pos); - if (endPos == -1) + size_t endPos = text.find(',', pos); + if (endPos == std::string::npos) endPos = text.size(); std::string const idC = text.substr(pos, endPos - pos); diff --git a/src/Ai/Base/Actions/InventoryChangeFailureAction.cpp b/src/Ai/Base/Actions/InventoryChangeFailureAction.cpp index ef7cd92399..9dea4a71be 100644 --- a/src/Ai/Base/Actions/InventoryChangeFailureAction.cpp +++ b/src/Ai/Base/Actions/InventoryChangeFailureAction.cpp @@ -4,6 +4,7 @@ */ #include "InventoryChangeFailureAction.h" +#include "BotChatService.h" #include "Event.h" #include "Playerbots.h" @@ -94,7 +95,7 @@ bool InventoryChangeFailureAction::Execute(Event event) std::string const msg = messages[(InventoryResult)err]; if (!msg.empty()) { - botAI->TellError(msg); + botAI->GetServices().GetChatService().TellError(msg); return true; } diff --git a/src/Ai/Base/Actions/InviteToGroupAction.cpp b/src/Ai/Base/Actions/InviteToGroupAction.cpp index bec515fefb..1e0e318495 100644 --- a/src/Ai/Base/Actions/InviteToGroupAction.cpp +++ b/src/Ai/Base/Actions/InviteToGroupAction.cpp @@ -4,6 +4,8 @@ */ #include "InviteToGroupAction.h" +#include "Bot/Core/ManagerRegistry.h" +#include "BotRoleService.h" #include "BroadcastHelper.h" #include "Event.h" @@ -13,6 +15,7 @@ #include "Playerbots.h" #include "PlayerbotWorldThreadProcessor.h" #include "ServerFacade.h" +#include "BotChatService.h" bool InviteToGroupAction::Invite(Player* inviter, Player* player) { @@ -44,7 +47,7 @@ bool InviteToGroupAction::Invite(Player* inviter, Player* player) return true; } -bool InviteNearbyToGroupAction::Execute(Event event) +bool InviteNearbyToGroupAction::Execute(Event /*event*/) { GuidVector nearGuids = botAI->GetAiObjectContext()->GetValue("nearest friendly players")->Get(); for (auto& i : nearGuids) @@ -99,7 +102,7 @@ bool InviteNearbyToGroupAction::Execute(Event event) sPlayerbotWorldProcessor->QueueOperation(std::move(convertOp)); } - if (sPlayerbotAIConfig->inviteChat && sRandomPlayerbotMgr->IsRandomBot(bot)) + if (sPlayerbotAIConfig->inviteChat && sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) { std::map placeholders; placeholders["%player"] = player->GetName(); @@ -166,10 +169,8 @@ std::vector InviteGuildToGroupAction::getGuildMembers() return worker.GetResult(); } -bool InviteGuildToGroupAction::Execute(Event event) +bool InviteGuildToGroupAction::Execute(Event /*event*/) { - Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId()); - for (auto& member : getGuildMembers()) { Player* player = member; @@ -234,7 +235,7 @@ bool InviteGuildToGroupAction::Execute(Event event) } if (sPlayerbotAIConfig->inviteChat && - (sRandomPlayerbotMgr->IsRandomBot(bot) || !botAI->HasActivePlayerMaster())) + (sManagerRegistry.GetRandomBotManager().IsRandomBot(bot) || !botAI->HasActivePlayerMaster())) { BroadcastHelper::BroadcastGuildGroupOrRaidInvite(botAI, bot, player, group); } @@ -311,9 +312,9 @@ bool LfgAction::Execute(Event event) allowedRoles[BOT_ROLE_HEALER] = 1; allowedRoles[BOT_ROLE_DPS] = 3; - BotRoles role = botAI->IsTank(requester, false) + BotRoles role = BotRoleService::IsTankStatic(requester, false) ? BOT_ROLE_TANK - : (botAI->IsHeal(requester, false) ? BOT_ROLE_HEALER : BOT_ROLE_DPS); + : (BotRoleService::IsHealStatic(requester, false) ? BOT_ROLE_HEALER : BOT_ROLE_DPS); Classes cls = (Classes)requester->getClass(); if (group) @@ -386,8 +387,8 @@ bool LfgAction::Execute(Event event) if (!botAI->IsSafe(player)) return false; - role = botAI->IsTank(player, false) ? BOT_ROLE_TANK - : (botAI->IsHeal(player, false) ? BOT_ROLE_HEALER : BOT_ROLE_DPS); + role = BotRoleService::IsTankStatic(player, false) ? BOT_ROLE_TANK + : (BotRoleService::IsHealStatic(player, false) ? BOT_ROLE_HEALER : BOT_ROLE_DPS); cls = (Classes)player->getClass(); if (allowedRoles[role] > 0) @@ -406,7 +407,7 @@ bool LfgAction::Execute(Event event) allowedClassNr[cls][role]--; } - role = botAI->IsTank(bot, false) ? BOT_ROLE_TANK : (botAI->IsHeal(bot, false) ? BOT_ROLE_HEALER : BOT_ROLE_DPS); + role = BotRoleService::IsTankStatic(bot, false) ? BOT_ROLE_TANK : (BotRoleService::IsHealStatic(bot, false) ? BOT_ROLE_HEALER : BOT_ROLE_DPS); cls = (Classes)bot->getClass(); if (allowedRoles[role] == 0) @@ -441,7 +442,7 @@ bool LfgAction::Execute(Event event) { out << "Joining as " << placeholders["%role"] << ", " << placeholders["%spotsleft"] << " " << placeholders["%role"] << " spots left."; - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); //botAI->DoSpecificAction("autogear"); //botAI->DoSpecificAction("maintenance"); @@ -449,7 +450,7 @@ bool LfgAction::Execute(Event event) else { out << "Joining as " << placeholders["%role"] << "."; - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); //botAI->DoSpecificAction("autogear"); //botAI->DoSpecificAction("maintenance"); diff --git a/src/Ai/Base/Actions/LeaveGroupAction.cpp b/src/Ai/Base/Actions/LeaveGroupAction.cpp index a279c9426c..d9c963cf3c 100644 --- a/src/Ai/Base/Actions/LeaveGroupAction.cpp +++ b/src/Ai/Base/Actions/LeaveGroupAction.cpp @@ -3,8 +3,10 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "LeaveGroupAction.h" +#include "Bot/Core/ManagerRegistry.h" #include "Event.h" #include "PlayerbotAIConfig.h" #include "Playerbots.h" @@ -33,7 +35,7 @@ bool PartyCommandAction::Execute(Event event) Player* master = GetMaster(); if (master && member == master->GetName()) { - if (sRandomPlayerbotMgr->IsRandomBot(bot)) + if (sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) { Player* newMaster = botAI->FindNewMaster(); if (newMaster || bot->InBattleground()) @@ -86,13 +88,13 @@ bool LeaveGroupAction::Leave() Player* master = botAI -> GetMaster(); if (master) - botAI->TellMaster("Goodbye!", PLAYERBOT_SECURITY_TALK); + botAI->GetServices().GetChatService().TellMaster("Goodbye!", PLAYERBOT_SECURITY_TALK); botAI->LeaveOrDisbandGroup(); return true; } -bool LeaveFarAwayAction::Execute(Event event) +bool LeaveFarAwayAction::Execute(Event /*event*/) { // allow bot to leave party when they want return Leave(); diff --git a/src/Ai/Base/Actions/LfgActions.cpp b/src/Ai/Base/Actions/LfgActions.cpp index 15f8b92f25..ca9e0a084b 100644 --- a/src/Ai/Base/Actions/LfgActions.cpp +++ b/src/Ai/Base/Actions/LfgActions.cpp @@ -4,6 +4,8 @@ */ #include "LfgActions.h" +#include "Bot/Core/ManagerRegistry.h" +#include "BotRoleService.h" #include "AiFactory.h" #include "ItemVisitors.h" @@ -13,18 +15,19 @@ #include "Playerbots.h" #include "World.h" #include "WorldPacket.h" +#include "BotItemService.h" using namespace lfg; -bool LfgJoinAction::Execute(Event event) { return JoinLFG(); } +bool LfgJoinAction::Execute(Event /*event*/) { return JoinLFG(); } uint32 LfgJoinAction::GetRoles() { - if (!sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) { - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) return PLAYER_ROLE_TANK; - if (botAI->IsHeal(bot)) + if (BotRoleService::IsHealStatic(bot)) return PLAYER_ROLE_HEALER; else return PLAYER_ROLE_DAMAGE; @@ -115,7 +118,7 @@ bool LfgJoinAction::JoinLFG() auto const& botLevel = bot->GetLevel(); /*LFG_TYPE_RANDOM on classic is 15-58 so bot over level 25 will never queue*/ - if (dungeon->MinLevel && (botLevel < dungeon->MinLevel || botLevel > dungeon->MaxLevel) || + if ((dungeon->MinLevel && (botLevel < dungeon->MinLevel || botLevel > dungeon->MaxLevel)) || (botLevel > dungeon->MinLevel + 10 && dungeon->TypeID == LFG_TYPE_DUNGEON)) continue; @@ -149,7 +152,7 @@ bool LfgJoinAction::JoinLFG() many ? "several dungeons" : dungeon->Name[0]); // Set RbotAId Browser comment - std::string const _gs = std::to_string(botAI->GetEquipGearScore(bot/*, false, false*/)); + std::string const _gs = std::to_string(botAI->GetServices().GetItemService().GetEquipGearScore(bot/*, false, false*/)); // JoinLfg is not threadsafe, so make packet and queue into session // sLFGMgr->JoinLfg(bot, roleMask, list, _gs); @@ -170,14 +173,11 @@ bool LfgJoinAction::JoinLFG() return true; } -bool LfgRoleCheckAction::Execute(Event event) +bool LfgRoleCheckAction::Execute(Event /*event*/) { - if (Group* group = bot->GetGroup()) + if (bot->GetGroup()) { - uint32 currentRoles = sLFGMgr->GetRoles(bot->GetGUID()); uint32 newRoles = GetRoles(); - // if (currentRoles == newRoles) - // return false; WorldPacket* packet = new WorldPacket(CMSG_LFG_SET_ROLES); *packet << (uint8)newRoles; @@ -216,9 +216,9 @@ bool LfgAcceptAction::Execute(Event event) *packet << id << true; bot->GetSession()->QueuePacket(packet); - if (sRandomPlayerbotMgr->IsRandomBot(bot) && !bot->GetGroup()) + if (sManagerRegistry.GetRandomBotManager().IsRandomBot(bot) && !bot->GetGroup()) { - sRandomPlayerbotMgr->Refresh(bot); + sManagerRegistry.GetRandomBotManager().Refresh(bot); botAI->ResetStrategies(); } @@ -251,9 +251,9 @@ bool LfgAcceptAction::Execute(Event event) *packet << id << true; bot->GetSession()->QueuePacket(packet); - if (sRandomPlayerbotMgr->IsRandomBot(bot) && !bot->GetGroup()) + if (sManagerRegistry.GetRandomBotManager().IsRandomBot(bot) && !bot->GetGroup()) { - sRandomPlayerbotMgr->Refresh(bot); + sManagerRegistry.GetRandomBotManager().Refresh(bot); botAI->ResetStrategies(); } @@ -265,7 +265,7 @@ bool LfgAcceptAction::Execute(Event event) return false; } -bool LfgLeaveAction::Execute(Event event) +bool LfgLeaveAction::Execute(Event /*event*/) { // Don't leave if lfg strategy enabled // if (botAI->HasStrategy("lfg", BOT_STATE_NON_COMBAT)) @@ -337,7 +337,7 @@ bool LfgJoinAction::isUseful() if (bot->isDead()) return false; - if (!sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) return false; Map* map = bot->GetMap(); diff --git a/src/Ai/Base/Actions/ListQuestsActions.cpp b/src/Ai/Base/Actions/ListQuestsActions.cpp index 7968417059..8e534f7b59 100644 --- a/src/Ai/Base/Actions/ListQuestsActions.cpp +++ b/src/Ai/Base/Actions/ListQuestsActions.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "ListQuestsActions.h" #include "Event.h" @@ -44,21 +45,21 @@ void ListQuestsAction::ListQuests(QuestListFilter filter, QuestTravelDetail trav bool showCompleted = filter & QUEST_LIST_FILTER_COMPLETED; if (showIncompleted) - botAI->TellMaster("--- Incompleted quests ---"); + botAI->GetServices().GetChatService().TellMaster("--- Incompleted quests ---"); uint32 incompleteCount = ListQuests(false, !showIncompleted, travelDetail); if (showCompleted) - botAI->TellMaster("--- Completed quests ---"); + botAI->GetServices().GetChatService().TellMaster("--- Completed quests ---"); uint32 completeCount = ListQuests(true, !showCompleted, travelDetail); - botAI->TellMaster("--- Summary ---"); + botAI->GetServices().GetChatService().TellMaster("--- Summary ---"); std::ostringstream out; out << "Total: " << (completeCount + incompleteCount) << " / 25 (incompleted: " << incompleteCount << ", completed: " << completeCount << ")"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } uint32 ListQuestsAction::ListQuests(bool completed, bool silent, QuestTravelDetail travelDetail) @@ -85,7 +86,7 @@ uint32 ListQuestsAction::ListQuests(bool completed, bool silent, QuestTravelDeta if (silent) continue; - botAI->TellMaster(chat->FormatQuest(pQuest)); + botAI->GetServices().GetChatService().TellMaster(chat->FormatQuest(pQuest)); if (travelDetail != QUEST_TRAVEL_DETAIL_NONE && target->getDestination()) { @@ -99,7 +100,7 @@ uint32 ListQuestsAction::ListQuests(bool completed, bool silent, QuestTravelDeta std::ostringstream out; out << "[Active] traveling " << target->getPosition()->distance(botPos); out << " to " << QuestDestination->getTitle(); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } } } @@ -134,7 +135,7 @@ uint32 ListQuestsAction::ListQuests(bool completed, bool silent, QuestTravelDeta if (desRange > 0) out << desRange << " out of range."; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } else if (travelDetail == QUEST_TRAVEL_DETAIL_FULL) { @@ -173,7 +174,7 @@ uint32 ListQuestsAction::ListQuests(bool completed, bool silent, QuestTravelDeta if (dest->isFull(bot)) out << " crowded"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); limit++; } diff --git a/src/Ai/Base/Actions/ListSpellsAction.cpp b/src/Ai/Base/Actions/ListSpellsAction.cpp index 6f67aef1aa..b2ae59d653 100644 --- a/src/Ai/Base/Actions/ListSpellsAction.cpp +++ b/src/Ai/Base/Actions/ListSpellsAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "ListSpellsAction.h" #include "Event.h" @@ -284,11 +285,11 @@ bool ListSpellsAction::Execute(Event event) if (spells.empty()) { // CHANGE: Give early feedback when no spells match the filter. - botAI->TellMaster("No spells found."); + botAI->GetServices().GetChatService().TellMaster("No spells found."); return true; } - botAI->TellMaster("=== Spells ==="); + botAI->GetServices().GetChatService().TellMaster("=== Spells ==="); std::sort(spells.begin(), spells.end(), CompareSpells); @@ -300,7 +301,7 @@ bool ListSpellsAction::Execute(Event event) // cheap comparator above, sending all lines here is safe and keeps // behaviour compatible with existing addons. for (std::vector::const_iterator i = spells.begin(); i != spells.end(); ++i) - botAI->TellMasterNoFacing(i->second); + botAI->GetServices().GetChatService().TellMasterNoFacing(i->second); return true; } \ No newline at end of file diff --git a/src/Ai/Base/Actions/LogLevelAction.cpp b/src/Ai/Base/Actions/LogLevelAction.cpp index cd8b5c28d4..0ea4d40540 100644 --- a/src/Ai/Base/Actions/LogLevelAction.cpp +++ b/src/Ai/Base/Actions/LogLevelAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "LogLevelAction.h" #include "Event.h" @@ -24,7 +25,7 @@ bool LogLevelAction::Execute(Event event) out << "My log level is " << logLevel2string(value->Get()); } - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return true; } diff --git a/src/Ai/Base/Actions/LootAction.cpp b/src/Ai/Base/Actions/LootAction.cpp index 2e0656e711..f86d840eb6 100644 --- a/src/Ai/Base/Actions/LootAction.cpp +++ b/src/Ai/Base/Actions/LootAction.cpp @@ -4,6 +4,8 @@ */ #include "LootAction.h" +#include "Bot/Core/ManagerRegistry.h" +#include "BotChatService.h" #include "ChatHelper.h" #include "Event.h" @@ -17,6 +19,7 @@ #include "ServerFacade.h" #include "GuildMgr.h" #include "BroadcastHelper.h" +#include "BotSpellService.h" bool LootAction::Execute(Event /*event*/) { @@ -122,13 +125,13 @@ bool OpenLootAction::DoLoot(LootObject& lootObject) switch (skill) { case SKILL_ENGINEERING: - return botAI->HasSkill(SKILL_ENGINEERING) ? botAI->CastSpell(ENGINEERING, creature) : false; + return botAI->HasSkill(SKILL_ENGINEERING) ? botAI->GetServices().GetSpellService().CastSpell(ENGINEERING, creature) : false; case SKILL_HERBALISM: - return botAI->HasSkill(SKILL_HERBALISM) ? botAI->CastSpell(32605, creature) : false; + return botAI->HasSkill(SKILL_HERBALISM) ? botAI->GetServices().GetSpellService().CastSpell(32605, creature) : false; case SKILL_MINING: - return botAI->HasSkill(SKILL_MINING) ? botAI->CastSpell(32606, creature) : false; + return botAI->HasSkill(SKILL_MINING) ? botAI->GetServices().GetSpellService().CastSpell(32606, creature) : false; default: - return botAI->HasSkill(SKILL_SKINNING) ? botAI->CastSpell(SKINNING, creature) : false; + return botAI->HasSkill(SKILL_SKINNING) ? botAI->GetServices().GetSpellService().CastSpell(SKINNING, creature) : false; } } @@ -148,16 +151,16 @@ bool OpenLootAction::DoLoot(LootObject& lootObject) return false; if (lootObject.skillId == SKILL_MINING) - return botAI->HasSkill(SKILL_MINING) ? botAI->CastSpell(MINING, bot) : false; + return botAI->HasSkill(SKILL_MINING) ? botAI->GetServices().GetSpellService().CastSpell(MINING, bot) : false; if (lootObject.skillId == SKILL_HERBALISM) - return botAI->HasSkill(SKILL_HERBALISM) ? botAI->CastSpell(HERB_GATHERING, bot) : false; + return botAI->HasSkill(SKILL_HERBALISM) ? botAI->GetServices().GetSpellService().CastSpell(HERB_GATHERING, bot) : false; uint32 spellId = GetOpeningSpell(lootObject); if (!spellId) return false; - return botAI->CastSpell(spellId, bot); + return botAI->GetServices().GetSpellService().CastSpell(spellId, bot); } uint32 OpenLootAction::GetOpeningSpell(LootObject& lootObject) @@ -241,6 +244,8 @@ bool OpenLootAction::CanOpenLock(LootObject& /*lootObject*/, SpellInfo const* sp if (CanOpenLock(skillId, lockInfo->Skill[j])) return true; } + default: + break; } } } @@ -264,15 +269,15 @@ uint32 StoreLootAction::RoundPrice(double price) if (price < 10000) { - return (uint32)(price / 100.0) * 100; + return (uint32)(price / 100.0f) * 100; } if (price < 100000) { - return (uint32)(price / 1000.0) * 1000; + return (uint32)(price / 1000.0f) * 1000; } - return (uint32)(price / 10000.0) * 10000; + return (uint32)(price / 10000.0f) * 10000; } bool StoreLootAction::AuctionItem(uint32 itemId) @@ -294,7 +299,7 @@ bool StoreLootAction::AuctionItem(uint32 itemId) AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(ahEntry); - uint32 price = oldItem->GetCount() * proto->BuyPrice * sRandomPlayerbotMgr->GetBuyMultiplier(bot); + uint32 price = oldItem->GetCount() * proto->BuyPrice * sManagerRegistry.GetRandomBotManager().GetBuyMultiplier(bot); uint32 stackCount = urand(1, proto->GetMaxStackSize()); if (!price || !stackCount) @@ -426,9 +431,9 @@ bool StoreLootAction::Execute(Event event) } Player* master = botAI->GetMaster(); - if (sRandomPlayerbotMgr->IsRandomBot(bot) && master) + if (sManagerRegistry.GetRandomBotManager().IsRandomBot(bot) && master) { - uint32 price = itemcount * proto->BuyPrice * sRandomPlayerbotMgr->GetBuyMultiplier(bot) + gold; + uint32 price = itemcount * proto->BuyPrice * sManagerRegistry.GetRandomBotManager().GetBuyMultiplier(bot) + gold; if (price) sRandomPlayerbotMgr->AddTradeDiscount(bot, master, price); @@ -445,10 +450,10 @@ bool StoreLootAction::Execute(Event event) botAI->SetNextCheckDelay(sPlayerbotAIConfig->lootDelay); if (proto->Quality > ITEM_QUALITY_NORMAL && !urand(0, 50) && botAI->HasStrategy("emote", BOT_STATE_NON_COMBAT) && sPlayerbotAIConfig->randomBotEmote) - botAI->PlayEmote(TEXT_EMOTE_CHEER); + botAI->GetServices().GetChatService().PlayEmote(TEXT_EMOTE_CHEER); if (proto->Quality >= ITEM_QUALITY_RARE && !urand(0, 1) && botAI->HasStrategy("emote", BOT_STATE_NON_COMBAT) && sPlayerbotAIConfig->randomBotEmote) - botAI->PlayEmote(TEXT_EMOTE_CHEER); + botAI->GetServices().GetChatService().PlayEmote(TEXT_EMOTE_CHEER); BroadcastHelper::BroadcastLootingItem(botAI, bot, proto); } diff --git a/src/Ai/Base/Actions/LootRollAction.cpp b/src/Ai/Base/Actions/LootRollAction.cpp index 912f75fddf..c26e04bd64 100644 --- a/src/Ai/Base/Actions/LootRollAction.cpp +++ b/src/Ai/Base/Actions/LootRollAction.cpp @@ -13,7 +13,7 @@ #include "PlayerbotAIConfig.h" #include "Playerbots.h" -bool LootRollAction::Execute(Event event) +bool LootRollAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) @@ -27,7 +27,6 @@ bool LootRollAction::Execute(Event event) continue; } ObjectGuid guid = roll->itemGUID; - uint32 slot = roll->itemSlot; uint32 itemId = roll->itemid; int32 randomProperty = 0; if (roll->itemRandomPropId) @@ -170,7 +169,7 @@ bool MasterLootRollAction::Execute(Event event) p.rpos(0); // reset packet pointer p >> creatureGuid; // creature guid what we're looting - p >> mapId; /// 3.3.3 mapid + p >> mapId; /// 3.3f.3 mapid p >> itemSlot; // the itemEntryId for the item that shall be rolled for p >> itemId; // the itemEntryId for the item that shall be rolled for p >> randomSuffix; // randomSuffix @@ -186,7 +185,6 @@ bool MasterLootRollAction::Execute(Event event) if (!group) return false; - RollVote vote = CalculateRollVote(proto); group->CountRollVote(bot->GetGUID(), creatureGuid, CalculateRollVote(proto)); return true; @@ -253,10 +251,13 @@ bool RollAction::Execute(Event event) { case ITEM_CLASS_WEAPON: case ITEM_CLASS_ARMOR: - if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_BAD_EQUIP) - { - bot->DoRandomRoll(0,100); - } + if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_BAD_EQUIP) + { + bot->DoRandomRoll(0, 100); + } + break; + default: + break; } return true; } diff --git a/src/Ai/Base/Actions/LootStrategyAction.cpp b/src/Ai/Base/Actions/LootStrategyAction.cpp index 68f8089c2e..6a27ace677 100644 --- a/src/Ai/Base/Actions/LootStrategyAction.cpp +++ b/src/Ai/Base/Actions/LootStrategyAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "LootStrategyAction.h" #include "ChatHelper.h" @@ -16,7 +17,6 @@ bool LootStrategyAction::Execute(Event event) { std::string const strategy = event.getParam(); - LootObjectStack* lootItems = AI_VALUE(LootObjectStack*, "available loot"); std::set& alwaysLootItems = AI_VALUE(std::set&, "always loot list"); Value* lootStrategy = context->GetValue("loot strategy"); @@ -26,7 +26,7 @@ bool LootStrategyAction::Execute(Event event) std::ostringstream out; out << "Loot strategy: "; out << lootStrategy->Get()->GetName(); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } { @@ -42,7 +42,7 @@ bool LootStrategyAction::Execute(Event event) out << chat->FormatItem(proto); } - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } } else @@ -55,7 +55,7 @@ bool LootStrategyAction::Execute(Event event) std::ostringstream out; out << "Loot strategy set to " << lootStrategy->Get()->GetName(); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return true; } @@ -71,7 +71,7 @@ bool LootStrategyAction::Execute(Event event) out << (StoreLootAction::IsLootAllowed(itemid, botAI) ? "|cFF000000Will loot " : "|c00FF0000Won't loot ") << ChatHelper::FormatItem(proto); - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); } } else if (remove) @@ -80,12 +80,12 @@ bool LootStrategyAction::Execute(Event event) if (j != alwaysLootItems.end()) alwaysLootItems.erase(j); - botAI->TellMaster("Item(s) removed from always loot list"); + botAI->GetServices().GetChatService().TellMaster("Item(s) removed from always loot list"); } else { alwaysLootItems.insert(itemid); - botAI->TellMaster("Item(s) added to always loot list"); + botAI->GetServices().GetChatService().TellMaster("Item(s) added to always loot list"); } } } diff --git a/src/Ai/Base/Actions/MailAction.cpp b/src/Ai/Base/Actions/MailAction.cpp index 4ee2fee6d7..1d518f2562 100644 --- a/src/Ai/Base/Actions/MailAction.cpp +++ b/src/Ai/Base/Actions/MailAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "MailAction.h" #include "ChatHelper.h" @@ -17,7 +18,7 @@ class TellMailProcessor : public MailProcessor public: bool Before(PlayerbotAI* botAI) override { - botAI->TellMaster("=== Mailbox ==="); + botAI->GetServices().GetChatService().TellMaster("=== Mailbox ==="); tells.clear(); return true; } @@ -64,7 +65,7 @@ class TellMailProcessor : public MailProcessor bool After(PlayerbotAI* botAI) override { for (std::list::iterator i = tells.begin(); i != tells.end(); ++i) - botAI->TellMaster(*i); + botAI->GetServices().GetChatService().TellMaster(*i); return true; } @@ -78,12 +79,12 @@ class TellMailProcessor : public MailProcessor class TakeMailProcessor : public MailProcessor { public: - bool Process(uint32 index, Mail* mail, PlayerbotAI* botAI) override + bool Process(uint32 /*index*/, Mail* mail, PlayerbotAI* botAI) override { Player* bot = botAI->GetBot(); if (!CheckBagSpace(bot)) { - botAI->TellError("Not enough bag space"); + botAI->GetServices().GetChatService().TellError("Not enough bag space"); return false; } @@ -92,7 +93,7 @@ class TakeMailProcessor : public MailProcessor { std::ostringstream out; out << mail->subject << ", |cffffff00" << ChatHelper::formatMoney(mail->money) << "|cff00ff00 processed"; - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); WorldPacket packet; packet << mailbox; @@ -120,7 +121,7 @@ class TakeMailProcessor : public MailProcessor out << mail->subject << ", " << ChatHelper::FormatItem(item->GetTemplate()) << "|cff00ff00 processed"; bot->GetSession()->HandleMailTakeItem(packet); - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); } RemoveMail(bot, mail->messageID, mailbox); @@ -134,7 +135,7 @@ class TakeMailProcessor : public MailProcessor private: bool CheckBagSpace(Player* bot) { - uint32 totalused = 0, total = 16; + uint32 totalused = 0; for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++) if (bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) ++totalused; @@ -157,12 +158,12 @@ class TakeMailProcessor : public MailProcessor class DeleteMailProcessor : public MailProcessor { public: - bool Process(uint32 index, Mail* mail, PlayerbotAI* botAI) override + bool Process(uint32 /*index*/, Mail* mail, PlayerbotAI* botAI) override { std::ostringstream out; out << "|cffffffff" << mail->subject << "|cffff0000 deleted"; RemoveMail(botAI->GetBot(), mail->messageID, FindMailbox(botAI)); - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); return true; } @@ -172,11 +173,11 @@ class DeleteMailProcessor : public MailProcessor class ReadMailProcessor : public MailProcessor { public: - bool Process(uint32 index, Mail* mail, PlayerbotAI* botAI) override + bool Process(uint32 /*index*/, Mail* mail, PlayerbotAI* botAI) override { std::ostringstream out, body; out << "|cffffffff" << mail->subject; - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); return true; } @@ -256,7 +257,7 @@ bool MailAction::Execute(Event event) if (!MailProcessor::FindMailbox(botAI)) { - botAI->TellError("There is no mailbox nearby"); + botAI->GetServices().GetChatService().TellError("There is no mailbox nearby"); return false; } @@ -271,7 +272,7 @@ bool MailAction::Execute(Event event) std::string const text = event.getParam(); if (text.empty()) { - botAI->TellMaster( + botAI->GetServices().GetChatService().TellMaster( "whisper 'mail ?' to query mailbox, 'mail take/delete/read filter' to take/delete/read mails by filter"); return false; } @@ -285,7 +286,7 @@ bool MailAction::Execute(Event event) { std::ostringstream out; out << action << ": I don't know how to do that"; - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); return false; } diff --git a/src/Ai/Base/Actions/MoveToRpgTargetAction.cpp b/src/Ai/Base/Actions/MoveToRpgTargetAction.cpp index 618af10af1..cc29764ced 100644 --- a/src/Ai/Base/Actions/MoveToRpgTargetAction.cpp +++ b/src/Ai/Base/Actions/MoveToRpgTargetAction.cpp @@ -10,8 +10,9 @@ #include "Event.h" #include "LastMovementValue.h" #include "Playerbots.h" +#include "BotChatService.h" -bool MoveToRpgTargetAction::Execute(Event event) +bool MoveToRpgTargetAction::Execute(Event /*event*/) { GuidPosition guidP = AI_VALUE(GuidPosition, "rpg target"); Unit* unit = botAI->GetUnit(guidP); @@ -24,7 +25,6 @@ bool MoveToRpgTargetAction::Execute(Event event) { return false; } - Player* player = guidP.GetPlayer(); WorldObject* wo = nullptr; if (unit) @@ -39,7 +39,7 @@ bool MoveToRpgTargetAction::Execute(Event event) std::ostringstream out; out << "Heading to: "; out << chat->FormatWorldobject(guidP.GetWorldObject()); - botAI->TellMasterNoFacing(out); + botAI->GetServices().GetChatService().TellMasterNoFacing(out); } if (guidP.IsPlayer()) @@ -82,15 +82,15 @@ bool MoveToRpgTargetAction::Execute(Event event) if (bot->IsWithinLOS(x, y, z)) { if (!unit || !unit->isMoving()) - angle = wo->GetAngle(bot) + (M_PI * irand(-25, 25) / 100.0); // Closest 45 degrees towards the target + angle = wo->GetAngle(bot) + (M_PI * irand(-25, 25) / 100.0f); // Closest 45 degrees towards the target else angle = wo->GetOrientation() + - (M_PI * irand(-25, 25) / 100.0); // 45 degrees infront of target (leading it's movement) + (M_PI * irand(-25, 25) / 100.0f); // 45 degrees infront of target (leading it's movement) distance = frand(0.5f, 1.f); } else - angle = 2 * M_PI * urand(0, 100) / 100.0; // A circle around the target. + angle = 2 * M_PI * urand(0, 100) / 100.0f; // A circle around the target. x += cos(angle) * INTERACTION_DISTANCE * distance; y += sin(angle) * INTERACTION_DISTANCE * distance; diff --git a/src/Ai/Base/Actions/MoveToTravelTargetAction.cpp b/src/Ai/Base/Actions/MoveToTravelTargetAction.cpp index ed60339b82..4b811e3194 100644 --- a/src/Ai/Base/Actions/MoveToTravelTargetAction.cpp +++ b/src/Ai/Base/Actions/MoveToTravelTargetAction.cpp @@ -9,8 +9,9 @@ #include "LootObjectStack.h" #include "PathGenerator.h" #include "Playerbots.h" +#include "BotChatService.h" -bool MoveToTravelTargetAction::Execute(Event event) +bool MoveToTravelTargetAction::Execute(Event /*event*/) { TravelTarget* target = AI_VALUE(TravelTarget*, "travel target"); @@ -62,7 +63,7 @@ bool MoveToTravelTargetAction::Execute(Event event) out << member->GetName(); - botAI->TellMasterNoFacing(out); + botAI->GetServices().GetChatService().TellMasterNoFacing(out); } target->setExpireIn(target->getTimeLeft() + sPlayerbotAIConfig->maxWaitForMove); @@ -76,7 +77,7 @@ bool MoveToTravelTargetAction::Execute(Event event) float maxDistance = target->getDestination()->getRadiusMin(); // Evenly distribute around the target. - float angle = 2 * M_PI * urand(0, 100) / 100.0; + float angle = 2 * M_PI * urand(0, 100) / 100.0f; if (target->getMaxTravelTime() > target->getTimeLeft()) // The bot is late. Speed it up. { @@ -90,7 +91,7 @@ bool MoveToTravelTargetAction::Execute(Event event) float z = location.GetPositionZ(); float mapId = location.GetMapId(); - // Move between 0.5 and 1.0 times the maxDistance. + // Move between 0.5f and 1.0f times the maxDistance. float mod = frand(50.f, 100.f) / 100.0f; x += cos(angle) * maxDistance * mod; diff --git a/src/Ai/Base/Actions/MovementActions.cpp b/src/Ai/Base/Actions/MovementActions.cpp index 69e5c278ba..dcedb5cd20 100644 --- a/src/Ai/Base/Actions/MovementActions.cpp +++ b/src/Ai/Base/Actions/MovementActions.cpp @@ -4,6 +4,7 @@ */ #include "MovementActions.h" +#include "BotSpellService.h" #include #include @@ -11,6 +12,7 @@ #include #include "Corpse.h" +#include "BotRoleService.h" #include "Event.h" #include "FleeManager.h" #include "G3D/Vector3.h" @@ -42,6 +44,7 @@ #include "Unit.h" #include "Vehicle.h" #include "WaypointMovementGenerator.h" +#include "BotChatService.h" MovementAction::MovementAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name) { @@ -54,7 +57,7 @@ void MovementAction::CreateWp(Player* wpOwner, float x, float y, float z, float float delay = 1000.0f * dist / wpOwner->GetSpeed(MOVE_RUN) + sPlayerbotAIConfig->reactDelay; // if (!important) - // delay *= 0.25; + // delay *= 0.25f; Creature* wpCreature = wpOwner->SummonCreature(entry, x, y, z - 1, o, TEMPSUMMON_TIMED_DESPAWN, delay); botAI->GetBot()->AddAura(246, wpCreature); @@ -78,7 +81,6 @@ bool MovementAction::JumpTo(uint32 mapId, float x, float y, float z, MovementPri { return false; } - float botZ = bot->GetPositionZ(); float speed = bot->GetSpeed(MOVE_RUN); MotionMaster& mm = *bot->GetMotionMaster(); mm.Clear(); @@ -100,9 +102,6 @@ bool MovementAction::MoveNear(WorldObject* target, float distance, MovementPrior distance += target->GetCombatReach(); - float x = target->GetPositionX(); - float y = target->GetPositionY(); - float z = target->GetPositionZ(); float followAngle = GetFollowAngle(); for (float angle = followAngle; angle <= followAngle + static_cast(2 * M_PI); @@ -120,7 +119,7 @@ bool MovementAction::MoveNear(WorldObject* target, float distance, MovementPrior return true; } - // botAI->TellError("All paths not in LOS"); + // botAI->GetServices().GetChatService().TellError("All paths not in LOS"); return false; } @@ -154,7 +153,7 @@ bool MovementAction::MoveToLOS(WorldObject* target, bool ranged) for (auto& point : path.GetPath()) { if (botAI->HasStrategy("debug move", BOT_STATE_NON_COMBAT)) - CreateWp(bot, point.x, point.y, point.z, 0.0, 2334); + CreateWp(bot, point.x, point.y, point.z, 0.0f, 2334); float distPoint = target->GetDistance(point.x, point.y, point.z); if (distPoint < dist && target->IsWithinLOS(point.x, point.y, point.z + bot->GetCollisionHeight())) @@ -171,12 +170,12 @@ bool MovementAction::MoveToLOS(WorldObject* target, bool ranged) if (dest.isSet()) return MoveTo(dest.mapId, dest.x, dest.y, dest.z); else - botAI->TellError("All paths not in LOS"); + botAI->GetServices().GetChatService().TellError("All paths not in LOS"); return false; } -bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, bool react, bool normal_only, +bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool /*idle*/, bool /*react*/, bool normal_only, bool exact_waypoint, MovementPriority priority, bool lessDelay, bool backwards) { UpdateMovementState(); @@ -232,7 +231,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // if (bot->IsNonMeleeSpellCast(true)) // { // bot->CastStop(); - // botAI->InterruptSpell(); + // botAI->GetServices().GetSpellService().InterruptSpell(); // } DoMovePoint(bot, x, y, z, generatePath, backwards); float delay = 1000.0f * MoveDelay(distance, backwards); @@ -262,9 +261,8 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // if (bot->IsNonMeleeSpellCast(true)) // { // bot->CastStop(); - // botAI->InterruptSpell(); + // botAI->GetServices().GetSpellService().InterruptSpell(); // } - G3D::Vector3 endP = path.back(); DoMovePoint(bot, x, y, z, generatePath, backwards); float delay = 1000.0f * MoveDelay(distance, backwards); if (lessDelay) @@ -342,7 +340,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // WorldPosition movePosition; //The actual end location // float totalDistance = startPosition.distance(endPosition); //Total distance to where we want to go - // float maxDistChange = totalDistance * 0.1; //Maximum change between previous destination + // float maxDistChange = totalDistance * 0.1f; //Maximum change between previous destination // before needing a recalulation // if (totalDistance < minDist) @@ -401,7 +399,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // bot->StopMoving(); // if (botAI->HasStrategy("debug move", BOT_STATE_NON_COMBAT)) - // botAI->TellMasterNoFacing("I have no path"); + // botAI->GetServices().GetChatService().TellMasterNoFacing("I have no path"); // LOG_DEBUG("playerbots", "sServerFacade->IsDistanceGreaterThan(totalDistance, maxDist * 3)"); // return false; // } @@ -431,14 +429,14 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // { // if (movePath.makeShortCut(startPosition, maxDist)) // if (botAI->HasStrategy("debug move", BOT_STATE_NON_COMBAT)) - // botAI->TellMasterNoFacing("Found a shortcut."); + // botAI->GetServices().GetChatService().TellMasterNoFacing("Found a shortcut."); // if (movePath.empty()) // { // AI_VALUE(LastMovement&, "last movement").setPath(movePath); // if (botAI->HasStrategy("debug move", BOT_STATE_NON_COMBAT)) - // botAI->TellMasterNoFacing("Too far from path. Rebuilding."); + // botAI->GetServices().GetChatService().TellMasterNoFacing("Too far from path. Rebuilding."); // LOG_DEBUG("playerbots", "movePath.empty()"); // return true; // } @@ -465,7 +463,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // std::ostringstream out; // out << sPlayerbotAIConfig->GetTimestampStr() << "+00,"; // out << bot->GetName() << ","; - // if (telePos && telePos.GetExactDist(movePosition) > 0.001) + // if (telePos && telePos.GetExactDist(movePosition) > 0.001f) // startPosition.printWKT({ startPosition, movePosition, telePos }, out, 1); // else // startPosition.printWKT({ startPosition, movePosition }, out, 1); @@ -569,7 +567,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // AI_VALUE(LastMovement&, "last movement").setPath(movePath); // if (botAI->HasStrategy("debug move", BOT_STATE_NON_COMBAT)) - // botAI->TellMasterNoFacing("No point. Rebuilding."); + // botAI->GetServices().GetChatService().TellMasterNoFacing("No point. Rebuilding."); // LOG_DEBUG("playerbots", "!movePosition || movePosition.getMapId() != bot->GetMapId()"); // return false; // } @@ -594,7 +592,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // AI_VALUE(LastMovement&, "last movement").setPath(movePath); // if (botAI->HasStrategy("debug move", BOT_STATE_NON_COMBAT)) - // botAI->TellMasterNoFacing("No point. Rebuilding."); + // botAI->GetServices().GetChatService().TellMasterNoFacing("No point. Rebuilding."); // return false; // } @@ -621,7 +619,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // } // //Log bot movement - // if (sPlayerbotAIConfig->hasLog("bot_movement.csv") && lastMove.lastMoveShort.GetExactDist(movePosition) > 0.001) + // if (sPlayerbotAIConfig->hasLog("bot_movement.csv") && lastMove.lastMoveShort.GetExactDist(movePosition) > 0.001f) // { // std::ostringstream out; // out << sPlayerbotAIConfig->GetTimestampStr() << "+00,"; @@ -650,7 +648,7 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // if (bot->IsNonMeleeSpellCast(true)) // { // bot->CastStop(); - // botAI->InterruptSpell(); + // botAI->GetServices().GetSpellService().InterruptSpell(); // } // } @@ -779,8 +777,6 @@ bool MovementAction::MoveTo(WorldObject* target, float distance, MovementPriorit float by = bot->GetPositionY(); float bz = bot->GetPositionZ(); - float tx = target->GetPositionX(); - float ty = target->GetPositionY(); float tz = target->GetPositionZ(); float distanceToTarget = bot->GetDistance(target); @@ -812,10 +808,6 @@ bool MovementAction::ReachCombatTo(Unit* target, float distance) if (!IsMovingAllowed(target)) return false; - float bx = bot->GetPositionX(); - float by = bot->GetPositionY(); - float bz = bot->GetPositionZ(); - float tx = target->GetPositionX(); float ty = target->GetPositionY(); float tz = target->GetPositionZ(); @@ -905,7 +897,7 @@ bool MovementAction::IsMovingAllowed(WorldObject* target) return IsMovingAllowed(); } -bool MovementAction::IsMovingAllowed(uint32 mapId, float x, float y, float z) +bool MovementAction::IsMovingAllowed(uint32 /*mapId*/, float /*x*/, float /*y*/, float /*z*/) { // removed sqrt as means distance limit was effectively 22500 (ReactDistance�) // leaving it commented incase we find ReactDistance limit causes problems @@ -918,7 +910,7 @@ bool MovementAction::IsMovingAllowed(uint32 mapId, float x, float y, float z) return IsMovingAllowed(); } -bool MovementAction::IsDuplicateMove(uint32 mapId, float x, float y, float z) +bool MovementAction::IsDuplicateMove(uint32 /*mapId*/, float x, float y, float z) { LastMovement& lastMove = *context->GetValue("last movement"); @@ -1120,7 +1112,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) if (!bot->InBattleground() && sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target), sPlayerbotAIConfig->followDistance)) { - // botAI->TellError("No need to follow"); + // botAI->GetServices().GetChatService().TellError("No need to follow"); return false; } @@ -1164,13 +1156,13 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) if (bot->isDead() && botAI->GetMaster()->IsAlive()) { bot->ResurrectPlayer(1.0f, false); - botAI->TellMasterNoFacing("I live, again!"); + botAI->GetServices().GetChatService().TellMasterNoFacing("I live, again!"); } else - botAI->TellError("I am stuck while following"); + botAI->GetServices().GetChatService().TellError("I am stuck while following"); bot->CombatStop(true); - botAI->TellMasterNoFacing("I will there soon."); + botAI->GetServices().GetChatService().TellMasterNoFacing("I will there soon."); bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP); bot->TeleportTo(target->GetMapId(), target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation()); return false; @@ -1214,7 +1206,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) float lDist = botPos.fDist(longMove); float tDist = botPos.fDist(tarPos); float ang = botPos.getAngleBetween(tarPos, longMove); - if ((lDist * 1.5 < tDist && ang < static_cast(M_PI) / 2) || + if ((lDist * 1.5f < tDist && ang < static_cast(M_PI) / 2) || target->HasUnitState(UNIT_STATE_IN_FLIGHT)) { return MoveTo(longMove.getMapId(), longMove.getX(), longMove.getY(), longMove.getZ()); @@ -1244,7 +1236,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) if (sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target), sPlayerbotAIConfig->followDistance)) { - // botAI->TellError("No need to follow"); + // botAI->GetServices().GetChatService().TellError("No need to follow"); return false; } @@ -1254,7 +1246,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) if (!bot->InBattleground() && sServerFacade->IsDistanceLessOrEqualThan(sServerFacade->GetDistance2d(bot, target), sPlayerbotAIConfig->followDistance)) { - // botAI->TellError("No need to follow"); + // botAI->GetServices().GetChatService().TellError("No need to follow"); return false; } @@ -1266,7 +1258,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) if (bot->IsNonMeleeSpellCast(true)) { bot->CastStop(); - botAI->InterruptSpell(); + botAI->GetServices().GetSpellService().InterruptSpell(); } // AI_VALUE(LastMovement&, "last movement").Set(target); @@ -1286,7 +1278,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle) return true; } -bool MovementAction::ChaseTo(WorldObject* obj, float distance, float angle) +bool MovementAction::ChaseTo(WorldObject* obj, float distance, float /*angle*/) { if (!IsMovingAllowed()) { @@ -1312,7 +1304,7 @@ bool MovementAction::ChaseTo(WorldObject* obj, float distance, float angle) if (bot->IsNonMeleeSpellCast(true)) { bot->CastStop(); - botAI->InterruptSpell(); + botAI->GetServices().GetSpellService().InterruptSpell(); } // bot->GetMotionMaster()->Clear(); @@ -1383,7 +1375,7 @@ bool MovementAction::Flee(Unit* target) if (!IsMovingAllowed()) { - botAI->TellError("I am stuck while fleeing"); + botAI->GetServices().GetChatService().TellError("I am stuck while fleeing"); return false; } @@ -1414,10 +1406,9 @@ bool MovementAction::Flee(Unit* target) if (!player || player == bot || !player->IsAlive()) continue; - if (botAI->IsTank(player)) + if (BotRoleService::IsTankStatic(player)) { float distanceToTank = sServerFacade->GetDistance2d(bot, player); - float distanceToTarget = sServerFacade->GetDistance2d(bot, target); if (distanceToTank < fleeDistance) { fleeTarget = player; @@ -1437,11 +1428,9 @@ bool MovementAction::Flee(Unit* target) } else // bot is not targeted, try to flee dps/healers { - bool isHealer = botAI->IsHeal(bot); - bool isDps = !isHealer && !botAI->IsTank(bot); - bool isTank = botAI->IsTank(bot); + bool isHealer = BotRoleService::IsHealStatic(bot); bool needHealer = !isHealer && AI_VALUE2(uint8, "health", "self target") < 50; - bool isRanged = botAI->IsRanged(bot); + bool isRanged = BotRoleService::IsRangedStatic(bot); Group* group = bot->GetGroup(); if (group) @@ -1458,7 +1447,7 @@ bool MovementAction::Flee(Unit* target) if (!player || player == bot || !player->IsAlive()) continue; - if ((isHealer && botAI->IsHeal(player)) || needHealer) + if ((isHealer && BotRoleService::IsHealStatic(player)) || needHealer) { float distanceToHealer = sServerFacade->GetDistance2d(bot, player); float distanceToTarget = sServerFacade->GetDistance2d(player, target); @@ -1471,7 +1460,7 @@ bool MovementAction::Flee(Unit* target) possibleTargets.push_back(fleeTarget); } } - else if (isRanged && botAI->IsRanged(player)) + else if (isRanged && BotRoleService::IsRangedStatic(player)) { float distanceToRanged = sServerFacade->GetDistance2d(bot, player); float distanceToTarget = sServerFacade->GetDistance2d(player, target); @@ -1532,14 +1521,14 @@ bool MovementAction::Flee(Unit* target) } } - FleeManager manager(bot, botAI->GetRange("flee"), bot->GetAngle(target) + M_PI); + FleeManager manager(bot, botAI->GetRange("flee")); if (!manager.isUseful()) return false; float rx, ry, rz; if (!manager.CalculateDestination(&rx, &ry, &rz)) { - botAI->TellError("Nowhere to flee"); + botAI->GetServices().GetChatService().TellError("Nowhere to flee"); return false; } @@ -1844,7 +1833,7 @@ void MovementAction::DoMovePoint(Unit* unit, float x, float y, float z, bool gen } } -bool FleeAction::Execute(Event event) +bool FleeAction::Execute(Event /*event*/) { return MoveAway(AI_VALUE(Unit*, "current target"), sPlayerbotAIConfig->fleeDistance, true); } @@ -1862,7 +1851,7 @@ bool FleeAction::isUseful() return true; } -bool FleeWithPetAction::Execute(Event event) +bool FleeWithPetAction::Execute(Event /*event*/) { if (Pet* pet = bot->GetPet()) { @@ -1883,7 +1872,7 @@ bool AvoidAoeAction::isUseful() return AI_VALUE(Aura*, "area debuff") || !traps.empty() || !triggers.empty(); } -bool AvoidAoeAction::Execute(Event event) +bool AvoidAoeAction::Execute(Event /*event*/) { // Case #1: Aura with dynamic object (e.g. rain of fire) if (AvoidAuraWithDynamicObj()) @@ -1919,7 +1908,7 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj() { return false; } - const SpellInfo* spellInfo = aura->GetSpellInfo(); + SpellInfo const* spellInfo = aura->GetSpellInfo(); if (!spellInfo) { return false; @@ -1975,7 +1964,7 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage() { continue; } - const GameObjectTemplate* goInfo = go->GetGOInfo(); + GameObjectTemplate const* goInfo = go->GetGOInfo(); if (!goInfo) { continue; @@ -1994,7 +1983,7 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage() sPlayerbotAIConfig->aoeAvoidSpellWhitelist.end()) continue; - const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo || spellInfo->IsPositive()) { continue; @@ -2054,10 +2043,10 @@ bool AvoidAoeAction::AvoidUnitWithDamageAura() for (auto i = list.begin(); i != list.end(); ++i) { AuraEffect* aurEff = *i; - const SpellInfo* spellInfo = aurEff->GetSpellInfo(); + SpellInfo const* spellInfo = aurEff->GetSpellInfo(); if (!spellInfo) continue; - const SpellInfo* triggerSpellInfo = + SpellInfo const* triggerSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[aurEff->GetEffIndex()].TriggerSpell); if (!triggerSpellInfo) continue; @@ -2240,7 +2229,7 @@ bool MovementAction::FleePosition(Position pos, float radius, uint32 minInterval return false; Position bestPos; - if (botAI->IsMelee(bot)) + if (BotRoleService::IsMeleeStatic(bot)) { bestPos = BestPositionForMeleeToFlee(pos, radius); } @@ -2317,7 +2306,7 @@ bool CombatFormationMoveAction::isUseful() return true; } -bool CombatFormationMoveAction::Execute(Event event) +bool CombatFormationMoveAction::Execute(Event /*event*/) { float dis = AI_VALUE(float, "disperse distance"); if (dis <= 0.0f || (!bot->IsInCombat() && botAI->HasStrategy("stay", BotState::BOT_STATE_NON_COMBAT)) || @@ -2353,7 +2342,7 @@ Position CombatFormationMoveAction::AverageGroupPos(float dis, bool ranged, bool if (!self && member == bot) continue; - if (ranged && !PlayerbotAI::IsRanged(member)) + if (ranged && !BotRoleService::IsRangedStatic(member)) continue; if (!member->IsAlive() || member->GetMapId() != bot->GetMapId() || member->IsCharmed() || @@ -2391,7 +2380,7 @@ float CombatFormationMoveAction::AverageGroupAngle(Unit* from, bool ranged, bool if (!self && member == bot) continue; - if (ranged && !PlayerbotAI::IsRanged(member)) + if (ranged && !BotRoleService::IsRangedStatic(member)) continue; if (!member->IsAlive() || member->GetMapId() != bot->GetMapId() || member->IsCharmed() || @@ -2412,10 +2401,10 @@ float CombatFormationMoveAction::AverageGroupAngle(Unit* from, bool ranged, bool return atan2(sumY, sumX); } -Position CombatFormationMoveAction::GetNearestPosition(const std::vector& positions) +Position CombatFormationMoveAction::GetNearestPosition(std::vector const& positions) { Position result; - for (const Position& pos : positions) + for (Position const& pos : positions) { if (bot->GetExactDist(pos) < bot->GetExactDist(result)) result = pos; @@ -2448,7 +2437,7 @@ Player* CombatFormationMoveAction::NearestGroupMember(float dis) return result; } -bool TankFaceAction::Execute(Event event) +bool TankFaceAction::Execute(Event /*event*/) { Unit* target = AI_VALUE(Unit*, "current target"); if (!target) @@ -2532,7 +2521,7 @@ bool RearFlankAction::isUseful() return inFront || inRear; } -bool RearFlankAction::Execute(Event event) +bool RearFlankAction::Execute(Event /*event*/) { Unit* target = AI_VALUE(Unit*, "current target"); if (!target) @@ -2565,12 +2554,12 @@ bool DisperseSetAction::Execute(Event event) if (text == "disable") { RESET_AI_VALUE(float, "disperse distance"); - botAI->TellMasterNoFacing("Disable disperse"); + botAI->GetServices().GetChatService().TellMasterNoFacing("Disable disperse"); return true; } if (text == "enable" || text == "reset") { - if (botAI->IsMelee(bot)) + if (BotRoleService::IsMeleeStatic(bot)) { SET_AI_VALUE(float, "disperse distance", DEFAULT_DISPERSE_DISTANCE_MELEE); } @@ -2581,7 +2570,7 @@ bool DisperseSetAction::Execute(Event event) float dis = AI_VALUE(float, "disperse distance"); std::ostringstream out; out << "Enable disperse distance " << std::setprecision(2) << dis; - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); return true; } if (text == "increase") @@ -2591,13 +2580,13 @@ bool DisperseSetAction::Execute(Event event) if (dis <= 0.0f) { out << "Enable disperse first"; - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); return true; } dis += 1.0f; SET_AI_VALUE(float, "disperse distance", dis); out << "Increase disperse distance to " << std::setprecision(2) << dis; - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); return true; } if (text == "decrease") @@ -2611,7 +2600,7 @@ bool DisperseSetAction::Execute(Event event) SET_AI_VALUE(float, "disperse distance", dis); std::ostringstream out; out << "Increase disperse distance to " << std::setprecision(2) << dis; - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); return true; } if (text.starts_with("set")) @@ -2629,7 +2618,7 @@ bool DisperseSetAction::Execute(Event event) SET_AI_VALUE(float, "disperse distance", dis); out << "Set disperse distance to " << std::setprecision(2) << dis; } - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); return true; } std::ostringstream out; @@ -2639,13 +2628,13 @@ bool DisperseSetAction::Execute(Event event) { out << "(Current disperse distance: " << std::setprecision(2) << dis << ")"; } - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); return true; } -bool RunAwayAction::Execute(Event event) { return Flee(AI_VALUE(Unit*, "group leader")); } +bool RunAwayAction::Execute(Event /*event*/) { return Flee(AI_VALUE(Unit*, "group leader")); } -bool MoveToLootAction::Execute(Event event) +bool MoveToLootAction::Execute(Event /*event*/) { LootObject loot = AI_VALUE(LootObject, "loot target"); if (!loot.IsLootPossible(bot)) @@ -2654,7 +2643,7 @@ bool MoveToLootAction::Execute(Event event) return MoveNear(loot.GetWorldObject(bot), sPlayerbotAIConfig->contactDistance); } -bool MoveOutOfEnemyContactAction::Execute(Event event) +bool MoveOutOfEnemyContactAction::Execute(Event /*event*/) { Unit* target = AI_VALUE(Unit*, "current target"); if (!target) @@ -2665,7 +2654,7 @@ bool MoveOutOfEnemyContactAction::Execute(Event event) bool MoveOutOfEnemyContactAction::isUseful() { return AI_VALUE2(bool, "inside target", "current target"); } -bool SetFacingTargetAction::Execute(Event event) +bool SetFacingTargetAction::Execute(Event /*event*/) { Unit* target = AI_VALUE(Unit*, "current target"); if (!target) @@ -2691,7 +2680,7 @@ bool SetFacingTargetAction::isPossible() return true; } -bool SetBehindTargetAction::Execute(Event event) +bool SetBehindTargetAction::Execute(Event /*event*/) { Unit* target = AI_VALUE(Unit*, "current target"); if (!target) @@ -2751,7 +2740,7 @@ bool SetBehindTargetAction::Execute(Event event) false, true, MovementPriority::MOVEMENT_COMBAT); } -bool MoveOutOfCollisionAction::Execute(Event event) +bool MoveOutOfCollisionAction::Execute(Event /*event*/) { float angle = M_PI * 2000 / frand(1.f, 1000.f); float distance = sPlayerbotAIConfig->followDistance; @@ -2762,14 +2751,14 @@ bool MoveOutOfCollisionAction::Execute(Event event) bool MoveOutOfCollisionAction::isUseful() { // do not avoid collision on vehicle - if (botAI->IsInVehicle()) + if (botAI->GetServices().GetSpellService().IsInVehicle()) return false; return AI_VALUE2(bool, "collision", "self target") && botAI->GetAiObjectContext()->GetValue("nearest friendly players")->Get().size() < 15; } -bool MoveRandomAction::Execute(Event event) +bool MoveRandomAction::Execute(Event /*event*/) { float distance = sPlayerbotAIConfig->tooCloseDistance + urand(10, 30); @@ -2782,9 +2771,6 @@ bool MoveRandomAction::Execute(Event event) float angle = (float)rand_norm() * static_cast(M_PI); x += urand(0, distance) * cos(angle); y += urand(0, distance) * sin(angle); - float ox = x; - float oy = y; - float oz = z; if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), x, y, z)) continue; @@ -2801,9 +2787,9 @@ bool MoveRandomAction::Execute(Event event) bool MoveRandomAction::isUseful() { return !AI_VALUE(GuidPosition, "rpg target"); } -bool MoveInsideAction::Execute(Event event) { return MoveInside(bot->GetMapId(), x, y, bot->GetPositionZ(), distance); } +bool MoveInsideAction::Execute(Event /*event*/) { return MoveInside(bot->GetMapId(), x, y, bot->GetPositionZ(), distance); } -bool RotateAroundTheCenterPointAction::Execute(Event event) +bool RotateAroundTheCenterPointAction::Execute(Event /*event*/) { uint32 next_point = GetCurrWaypoint(); if (MoveTo(bot->GetMapId(), waypoints[next_point].first, waypoints[next_point].second, bot->GetPositionZ(), false, @@ -2823,10 +2809,9 @@ bool MoveFromGroupAction::Execute(Event event) return MoveFromGroup(distance); } -bool MoveAwayFromCreatureAction::Execute(Event event) +bool MoveAwayFromCreatureAction::Execute(Event /*event*/) { GuidVector targets = AI_VALUE(GuidVector, "nearest npcs"); - Creature* nearestCreature = bot->FindNearestCreature(creatureId, range, alive); // Find all creatures with the specified Id std::vector creatures; @@ -2904,11 +2889,8 @@ bool MoveAwayFromCreatureAction::Execute(Event event) bool MoveAwayFromCreatureAction::isPossible() { return bot->CanFreeMove(); } -bool MoveAwayFromPlayerWithDebuffAction::Execute(Event event) +bool MoveAwayFromPlayerWithDebuffAction::Execute(Event /*event*/) { - Player* closestPlayer = nullptr; - float minDistance = 0.0f; - Group* group = bot->GetGroup(); if (!group) { diff --git a/src/Ai/Base/Actions/MovementActions.h b/src/Ai/Base/Actions/MovementActions.h index d12bcd555c..ac7403f389 100644 --- a/src/Ai/Base/Actions/MovementActions.h +++ b/src/Ai/Base/Actions/MovementActions.h @@ -109,7 +109,7 @@ class FleeWithPetAction : public MovementAction class AvoidAoeAction : public MovementAction { public: - AvoidAoeAction(PlayerbotAI* botAI, int moveInterval = 1000) + AvoidAoeAction(PlayerbotAI* botAI, uint32 moveInterval = 1000) : MovementAction(botAI, "avoid aoe"), moveInterval(moveInterval) { } @@ -122,14 +122,14 @@ class AvoidAoeAction : public MovementAction bool AvoidGameObjectWithDamage(); bool AvoidUnitWithDamageAura(); time_t lastTellTimer = 0; - int lastMoveTimer = 0; - int moveInterval; + uint32 lastMoveTimer = 0; + uint32 moveInterval; }; class CombatFormationMoveAction : public MovementAction { public: - CombatFormationMoveAction(PlayerbotAI* botAI, std::string name = "combat formation move", int moveInterval = 1000) + CombatFormationMoveAction(PlayerbotAI* botAI, std::string name = "combat formation move", uint32 moveInterval = 1000) : MovementAction(botAI, name), moveInterval(moveInterval) { } @@ -141,9 +141,9 @@ class CombatFormationMoveAction : public MovementAction Position AverageGroupPos(float dis = sPlayerbotAIConfig->sightDistance, bool ranged = false, bool self = false); Player* NearestGroupMember(float dis = sPlayerbotAIConfig->sightDistance); float AverageGroupAngle(Unit* from, bool ranged = false, bool self = false); - Position GetNearestPosition(const std::vector& positions); - int lastMoveTimer = 0; - int moveInterval; + Position GetNearestPosition(std::vector const& positions); + uint32 lastMoveTimer = 0; + uint32 moveInterval; }; class TankFaceAction : public CombatFormationMoveAction @@ -276,7 +276,7 @@ class RotateAroundTheCenterPointAction : public MovementAction this->intervals = intervals; this->clockwise = clockwise; this->call_counters = 0; - for (int i = 0; i < intervals; i++) + for (uint32 i = 0; i < intervals; i++) { float angle = start_angle + 2 * M_PI * i / intervals; waypoints.push_back(std::make_pair(center_x + cos(angle) * radius, center_y + sin(angle) * radius)); diff --git a/src/Ai/Base/Actions/NonCombatActions.cpp b/src/Ai/Base/Actions/NonCombatActions.cpp index f87aa891dd..54030f397e 100644 --- a/src/Ai/Base/Actions/NonCombatActions.cpp +++ b/src/Ai/Base/Actions/NonCombatActions.cpp @@ -7,6 +7,7 @@ #include "Event.h" #include "Playerbots.h" +#include "BotSpellService.h" bool DrinkAction::Execute(Event event) { @@ -25,7 +26,7 @@ bool DrinkAction::Execute(Event event) // return false; } bot->SetStandState(UNIT_STAND_STATE_SIT); - botAI->InterruptSpell(); + botAI->GetServices().GetSpellService().InterruptSpell(); // float hp = bot->GetHealthPercent(); float mp = bot->GetPowerPct(POWER_MANA); @@ -41,7 +42,7 @@ bool DrinkAction::Execute(Event event) bot->AddAura(25990, bot); return true; - // return botAI->CastSpell(24707, bot); + // return botAI->GetServices().GetSpellService().CastSpell(24707, bot); } return UseItemAction::Execute(event); @@ -58,7 +59,7 @@ bool DrinkAction::isPossible() { return !bot->IsInCombat() && !bot->IsMounted() && - !botAI->HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form", + !botAI->GetServices().GetSpellService().HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form", "aquatic form","flight form", "swift flight form", nullptr) && (botAI->HasCheat(BotCheatMask::food) || UseItemAction::isPossible()); } @@ -81,7 +82,7 @@ bool EatAction::Execute(Event event) } bot->SetStandState(UNIT_STAND_STATE_SIT); - botAI->InterruptSpell(); + botAI->GetServices().GetSpellService().InterruptSpell(); float hp = bot->GetHealthPct(); // float mp = bot->HasMana() ? bot->GetPowerPercent() : 0.f; @@ -112,7 +113,7 @@ bool EatAction::isPossible() { return !bot->IsInCombat() && !bot->IsMounted() && - !botAI->HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form", + !botAI->GetServices().GetSpellService().HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form", "aquatic form","flight form", "swift flight form", nullptr) && (botAI->HasCheat(BotCheatMask::food) || UseItemAction::isPossible()); } diff --git a/src/Ai/Base/Actions/OpenItemAction.cpp b/src/Ai/Base/Actions/OpenItemAction.cpp index d1ebcbaaaf..3939866231 100644 --- a/src/Ai/Base/Actions/OpenItemAction.cpp +++ b/src/Ai/Base/Actions/OpenItemAction.cpp @@ -1,3 +1,4 @@ +#include "BotChatService.h" #include "OpenItemAction.h" #include "PlayerbotAI.h" #include "ItemTemplate.h" @@ -6,12 +7,13 @@ #include "ObjectMgr.h" #include "LootObjectStack.h" #include "AiObjectContext.h" +#include "BotItemService.h" bool OpenItemAction::Execute(Event event) { bool foundOpenable = false; - Item* item = botAI->FindOpenableItem(); + Item* item = botAI->GetServices().GetItemService().FindOpenableItem(); if (item) { uint8 bag = item->GetBagSlot(); // Retrieves the bag slot (0 for main inventory) @@ -37,5 +39,5 @@ void OpenItemAction::OpenItem(Item* item, uint8 bag, uint8 slot) std::ostringstream out; out << "Opened item: " << item->GetTemplate()->Name1; - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); } diff --git a/src/Ai/Base/Actions/OutfitAction.cpp b/src/Ai/Base/Actions/OutfitAction.cpp index 78f7f9bc45..84296266c7 100644 --- a/src/Ai/Base/Actions/OutfitAction.cpp +++ b/src/Ai/Base/Actions/OutfitAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "OutfitAction.h" #include "Event.h" @@ -17,9 +18,9 @@ bool OutfitAction::Execute(Event event) if (param == "?") { List(); - botAI->TellMaster("outfit +[item] to add items"); - botAI->TellMaster("outfit -[item] to remove items"); - botAI->TellMaster("outfit equip/replace to equip items"); + botAI->GetServices().GetChatService().TellMaster("outfit +[item] to add items"); + botAI->GetServices().GetChatService().TellMaster("outfit -[item] to remove items"); + botAI->GetServices().GetChatService().TellMaster("outfit equip/replace to equip items"); } else { @@ -31,7 +32,7 @@ bool OutfitAction::Execute(Event event) std::ostringstream out; out << "Setting outfit " << name << " as " << param; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return true; } @@ -49,7 +50,7 @@ bool OutfitAction::Execute(Event event) { std::ostringstream out; out << "Equipping outfit " << name; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); EquipItems(outfit); return true; @@ -58,7 +59,7 @@ bool OutfitAction::Execute(Event event) { std::ostringstream out; out << "Replacing current equip with outfit " << name; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++) { @@ -83,7 +84,7 @@ bool OutfitAction::Execute(Event event) { std::ostringstream out; out << "Resetting outfit " << name; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); Save(name, ItemIds()); return true; @@ -92,7 +93,7 @@ bool OutfitAction::Execute(Event event) { std::ostringstream out; out << "Updating with current items outfit " << name; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); Update(name); return true; @@ -120,7 +121,7 @@ bool OutfitAction::Execute(Event event) } out << name; - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); } Save(name, outfit); @@ -178,7 +179,7 @@ void OutfitAction::List() if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId)) out << chat->FormatItem(proto) << " "; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } } diff --git a/src/Ai/Base/Actions/PassLeadershipToMasterAction.cpp b/src/Ai/Base/Actions/PassLeadershipToMasterAction.cpp index 87890c1c57..1188e18b46 100644 --- a/src/Ai/Base/Actions/PassLeadershipToMasterAction.cpp +++ b/src/Ai/Base/Actions/PassLeadershipToMasterAction.cpp @@ -5,12 +5,14 @@ #include "PassLeadershipToMasterAction.h" +#include "Bot/Core/ManagerRegistry.h" #include "Event.h" #include "PlayerbotOperations.h" #include "Playerbots.h" #include "PlayerbotWorldThreadProcessor.h" +#include "BotChatService.h" -bool PassLeadershipToMasterAction::Execute(Event event) +bool PassLeadershipToMasterAction::Execute(Event /*event*/) { if (Player* master = GetMaster()) if (master && master != bot && bot->GetGroup() && bot->GetGroup()->IsMember(master->GetGUID())) @@ -19,9 +21,9 @@ bool PassLeadershipToMasterAction::Execute(Event event) sPlayerbotWorldProcessor->QueueOperation(std::move(setLeaderOp)); if (!message.empty()) - botAI->TellMasterNoFacing(message); + botAI->GetServices().GetChatService().TellMasterNoFacing(message); - if (sRandomPlayerbotMgr->IsRandomBot(bot)) + if (sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) { botAI->ResetStrategies(); botAI->Reset(); diff --git a/src/Ai/Base/Actions/PetitionSignAction.cpp b/src/Ai/Base/Actions/PetitionSignAction.cpp index 9b6cebfcb4..8e6e8b1381 100644 --- a/src/Ai/Base/Actions/PetitionSignAction.cpp +++ b/src/Ai/Base/Actions/PetitionSignAction.cpp @@ -4,6 +4,7 @@ */ #include "PetitionSignAction.h" +#include "BotChatService.h" #include "ArenaTeam.h" #include "Event.h" @@ -38,7 +39,7 @@ bool PetitionSignAction::Execute(Event event) if (bot->GetArenaTeamId(slot)) { // player is already in an arena team - botAI->TellError("Sorry, I am already in such team"); + botAI->GetServices().GetChatService().TellError("Sorry, I am already in such team"); accept = false; } } @@ -46,13 +47,13 @@ bool PetitionSignAction::Execute(Event event) { if (bot->GetGuildId()) { - botAI->TellError("Sorry, I am in a guild already"); + botAI->GetServices().GetChatService().TellError("Sorry, I am in a guild already"); accept = false; } if (bot->GetGuildIdInvited()) { - botAI->TellError("Sorry, I am invited to a guild already"); + botAI->GetServices().GetChatService().TellError("Sorry, I am invited to a guild already"); accept = false; } @@ -60,7 +61,7 @@ bool PetitionSignAction::Execute(Event event) /*if (QueryResult* result = CharacterDatabase.Query("SELECT playerguid FROM petition_sign WHERE player_account = {} AND petitionguid = {}'", bot->GetSession()->GetAccountId(), petitionGuid.GetCounter())) { - botAI->TellError("Sorry, I already signed this pettition"); + botAI->GetServices().GetChatService().TellError("Sorry, I already signed this pettition"); accept = false; } */ diff --git a/src/Ai/Base/Actions/PetsAction.cpp b/src/Ai/Base/Actions/PetsAction.cpp index cb9dc93957..11d9e3477c 100644 --- a/src/Ai/Base/Actions/PetsAction.cpp +++ b/src/Ai/Base/Actions/PetsAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "PetsAction.h" #include "CharmInfo.h" @@ -25,7 +26,7 @@ bool PetsAction::Execute(Event event) // If no parameter is provided, show usage instructions and return. std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( "pet_usage_error", "Usage: pet ", {}); - botAI->TellError(text); + botAI->GetServices().GetChatService().TellError(text); return false; } @@ -54,7 +55,7 @@ bool PetsAction::Execute(Event event) { std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( "pet_no_pet_error", "You have no pet or guardian pet.", {}); - botAI->TellError(text); + botAI->GetServices().GetChatService().TellError(text); return false; } @@ -112,7 +113,7 @@ bool PetsAction::Execute(Event event) std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( "pet_stance_report", "Current stance of %type \"%name\": %stance.", {{"type", type}, {"name", name}, {"stance", stance}}); - botAI->TellMaster(text); + botAI->GetServices().GetChatService().TellMaster(text); } return true; } @@ -135,21 +136,21 @@ bool PetsAction::Execute(Event event) { std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( "pet_no_target_error", "No valid target selected by master.", {}); - botAI->TellError(text); + botAI->GetServices().GetChatService().TellError(text); return false; } if (!targetUnit->IsAlive()) { std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( "pet_target_dead_error", "Target is not alive.", {}); - botAI->TellError(text); + botAI->GetServices().GetChatService().TellError(text); return false; } if (!bot->IsValidAttackTarget(targetUnit)) { std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( "pet_invalid_target_error", "Target is not a valid attack target for the bot.", {}); - botAI->TellError(text); + botAI->GetServices().GetChatService().TellError(text); return false; } if (sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()) && @@ -158,7 +159,7 @@ bool PetsAction::Execute(Event event) { std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( "pet_pvp_prohibited_error", "I cannot command my pet to attack players in PvP prohibited areas.", {}); - botAI->TellError(text); + botAI->GetServices().GetChatService().TellError(text); return false; } @@ -212,13 +213,13 @@ bool PetsAction::Execute(Event event) { std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( "pet_attack_success", "Pet commanded to attack your target.", {}); - botAI->TellMaster(text); + botAI->GetServices().GetChatService().TellMaster(text); } else if (!didAttack) { std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( "pet_attack_failed", "Pet did not attack. (Already attacking or unable to attack target)", {}); - botAI->TellError(text); + botAI->GetServices().GetChatService().TellError(text); } return didAttack; } @@ -230,7 +231,7 @@ bool PetsAction::Execute(Event event) { std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( "pet_follow_success", "Pet commanded to follow.", {}); - botAI->TellMaster(text); + botAI->GetServices().GetChatService().TellMaster(text); } return true; } @@ -271,7 +272,7 @@ bool PetsAction::Execute(Event event) { std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( "pet_stay_success", "Pet commanded to stay.", {}); - botAI->TellMaster(text); + botAI->GetServices().GetChatService().TellMaster(text); } return true; } @@ -281,7 +282,7 @@ bool PetsAction::Execute(Event event) std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( "pet_unknown_command_error", "Unknown pet command: %param. Use: pet ", {{"param", param}}); - botAI->TellError(text); + botAI->GetServices().GetChatService().TellError(text); return false; } @@ -300,7 +301,7 @@ bool PetsAction::Execute(Event event) std::string text = sPlayerbotTextMgr->GetBotTextOrDefault( "pet_stance_set_success", "Pet stance set to %stance.", {{"stance", stanceText}}); - botAI->TellMaster(text); + botAI->GetServices().GetChatService().TellMaster(text); } return true; diff --git a/src/Ai/Base/Actions/PetsAction.h b/src/Ai/Base/Actions/PetsAction.h index f9334e3194..1afab02ff3 100644 --- a/src/Ai/Base/Actions/PetsAction.h +++ b/src/Ai/Base/Actions/PetsAction.h @@ -17,12 +17,11 @@ class PlayerbotAI; class PetsAction : public Action { public: - PetsAction(PlayerbotAI* botAI, const std::string& defaultCmd = "") : Action(botAI, "pet"), defaultCmd(defaultCmd) {} + PetsAction(PlayerbotAI* botAI, std::string const& defaultCmd = "") : Action(botAI, "pet"), defaultCmd(defaultCmd) {} bool Execute(Event event) override; private: - bool warningEnabled = true; std::string defaultCmd; }; diff --git a/src/Ai/Base/Actions/PositionAction.cpp b/src/Ai/Base/Actions/PositionAction.cpp index 2de27f0414..5bbeedba10 100644 --- a/src/Ai/Base/Actions/PositionAction.cpp +++ b/src/Ai/Base/Actions/PositionAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "PositionAction.h" #include "Event.h" @@ -24,7 +25,7 @@ void TellPosition(PlayerbotAI* botAI, std::string const name, PositionInfo pos) else out << " is not set"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } bool PositionAction::Execute(Event event) @@ -52,7 +53,7 @@ bool PositionAction::Execute(Event event) std::vector params = split(param, ' '); if (params.size() != 2) { - botAI->TellMaster("Whisper position ?/set/reset"); + botAI->GetServices().GetChatService().TellMaster("Whisper position ?/set/reset"); return false; } @@ -73,7 +74,7 @@ bool PositionAction::Execute(Event event) std::ostringstream out; out << "Position " << name << " is set"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return true; } @@ -84,7 +85,7 @@ bool PositionAction::Execute(Event event) std::ostringstream out; out << "Position " << name << " is set"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return true; } @@ -95,21 +96,21 @@ bool PositionAction::Execute(Event event) std::ostringstream out; out << "Position " << name << " is reset"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return true; } return false; } -bool MoveToPositionAction::Execute(Event event) +bool MoveToPositionAction::Execute(Event /*event*/) { PositionInfo pos = context->GetValue("position")->Get()[qualifier]; if (!pos.isSet()) { std::ostringstream out; out << "Position " << qualifier << " is not set"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return false; } @@ -123,7 +124,7 @@ bool MoveToPositionAction::isUseful() return pos.isSet() && distance > sPlayerbotAIConfig->followDistance && distance < sPlayerbotAIConfig->reactDistance; } -bool SetReturnPositionAction::Execute(Event event) +bool SetReturnPositionAction::Execute(Event /*event*/) { PositionMap& posMap = context->GetValue("position")->Get(); PositionInfo returnPos = posMap["return"]; @@ -169,7 +170,7 @@ bool ReturnToStayPositionAction::isPossible() const float distance = bot->GetDistance(stayPosition.x, stayPosition.y, stayPosition.z); if (distance > sPlayerbotAIConfig->reactDistance) { - botAI->TellMaster("The stay position is too far to return. I am going to stay where I am now"); + botAI->GetServices().GetChatService().TellMaster("The stay position is too far to return. I am going to stay where I am now"); // Set the stay position to current position stayPosition.Set(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId()); diff --git a/src/Ai/Base/Actions/QueryItemUsageAction.cpp b/src/Ai/Base/Actions/QueryItemUsageAction.cpp index 2fbc75af8f..7f082706b7 100644 --- a/src/Ai/Base/Actions/QueryItemUsageAction.cpp +++ b/src/Ai/Base/Actions/QueryItemUsageAction.cpp @@ -3,8 +3,10 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "QueryItemUsageAction.h" +#include "Bot/Core/ManagerRegistry.h" #include "ChatHelper.h" #include "Event.h" #include "ItemUsageValue.h" @@ -36,7 +38,7 @@ bool QueryItemUsageAction::Execute(Event event) uint32 total = bot->GetItemCount(itemTemplate->ItemId, true); std::string itemInfo = QueryItem(itemTemplate, count, total); - botAI->TellMaster(itemInfo); + botAI->GetServices().GetChatService().TellMaster(itemInfo); return true; // Only process the first valid item } @@ -118,7 +120,7 @@ std::string const QueryItemUsageAction::QueryItemUsage(ItemTemplate const* item) std::string const QueryItemUsageAction::QueryItemPrice(ItemTemplate const* item) { - if (!sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) return ""; if (item->Bonding == BIND_WHEN_PICKED_UP) @@ -133,7 +135,7 @@ std::string const QueryItemUsageAction::QueryItemPrice(ItemTemplate const* item) { Item* sell = *i; int32 price = - sell->GetCount() * sell->GetTemplate()->SellPrice * sRandomPlayerbotMgr->GetSellMultiplier(bot); + sell->GetCount() * sell->GetTemplate()->SellPrice * sManagerRegistry.GetRandomBotManager().GetSellMultiplier(bot); if (!sellPrice || sellPrice > price) sellPrice = price; } @@ -147,7 +149,7 @@ std::string const QueryItemUsageAction::QueryItemPrice(ItemTemplate const* item) if (usage == ITEM_USAGE_NONE) return msg.str(); - int32 buyPrice = item->BuyPrice * sRandomPlayerbotMgr->GetBuyMultiplier(bot); + int32 buyPrice = item->BuyPrice * sManagerRegistry.GetRandomBotManager().GetBuyMultiplier(bot); if (buyPrice) { if (sellPrice) diff --git a/src/Ai/Base/Actions/QueryQuestAction.cpp b/src/Ai/Base/Actions/QueryQuestAction.cpp index 6ceccad975..694e9154ab 100644 --- a/src/Ai/Base/Actions/QueryQuestAction.cpp +++ b/src/Ai/Base/Actions/QueryQuestAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "QueryQuestAction.h" #include "ChatHelper.h" @@ -11,12 +12,11 @@ void QueryQuestAction::TellObjective(std::string const name, uint32 available, uint32 required) { - botAI->TellMaster(chat->FormatQuestObjective(name, available, required)); + botAI->GetServices().GetChatService().TellMaster(chat->FormatQuestObjective(name, available, required)); } bool QueryQuestAction::Execute(Event event) { - Player* requester = event.getOwner() ? event.getOwner() : GetMaster(); Player* bot = botAI->GetBot(); WorldPosition botPos(bot); WorldPosition* ptr_botpos = &botPos; @@ -60,12 +60,12 @@ bool QueryQuestAction::Execute(Event event) if (bot->GetQuestStatus(questId) == QUEST_STATUS_COMPLETE) { out << "|c0000FF00completed|r ---"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } else { out << "|c00FF0000not completed|r ---"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); TellObjectives(questId); } @@ -98,7 +98,7 @@ bool QueryQuestAction::Execute(Event event) if (!dest->isActive(bot)) out << " not active"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); limit++; } @@ -117,7 +117,7 @@ void QueryQuestAction::TellObjectives(uint32 questId) // Checks if the questTemplate is valid if (!questTemplate) { - botAI->TellMaster("Quest template not found."); + botAI->GetServices().GetChatService().TellMaster("Quest template not found."); return; } @@ -127,7 +127,7 @@ void QueryQuestAction::TellObjectives(uint32 questId) { // Checks for objective text if (!questTemplate->ObjectiveText[i].empty()) - botAI->TellMaster(questTemplate->ObjectiveText[i]); + botAI->GetServices().GetChatService().TellMaster(questTemplate->ObjectiveText[i]); // Checks for required items if (questTemplate->RequiredItemId[i]) diff --git a/src/Ai/Base/Actions/QuestAction.cpp b/src/Ai/Base/Actions/QuestAction.cpp index b96c539475..fa4d65671c 100644 --- a/src/Ai/Base/Actions/QuestAction.cpp +++ b/src/Ai/Base/Actions/QuestAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "QuestAction.h" #include @@ -16,6 +17,7 @@ #include "ReputationMgr.h" #include "ServerFacade.h" #include "BroadcastHelper.h" +#include "BotItemService.h" bool QuestAction::Execute(Event event) { @@ -152,13 +154,13 @@ bool QuestAction::CompleteQuest(Player* player, uint32 entry) player->ModifyMoney(-ReqOrRewMoney); } - const std::string text_quest = ChatHelper::FormatQuest(pQuest); + std::string const text_quest = ChatHelper::FormatQuest(pQuest); if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT) || botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) { LOG_INFO("playerbots", "{} => Quest [ {} ] completed", bot->GetName(), pQuest->GetTitle()); bot->Say("Quest [ " + text_quest + " ] completed", LANG_UNIVERSAL); } - botAI->TellMasterNoFacing("Quest completed " + text_quest); + botAI->GetServices().GetChatService().TellMasterNoFacing("Quest completed " + text_quest); player->CompleteQuest(entry); @@ -186,7 +188,7 @@ bool QuestAction::ProcessQuests(WorldObject* questGiver) { //if (botAI->HasStrategy("debug", BotState::BOT_STATE_COMBAT) || botAI->HasStrategy("debug", BotState::BOT_STATE_NON_COMBAT)) - botAI->TellError("Cannot talk to quest giver"); + botAI->GetServices().GetChatService().TellError("Cannot talk to quest giver"); return false; } @@ -249,14 +251,14 @@ bool QuestAction::AcceptQuest(Quest const* quest, ObjectGuid questGiver) { BroadcastHelper::BroadcastQuestAccepted(botAI, bot, quest); out << "Accepted " << chat->FormatQuest(quest); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return true; } out << "Cannot accept"; } out << " " << chat->FormatQuest(quest); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return false; } @@ -286,7 +288,7 @@ bool QuestUpdateCompleteAction::Execute(Event event) // } const auto format = ChatHelper::FormatQuest(qInfo); if (botAI->GetMaster()) - botAI->TellMasterNoFacing("Quest completed " + format); + botAI->GetServices().GetChatService().TellMasterNoFacing("Quest completed " + format); BroadcastHelper::BroadcastQuestUpdateComplete(botAI, bot, qInfo); botAI->rpgStatistic.questCompleted++; // LOG_DEBUG("playerbots", "[New rpg] {} complete quest {}", bot->GetName(), qInfo->GetQuestId()); @@ -307,11 +309,11 @@ bool QuestUpdateAddKillAction::Execute(Event event) uint32 entry, questId, available, required; p >> questId >> entry >> available >> required; // LOG_INFO("playerbots", "[New rpg] Quest {} -> Creature {} ({}/{})", questId, entry, available, required); - const Quest* qInfo = sObjectMgr->GetQuestTemplate(questId); + Quest const* qInfo = sObjectMgr->GetQuestTemplate(questId); if (qInfo && (entry & 0x80000000)) { entry &= 0x7FFFFFFF; - const GameObjectTemplate* info = sObjectMgr->GetGameObjectTemplate(entry); + GameObjectTemplate const* info = sObjectMgr->GetGameObjectTemplate(entry); if (info) { std::string infoName = botAI->GetLocalizedGameObjectName(entry); @@ -320,7 +322,7 @@ bool QuestUpdateAddKillAction::Execute(Event event) { std::ostringstream out; out << infoName << " " << available << "/" << required << " " << ChatHelper::FormatQuest(qInfo); - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); } } } @@ -335,7 +337,7 @@ bool QuestUpdateAddKillAction::Execute(Event event) { std::ostringstream out; out << infoName << " " << available << "/" << required << " " << ChatHelper::FormatQuest(qInfo); - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); } } } @@ -350,16 +352,15 @@ bool QuestUpdateAddItemAction::Execute(Event event) uint32 itemId, count; p >> itemId >> count; - Player* requester = event.getOwner() ? event.getOwner() : GetMaster(); auto const* itemPrototype = sObjectMgr->GetItemTemplate(itemId); if (itemPrototype) { std::map placeholders; placeholders["%item_link"] = botAI->GetChatHelper()->FormatItem(itemPrototype); - uint32 availableItemsCount = botAI->GetInventoryItemsCountWithId(itemId); + uint32 availableItemsCount = botAI->GetServices().GetItemService().GetInventoryItemsCountWithId(itemId); placeholders["%quest_obj_available"] = std::to_string(availableItemsCount); - for (auto const& pair : botAI->GetCurrentQuestsRequiringItemId(itemId)) + for (auto const& pair : botAI->GetServices().GetItemService().GetCurrentQuestsRequiringItemId(itemId)) { placeholders["%quest_link"] = chat->FormatQuest(pair.first); uint32 requiredItemsCount = pair.second; @@ -367,7 +368,7 @@ bool QuestUpdateAddItemAction::Execute(Event event) if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_COMBAT) || botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT)) { const auto text = BOT_TEXT2("%quest_link - %item_link %quest_obj_available/%quest_obj_required", placeholders); - botAI->Say(text); + botAI->GetServices().GetChatService().Say(text); LOG_INFO("playerbots", "{} => {}", bot->GetName(), text); } @@ -391,7 +392,7 @@ bool QuestItemPushResultAction::Execute(Event event) if (guid != bot->GetGUID()) return false; - const ItemTemplate* proto = sObjectMgr->GetItemTemplate(itemEntry); + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemEntry); if (!proto) return false; @@ -401,12 +402,10 @@ bool QuestItemPushResultAction::Execute(Event event) if (!questId) continue; - const Quest* quest = sObjectMgr->GetQuestTemplate(questId); + Quest const* quest = sObjectMgr->GetQuestTemplate(questId); if (!quest) return false; - const QuestStatusData& q_status = bot->getQuestStatusMap().at(questId); - for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; i++) { uint32 itemId = quest->RequiredItemId[i]; @@ -414,7 +413,7 @@ bool QuestItemPushResultAction::Execute(Event event) continue; int32 previousCount = itemCount - count; - if (itemId == itemEntry && previousCount < quest->RequiredItemCount[i]) + if (itemId == itemEntry && previousCount < static_cast(quest->RequiredItemCount[i])) { if (botAI->GetMaster()) { @@ -423,7 +422,7 @@ bool QuestItemPushResultAction::Execute(Event event) int32 required = quest->RequiredItemCount[i]; int32 available = std::min((int32)itemCount, required); out << itemLink << " " << available << "/" << required << " " << ChatHelper::FormatQuest(quest); - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); } } } @@ -432,7 +431,7 @@ bool QuestItemPushResultAction::Execute(Event event) return false; } -bool QuestUpdateFailedAction::Execute(Event event) +bool QuestUpdateFailedAction::Execute(Event /*event*/) { //opcode SMSG_QUESTUPDATE_FAILED is never sent...(yet?) return false; @@ -446,20 +445,18 @@ bool QuestUpdateFailedTimerAction::Execute(Event event) uint32 questId; p >> questId; - Player* requester = event.getOwner() ? event.getOwner() : GetMaster(); - Quest const* qInfo = sObjectMgr->GetQuestTemplate(questId); if (qInfo) { std::map placeholders; placeholders["%quest_link"] = botAI->GetChatHelper()->FormatQuest(qInfo); - botAI->TellMaster(BOT_TEXT2("Failed timer for %quest_link, abandoning", placeholders)); + botAI->GetServices().GetChatService().TellMaster(BOT_TEXT2("Failed timer for %quest_link, abandoning", placeholders)); BroadcastHelper::BroadcastQuestUpdateFailedTimer(botAI, bot, qInfo); } else { - botAI->TellMaster("Failed timer for " + std::to_string(questId)); + botAI->GetServices().GetChatService().TellMaster("Failed timer for " + std::to_string(questId)); } //drop quest diff --git a/src/Ai/Base/Actions/QuestConfirmAcceptAction.cpp b/src/Ai/Base/Actions/QuestConfirmAcceptAction.cpp index cc23fdaf0a..b91272b527 100644 --- a/src/Ai/Base/Actions/QuestConfirmAcceptAction.cpp +++ b/src/Ai/Base/Actions/QuestConfirmAcceptAction.cpp @@ -1,3 +1,4 @@ +#include "BotChatService.h" #include "QuestConfirmAcceptAction.h" #include "WorldPacket.h" @@ -17,7 +18,7 @@ bool QuestConfirmAcceptAction::Execute(Event event) } std::ostringstream out; out << "Quest: " << chat->FormatQuest(quest) << " confirm accept"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); bot->GetSession()->HandleQuestConfirmAccept(sendPacket); return true; } \ No newline at end of file diff --git a/src/Ai/Base/Actions/RandomBotUpdateAction.cpp b/src/Ai/Base/Actions/RandomBotUpdateAction.cpp index 61a8360e59..b3bcc0d01c 100644 --- a/src/Ai/Base/Actions/RandomBotUpdateAction.cpp +++ b/src/Ai/Base/Actions/RandomBotUpdateAction.cpp @@ -8,7 +8,7 @@ #include "Event.h" #include "Playerbots.h" -bool RandomBotUpdateAction::Execute(Event event) +bool RandomBotUpdateAction::Execute(Event /*event*/) { if (!sRandomPlayerbotMgr->IsRandomBot(bot)) return false; diff --git a/src/Ai/Base/Actions/RangeAction.cpp b/src/Ai/Base/Actions/RangeAction.cpp index 3cf9e6930d..584d377cb9 100644 --- a/src/Ai/Base/Actions/RangeAction.cpp +++ b/src/Ai/Base/Actions/RangeAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "RangeAction.h" #include "Event.h" @@ -37,7 +38,7 @@ bool RangeAction::Execute(Event event) std::ostringstream out; out << qualifier << " range set to: " << newVal; - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); return true; } @@ -53,5 +54,5 @@ void RangeAction::PrintRange(std::string const type) else out << botAI->GetRange(type) << " (default)"; - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); } diff --git a/src/Ai/Base/Actions/ReachTargetActions.cpp b/src/Ai/Base/Actions/ReachTargetActions.cpp index 2be0da6beb..1294c2ccc1 100644 --- a/src/Ai/Base/Actions/ReachTargetActions.cpp +++ b/src/Ai/Base/Actions/ReachTargetActions.cpp @@ -10,7 +10,7 @@ #include "Playerbots.h" #include "ServerFacade.h" -bool ReachTargetAction::Execute(Event event) { return ReachCombatTo(AI_VALUE(Unit*, GetTargetName()), distance); } +bool ReachTargetAction::Execute(Event /*event*/) { return ReachCombatTo(AI_VALUE(Unit*, GetTargetName()), distance); } bool ReachTargetAction::isUseful() { diff --git a/src/Ai/Base/Actions/ReadyCheckAction.cpp b/src/Ai/Base/Actions/ReadyCheckAction.cpp index 2fc25e6dcc..8e731ab75f 100644 --- a/src/Ai/Base/Actions/ReadyCheckAction.cpp +++ b/src/Ai/Base/Actions/ReadyCheckAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include #include #include @@ -45,7 +46,7 @@ std::once_flag ReadyChecker::initFlag; class HealthChecker : public ReadyChecker { public: - bool Check(PlayerbotAI* botAI, AiObjectContext* context) override + bool Check(PlayerbotAI* /*botAI*/, AiObjectContext* context) override { return AI_VALUE2(uint8, "health", "self target") > sPlayerbotAIConfig->almostFullHealth; } @@ -56,7 +57,7 @@ class HealthChecker : public ReadyChecker class ManaChecker : public ReadyChecker { public: - bool Check(PlayerbotAI* botAI, AiObjectContext* context) override + bool Check(PlayerbotAI* /*botAI*/, AiObjectContext* context) override { return !AI_VALUE2(bool, "has mana", "self target") || AI_VALUE2(uint8, "mana", "self target") > sPlayerbotAIConfig->mediumHealth; @@ -68,7 +69,7 @@ class ManaChecker : public ReadyChecker class DistanceChecker : public ReadyChecker { public: - bool Check(PlayerbotAI* botAI, AiObjectContext* context) override + bool Check(PlayerbotAI* botAI, AiObjectContext* /*context*/) override { Player* bot = botAI->GetBot(); if (Player* master = botAI->GetMaster()) @@ -90,26 +91,26 @@ class DistanceChecker : public ReadyChecker class HunterChecker : public ReadyChecker { public: - bool Check(PlayerbotAI* botAI, AiObjectContext* context) override + bool Check(PlayerbotAI* botAI, AiObjectContext* /*context*/) override { Player* bot = botAI->GetBot(); if (bot->getClass() == CLASS_HUNTER) { if (!bot->GetUInt32Value(PLAYER_AMMO_ID)) { - botAI->TellError("Out of ammo!"); + botAI->GetServices().GetChatService().TellError("Out of ammo!"); return false; } if (!bot->GetPet()) { - botAI->TellError("No pet!"); + botAI->GetServices().GetChatService().TellError("No pet!"); return false; } if (bot->GetPet()->GetHappinessState() == UNHAPPY) { - botAI->TellError("Pet is unhappy!"); + botAI->GetServices().GetChatService().TellError("Pet is unhappy!"); return false; } } @@ -126,7 +127,7 @@ class ItemCountChecker : public ReadyChecker public: ItemCountChecker(std::string const item, std::string const name) : item(item), name(name) {} - bool Check(PlayerbotAI* botAI, AiObjectContext* context) override + bool Check(PlayerbotAI* /*botAI*/, AiObjectContext* context) override { return AI_VALUE2(uint32, "item count", item) > 0; } @@ -196,24 +197,24 @@ bool ReadyCheckAction::ReadyCheck() std::ostringstream out; uint32 hp = AI_VALUE2(uint32, "item count", "healing potion"); - out << formatPercent("Hp", hp, 100.0 * hp / 5); + out << formatPercent("Hp", hp, 100.0f * hp / 5); out << ", "; uint32 food = AI_VALUE2(uint32, "item count", "food"); - out << formatPercent("Food", food, 100.0 * food / 20); + out << formatPercent("Food", food, 100.0f * food / 20); if (AI_VALUE2(bool, "has mana", "self target")) { out << ", "; uint32 mp = AI_VALUE2(uint32, "item count", "mana potion"); - out << formatPercent("Mp", mp, 100.0 * mp / 5); + out << formatPercent("Mp", mp, 100.0f * mp / 5); out << ", "; uint32 water = AI_VALUE2(uint32, "item count", "water"); - out << formatPercent("Water", water, 100.0 * water / 20); + out << formatPercent("Water", water, 100.0f * water / 20); } - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); WorldPacket packet(MSG_RAID_READY_CHECK); packet << bot->GetGUID(); @@ -225,4 +226,4 @@ bool ReadyCheckAction::ReadyCheck() return true; } -bool FinishReadyCheckAction::Execute(Event event) { return ReadyCheck(); } +bool FinishReadyCheckAction::Execute(Event /*event*/) { return ReadyCheck(); } diff --git a/src/Ai/Base/Actions/ReleaseSpiritAction.cpp b/src/Ai/Base/Actions/ReleaseSpiritAction.cpp index 17fcc42a0b..8d02134355 100644 --- a/src/Ai/Base/Actions/ReleaseSpiritAction.cpp +++ b/src/Ai/Base/Actions/ReleaseSpiritAction.cpp @@ -14,6 +14,7 @@ #include "ServerFacade.h" #include "Corpse.h" #include "Log.h" +#include "BotChatService.h" // ReleaseSpiritAction implementation bool ReleaseSpiritAction::Execute(Event event) @@ -22,7 +23,7 @@ bool ReleaseSpiritAction::Execute(Event event) { if (!bot->InBattleground()) { - botAI->TellMasterNoFacing("I am not dead, will wait here"); + botAI->GetServices().GetChatService().TellMasterNoFacing("I am not dead, will wait here"); // -follow in bg is overwriten each tick with +follow // +stay in bg causes stuttering effect as bot is cycled between +stay and +follow each tick botAI->ChangeStrategy("-follow,+stay", BOT_STATE_NON_COMBAT); @@ -33,15 +34,15 @@ bool ReleaseSpiritAction::Execute(Event event) if (bot->GetCorpse() && bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) { - botAI->TellMasterNoFacing("I am already a spirit"); + botAI->GetServices().GetChatService().TellMasterNoFacing("I am already a spirit"); return false; } - const WorldPacket& packet = event.getPacket(); - const std::string message = !packet.empty() && packet.GetOpcode() == CMSG_REPOP_REQUEST + WorldPacket const& packet = event.getPacket(); + std::string const message = !packet.empty() && packet.GetOpcode() == CMSG_REPOP_REQUEST ? "Releasing..." : "Meet me at the graveyard"; - botAI->TellMasterNoFacing(message); + botAI->GetServices().GetChatService().TellMasterNoFacing(message); IncrementDeathCount(); bot->DurabilityRepairAll(false, 1.0f, false); @@ -65,9 +66,9 @@ void ReleaseSpiritAction::IncrementDeathCount() const } } -void ReleaseSpiritAction::LogRelease(const std::string& releaseMsg, bool isAutoRelease) const +void ReleaseSpiritAction::LogRelease(std::string const& releaseMsg, bool /*isAutoRelease*/) const { - const std::string teamPrefix = bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H"; + std::string const teamPrefix = bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H"; LOG_DEBUG("playerbots", "Bot {} {}:{} <{}> {}", bot->GetGUID().ToString().c_str(), @@ -78,7 +79,7 @@ void ReleaseSpiritAction::LogRelease(const std::string& releaseMsg, bool isAutoR } // AutoReleaseSpiritAction implementation -bool AutoReleaseSpiritAction::Execute(Event event) +bool AutoReleaseSpiritAction::Execute(Event /*event*/) { IncrementDeathCount(); bot->DurabilityRepairAll(false, 1.0f, false); @@ -214,9 +215,9 @@ bool AutoReleaseSpiritAction::ShouldDelayBattlegroundRelease() const return true; } -bool RepopAction::Execute(Event event) +bool RepopAction::Execute(Event /*event*/) { - const GraveyardStruct* graveyard = GetGrave( + GraveyardStruct const* graveyard = GetGrave( AI_VALUE(uint32, "death count") > 10 || CalculateDeadTime() > 30 * MINUTE ); @@ -241,7 +242,7 @@ int64 RepopAction::CalculateDeadTime() const return bot->isDead() ? 0 : 60 * MINUTE; } -void RepopAction::PerformGraveyardTeleport(const GraveyardStruct* graveyard) const +void RepopAction::PerformGraveyardTeleport(GraveyardStruct const* graveyard) const { bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP); bot->TeleportTo(graveyard->Map, graveyard->x, graveyard->y, graveyard->z, 0.f); @@ -250,7 +251,7 @@ void RepopAction::PerformGraveyardTeleport(const GraveyardStruct* graveyard) con } // SelfResurrectAction implementation for Warlock's Soulstone Resurrection/Shaman's Reincarnation -bool SelfResurrectAction::Execute(Event event) +bool SelfResurrectAction::Execute(Event /*event*/) { if (!bot->IsAlive() && bot->GetUInt32Value(PLAYER_SELF_RES_SPELL)) { diff --git a/src/Ai/Base/Actions/ReleaseSpiritAction.h b/src/Ai/Base/Actions/ReleaseSpiritAction.h index 57851214a1..64d4c2d3fc 100644 --- a/src/Ai/Base/Actions/ReleaseSpiritAction.h +++ b/src/Ai/Base/Actions/ReleaseSpiritAction.h @@ -14,11 +14,11 @@ class PlayerbotAI; class ReleaseSpiritAction : public Action { public: - ReleaseSpiritAction(PlayerbotAI* botAI, const std::string& name = "release") + ReleaseSpiritAction(PlayerbotAI* botAI, std::string const& name = "release") : Action(botAI, name) {} bool Execute(Event event) override; - void LogRelease(const std::string& releaseType, bool isAutoRelease = false) const; + void LogRelease(std::string const& releaseType, bool isAutoRelease = false) const; protected: void IncrementDeathCount() const; @@ -27,7 +27,7 @@ class ReleaseSpiritAction : public Action class AutoReleaseSpiritAction : public ReleaseSpiritAction { public: - AutoReleaseSpiritAction(PlayerbotAI* botAI, const std::string& name = "auto release") + AutoReleaseSpiritAction(PlayerbotAI* botAI, std::string const& name = "auto release") : ReleaseSpiritAction(botAI, name) {} bool Execute(Event event) override; @@ -44,7 +44,7 @@ class AutoReleaseSpiritAction : public ReleaseSpiritAction class RepopAction : public SpiritHealerAction { public: - RepopAction(PlayerbotAI* botAI, const std::string& name = "repop") + RepopAction(PlayerbotAI* botAI, std::string const& name = "repop") : SpiritHealerAction(botAI, name) {} bool Execute(Event event) override; @@ -52,7 +52,7 @@ class RepopAction : public SpiritHealerAction private: int64 CalculateDeadTime() const; - void PerformGraveyardTeleport(const GraveyardStruct* graveyard) const; + void PerformGraveyardTeleport(GraveyardStruct const* graveyard) const; }; // SelfResurrectAction action registration diff --git a/src/Ai/Base/Actions/RememberTaxiAction.cpp b/src/Ai/Base/Actions/RememberTaxiAction.cpp index af5a540370..06e528ffab 100644 --- a/src/Ai/Base/Actions/RememberTaxiAction.cpp +++ b/src/Ai/Base/Actions/RememberTaxiAction.cpp @@ -28,7 +28,7 @@ bool RememberTaxiAction::Execute(Event event) case CMSG_ACTIVATETAXIEXPRESS: { ObjectGuid guid; - uint32 node_count, totalcost; + uint32 node_count; p >> guid >> node_count; LastMovement& movement = context->GetValue("last taxi")->Get(); @@ -42,6 +42,8 @@ bool RememberTaxiAction::Execute(Event event) return true; } + default: + break; } return false; diff --git a/src/Ai/Base/Actions/RemoveAuraAction.cpp b/src/Ai/Base/Actions/RemoveAuraAction.cpp index a863a5973b..3c2df4122c 100644 --- a/src/Ai/Base/Actions/RemoveAuraAction.cpp +++ b/src/Ai/Base/Actions/RemoveAuraAction.cpp @@ -4,6 +4,7 @@ */ #include "RemoveAuraAction.h" +#include "BotSpellService.h" #include "Event.h" #include "Playerbots.h" @@ -12,6 +13,6 @@ RemoveAuraAction::RemoveAuraAction(PlayerbotAI* botAI) : Action(botAI, "ra") {} bool RemoveAuraAction::Execute(Event event) { - botAI->RemoveAura(event.getParam()); + botAI->GetServices().GetSpellService().RemoveAura(event.getParam()); return true; } diff --git a/src/Ai/Base/Actions/RepairAllAction.cpp b/src/Ai/Base/Actions/RepairAllAction.cpp index a66ba3a1bb..84556e0ed6 100644 --- a/src/Ai/Base/Actions/RepairAllAction.cpp +++ b/src/Ai/Base/Actions/RepairAllAction.cpp @@ -8,6 +8,7 @@ #include "ChatHelper.h" #include "Event.h" #include "Playerbots.h" +#include "BotChatService.h" bool RepairAllAction::Execute(Event event) { @@ -46,7 +47,7 @@ bool RepairAllAction::Execute(Event event) { std::ostringstream out; out << "Repair: " << chat->formatMoney(totalCost) << " (" << unit->GetName() << ")"; - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); bot->PlayDistanceSound(1116); } @@ -56,6 +57,6 @@ bool RepairAllAction::Execute(Event event) return true; } - botAI->TellError("Cannot find any npc to repair at"); + botAI->GetServices().GetChatService().TellError("Cannot find any npc to repair at"); return false; } diff --git a/src/Ai/Base/Actions/ResetAiAction.cpp b/src/Ai/Base/Actions/ResetAiAction.cpp index 3df6bbcd2a..ded8720dca 100644 --- a/src/Ai/Base/Actions/ResetAiAction.cpp +++ b/src/Ai/Base/Actions/ResetAiAction.cpp @@ -3,8 +3,10 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "ResetAiAction.h" +#include "Bot/Core/ManagerRegistry.h" #include "Event.h" #include "Group.h" #include "ObjectGuid.h" @@ -44,8 +46,8 @@ bool ResetAiAction::Execute(Event event) } } } - sPlayerbotRepository->Reset(botAI); + sManagerRegistry.GetBotRepository().Reset(botAI); botAI->ResetStrategies(false); - botAI->TellMaster("AI was reset to defaults"); + botAI->GetServices().GetChatService().TellMaster("AI was reset to defaults"); return true; } diff --git a/src/Ai/Base/Actions/ResetInstancesAction.cpp b/src/Ai/Base/Actions/ResetInstancesAction.cpp index cce5eef1e3..25055d37ac 100644 --- a/src/Ai/Base/Actions/ResetInstancesAction.cpp +++ b/src/Ai/Base/Actions/ResetInstancesAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "ResetInstancesAction.h" #include "Playerbots.h" @@ -12,7 +13,7 @@ bool ResetInstancesAction::Execute(Event event) WorldPacket packet(CMSG_RESET_INSTANCES, 0); bot->GetSession()->HandleResetInstancesOpcode(packet); - botAI->TellMaster("Resetting all instances"); + botAI->GetServices().GetChatService().TellMaster("Resetting all instances"); return true; } diff --git a/src/Ai/Base/Actions/RevealGatheringItemAction.cpp b/src/Ai/Base/Actions/RevealGatheringItemAction.cpp index 9725dce5ce..b456b2168a 100644 --- a/src/Ai/Base/Actions/RevealGatheringItemAction.cpp +++ b/src/Ai/Base/Actions/RevealGatheringItemAction.cpp @@ -4,6 +4,7 @@ */ #include "RevealGatheringItemAction.h" +#include "BotChatService.h" #include "CellImpl.h" #include "ChatHelper.h" @@ -77,7 +78,7 @@ bool RevealGatheringItemAction::Execute(Event event) } // everything is fine, do it - botAI->Ping(go->GetPositionX(), go->GetPositionY()); + botAI->GetServices().GetChatService().Ping(go->GetPositionX(), go->GetPositionY()); bot->Say(msg.str(), LANG_UNIVERSAL); return true; } diff --git a/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp b/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp index ce0b1fa05d..fa4f6c5553 100644 --- a/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp +++ b/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp @@ -3,8 +3,10 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "ReviveFromCorpseAction.h" +#include "Bot/Core/ManagerRegistry.h" #include "Event.h" #include "FleeManager.h" #include "GameGraveyard.h" @@ -29,7 +31,7 @@ bool ReviveFromCorpseAction::Execute(Event event) { if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT)) { - botAI->TellMasterNoFacing("Welcome back!"); + botAI->GetServices().GetChatService().TellMasterNoFacing("Welcome back!"); botAI->ChangeStrategy("+follow,-stay", BOT_STATE_NON_COMBAT); return true; } @@ -74,7 +76,7 @@ bool ReviveFromCorpseAction::Execute(Event event) return true; } -bool FindCorpseAction::Execute(Event event) +bool FindCorpseAction::Execute(Event /*event*/) { if (bot->InBattleground()) return false; @@ -102,7 +104,7 @@ bool FindCorpseAction::Execute(Event event) // bot->GetName().c_str()); context->GetValue("death count")->Set(0); // sRandomPlayerbotMgr->RandomTeleportForLevel(bot); - sRandomPlayerbotMgr->Revive(bot); + sManagerRegistry.GetRandomBotManager().Revive(bot); return true; } } @@ -144,13 +146,13 @@ bool FindCorpseAction::Execute(Event event) moveToPos = leaderPos; else { - FleeManager manager(bot, reclaimDist, 0.0, urand(0, 1), moveToPos); + FleeManager manager(bot, reclaimDist, urand(0, 1), moveToPos); if (manager.isUseful()) { float rx, ry, rz; if (manager.CalculateDestination(&rx, &ry, &rz)) - moveToPos = WorldPosition(moveToPos.getMapId(), rx, ry, rz, 0.0); + moveToPos = WorldPosition(moveToPos.getMapId(), rx, ry, rz, 0.0f); else if (!moveToPos.GetReachableRandomPointOnGround(bot, reclaimDist, urand(0, 1))) moveToPos = corpsePos; } @@ -293,12 +295,12 @@ GraveyardStruct const* SpiritHealerAction::GetGrave(bool startZone) return ClosestGrave; } -bool SpiritHealerAction::Execute(Event event) +bool SpiritHealerAction::Execute(Event /*event*/) { Corpse* corpse = bot->GetCorpse(); if (!corpse) { - botAI->TellError("I am not a spirit"); + botAI->GetServices().GetChatService().TellError("I am not a spirit"); return false; } @@ -323,7 +325,7 @@ bool SpiritHealerAction::Execute(Event event) bot->SpawnCorpseBones(); context->GetValue("current target")->Set(nullptr); bot->SetTarget(); - botAI->TellMaster("Hello"); + botAI->GetServices().GetChatService().TellMaster("Hello"); if (dCount > 20) context->GetValue("death count")->Set(0); @@ -341,7 +343,7 @@ bool SpiritHealerAction::Execute(Event event) bool moved = false; if (bot->IsWithinLOS(ClosestGrave->x, ClosestGrave->y, ClosestGrave->z)) - moved = MoveNear(ClosestGrave->Map, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, 0.0); + moved = MoveNear(ClosestGrave->Map, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, 0.0f); else moved = MoveTo(ClosestGrave->Map, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, false, false); @@ -358,7 +360,7 @@ bool SpiritHealerAction::Execute(Event event) // LOG_INFO("playerbots", "Bot {} {}:{} <{}> can't find a spirit healer", bot->GetGUID().ToString().c_str(), // bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName().c_str()); - // botAI->TellError("Cannot find any spirit healer nearby"); + // botAI->GetServices().GetChatService().TellError("Cannot find any spirit healer nearby"); return false; } diff --git a/src/Ai/Base/Actions/RewardAction.cpp b/src/Ai/Base/Actions/RewardAction.cpp index 8fe1c6ff72..6d9de90e56 100644 --- a/src/Ai/Base/Actions/RewardAction.cpp +++ b/src/Ai/Base/Actions/RewardAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "RewardAction.h" #include "ChatHelper.h" @@ -39,7 +40,7 @@ bool RewardAction::Execute(Event event) if (groupLeaderUnit && Reward(itemId, groupLeaderUnit)) return true; - botAI->TellError("Cannot talk to quest giver"); + botAI->GetServices().GetChatService().TellError("Cannot talk to quest giver"); return false; } @@ -70,7 +71,7 @@ bool RewardAction::Reward(uint32 itemId, Object* questGiver) std::ostringstream out; out << chat->FormatItem(pRewardItem) << " rewarded"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return true; } diff --git a/src/Ai/Base/Actions/RpgAction.cpp b/src/Ai/Base/Actions/RpgAction.cpp index 6002eaf63a..a4895e2c60 100644 --- a/src/Ai/Base/Actions/RpgAction.cpp +++ b/src/Ai/Base/Actions/RpgAction.cpp @@ -15,8 +15,9 @@ #include "Playerbots.h" #include "ServerFacade.h" #include "RpgSubActions.h" +#include "BotChatService.h" -bool RpgAction::Execute(Event event) +bool RpgAction::Execute(Event /*event*/) { GuidPosition guidP = AI_VALUE(GuidPosition, "rpg target"); if (!guidP && botAI->GetMaster()) @@ -108,7 +109,7 @@ bool RpgAction::SetNextRpgAction() { std::vector> sortedActions; - for (int i = 0; i < actions.size(); i++) + for (size_t i = 0; i < actions.size(); i++) sortedActions.push_back(std::make_pair(actions[i], relevances[i])); std::sort(sortedActions.begin(), sortedActions.end(), [](std::pairi, std::pair j) {return i.second > j.second; }); @@ -116,7 +117,7 @@ bool RpgAction::SetNextRpgAction() std::stringstream ss; ss << "------" << chat->FormatWorldobject(AI_VALUE(GuidPosition, "rpg target").GetWorldObject()) << "------"; bot->Say(ss.str(), LANG_UNIVERSAL); - botAI->TellMasterNoFacing(ss.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(ss.str()); for (auto action : sortedActions) { @@ -124,7 +125,7 @@ bool RpgAction::SetNextRpgAction() out << " " << action.first->getName() << " " << action.second; - botAI->TellMasterNoFacing(out); + botAI->GetServices().GetChatService().TellMasterNoFacing(out); } } @@ -142,7 +143,7 @@ bool RpgAction::SetNextRpgAction() out << " " << action->getName(); - botAI->TellMasterNoFacing(out); + botAI->GetServices().GetChatService().TellMasterNoFacing(out); } SET_AI_VALUE(std::string, "next rpg action", action->getName()); diff --git a/src/Ai/Base/Actions/RpgSubActions.cpp b/src/Ai/Base/Actions/RpgSubActions.cpp index aa4269fa92..81ad2a0690 100644 --- a/src/Ai/Base/Actions/RpgSubActions.cpp +++ b/src/Ai/Base/Actions/RpgSubActions.cpp @@ -15,6 +15,7 @@ #include "Playerbots.h" #include "PossibleRpgTargetsValue.h" #include "SocialMgr.h" +#include "BotChatService.h" void RpgHelper::OnExecute(std::string nextAction) { @@ -99,7 +100,7 @@ Event RpgSubAction::ActionEvent(Event event) { return event; } bool RpgStayAction::isUseful() { return rpg->InRange() && !botAI->HasRealPlayerMaster(); } -bool RpgStayAction::Execute(Event event) +bool RpgStayAction::Execute(Event /*event*/) { bot->PlayerTalkClass->SendCloseGossip(); @@ -109,7 +110,7 @@ bool RpgStayAction::Execute(Event event) bool RpgWorkAction::isUseful() { return rpg->InRange() && !botAI->HasRealPlayerMaster(); } -bool RpgWorkAction::Execute(Event event) +bool RpgWorkAction::Execute(Event /*event*/) { bot->HandleEmoteCommand(EMOTE_STATE_USE_STANDING); rpg->AfterExecute(); @@ -118,7 +119,7 @@ bool RpgWorkAction::Execute(Event event) bool RpgEmoteAction::isUseful() { return rpg->InRange() && !botAI->HasRealPlayerMaster(); } -bool RpgEmoteAction::Execute(Event event) +bool RpgEmoteAction::Execute(Event /*event*/) { uint32 type = TalkAction::GetRandomEmote(rpg->guidP().GetUnit()); @@ -133,7 +134,7 @@ bool RpgEmoteAction::Execute(Event event) return true; } -bool RpgCancelAction::Execute(Event event) +bool RpgCancelAction::Execute(Event /*event*/) { RESET_AI_VALUE(GuidPosition, "rpg target"); rpg->OnExecute(""); @@ -142,7 +143,7 @@ bool RpgCancelAction::Execute(Event event) bool RpgTaxiAction::isUseful() { return rpg->InRange() && !botAI->HasRealPlayerMaster(); } -bool RpgTaxiAction::Execute(Event event) +bool RpgTaxiAction::Execute(Event /*event*/) { GuidPosition guidP = rpg->guidP(); @@ -203,7 +204,7 @@ bool RpgTaxiAction::Execute(Event event) return true; } -bool RpgDiscoverAction::Execute(Event event) +bool RpgDiscoverAction::Execute(Event /*event*/) { GuidPosition guidP = rpg->guidP(); @@ -222,7 +223,7 @@ bool RpgDiscoverAction::Execute(Event event) std::string const RpgStartQuestAction::ActionName() { return "accept all quests"; } -Event RpgStartQuestAction::ActionEvent(Event event) +Event RpgStartQuestAction::ActionEvent(Event /*event*/) { WorldPacket p(CMSG_QUESTGIVER_ACCEPT_QUEST); p << rpg->guid(); @@ -232,7 +233,7 @@ Event RpgStartQuestAction::ActionEvent(Event event) std::string const RpgEndQuestAction::ActionName() { return "talk to quest giver"; } -Event RpgEndQuestAction::ActionEvent(Event event) +Event RpgEndQuestAction::ActionEvent(Event /*event*/) { WorldPacket p(CMSG_QUESTGIVER_COMPLETE_QUEST); p << rpg->guid(); @@ -242,17 +243,17 @@ Event RpgEndQuestAction::ActionEvent(Event event) std::string const RpgBuyAction::ActionName() { return "buy"; } -Event RpgBuyAction::ActionEvent(Event event) { return Event("rpg action", "vendor"); } +Event RpgBuyAction::ActionEvent(Event /*event*/) { return Event("rpg action", "vendor"); } std::string const RpgSellAction::ActionName() { return "sell"; } -Event RpgSellAction::ActionEvent(Event event) { return Event("rpg action", "vendor"); } +Event RpgSellAction::ActionEvent(Event /*event*/) { return Event("rpg action", "vendor"); } std::string const RpgRepairAction::ActionName() { return "repair"; } std::string const RpgTrainAction::ActionName() { return "trainer"; } -bool RpgHealAction::Execute(Event event) +bool RpgHealAction::Execute(Event /*event*/) { bool retVal = false; @@ -270,6 +271,8 @@ bool RpgHealAction::Execute(Event event) case CLASS_SHAMAN: retVal = botAI->DoSpecificAction("healing wave on party", Event(), true); break; + default: + break; } return retVal; @@ -287,21 +290,21 @@ std::string const RpgBuyPetitionAction::ActionName() { return "buy petition"; } std::string const RpgUseAction::ActionName() { return "use"; } -Event RpgUseAction::ActionEvent(Event event) +Event RpgUseAction::ActionEvent(Event /*event*/) { return Event("rpg action", chat->FormatWorldobject(rpg->guidP().GetWorldObject())); } std::string const RpgSpellAction::ActionName() { return "cast random spell"; } -Event RpgSpellAction::ActionEvent(Event event) +Event RpgSpellAction::ActionEvent(Event /*event*/) { return Event("rpg action", chat->FormatWorldobject(rpg->guidP().GetWorldObject())); } std::string const RpgCraftAction::ActionName() { return "craft random item"; } -Event RpgCraftAction::ActionEvent(Event event) +Event RpgCraftAction::ActionEvent(Event /*event*/) { return Event("rpg action", chat->FormatWorldobject(rpg->guidP().GetWorldObject())); } @@ -341,7 +344,7 @@ std::vector RpgTradeUsefulAction::CanGiveItems(GuidPosition guidPosition) return giveItems; } -bool RpgTradeUsefulAction::Execute(Event event) +bool RpgTradeUsefulAction::Execute(Event /*event*/) { GuidPosition guidP = AI_VALUE(GuidPosition, "rpg target"); @@ -370,7 +373,7 @@ bool RpgTradeUsefulAction::Execute(Event event) if (bot->GetTradeData() && bot->GetTradeData()->HasItem(item->GetGUID())) { if (bot->GetGroup() && bot->GetGroup()->IsMember(guidP) && botAI->HasRealPlayerMaster()) - botAI->TellMasterNoFacing( + botAI->GetServices().GetChatService().TellMasterNoFacing( "You can use this " + chat->FormatItem(item->GetTemplate()) + " better than me, " + guidP.GetPlayer()->GetName() /*chat->FormatWorldobject(guidP.GetPlayer())*/ + "."); else @@ -416,7 +419,7 @@ bool RpgDuelAction::isUseful() return true; } -bool RpgDuelAction::Execute(Event event) +bool RpgDuelAction::Execute(Event /*event*/) { GuidPosition guidP = AI_VALUE(GuidPosition, "rpg target"); @@ -434,7 +437,7 @@ bool RpgMountAnimAction::isUseful() return AI_VALUE2(bool, "mounted", "self target") && !AI_VALUE2(bool, "moving", "self target"); } -bool RpgMountAnimAction::Execute(Event event) +bool RpgMountAnimAction::Execute(Event /*event*/) { WorldPacket p; bot->GetSession()->HandleMountSpecialAnimOpcode(p); diff --git a/src/Ai/Base/Actions/RtiAction.cpp b/src/Ai/Base/Actions/RtiAction.cpp index b34b3eec3d..86f09dde45 100644 --- a/src/Ai/Base/Actions/RtiAction.cpp +++ b/src/Ai/Base/Actions/RtiAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "RtiAction.h" #include "Event.h" @@ -25,13 +26,13 @@ bool RtiAction::Execute(Event event) outRti << "rti" << ": "; AppendRti(outRti, "rti"); - botAI->TellMaster(outRti); + botAI->GetServices().GetChatService().TellMaster(outRti); std::ostringstream outRtiCc; outRtiCc << "rti cc" << ": "; AppendRti(outRtiCc, "rti cc"); - botAI->TellMaster(outRtiCc); + botAI->GetServices().GetChatService().TellMaster(outRtiCc); return true; } @@ -40,7 +41,7 @@ bool RtiAction::Execute(Event event) std::ostringstream out; out << type << " set to: "; AppendRti(out, type); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return true; } @@ -55,7 +56,7 @@ void RtiAction::AppendRti(std::ostringstream& out, std::string const type) out << " (" << target->GetName() << ")"; } -bool MarkRtiAction::Execute(Event event) +bool MarkRtiAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) diff --git a/src/Ai/Base/Actions/RtscAction.cpp b/src/Ai/Base/Actions/RtscAction.cpp index 4ed91d29c0..d4e2c0e0aa 100644 --- a/src/Ai/Base/Actions/RtscAction.cpp +++ b/src/Ai/Base/Actions/RtscAction.cpp @@ -7,6 +7,7 @@ #include "Playerbots.h" #include "RTSCValues.h" +#include "BotChatService.h" bool RTSCAction::Execute(Event event) { @@ -20,15 +21,15 @@ bool RTSCAction::Execute(Event event) if (command != "reset" && !master->HasSpell(RTSC_MOVE_SPELL)) { master->learnSpell(RTSC_MOVE_SPELL, false); - botAI->TellMasterNoFacing("RTS control enabled."); - botAI->TellMasterNoFacing("Aedm (Awesome energetic do move) spell trained."); + botAI->GetServices().GetChatService().TellMasterNoFacing("RTS control enabled."); + botAI->GetServices().GetChatService().TellMasterNoFacing("Aedm (Awesome energetic do move) spell trained."); } else if (command == "reset") { if (master->HasSpell(RTSC_MOVE_SPELL)) { master->removeSpell(RTSC_MOVE_SPELL, SPEC_MASK_ALL, false); - botAI->TellMasterNoFacing("RTS control spell removed."); + botAI->GetServices().GetChatService().TellMasterNoFacing("RTS control spell removed."); } RESET_AI_VALUE(bool, "RTSC selected"); @@ -131,7 +132,7 @@ bool RTSCAction::Execute(Event event) out.seekp(-1, out.cur); out << "."; - botAI->TellMasterNoFacing(out); + botAI->GetServices().GetChatService().TellMasterNoFacing(out); } if (command.find("go ") != std::string::npos) diff --git a/src/Ai/Base/Actions/RtscAction.h b/src/Ai/Base/Actions/RtscAction.h index 81ec27c034..ab6705345d 100644 --- a/src/Ai/Base/Actions/RtscAction.h +++ b/src/Ai/Base/Actions/RtscAction.h @@ -10,7 +10,7 @@ class PlayerbotAI; -#define RTSC_MOVE_SPELL 30758 // Aedm (Awesome Energetic do move) +constexpr uint32 RTSC_MOVE_SPELL = 30758; // Aedm (Awesome Energetic do move) class RTSCAction : public SeeSpellAction { diff --git a/src/Ai/Base/Actions/SaveManaAction.cpp b/src/Ai/Base/Actions/SaveManaAction.cpp index bfabe36a64..402330bd31 100644 --- a/src/Ai/Base/Actions/SaveManaAction.cpp +++ b/src/Ai/Base/Actions/SaveManaAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "SaveManaAction.h" #include "Event.h" @@ -17,7 +18,7 @@ bool SaveManaAction::Execute(Event event) { std::ostringstream out; out << "Mana save level: " << Format(value); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return true; } @@ -56,7 +57,7 @@ bool SaveManaAction::Execute(Event event) std::ostringstream out; out << "Mana save level set: " << Format(value); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return true; } @@ -65,11 +66,11 @@ std::string const SaveManaAction::Format(double value) { std::ostringstream out; - if (value <= 1.0) + if (value <= 1.0f) out << "|cFF808080"; - else if (value <= 5.0) + else if (value <= 5.0f) out << "|cFF00FF00"; - else if (value <= 7.0) + else if (value <= 7.0f) out << "|cFFFFFF00"; else out << "|cFFFF0000"; diff --git a/src/Ai/Base/Actions/SayAction.cpp b/src/Ai/Base/Actions/SayAction.cpp index 00fae65293..32e926c6f5 100644 --- a/src/Ai/Base/Actions/SayAction.cpp +++ b/src/Ai/Base/Actions/SayAction.cpp @@ -15,7 +15,7 @@ #include "PlayerbotTextMgr.h" #include "Playerbots.h" -static const std::unordered_set noReplyMsgs = { +static std::unordered_set const noReplyMsgs = { "join", "leave", "follow", @@ -50,13 +50,13 @@ static const std::unordered_set noReplyMsgs = { "reset strats", "home", }; -static const std::unordered_set noReplyMsgParts = { +static std::unordered_set const noReplyMsgParts = { "+", "-", "@", "follow target", "focus heal", "cast ", "accept [", "e [", "destroy [", "go zone"}; -static const std::unordered_set noReplyMsgStarts = {"e ", "accept ", "cast ", "destroy "}; +static std::unordered_set const noReplyMsgStarts = {"e ", "accept ", "cast ", "destroy "}; SayAction::SayAction(PlayerbotAI* botAI) : Action(botAI, "say"), Qualified() {} -bool SayAction::Execute(Event event) +bool SayAction::Execute(Event /*event*/) { std::string text = ""; std::map placeholders; @@ -81,6 +81,8 @@ bool SayAction::Execute(Event event) case ITEM_SUBCLASS_WEAPON_CROSSBOW: placeholders[""] = "arrows"; break; + default: + break; } } } @@ -92,7 +94,6 @@ bool SayAction::Execute(Event event) } // set delay before next say - time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier); uint32 nextTime = time(nullptr) + urand(1, 30); botAI->GetAiObjectContext()->GetValue("last said", qualifier)->Set(nextTime); @@ -157,9 +158,8 @@ bool SayAction::isUseful() return (time(nullptr) - lastSaid) > 30; } -void ChatReplyAction::ChatReplyDo(Player* bot, uint32& type, uint32& guid1, uint32& guid2, std::string& msg, std::string& chanName, std::string& name) +void ChatReplyAction::ChatReplyDo(Player* bot, uint32& type, uint32& guid1, uint32& /*guid2*/, std::string& msg, std::string& chanName, std::string& name) { - ChatReplyType replyType = REPLY_NOT_UNDERSTAND; // default not understand std::string respondsText = ""; // if we're just commanding bots around, don't respond... @@ -174,7 +174,7 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32& type, uint32& guid1, uint // second one is for partial matches like + or - where we change strats if (std::any_of(noReplyMsgParts.begin(), noReplyMsgParts.end(), - [&msg](const std::string& part) { return msg.find(part) != std::string::npos; })) + [&msg](std::string const& part) { return msg.find(part) != std::string::npos; })) { /*std::ostringstream out; out << "DEBUG ChatReplyDo decided to ignore partial blocklist match" << msg; @@ -183,7 +183,7 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32& type, uint32& guid1, uint } if (std::any_of(noReplyMsgStarts.begin(), noReplyMsgStarts.end(), - [&msg](const std::string& start) + [&msg](std::string const& start) { return msg.find(start) == 0; // Check if the start matches the beginning of msg })) @@ -224,7 +224,7 @@ void ChatReplyAction::ChatReplyDo(Player* bot, uint32& type, uint32& guid1, uint SendGeneralResponse(bot, chatChannelSource, messageRepy, name); } -bool ChatReplyAction::HandleThunderfuryReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name) +bool ChatReplyAction::HandleThunderfuryReply(Player* bot, ChatChannelSource chatChannelSource, std::string& /*msg*/, std::string& /*name*/) { std::map placeholders; const auto thunderfury = sObjectMgr->GetItemTemplate(19019); @@ -252,7 +252,7 @@ bool ChatReplyAction::HandleThunderfuryReply(Player* bot, ChatChannelSource chat return true; } -bool ChatReplyAction::HandleToxicLinksReply(Player* bot, ChatChannelSource chatChannelSource, std::string& msg, std::string& name) +bool ChatReplyAction::HandleToxicLinksReply(Player* bot, ChatChannelSource chatChannelSource, std::string& /*msg*/, std::string& /*name*/) { //quests std::vector incompleteQuests; @@ -704,6 +704,8 @@ std::string ChatReplyAction::GenerateReplyMessage(Player* bot, std::string& inco case 3: msg = "afraid that was before i was around or paying attention"; break; + default: + break; } msg = std::regex_replace(msg, std::regex("%s"), name); @@ -733,6 +735,8 @@ std::string ChatReplyAction::GenerateReplyMessage(Player* bot, std::string& inco case 4: msg = "is it me?"; break; + default: + break; } msg = std::regex_replace(msg, std::regex("%s"), name); @@ -768,6 +772,8 @@ std::string ChatReplyAction::GenerateReplyMessage(Player* bot, std::string& inco case 6: msg = "dunno %s"; break; + default: + break; } msg = std::regex_replace(msg, std::regex("%s"), name); @@ -803,6 +809,8 @@ std::string ChatReplyAction::GenerateReplyMessage(Player* bot, std::string& inco case 6: msg = "dunno %s"; break; + default: + break; } msg = std::regex_replace(msg, std::regex("%s"), name); @@ -838,6 +846,8 @@ std::string ChatReplyAction::GenerateReplyMessage(Player* bot, std::string& inco case 6: msg = "dunno %s"; break; + default: + break; } msg = std::regex_replace(msg, std::regex("%s"), name); respondsText = msg; @@ -867,6 +877,8 @@ std::string ChatReplyAction::GenerateReplyMessage(Player* bot, std::string& inco case 3: msg = "afraid that was before i was around or paying attention"; break; + default: + break; } msg = std::regex_replace(msg, std::regex("%s"), name); respondsText = msg; @@ -901,6 +913,8 @@ std::string ChatReplyAction::GenerateReplyMessage(Player* bot, std::string& inco case 6: msg = "no"; break; + default: + break; } msg = std::regex_replace(msg, std::regex("%s"), name); respondsText = msg; @@ -941,12 +955,16 @@ std::string ChatReplyAction::GenerateReplyMessage(Player* bot, std::string& inco case 8: msg = "maybe"; break; + default: + break; } msg = std::regex_replace(msg, std::regex("%s"), name); respondsText = msg; found = true; break; } + default: + break; } } } @@ -971,6 +989,8 @@ std::string ChatReplyAction::GenerateReplyMessage(Player* bot, std::string& inco case 2: msg = word[verb_pos ? verb_pos - 1 : verb_pos + 1] + " will " + word[verb_pos + 1] + " again though %s"; break; + default: + break; } msg = std::regex_replace(msg, std::regex("%s"), name); respondsText = msg; @@ -993,6 +1013,8 @@ std::string ChatReplyAction::GenerateReplyMessage(Player* bot, std::string& inco case 2: msg = "yeah i know " + word[verb_pos ? verb_pos - 1 : verb_pos + 1] + " is a " + word[verb_pos + 1]; break; + default: + break; } msg = std::regex_replace(msg, std::regex("%s"), name); respondsText = msg; @@ -1015,12 +1037,16 @@ std::string ChatReplyAction::GenerateReplyMessage(Player* bot, std::string& inco case 2: msg = "are you saying " + word[verb_pos ? verb_pos - 1 : verb_pos + 1] + " will " + word[verb_pos + 1] + " " + word[verb_pos + 2] + " %s?"; break; + default: + break; } msg = std::regex_replace(msg, std::regex("%s"), name); respondsText = msg; found = true; break; } + default: + break; } } diff --git a/src/Ai/Base/Actions/SayAction.h b/src/Ai/Base/Actions/SayAction.h index 5bf9a8f044..e7a787270b 100644 --- a/src/Ai/Base/Actions/SayAction.h +++ b/src/Ai/Base/Actions/SayAction.h @@ -29,7 +29,7 @@ class ChatReplyAction : public Action { public: ChatReplyAction(PlayerbotAI* ai) : Action(ai, "chat message") {} - virtual bool Execute(Event event) { return true; } + virtual bool Execute(Event /*event*/) { return true; } bool isUseful() { return true; } static void ChatReplyDo(Player* bot, uint32& type, uint32& guid1, uint32& guid2, std::string& msg, std::string& chanName, std::string& name); diff --git a/src/Ai/Base/Actions/SecurityCheckAction.cpp b/src/Ai/Base/Actions/SecurityCheckAction.cpp index c47a6e52cb..60a80ab7f3 100644 --- a/src/Ai/Base/Actions/SecurityCheckAction.cpp +++ b/src/Ai/Base/Actions/SecurityCheckAction.cpp @@ -4,13 +4,15 @@ */ #include "SecurityCheckAction.h" +#include "Bot/Core/ManagerRegistry.h" +#include "BotChatService.h" #include "Event.h" #include "Playerbots.h" bool SecurityCheckAction::isUseful() { - return sRandomPlayerbotMgr->IsRandomBot(bot) && botAI->GetMaster() && + return sManagerRegistry.GetRandomBotManager().IsRandomBot(bot) && botAI->GetMaster() && botAI->GetMaster()->GetSession()->GetSecurity() < SEC_GAMEMASTER && !GET_PLAYERBOT_AI(botAI->GetMaster()); } @@ -25,7 +27,7 @@ bool SecurityCheckAction::Execute(Event event) if ((botAI->GetGroupLeader()->GetSession()->GetSecurity() == SEC_PLAYER) && (!bot->GetGuildId() || bot->GetGuildId() != botAI->GetGroupLeader()->GetGuildId())) { - botAI->TellError("I will play with this loot type only if I'm in your guild :/"); + botAI->GetServices().GetChatService().TellError("I will play with this loot type only if I'm in your guild :/"); botAI->ChangeStrategy("+passive,+stay", BOT_STATE_NON_COMBAT); botAI->ChangeStrategy("+passive,+stay", BOT_STATE_COMBAT); } diff --git a/src/Ai/Base/Actions/SeeSpellAction.cpp b/src/Ai/Base/Actions/SeeSpellAction.cpp index 88848ca81f..75b9e7eabd 100644 --- a/src/Ai/Base/Actions/SeeSpellAction.cpp +++ b/src/Ai/Base/Actions/SeeSpellAction.cpp @@ -23,7 +23,7 @@ Creature* SeeSpellAction::CreateWps(Player* wpOwner, float x, float y, float z, float delay = 1000.0f * dist / wpOwner->GetSpeed(MOVE_RUN) + sPlayerbotAIConfig->reactDelay; if (!important) - delay *= 0.25; + delay *= 0.25f; Creature* wpCreature = wpOwner->SummonCreature(entry, x, y, z - 1, o, TEMPSUMMON_TIMED_DESPAWN, delay); if (!important) diff --git a/src/Ai/Base/Actions/SellAction.cpp b/src/Ai/Base/Actions/SellAction.cpp index 8ff78f3af3..c42b88b729 100644 --- a/src/Ai/Base/Actions/SellAction.cpp +++ b/src/Ai/Base/Actions/SellAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "SellAction.h" #include "Event.h" @@ -84,7 +85,7 @@ bool SellAction::Execute(Event event) return true; } - botAI->TellError("Usage: s gray/*/vendor/[item link]"); + botAI->GetServices().GetChatService().TellError("Usage: s gray/*/vendor/[item link]"); return false; } @@ -128,7 +129,7 @@ void SellAction::Sell(Item* item) } out << "Selling " << chat->FormatItem(item->GetTemplate()); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); bot->PlayDistanceSound(120); break; diff --git a/src/Ai/Base/Actions/SendMailAction.cpp b/src/Ai/Base/Actions/SendMailAction.cpp index c6184c4b98..faafab9aaf 100644 --- a/src/Ai/Base/Actions/SendMailAction.cpp +++ b/src/Ai/Base/Actions/SendMailAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "SendMailAction.h" #include "ChatHelper.h" @@ -78,7 +79,7 @@ bool SendMailAction::Execute(Event event) if (bot->GetMoney() < money) { - botAI->TellError("I don't have enough money"); + botAI->GetServices().GetChatService().TellError("I don't have enough money"); return false; } @@ -101,7 +102,7 @@ bool SendMailAction::Execute(Event event) std::ostringstream out; out << "Sending mail to " << receiver->GetName(); - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); return true; } diff --git a/src/Ai/Base/Actions/SetCraftAction.cpp b/src/Ai/Base/Actions/SetCraftAction.cpp index f062ce5c23..c42fc0003d 100644 --- a/src/Ai/Base/Actions/SetCraftAction.cpp +++ b/src/Ai/Base/Actions/SetCraftAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "SetCraftAction.h" #include "ChatHelper.h" @@ -24,7 +25,7 @@ bool SetCraftAction::Execute(Event event) if (link == "reset") { data.Reset(); - botAI->TellMaster("I will not craft anything"); + botAI->GetServices().GetChatService().TellMaster("I will not craft anything"); return true; } @@ -37,7 +38,7 @@ bool SetCraftAction::Execute(Event event) ItemIds itemIds = chat->parseItems(link); if (itemIds.empty()) { - botAI->TellMaster("Usage: 'craft [itemId]' or 'craft reset'"); + botAI->GetServices().GetChatService().TellMaster("Usage: 'craft [itemId]' or 'craft reset'"); return false; } @@ -97,7 +98,7 @@ bool SetCraftAction::Execute(Event event) if (data.required.empty()) { - botAI->TellMaster("I cannot craft this"); + botAI->GetServices().GetChatService().TellMaster("I cannot craft this"); return false; } @@ -112,7 +113,7 @@ void SetCraftAction::TellCraft() CraftData& data = AI_VALUE(CraftData&, "craft"); if (data.IsEmpty()) { - botAI->TellMaster("I will not craft anything"); + botAI->GetServices().GetChatService().TellMaster("I will not craft anything"); return; } @@ -149,7 +150,7 @@ void SetCraftAction::TellCraft() } out << " (craft fee: " << chat->formatMoney(GetCraftFee(data)) << ")"; - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); } uint32 SetCraftAction::GetCraftFee(CraftData& data) diff --git a/src/Ai/Base/Actions/SetHomeAction.cpp b/src/Ai/Base/Actions/SetHomeAction.cpp index 44f3c7b49f..f127f2d69b 100644 --- a/src/Ai/Base/Actions/SetHomeAction.cpp +++ b/src/Ai/Base/Actions/SetHomeAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "SetHomeAction.h" #include "Event.h" @@ -30,14 +31,14 @@ bool SetHomeAction::Execute(Event event) { Creature* creature = botAI->GetCreature(selection); bot->GetSession()->SendBindPoint(creature); - botAI->TellMaster("This inn is my new home"); + botAI->GetServices().GetChatService().TellMaster("This inn is my new home"); return true; } else { Creature* creature = botAI->GetCreature(selection); bot->GetSession()->SendBindPoint(creature); - botAI->TellMaster("This inn is my new home"); + botAI->GetServices().GetChatService().TellMaster("This inn is my new home"); return true; } } @@ -50,10 +51,10 @@ bool SetHomeAction::Execute(Event event) continue; bot->GetSession()->SendBindPoint(unit); - botAI->TellMaster("This inn is my new home"); + botAI->GetServices().GetChatService().TellMaster("This inn is my new home"); return true; } - botAI->TellError("Can't find any innkeeper around"); + botAI->GetServices().GetChatService().TellError("Can't find any innkeeper around"); return false; } diff --git a/src/Ai/Base/Actions/ShareQuestAction.cpp b/src/Ai/Base/Actions/ShareQuestAction.cpp index d4b470b4c1..21f3ea1f80 100644 --- a/src/Ai/Base/Actions/ShareQuestAction.cpp +++ b/src/Ai/Base/Actions/ShareQuestAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "ShareQuestAction.h" #include "Event.h" @@ -32,7 +33,7 @@ bool ShareQuestAction::Execute(Event event) WorldPacket p; p << entry; bot->GetSession()->HandlePushQuestToParty(p); - botAI->TellMaster("Quest shared"); + botAI->GetServices().GetChatService().TellMaster("Quest shared"); return true; } } @@ -40,9 +41,8 @@ bool ShareQuestAction::Execute(Event event) return false; } -bool AutoShareQuestAction::Execute(Event event) +bool AutoShareQuestAction::Execute(Event /*event*/) { - Player* requester = event.getOwner() ? event.getOwner() : GetMaster(); bool shared = false; for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot) @@ -99,7 +99,7 @@ bool AutoShareQuestAction::Execute(Event event) WorldPacket p; p << logQuest; bot->GetSession()->HandlePushQuestToParty(p); - botAI->TellMaster("Quest shared"); + botAI->GetServices().GetChatService().TellMaster("Quest shared"); shared = true; } diff --git a/src/Ai/Base/Actions/SkipSpellsListAction.cpp b/src/Ai/Base/Actions/SkipSpellsListAction.cpp index ad7e86a5d1..ae9ba6862d 100644 --- a/src/Ai/Base/Actions/SkipSpellsListAction.cpp +++ b/src/Ai/Base/Actions/SkipSpellsListAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "SkipSpellsListAction.h" #include "Event.h" @@ -30,7 +31,7 @@ bool SkipSpellsListAction::Execute(Event event) if (cmd == "reset") { skipSpells.clear(); - botAI->TellMaster("Ignored spell list is empty"); + botAI->GetServices().GetChatService().TellMaster("Ignored spell list is empty"); return true; } @@ -39,7 +40,7 @@ bool SkipSpellsListAction::Execute(Event event) std::ostringstream out; if (skipSpells.empty()) { - botAI->TellMaster("Ignored spell list is empty"); + botAI->GetServices().GetChatService().TellMaster("Ignored spell list is empty"); return true; } @@ -60,7 +61,7 @@ bool SkipSpellsListAction::Execute(Event event) out << chat->FormatSpell(spellInfo); } - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } else { @@ -71,7 +72,7 @@ bool SkipSpellsListAction::Execute(Event event) uint32 spellId = chat->parseSpell(cmd); if (!spellId) { - botAI->TellError("Unknown spell"); + botAI->GetServices().GetChatService().TellError("Unknown spell"); return false; } @@ -88,7 +89,7 @@ bool SkipSpellsListAction::Execute(Event event) std::ostringstream out; out << chat->FormatSpell(spellInfo) << " removed from ignored spells"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return true; } } @@ -101,7 +102,7 @@ bool SkipSpellsListAction::Execute(Event event) std::ostringstream out; out << chat->FormatSpell(spellInfo) << " added to ignored spells"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return true; } } diff --git a/src/Ai/Base/Actions/StatsAction.cpp b/src/Ai/Base/Actions/StatsAction.cpp index f6872c8468..13db65158e 100644 --- a/src/Ai/Base/Actions/StatsAction.cpp +++ b/src/Ai/Base/Actions/StatsAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "StatsAction.h" #include "ChatHelper.h" @@ -27,7 +28,7 @@ bool StatsAction::Execute(Event event) ListXP(out); } - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return true; } @@ -180,5 +181,5 @@ double StatsAction::RepairPercent(uint16 pos) if (!curDurability) return 0; - return curDurability * 100.0 / maxDurability; + return curDurability * 100.0f / maxDurability; } diff --git a/src/Ai/Base/Actions/StayActions.cpp b/src/Ai/Base/Actions/StayActions.cpp index 525ff8d53e..354fe9aaa2 100644 --- a/src/Ai/Base/Actions/StayActions.cpp +++ b/src/Ai/Base/Actions/StayActions.cpp @@ -4,6 +4,7 @@ */ #include "StayActions.h" +#include "BotChatService.h" #include "Event.h" #include "LastMovementValue.h" @@ -15,7 +16,7 @@ bool StayActionBase::Stay() AI_VALUE(LastMovement&, "last movement").Set(nullptr); // if (!urand(0, 10)) - // botAI->PlaySound(TEXT_EMOTE_YAWN); + // botAI->GetServices().GetChatService().PlaySound(TEXT_EMOTE_YAWN); if (bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE) return false; @@ -39,7 +40,7 @@ bool StayActionBase::Stay() return true; } -bool StayAction::Execute(Event event) { return Stay(); } +bool StayAction::Execute(Event /*event*/) { return Stay(); } bool StayAction::isUseful() { @@ -47,7 +48,6 @@ bool StayAction::isUseful() PositionInfo stayPosition = AI_VALUE(PositionMap&, "position")["stay"]; if (stayPosition.isSet()) { - const float distance = bot->GetDistance(stayPosition.x, stayPosition.y, stayPosition.z); if (sPlayerbotAIConfig->followDistance) { return false; @@ -64,7 +64,7 @@ bool StayAction::isUseful() return AI_VALUE2(bool, "moving", "self target"); } -bool SitAction::Execute(Event event) +bool SitAction::Execute(Event /*event*/) { if (bot->isMoving()) return false; diff --git a/src/Ai/Base/Actions/SuggestWhatToDoAction.cpp b/src/Ai/Base/Actions/SuggestWhatToDoAction.cpp index 159bfd4ebb..0a5383f3f9 100644 --- a/src/Ai/Base/Actions/SuggestWhatToDoAction.cpp +++ b/src/Ai/Base/Actions/SuggestWhatToDoAction.cpp @@ -6,6 +6,7 @@ #include #include "SuggestWhatToDoAction.h" +#include "Bot/Core/ManagerRegistry.h" #include "ServerFacade.h" #include "ChannelMgr.h" #include "Event.h" @@ -54,7 +55,7 @@ SuggestWhatToDoAction::SuggestWhatToDoAction(PlayerbotAI* botAI, std::string con bool SuggestWhatToDoAction::isUseful() { - if (!sRandomPlayerbotMgr->IsRandomBot(bot) || bot->GetGroup() || bot->GetInstanceId() || bot->GetBattleground()) + if (!sManagerRegistry.GetRandomBotManager().IsRandomBot(bot) || bot->GetGroup() || bot->GetInstanceId() || bot->GetBattleground()) return false; std::string qualifier = "suggest what to do"; @@ -69,7 +70,6 @@ bool SuggestWhatToDoAction::Execute(Event event) fnct_ptr(); std::string const qualifier = "suggest what to do"; - time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier); botAI->GetAiObjectContext()->GetValue("last said", qualifier)->Set(time(nullptr) + urand(1, 60)); return true; @@ -227,7 +227,7 @@ void SuggestWhatToDoAction::thunderfury() class FindTradeItemsVisitor : public IterateItemsVisitor { public: - FindTradeItemsVisitor(uint32 quality) : quality(quality), IterateItemsVisitor() {} + FindTradeItemsVisitor(uint32 quality) : IterateItemsVisitor(), quality(quality) {} bool Visit(Item* item) override { @@ -373,7 +373,7 @@ bool SuggestTradeAction::Execute(Event event) if (!proto) return false; - uint32 price = proto->SellPrice * sRandomPlayerbotMgr->GetSellMultiplier(bot) * count; + uint32 price = proto->SellPrice * sManagerRegistry.GetRandomBotManager().GetSellMultiplier(bot) * count; if (!price) return false; diff --git a/src/Ai/Base/Actions/TalkToQuestGiverAction.cpp b/src/Ai/Base/Actions/TalkToQuestGiverAction.cpp index 7f791d0d63..c3f864cd73 100644 --- a/src/Ai/Base/Actions/TalkToQuestGiverAction.cpp +++ b/src/Ai/Base/Actions/TalkToQuestGiverAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "TalkToQuestGiverAction.h" #include "ChatHelper.h" @@ -64,7 +65,7 @@ bool TalkToQuestGiverAction::ProcessQuest(Quest const* quest, Object* questGiver } out << ": " << chat->FormatQuest(quest); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return isCompleted; } @@ -89,8 +90,8 @@ bool TalkToQuestGiverAction::TurnInQuest(Quest const* quest, Object* questGiver, if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT) || botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) { - const Quest* pQuest = sObjectMgr->GetQuestTemplate(questID); - const std::string text_quest = ChatHelper::FormatQuest(pQuest); + Quest const* pQuest = sObjectMgr->GetQuestTemplate(questID); + std::string const text_quest = ChatHelper::FormatQuest(pQuest); LOG_INFO("playerbots", "{} => Quest [ {} ] completed", bot->GetName(), pQuest->GetTitle()); bot->Say("Quest [ " + text_quest + " ] completed", LANG_UNIVERSAL); } @@ -231,7 +232,6 @@ void TalkToQuestGiverAction::AskToSelectReward(Quest const* quest, std::ostrings for (uint8 i = 0; i < quest->GetRewChoiceItemsCount(); ++i) { ItemTemplate const* item = sObjectMgr->GetItemTemplate(quest->RewardChoiceItemId[i]); - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", quest->RewardChoiceItemId[i]); if (!forEquip || BestRewards(quest).count(i) > 0) { @@ -239,7 +239,7 @@ void TalkToQuestGiverAction::AskToSelectReward(Quest const* quest, std::ostrings } } - botAI->TellMaster(msg); + botAI->GetServices().GetChatService().TellMaster(msg); out << "Reward pending"; } @@ -248,7 +248,6 @@ bool TurnInQueryQuestAction::Execute(Event event) WorldPacket pakcet = event.getPacket(); ObjectGuid guid; uint32 questId; - ObjectGuid unk1; pakcet >> guid >> questId; Object* object = ObjectAccessor::GetObjectByTypeMask(*bot, guid, TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT | TYPEMASK_ITEM); @@ -303,6 +302,6 @@ bool TurnInQueryQuestAction::Execute(Event event) } out << ": " << chat->FormatQuest(quest); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return true; } diff --git a/src/Ai/Base/Actions/TameAction.cpp b/src/Ai/Base/Actions/TameAction.cpp index 5b3eda1034..30f9fac571 100644 --- a/src/Ai/Base/Actions/TameAction.cpp +++ b/src/Ai/Base/Actions/TameAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "TameAction.h" #include #include @@ -20,7 +21,7 @@ #include "SpellMgr.h" #include "WorldSession.h" -bool IsExoticPet(const CreatureTemplate* creature) +bool IsExoticPet(CreatureTemplate const* creature) { // Use the IsExotic() method from CreatureTemplate return creature && creature->IsExotic(); @@ -53,13 +54,12 @@ bool TameAction::Execute(Event event) { std::set normalFamilies; std::set exoticFamilies; - Player* bot = botAI->GetBot(); // Loop over all creature templates and collect tameable families CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates(); for (auto itr = creatures->begin(); itr != creatures->end(); ++itr) { - const CreatureTemplate& creature = itr->second; + CreatureTemplate const& creature = itr->second; if (!creature.IsTameable(true)) continue; @@ -101,7 +101,7 @@ bool TameAction::Execute(Event event) } } - botAI->TellError(oss.str()); + botAI->GetServices().GetChatService().TellError(oss.str()); return true; } @@ -126,7 +126,7 @@ bool TameAction::Execute(Event event) } catch (...) { - botAI->TellError("Invalid tame id."); + botAI->GetServices().GetChatService().TellError("Invalid tame id."); } } else if (mode == "family" && !value.empty()) @@ -140,7 +140,7 @@ bool TameAction::Execute(Event event) else { // Unrecognized command or missing argument; show usage - botAI->TellError( + botAI->GetServices().GetChatService().TellError( "Usage: tame name | tame id | tame family | tame rename | tame abandon"); return false; } @@ -161,18 +161,18 @@ bool TameAction::Execute(Event event) { std::ostringstream oss; oss << "Pet changed to " << lastPetName << ", ID: " << lastPetId << "."; - botAI->TellMaster(oss.str()); + botAI->GetServices().GetChatService().TellMaster(oss.str()); } else { - botAI->TellMaster("Pet changed and initialized!"); + botAI->GetServices().GetChatService().TellMaster("Pet changed and initialized!"); } } return true; } -bool TameAction::SetPetByName(const std::string& name) +bool TameAction::SetPetByName(std::string const& name) { // Make a lowercase copy of the input name for case-insensitive comparison std::string lowerName = name; @@ -185,7 +185,7 @@ bool TameAction::SetPetByName(const std::string& name) // Iterate through all creature templates for (auto itr = creatures->begin(); itr != creatures->end(); ++itr) { - const CreatureTemplate& creature = itr->second; + CreatureTemplate const& creature = itr->second; std::string creatureName = creature.Name; // Convert creature's name to lowercase for comparison std::transform(creatureName.begin(), creatureName.end(), creatureName.begin(), ::tolower); @@ -200,7 +200,7 @@ bool TameAction::SetPetByName(const std::string& name) // If the creature is exotic and the bot doesn't have Beast Mastery, show error and fail if (IsExoticPet(&creature) && !HasBeastMastery(bot)) { - botAI->TellError("I cannot use exotic pets unless I have the Beast Mastery talent."); + botAI->GetServices().GetChatService().TellError("I cannot use exotic pets unless I have the Beast Mastery talent."); return false; } @@ -217,7 +217,7 @@ bool TameAction::SetPetByName(const std::string& name) } // If no suitable pet found, show an error and return failure - botAI->TellError("No tameable pet found with name: " + name); + botAI->GetServices().GetChatService().TellError("No tameable pet found with name: " + name); return false; } @@ -234,21 +234,21 @@ bool TameAction::SetPetById(uint32 id) if (!creature->IsTameable(true)) { // If not tameable at all, show an error and fail - botAI->TellError("No tameable pet found with id: " + std::to_string(id)); + botAI->GetServices().GetChatService().TellError("No tameable pet found with id: " + std::to_string(id)); return false; } // If it's an exotic pet, make sure the bot has the Beast Mastery talent if (IsExoticPet(creature) && !HasBeastMastery(bot)) { - botAI->TellError("I cannot use exotic pets unless I have the Beast Mastery talent."); + botAI->GetServices().GetChatService().TellError("I cannot use exotic pets unless I have the Beast Mastery talent."); return false; } // Check if the bot is actually allowed to tame this pet (honoring exotic pet rules) if (!creature->IsTameable(bot->CanTameExoticPets())) { - botAI->TellError("No tameable pet found with id: " + std::to_string(id)); + botAI->GetServices().GetChatService().TellError("No tameable pet found with id: " + std::to_string(id)); return false; } @@ -260,11 +260,11 @@ bool TameAction::SetPetById(uint32 id) } // If no valid creature was found by id, show an error - botAI->TellError("No tameable pet found with id: " + std::to_string(id)); + botAI->GetServices().GetChatService().TellError("No tameable pet found with id: " + std::to_string(id)); return false; } -bool TameAction::SetPetByFamily(const std::string& family) +bool TameAction::SetPetByFamily(std::string const& family) { // Convert the input family name to lowercase for case-insensitive comparison std::string lowerFamily = family; @@ -275,13 +275,13 @@ bool TameAction::SetPetByFamily(const std::string& family) Player* bot = botAI->GetBot(); // Prepare a list of candidate creatures and track if any exotic pet is found - std::vector candidates; + std::vector candidates; bool foundExotic = false; // Iterate through all creature templates for (auto itr = creatures->begin(); itr != creatures->end(); ++itr) { - const CreatureTemplate& creature = itr->second; + CreatureTemplate const& creature = itr->second; // Skip if this creature is never tameable if (!creature.IsTameable(true)) @@ -318,9 +318,9 @@ bool TameAction::SetPetByFamily(const std::string& family) if (candidates.empty()) { if (foundExotic && !HasBeastMastery(bot)) - botAI->TellError("I cannot use exotic pets unless I have the Beast Mastery talent."); + botAI->GetServices().GetChatService().TellError("I cannot use exotic pets unless I have the Beast Mastery talent."); else - botAI->TellError("No tameable pet found with family: " + family); + botAI->GetServices().GetChatService().TellError("No tameable pet found with family: " + family); return false; } @@ -329,7 +329,7 @@ bool TameAction::SetPetByFamily(const std::string& family) std::mt19937 gen(rd()); std::uniform_int_distribution<> dis(0, candidates.size() - 1); - const CreatureTemplate* selected = candidates[dis(gen)]; + CreatureTemplate const* selected = candidates[dis(gen)]; // Save the selected pet's name and id for feedback lastPetName = selected->Name; @@ -338,21 +338,21 @@ bool TameAction::SetPetByFamily(const std::string& family) return CreateAndSetPet(selected->Entry); } -bool TameAction::RenamePet(const std::string& newName) +bool TameAction::RenamePet(std::string const& newName) { Player* bot = botAI->GetBot(); Pet* pet = bot->GetPet(); // Check if the bot currently has a pet if (!pet) { - botAI->TellError("You have no pet to rename."); + botAI->GetServices().GetChatService().TellError("You have no pet to rename."); return false; } // Validate the new name: must not be empty and max 12 characters if (newName.empty() || newName.length() > 12) { - botAI->TellError("Pet name must be between 1 and 12 alphabetic characters."); + botAI->GetServices().GetChatService().TellError("Pet name must be between 1 and 12 alphabetic characters."); return false; } @@ -361,7 +361,7 @@ bool TameAction::RenamePet(const std::string& newName) { if (!std::isalpha(static_cast(c))) { - botAI->TellError("Pet name must only contain alphabetic characters (A-Z, a-z)."); + botAI->GetServices().GetChatService().TellError("Pet name must only contain alphabetic characters (A-Z, a-z)."); return false; } } @@ -375,7 +375,7 @@ bool TameAction::RenamePet(const std::string& newName) // Check if the new name is reserved or forbidden if (sObjectMgr->IsReservedName(normalized)) { - botAI->TellError("That pet name is forbidden. Please choose another name."); + botAI->GetServices().GetChatService().TellError("That pet name is forbidden. Please choose another name."); return false; } @@ -385,8 +385,8 @@ bool TameAction::RenamePet(const std::string& newName) bot->GetSession()->SendPetNameQuery(pet->GetGUID(), pet->GetEntry()); // Notify the master about the rename and give a tip to update the client name display - botAI->TellMaster("Your pet has been renamed to " + normalized + "!"); - botAI->TellMaster("If you do not see the new name, please dismiss and recall your pet."); + botAI->GetServices().GetChatService().TellMaster("Your pet has been renamed to " + normalized + "!"); + botAI->GetServices().GetChatService().TellMaster("If you do not see the new name, please dismiss and recall your pet."); // Remove the current pet and (re-)cast Call Pet spell if the bot is a hunter bot->RemovePet(nullptr, PET_SAVE_AS_CURRENT, true); @@ -404,7 +404,7 @@ bool TameAction::CreateAndSetPet(uint32 creatureEntry) // Ensure the player is a hunter and at least level 10 (required for pets) if (bot->getClass() != CLASS_HUNTER || bot->GetLevel() < 10) { - botAI->TellError("Only level 10+ hunters can have pets."); + botAI->GetServices().GetChatService().TellError("Only level 10+ hunters can have pets."); return false; } @@ -412,7 +412,7 @@ bool TameAction::CreateAndSetPet(uint32 creatureEntry) CreatureTemplate const* creature = sObjectMgr->GetCreatureTemplate(creatureEntry); if (!creature) { - botAI->TellError("Creature template not found."); + botAI->GetServices().GetChatService().TellError("Creature template not found."); return false; } @@ -433,7 +433,7 @@ bool TameAction::CreateAndSetPet(uint32 creatureEntry) Pet* pet = bot->CreateTamedPetFrom(creatureEntry, 0); if (!pet) { - botAI->TellError("Failed to create pet."); + botAI->GetServices().GetChatService().TellError("Failed to create pet."); return false; } @@ -488,13 +488,13 @@ bool TameAction::AbandonPet() // Remove the pet from the bot and mark it as deleted in the database bot->RemovePet(pet, PET_SAVE_AS_DELETED); // Inform the bot's master/player that the pet was abandoned - botAI->TellMaster("Your pet has been abandoned."); + botAI->GetServices().GetChatService().TellMaster("Your pet has been abandoned."); return true; } else { // If there is no hunter pet, show an error message - botAI->TellError("You have no hunter pet to abandon."); + botAI->GetServices().GetChatService().TellError("You have no hunter pet to abandon."); return false; } } diff --git a/src/Ai/Base/Actions/TameAction.h b/src/Ai/Base/Actions/TameAction.h index 234eaec2ab..5652dc2db5 100644 --- a/src/Ai/Base/Actions/TameAction.h +++ b/src/Ai/Base/Actions/TameAction.h @@ -20,10 +20,10 @@ class TameAction : public Action bool Execute(Event event) override; private: - bool SetPetByName(const std::string& name); + bool SetPetByName(std::string const& name); bool SetPetById(uint32 id); - bool SetPetByFamily(const std::string& family); - bool RenamePet(const std::string& newName); + bool SetPetByFamily(std::string const& family); + bool RenamePet(std::string const& newName); bool CreateAndSetPet(uint32 creatureEntry); bool AbandonPet(); diff --git a/src/Ai/Base/Actions/TaxiAction.cpp b/src/Ai/Base/Actions/TaxiAction.cpp index 1f9e89cc18..6b581af380 100644 --- a/src/Ai/Base/Actions/TaxiAction.cpp +++ b/src/Ai/Base/Actions/TaxiAction.cpp @@ -3,7 +3,10 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "TaxiAction.h" +#include "BotSpellService.h" +#include "BotRoleService.h" #include "Event.h" #include "LastMovementValue.h" @@ -13,7 +16,7 @@ bool TaxiAction::Execute(Event event) { - botAI->RemoveShapeshift(); + botAI->GetServices().GetSpellService().RemoveShapeshift(); LastMovement& movement = context->GetValue("last taxi")->Get(); @@ -24,7 +27,7 @@ bool TaxiAction::Execute(Event event) { movement.taxiNodes.clear(); movement.Set(nullptr); - botAI->TellMaster("I am ready for the next flight"); + botAI->GetServices().GetChatService().TellMaster("I am ready for the next flight"); return true; } @@ -59,7 +62,7 @@ bool TaxiAction::Execute(Event event) // Only for follower bots if (botAI->HasRealPlayerMaster()) { - uint32 index = botAI->GetGroupSlotIndex(bot); + uint32 index = botAI->GetServices().GetRoleService().GetGroupSlotIndex(bot); uint32 delay = delayMin + index * gapMs + urand(0, gapJitterMs); delay = std::min(delay, delayMax); @@ -82,7 +85,7 @@ bool TaxiAction::Execute(Event event) if (param == "?") { - botAI->TellMasterNoFacing("=== Taxi ==="); + botAI->GetServices().GetChatService().TellMasterNoFacing("=== Taxi ==="); uint32 index = 1; for (uint32 node : nodes) @@ -97,7 +100,7 @@ bool TaxiAction::Execute(Event event) std::ostringstream out; out << index++ << ": " << dest->name[0]; - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); } return true; @@ -118,13 +121,13 @@ bool TaxiAction::Execute(Event event) { movement.taxiNodes.clear(); movement.Set(nullptr); - botAI->TellError("I can't fly with you"); + botAI->GetServices().GetChatService().TellError("I can't fly with you"); return false; } return true; } - botAI->TellError("Cannot find any flightmaster to talk"); + botAI->GetServices().GetChatService().TellError("Cannot find any flightmaster to talk"); return false; } diff --git a/src/Ai/Base/Actions/TeleportAction.cpp b/src/Ai/Base/Actions/TeleportAction.cpp index 65aa24e09f..7eee442ad3 100644 --- a/src/Ai/Base/Actions/TeleportAction.cpp +++ b/src/Ai/Base/Actions/TeleportAction.cpp @@ -8,6 +8,7 @@ #include "Event.h" #include "LastMovementValue.h" #include "Playerbots.h" +#include "BotChatService.h" bool TeleportAction::Execute(Event event) { @@ -52,7 +53,7 @@ bool TeleportAction::Execute(Event event) { std::ostringstream out; out << "Using portal: " << closestPortal->GetName(); - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); WorldPacket data(CMSG_GAMEOBJ_USE); data << closestPortal->GetGUID(); @@ -80,7 +81,7 @@ bool TeleportAction::Execute(Event event) std::ostringstream out; out << "Teleporting using " << goInfo->name; - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); botAI->ChangeStrategy("-follow,+stay", BOT_STATE_NON_COMBAT); @@ -106,6 +107,6 @@ bool TeleportAction::Execute(Event event) } // If no teleport option is found - botAI->TellError("Cannot find any portal to teleport"); + botAI->GetServices().GetChatService().TellError("Cannot find any portal to teleport"); return false; } diff --git a/src/Ai/Base/Actions/TellCastFailedAction.cpp b/src/Ai/Base/Actions/TellCastFailedAction.cpp index 792a324459..7cc6012d7d 100644 --- a/src/Ai/Base/Actions/TellCastFailedAction.cpp +++ b/src/Ai/Base/Actions/TellCastFailedAction.cpp @@ -4,7 +4,9 @@ */ #include "TellCastFailedAction.h" +#include "BotChatService.h" +#include "BotSpellService.h" #include "ChatHelper.h" #include "Event.h" #include "Playerbots.h" @@ -16,7 +18,7 @@ bool TellCastFailedAction::Execute(Event event) uint8 castCount, result; uint32 spellId; p >> castCount >> spellId >> result; - botAI->SpellInterrupted(spellId); + botAI->GetServices().GetSpellService().SpellInterrupted(spellId); if (result == SPELL_CAST_OK) return false; @@ -51,7 +53,7 @@ bool TellCastFailedAction::Execute(Event event) } if (spellInfo->CalcCastTime() >= 2000) - botAI->TellError(out.str()); + botAI->GetServices().GetChatService().TellError(out.str()); return true; } @@ -69,6 +71,6 @@ bool TellSpellAction::Execute(Event event) std::ostringstream out; out << chat->FormatSpell(spellInfo); - botAI->TellError(out.str()); + botAI->GetServices().GetChatService().TellError(out.str()); return true; } diff --git a/src/Ai/Base/Actions/TellGlyphsAction.cpp b/src/Ai/Base/Actions/TellGlyphsAction.cpp index 3eac104731..02f1eb616d 100644 --- a/src/Ai/Base/Actions/TellGlyphsAction.cpp +++ b/src/Ai/Base/Actions/TellGlyphsAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "TellGlyphsAction.h" #include "Event.h" @@ -105,9 +106,9 @@ bool TellGlyphsAction::Execute(Event event) // 4. Send chat messages //----------------------------------------------------------------- if (first) // no glyphs - botAI->TellMaster("No glyphs equipped"); + botAI->GetServices().GetChatService().TellMaster("No glyphs equipped"); else - botAI->TellMaster(std::string("Glyphs: ") + list.str()); + botAI->GetServices().GetChatService().TellMaster(std::string("Glyphs: ") + list.str()); return true; } diff --git a/src/Ai/Base/Actions/TellItemCountAction.cpp b/src/Ai/Base/Actions/TellItemCountAction.cpp index ade7571f30..99560396e3 100644 --- a/src/Ai/Base/Actions/TellItemCountAction.cpp +++ b/src/Ai/Base/Actions/TellItemCountAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "TellItemCountAction.h" #include "Event.h" @@ -23,7 +24,7 @@ bool TellItemCountAction::Execute(Event event) soulbound[proto->ItemId] = item->IsSoulBound(); } - botAI->TellMaster("=== Inventory ==="); + botAI->GetServices().GetChatService().TellMaster("=== Inventory ==="); for (std::map::iterator i = itemMap.begin(); i != itemMap.end(); ++i) { ItemTemplate const* proto = sObjectMgr->GetItemTemplate(i->first); diff --git a/src/Ai/Base/Actions/TellLosAction.cpp b/src/Ai/Base/Actions/TellLosAction.cpp index 9cd12b02e3..6db65146b5 100644 --- a/src/Ai/Base/Actions/TellLosAction.cpp +++ b/src/Ai/Base/Actions/TellLosAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "TellLosAction.h" #include #include @@ -56,30 +57,30 @@ bool TellLosAction::Execute(Event event) void TellLosAction::ListUnits(std::string const title, GuidVector units) { - botAI->TellMaster(title); + botAI->GetServices().GetChatService().TellMaster(title); for (ObjectGuid const guid : units) { if (Unit* unit = botAI->GetUnit(guid)) { - botAI->TellMaster(unit->GetNameForLocaleIdx(sWorld->GetDefaultDbcLocale())); + botAI->GetServices().GetChatService().TellMaster(unit->GetNameForLocaleIdx(sWorld->GetDefaultDbcLocale())); } } } void TellLosAction::ListGameObjects(std::string const title, GuidVector gos) { - botAI->TellMaster(title); + botAI->GetServices().GetChatService().TellMaster(title); for (ObjectGuid const guid : gos) { if (GameObject* go = botAI->GetGameObject(guid)) - botAI->TellMaster(chat->FormatGameobject(go)); + botAI->GetServices().GetChatService().TellMaster(chat->FormatGameobject(go)); } } bool TellAuraAction::Execute(Event event) { - botAI->TellMaster("--- Auras ---"); + botAI->GetServices().GetChatService().TellMaster("--- Auras ---"); sLog->outMessage("playerbot", LOG_LEVEL_DEBUG, "--- Auras ---"); Unit::AuraApplicationMap& map = bot->GetAppliedAuras(); for (Unit::AuraApplicationMap::iterator i = map.begin(); i != map.end(); ++i) @@ -87,7 +88,7 @@ bool TellAuraAction::Execute(Event event) Aura* aura = i->second->GetBase(); if (!aura) continue; - const std::string auraName = aura->GetSpellInfo()->SpellName[0]; + std::string const auraName = aura->GetSpellInfo()->SpellName[0]; sLog->outMessage("playerbot", LOG_LEVEL_DEBUG, "Info of Aura - name: " + auraName); AuraObjectType type = aura->GetType(); WorldObject* owner = aura->GetOwner(); @@ -97,7 +98,6 @@ bool TellAuraAction::Execute(Event event) std::string caster_name = caster ? caster->GetName() : "unknown"; bool is_area = aura->IsArea(); int32 duration = aura->GetDuration(); - const SpellInfo* spellInfo = aura->GetSpellInfo(); int32 spellId = aura->GetSpellInfo()->Id; bool isPositive = aura->GetSpellInfo()->IsPositive(); sLog->outMessage("playerbot", LOG_LEVEL_DEBUG, @@ -106,7 +106,7 @@ bool TellAuraAction::Execute(Event event) " isArea: " + std::to_string(is_area) + " duration: " + std::to_string(duration) + " spellId: " + std::to_string(spellId) + " isPositive: " + std::to_string(isPositive)); - botAI->TellMaster("Info of Aura - name: " + auraName + " caster: " + caster_name + " type: " + + botAI->GetServices().GetChatService().TellMaster("Info of Aura - name: " + auraName + " caster: " + caster_name + " type: " + std::to_string(type) + " owner: " + owner_name + " distance: " + std::to_string(distance) + " isArea: " + std::to_string(is_area) + " duration: " + std::to_string(duration) + " spellId: " + std::to_string(spellId) + " isPositive: " + std::to_string(isPositive)); @@ -122,7 +122,7 @@ bool TellAuraAction::Execute(Event event) " radius: " + std::to_string(radius) + " spell id: " + std::to_string(spellId) + " duration: " + std::to_string(duration)); - botAI->TellMaster(std::string("Info of DynamicObject -") + " name: " + dyn_owner->GetName() + + botAI->GetServices().GetChatService().TellMaster(std::string("Info of DynamicObject -") + " name: " + dyn_owner->GetName() + " radius: " + std::to_string(radius) + " spell id: " + std::to_string(spellId) + " duration: " + std::to_string(duration)); } @@ -133,7 +133,7 @@ bool TellAuraAction::Execute(Event event) bool TellEstimatedDpsAction::Execute(Event event) { float dps = AI_VALUE(float, "estimated group dps"); - botAI->TellMaster("Estimated Group DPS: " + std::to_string(dps)); + botAI->GetServices().GetChatService().TellMaster("Estimated Group DPS: " + std::to_string(dps)); return true; } @@ -143,13 +143,13 @@ bool TellCalculateItemAction::Execute(Event event) ItemWithRandomProperty item = chat->parseItemWithRandomProperty(text); StatsWeightCalculator calculator(bot); - const ItemTemplate* proto = sObjectMgr->GetItemTemplate(item.itemId); + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item.itemId); if (!proto) return false; float score = calculator.CalculateItem(item.itemId, item.randomPropertyId); std::ostringstream out; out << "Calculated score of " << chat->FormatItem(proto) << " : " << score; - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); return true; } \ No newline at end of file diff --git a/src/Ai/Base/Actions/TellMasterAction.cpp b/src/Ai/Base/Actions/TellMasterAction.cpp index 4b8d96d561..ec16ca318c 100644 --- a/src/Ai/Base/Actions/TellMasterAction.cpp +++ b/src/Ai/Base/Actions/TellMasterAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "TellMasterAction.h" #include "Event.h" @@ -10,13 +11,13 @@ bool TellMasterAction::Execute(Event event) { - botAI->TellMaster(text); + botAI->GetServices().GetChatService().TellMaster(text); return true; } bool OutOfReactRangeAction::Execute(Event event) { - botAI->TellMaster("Wait for me!"); + botAI->GetServices().GetChatService().TellMaster("Wait for me!"); return true; } diff --git a/src/Ai/Base/Actions/TellReputationAction.cpp b/src/Ai/Base/Actions/TellReputationAction.cpp index e08a38d840..4a8ea9ee91 100644 --- a/src/Ai/Base/Actions/TellReputationAction.cpp +++ b/src/Ai/Base/Actions/TellReputationAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "TellReputationAction.h" #include "Event.h" @@ -71,7 +72,7 @@ bool TellReputationAction::Execute(Event event) base -= ReputationMgr::PointsInRank[i]; out << " (" << (reputation - base) << "/" << ReputationMgr::PointsInRank[rank] << ")"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); return true; } diff --git a/src/Ai/Base/Actions/TellTargetAction.cpp b/src/Ai/Base/Actions/TellTargetAction.cpp index 7ecd8efb5a..e74bdd35a3 100644 --- a/src/Ai/Base/Actions/TellTargetAction.cpp +++ b/src/Ai/Base/Actions/TellTargetAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "TellTargetAction.h" #include "Event.h" @@ -16,7 +17,7 @@ bool TellTargetAction::Execute(Event event) { std::ostringstream out; out << "Attacking " << target->GetName(); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); context->GetValue("old target")->Set(target); } @@ -26,7 +27,7 @@ bool TellTargetAction::Execute(Event event) bool TellAttackersAction::Execute(Event event) { - botAI->TellMaster("--- Attackers ---"); + botAI->GetServices().GetChatService().TellMaster("--- Attackers ---"); GuidVector attackers = context->GetValue("attackers")->Get(); int32 count = 0; @@ -36,10 +37,10 @@ bool TellAttackersAction::Execute(Event event) if (!unit || !unit->IsAlive()) continue; - botAI->TellMaster(std::to_string(++count) + std::string(".") + unit->GetName()); + botAI->GetServices().GetChatService().TellMaster(std::to_string(++count) + std::string(".") + unit->GetName()); } - botAI->TellMaster("--- Threat ---"); + botAI->GetServices().GetChatService().TellMaster("--- Threat ---"); HostileReference* ref = bot->getHostileRefMgr().getFirst(); if (!ref) @@ -53,7 +54,7 @@ bool TellAttackersAction::Execute(Event event) std::ostringstream out; out << unit->GetName() << " (" << threat << ")"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); ref = ref->next(); } diff --git a/src/Ai/Base/Actions/TradeAction.cpp b/src/Ai/Base/Actions/TradeAction.cpp index ad81d9f93b..08d113d020 100644 --- a/src/Ai/Base/Actions/TradeAction.cpp +++ b/src/Ai/Base/Actions/TradeAction.cpp @@ -56,7 +56,7 @@ bool TradeAction::Execute(Event event) } size_t pos = text.rfind(" "); - int count = pos != std::string::npos ? atoi(text.substr(pos + 1).c_str()) : 1; + uint32 count = pos != std::string::npos ? atoi(text.substr(pos + 1).c_str()) : 1; std::vector found = parseItems(text); if (found.empty()) diff --git a/src/Ai/Base/Actions/TradeStatusAction.cpp b/src/Ai/Base/Actions/TradeStatusAction.cpp index 16ad2ca79d..f6527eed32 100644 --- a/src/Ai/Base/Actions/TradeStatusAction.cpp +++ b/src/Ai/Base/Actions/TradeStatusAction.cpp @@ -3,8 +3,10 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "TradeStatusAction.h" +#include "Bot/Core/ManagerRegistry.h" #include "CraftValue.h" #include "Event.h" #include "GuildTaskMgr.h" @@ -64,8 +66,6 @@ bool TradeStatusAction::Execute(Event event) uint32 discount = sRandomPlayerbotMgr->GetTradeDiscount(bot, trader); if (CheckTrade()) { - int32 botMoney = CalculateCost(bot, true); - std::map givenItemIds, takenItemIds; for (uint32 slot = 0; slot < TRADE_SLOT_TRADED_COUNT; ++slot) { @@ -138,7 +138,7 @@ void TradeStatusAction::BeginTrade() ListItemsVisitor visitor; IterateItems(&visitor); - botAI->TellMaster("=== Inventory ==="); + botAI->GetServices().GetChatService().TellMaster("=== Inventory ==="); TellItems(visitor.items, visitor.soulbound); if (sRandomPlayerbotMgr->IsRandomBot(bot)) @@ -148,7 +148,7 @@ void TradeStatusAction::BeginTrade() { std::ostringstream out; out << "Discount up to: " << chat->formatMoney(discount); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } } } @@ -161,16 +161,6 @@ bool TradeStatusAction::CheckTrade() if (!botAI->HasActivePlayerMaster() && GET_PLAYERBOT_AI(bot->GetTrader())) { - bool isGivingItem = false; - for (uint32 slot = 0; slot < TRADE_SLOT_TRADED_COUNT; ++slot) - { - Item* item = bot->GetTradeData()->GetItem((TradeSlots)slot); - if (item) - { - isGivingItem = true; - break; - } - } bool isGettingItem = false; for (uint32 slot = 0; slot < TRADE_SLOT_TRADED_COUNT; ++slot) { @@ -186,7 +176,7 @@ bool TradeStatusAction::CheckTrade() { if (bot->GetGroup() && bot->GetGroup()->IsMember(bot->GetTrader()->GetGUID()) && botAI->HasRealPlayerMaster()) - botAI->TellMasterNoFacing("Thank you " + chat->FormatWorldobject(bot->GetTrader())); + botAI->GetServices().GetChatService().TellMasterNoFacing("Thank you " + chat->FormatWorldobject(bot->GetTrader())); else bot->Say("Thank you " + chat->FormatWorldobject(bot->GetTrader()), (bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH)); @@ -205,7 +195,7 @@ bool TradeStatusAction::CheckTrade() int32 playerItemsMoney = CalculateCost(trader, false); int32 playerMoney = trader->GetTradeData()->GetMoney() + playerItemsMoney; if (playerMoney || botMoney) - botAI->PlaySound(playerMoney < botMoney ? TEXT_EMOTE_SIGH : TEXT_EMOTE_THANK); + botAI->GetServices().GetChatService().PlaySound(playerMoney < botMoney ? TEXT_EMOTE_SIGH : TEXT_EMOTE_THANK); return true; } @@ -231,8 +221,8 @@ bool TradeStatusAction::CheckTrade() { std::ostringstream out; out << chat->FormatItem(item->GetTemplate()) << " - This is not for sale"; - botAI->TellMaster(out); - botAI->PlaySound(TEXT_EMOTE_NO); + botAI->GetServices().GetChatService().TellMaster(out); + botAI->GetServices().GetChatService().PlaySound(TEXT_EMOTE_NO); return false; } @@ -246,8 +236,8 @@ bool TradeStatusAction::CheckTrade() { std::ostringstream out; out << chat->FormatItem(item->GetTemplate()) << " - I don't need this"; - botAI->TellMaster(out); - botAI->PlaySound(TEXT_EMOTE_NO); + botAI->GetServices().GetChatService().TellMaster(out); + botAI->GetServices().GetChatService().PlaySound(TEXT_EMOTE_NO); return false; } } @@ -258,7 +248,7 @@ bool TradeStatusAction::CheckTrade() if (!botItemsMoney && !playerItemsMoney) { - botAI->TellError("There are no items to trade"); + botAI->GetServices().GetChatService().TellError("There are no items to trade"); return false; } @@ -272,8 +262,8 @@ bool TradeStatusAction::CheckTrade() { if (moneyDelta < 0) { - botAI->TellError("You can use discount to buy items only"); - botAI->PlaySound(TEXT_EMOTE_NO); + botAI->GetServices().GetChatService().TellError("You can use discount to buy items only"); + botAI->GetServices().GetChatService().PlaySound(TEXT_EMOTE_NO); return false; } @@ -291,27 +281,29 @@ bool TradeStatusAction::CheckTrade() switch (urand(0, 4)) { case 0: - botAI->TellMaster("A pleasure doing business with you"); + botAI->GetServices().GetChatService().TellMaster("A pleasure doing business with you"); break; case 1: - botAI->TellMaster("Fair trade"); + botAI->GetServices().GetChatService().TellMaster("Fair trade"); break; case 2: - botAI->TellMaster("Thanks"); + botAI->GetServices().GetChatService().TellMaster("Thanks"); break; case 3: - botAI->TellMaster("Off with you"); + botAI->GetServices().GetChatService().TellMaster("Off with you"); + break; + default: break; } - botAI->PlaySound(TEXT_EMOTE_THANK); + botAI->GetServices().GetChatService().PlaySound(TEXT_EMOTE_THANK); return true; } std::ostringstream out; out << "I want " << chat->formatMoney(-(delta + discount)) << " for this"; - botAI->TellMaster(out); - botAI->PlaySound(TEXT_EMOTE_NO); + botAI->GetServices().GetChatService().TellMaster(out); + botAI->GetServices().GetChatService().PlaySound(TEXT_EMOTE_NO); return false; } @@ -353,11 +345,11 @@ int32 TradeStatusAction::CalculateCost(Player* player, bool sell) if (sell) { - sum += item->GetCount() * proto->SellPrice * sRandomPlayerbotMgr->GetSellMultiplier(bot); + sum += item->GetCount() * proto->SellPrice * sManagerRegistry.GetRandomBotManager().GetSellMultiplier(bot); } else { - sum += item->GetCount() * proto->BuyPrice * sRandomPlayerbotMgr->GetBuyMultiplier(bot); + sum += item->GetCount() * proto->BuyPrice * sManagerRegistry.GetRandomBotManager().GetBuyMultiplier(bot); } } diff --git a/src/Ai/Base/Actions/TradeStatusExtendedAction.cpp b/src/Ai/Base/Actions/TradeStatusExtendedAction.cpp index ada779e36d..3520d57f2f 100644 --- a/src/Ai/Base/Actions/TradeStatusExtendedAction.cpp +++ b/src/Ai/Base/Actions/TradeStatusExtendedAction.cpp @@ -1,9 +1,11 @@ +#include "BotChatService.h" #include "TradeStatusExtendedAction.h" #include "Event.h" #include "Player.h" #include "PlayerbotAI.h" #include "WorldPacket.h" #include "TradeData.h" +#include "BotSpellService.h" bool TradeStatusExtendedAction::Execute(Event event) { @@ -69,13 +71,13 @@ bool TradeStatusExtendedAction::Execute(Event event) if (bot->getClass() == CLASS_ROGUE && bot->HasSpell(1804) && lockbox->IsLocked()) // Pick Lock spell { - // botAI->CastSpell(1804, bot, lockbox); // Attempt to cast Pick Lock on the lockbox + // botAI->GetServices().GetSpellService().CastSpell(1804, bot, lockbox); // Attempt to cast Pick Lock on the lockbox botAI->DoSpecificAction("unlock traded item"); botAI->SetNextCheckDelay(4000); // Delay before accepting trade } else { - botAI->TellMaster("I can't unlock this item."); + botAI->GetServices().GetChatService().TellMaster("I can't unlock this item."); } } } diff --git a/src/Ai/Base/Actions/TrainerAction.cpp b/src/Ai/Base/Actions/TrainerAction.cpp index bca7406012..1e5a51c73a 100644 --- a/src/Ai/Base/Actions/TrainerAction.cpp +++ b/src/Ai/Base/Actions/TrainerAction.cpp @@ -3,8 +3,10 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "TrainerAction.h" +#include "Bot/Core/ManagerRegistry.h" #include "BudgetValues.h" #include "Event.h" #include "PlayerbotFactory.h" @@ -81,7 +83,7 @@ void TrainerAction::Iterate(Creature* creature, TrainerSpellAction action, Spell if (action) (this->*action)(cost, spell, out); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } TellFooter(totalCost); @@ -117,7 +119,7 @@ bool TrainerAction::Execute(Event event) if (trainer_spells.empty()) { - botAI->TellError("No spells can be learned from this trainer"); + botAI->GetServices().GetChatService().TellError("No spells can be learned from this trainer"); return false; } @@ -126,7 +128,7 @@ bool TrainerAction::Execute(Event event) if (spell) spells.insert(spell); - if (text.find("learn") != std::string::npos || sRandomPlayerbotMgr->IsRandomBot(bot) || + if (text.find("learn") != std::string::npos || sManagerRegistry.GetRandomBotManager().IsRandomBot(bot) || (sPlayerbotAIConfig->autoTrainSpells != "no" && (trainer->GetTrainerType() != Trainer::Type::Tradeskill || !botAI->HasActivePlayerMaster()))) // Todo rewrite to only exclude start primary profession skills and make @@ -142,7 +144,7 @@ void TrainerAction::TellHeader(Creature* creature) { std::ostringstream out; out << "--- Can learn from " << creature->GetName() << " ---"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } void TrainerAction::TellFooter(uint32 totalCost) @@ -151,19 +153,19 @@ void TrainerAction::TellFooter(uint32 totalCost) { std::ostringstream out; out << "Total cost: " << chat->formatMoney(totalCost); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } } -bool MaintenanceAction::Execute(Event event) +bool MaintenanceAction::Execute(Event /*event*/) { if (!sPlayerbotAIConfig->maintenanceCommand) { - botAI->TellError("maintenance command is not allowed, please check the configuration."); + botAI->GetServices().GetChatService().TellError("maintenance command is not allowed, please check the configuration."); return false; } - botAI->TellMaster("I'm maintaining"); + botAI->GetServices().GetChatService().TellMaster("I'm maintaining"); PlayerbotFactory factory(bot, bot->GetLevel()); if (!botAI->IsAlt()) @@ -255,7 +257,7 @@ bool MaintenanceAction::Execute(Event event) return true; } -bool RemoveGlyphAction::Execute(Event event) +bool RemoveGlyphAction::Execute(Event /*event*/) { for (uint32 slotIndex = 0; slotIndex < MAX_GLYPH_SLOT_INDEX; ++slotIndex) { @@ -265,22 +267,22 @@ bool RemoveGlyphAction::Execute(Event event) return true; } -bool AutoGearAction::Execute(Event event) +bool AutoGearAction::Execute(Event /*event*/) { if (!sPlayerbotAIConfig->autoGearCommand) { - botAI->TellError("autogear command is not allowed, please check the configuration."); + botAI->GetServices().GetChatService().TellError("autogear command is not allowed, please check the configuration."); return false; } if (!sPlayerbotAIConfig->autoGearCommandAltBots && !sPlayerbotAIConfig->IsInRandomAccountList(bot->GetSession()->GetAccountId())) { - botAI->TellError("You cannot use autogear on alt bots."); + botAI->GetServices().GetChatService().TellError("You cannot use autogear on alt bots."); return false; } - botAI->TellMaster("I'm auto gearing"); + botAI->GetServices().GetChatService().TellMaster("I'm auto gearing"); uint32 gs = sPlayerbotAIConfig->autoGearScoreLimit == 0 ? 0 : PlayerbotFactory::CalcMixedGearScore(sPlayerbotAIConfig->autoGearScoreLimit, diff --git a/src/Ai/Base/Actions/TravelAction.cpp b/src/Ai/Base/Actions/TravelAction.cpp index a290605718..6abb098a14 100644 --- a/src/Ai/Base/Actions/TravelAction.cpp +++ b/src/Ai/Base/Actions/TravelAction.cpp @@ -10,7 +10,7 @@ #include "GridNotifiersImpl.h" #include "Playerbots.h" -bool TravelAction::Execute(Event event) +bool TravelAction::Execute(Event /*event*/) { TravelTarget* target = AI_VALUE(TravelTarget*, "travel target"); @@ -37,7 +37,7 @@ bool TravelAction::Execute(Event event) if (!newTarget->IsAlive()) continue; - if (newTarget->GetEntry() == target->getDestination()->getEntry()) + if (static_cast(newTarget->GetEntry()) == target->getDestination()->getEntry()) continue; if (newTarget->IsInCombat()) @@ -131,9 +131,9 @@ bool MoveFromDarkPortalAction::Execute(Event event) RESET_AI_VALUE(GuidPosition, "rpg target"); if (bot->GetTeamId() == TEAM_ALLIANCE) - return MoveTo(530, -319.261f, 1027.213, 54.172638f, false, true); + return MoveTo(530, -319.261f, 1027.213f, 54.172638f, false, true); else - return MoveTo(530, -180.444f, 1027.947, 54.181538f, false, true); + return MoveTo(530, -180.444f, 1027.947f, 54.181538f, false, true); return false; } diff --git a/src/Ai/Base/Actions/UnequipAction.cpp b/src/Ai/Base/Actions/UnequipAction.cpp index f5b5f62856..b509def69f 100644 --- a/src/Ai/Base/Actions/UnequipAction.cpp +++ b/src/Ai/Base/Actions/UnequipAction.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "UnequipAction.h" #include "Event.h" @@ -78,5 +79,5 @@ void UnequipAction::UnequipItem(Item* item) std::ostringstream out; out << chat->FormatItem(item->GetTemplate()) << " unequipped"; - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); } diff --git a/src/Ai/Base/Actions/UnlockItemAction.cpp b/src/Ai/Base/Actions/UnlockItemAction.cpp index 367a4fcbfb..4c21e55cbb 100644 --- a/src/Ai/Base/Actions/UnlockItemAction.cpp +++ b/src/Ai/Base/Actions/UnlockItemAction.cpp @@ -1,3 +1,4 @@ +#include "BotChatService.h" #include "UnlockItemAction.h" #include "PlayerbotAI.h" #include "ItemTemplate.h" @@ -5,14 +6,16 @@ #include "Player.h" #include "ObjectMgr.h" #include "SpellInfo.h" +#include "BotSpellService.h" +#include "BotItemService.h" -#define PICK_LOCK_SPELL_ID 1804 +static constexpr uint32 PICK_LOCK_SPELL_ID = 1804; bool UnlockItemAction::Execute(Event event) { bool foundLockedItem = false; - Item* item = botAI->FindLockedItem(); + Item* item = botAI->GetServices().GetItemService().FindLockedItem(); if (item) { UnlockItem(item); @@ -25,14 +28,14 @@ bool UnlockItemAction::Execute(Event event) void UnlockItemAction::UnlockItem(Item* item) { // Use CastSpell to unlock the item - if (botAI->CastSpell(PICK_LOCK_SPELL_ID, bot, item)) + if (botAI->GetServices().GetSpellService().CastSpell(PICK_LOCK_SPELL_ID, bot, item)) { std::ostringstream out; out << "Used Pick Lock on: " << item->GetTemplate()->Name1; - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); } else { - botAI->TellError("Failed to cast Pick Lock."); + botAI->GetServices().GetChatService().TellError("Failed to cast Pick Lock."); } } diff --git a/src/Ai/Base/Actions/UnlockTradedItemAction.cpp b/src/Ai/Base/Actions/UnlockTradedItemAction.cpp index 047cd2e201..bc1fc705a5 100644 --- a/src/Ai/Base/Actions/UnlockTradedItemAction.cpp +++ b/src/Ai/Base/Actions/UnlockTradedItemAction.cpp @@ -1,9 +1,11 @@ +#include "BotChatService.h" #include "UnlockTradedItemAction.h" #include "Playerbots.h" #include "TradeData.h" #include "SpellInfo.h" +#include "BotSpellService.h" -#define PICK_LOCK_SPELL_ID 1804 +static constexpr uint32 PICK_LOCK_SPELL_ID = 1804; bool UnlockTradedItemAction::Execute(Event event) { @@ -18,13 +20,13 @@ bool UnlockTradedItemAction::Execute(Event event) Item* lockbox = tradeData->GetItem(TRADE_SLOT_NONTRADED); if (!lockbox) { - botAI->TellError("No item in the Do Not Trade slot."); + botAI->GetServices().GetChatService().TellError("No item in the Do Not Trade slot."); return false; } if (!CanUnlockItem(lockbox)) { - botAI->TellError("Cannot unlock this item."); + botAI->GetServices().GetChatService().TellError("Cannot unlock this item."); return false; } @@ -68,7 +70,7 @@ bool UnlockTradedItemAction::CanUnlockItem(Item* item) std::ostringstream out; out << "Lockpicking skill too low (" << botSkill << "/" << requiredSkill << ") to unlock: " << item->GetTemplate()->Name1; - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); } } } @@ -80,19 +82,19 @@ void UnlockTradedItemAction::UnlockItem(Item* item) { if (!bot->HasSpell(PICK_LOCK_SPELL_ID)) { - botAI->TellError("Cannot unlock, Pick Lock spell is missing."); + botAI->GetServices().GetChatService().TellError("Cannot unlock, Pick Lock spell is missing."); return; } // Use CastSpell to unlock the item - if (botAI->CastSpell(PICK_LOCK_SPELL_ID, bot->GetTrader(), item)) // Unit target is trader + if (botAI->GetServices().GetSpellService().CastSpell(PICK_LOCK_SPELL_ID, bot->GetTrader(), item)) // Unit target is trader { std::ostringstream out; out << "Picking Lock on traded item: " << item->GetTemplate()->Name1; - botAI->TellMaster(out.str()); + botAI->GetServices().GetChatService().TellMaster(out.str()); } else { - botAI->TellError("Failed to cast Pick Lock."); + botAI->GetServices().GetChatService().TellError("Failed to cast Pick Lock."); } } diff --git a/src/Ai/Base/Actions/UseItemAction.cpp b/src/Ai/Base/Actions/UseItemAction.cpp index bfb86ef0bd..06718757cb 100644 --- a/src/Ai/Base/Actions/UseItemAction.cpp +++ b/src/Ai/Base/Actions/UseItemAction.cpp @@ -1,3 +1,5 @@ +#include "BotChatService.h" +#include "BotSpellService.h" /* * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it * and/or modify it under version 3 of the License, or (at your option), any later version. @@ -35,7 +37,7 @@ bool UseItemAction::Execute(Event event) return UseItemOnGameObject(*items.begin(), *gos.begin()); } - botAI->TellError("No items (or game objects) available"); + botAI->GetServices().GetChatService().TellError("No items (or game objects) available"); return false; } @@ -49,7 +51,7 @@ bool UseItemAction::UseGameObject(ObjectGuid guid) std::ostringstream out; out << "Using " << chat->FormatGameobject(go); - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); return true; } @@ -69,7 +71,6 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni uint8 bagIndex = item->GetBagSlot(); uint8 slot = item->GetSlot(); - uint8 spell_index = 0; uint8 cast_count = 1; ObjectGuid item_guid = item->GetGUID(); uint32 glyphIndex = 0; @@ -81,7 +82,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni if (item->GetTemplate()->Spells[i].SpellId > 0) { spellId = item->GetTemplate()->Spells[i].SpellId; - if (!botAI->CanCastSpell(spellId, bot, false, itemTarget, item)) + if (!botAI->GetServices().GetSpellService().CanCastSpell(spellId, bot, false, itemTarget, item)) { return false; } @@ -125,7 +126,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni { bool fit = SocketItem(itemTarget, item) || SocketItem(itemTarget, item, true); if (!fit) - botAI->TellMaster("Socket does not fit"); + botAI->GetServices().GetChatService().TellMaster("Socket does not fit"); return fit; } @@ -176,7 +177,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni std::ostringstream out; out << "Got quest " << chat->FormatQuest(qInfo); - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); return true; } } @@ -197,7 +198,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni if (!spellId) continue; - if (!botAI->CanCastSpell(spellId, bot, false)) + if (!botAI->GetServices().GetSpellService().CanCastSpell(spellId, bot, false)) continue; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); @@ -271,7 +272,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni return false; // bot->SetStandState(UNIT_STAND_STATE_SIT); - botAI->InterruptSpell(); + botAI->GetServices().GetSpellService().InterruptSpell(); float hp = bot->GetHealthPct(); float mp = bot->GetPower(POWER_MANA) * 100.0f / bot->GetMaxPower(POWER_MANA); float p = 0.f; @@ -308,7 +309,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni return false; // botAI->SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown); - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); bot->GetSession()->HandleUseItemOpcode(packet); return true; } @@ -322,7 +323,7 @@ void UseItemAction::TellConsumableUse(Item* item, std::string const action, floa out << "/x" << item->GetCount(); out << " (" << round(percent) << "%)"; - botAI->TellMasterNoFacing(out.str()); + botAI->GetServices().GetChatService().TellMasterNoFacing(out.str()); } bool UseItemAction::SocketItem(Item* item, Item* gem, bool replace) @@ -376,7 +377,7 @@ bool UseItemAction::SocketItem(Item* item, Item* gem, bool replace) std::ostringstream out; out << "Socketing " << chat->FormatItem(item->GetTemplate()); out << " with " << chat->FormatItem(gem->GetTemplate()); - botAI->TellMaster(out); + botAI->GetServices().GetChatService().TellMaster(out); WorldPackets::Item::SocketGems nicePacket(std::move(packet)); nicePacket.Read(); @@ -440,7 +441,7 @@ bool UseRandomRecipe::Execute(Event event) bool used = UseItemAction::Execute(Event(name, recipeName)); if (used) - botAI->SetNextCheckDelay(3.0 * IN_MILLISECONDS); + botAI->SetNextCheckDelay(3.0f * IN_MILLISECONDS); return used; } diff --git a/src/Ai/Base/Actions/UseMeetingStoneAction.cpp b/src/Ai/Base/Actions/UseMeetingStoneAction.cpp index 323d9d5859..0c28d8291c 100644 --- a/src/Ai/Base/Actions/UseMeetingStoneAction.cpp +++ b/src/Ai/Base/Actions/UseMeetingStoneAction.cpp @@ -13,6 +13,7 @@ #include "PlayerbotAIConfig.h" #include "Playerbots.h" #include "PositionValue.h" +#include "BotChatService.h" bool UseMeetingStoneAction::Execute(Event event) { @@ -36,7 +37,7 @@ bool UseMeetingStoneAction::Execute(Event event) if (bot->IsInCombat()) { - botAI->TellError("I am in combat"); + botAI->GetServices().GetChatService().TellError("I am in combat"); return false; } @@ -55,7 +56,7 @@ bool UseMeetingStoneAction::Execute(Event event) return Teleport(master, bot, false); } -bool SummonAction::Execute(Event event) +bool SummonAction::Execute(Event /*event*/) { Player* master = GetMaster(); if (!master) @@ -75,13 +76,13 @@ bool SummonAction::Execute(Event event) if (SummonUsingGos(master, bot, true) || SummonUsingNpcs(master, bot, true)) { - botAI->TellMasterNoFacing("Hello!"); + botAI->GetServices().GetChatService().TellMasterNoFacing("Hello!"); return true; } if (SummonUsingGos(bot, master, true) || SummonUsingNpcs(bot, master, true)) { - botAI->TellMasterNoFacing("Welcome!"); + botAI->GetServices().GetChatService().TellMasterNoFacing("Welcome!"); return true; } @@ -101,7 +102,7 @@ bool SummonAction::SummonUsingGos(Player* summoner, Player* player, bool preserv return Teleport(summoner, player, preserveAuras); } - botAI->TellError(summoner == bot ? "There is no meeting stone nearby" : "There is no meeting stone near you"); + botAI->GetServices().GetChatService().TellError(summoner == bot ? "There is no meeting stone nearby" : "There is no meeting stone near you"); return false; } @@ -121,13 +122,13 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player, bool preser { if (!player->HasItemCount(6948, 1, false)) { - botAI->TellError(player == bot ? "I have no hearthstone" : "You have no hearthstone"); + botAI->GetServices().GetChatService().TellError(player == bot ? "I have no hearthstone" : "You have no hearthstone"); return false; } if (player->HasSpellCooldown(8690)) { - botAI->TellError(player == bot ? "My hearthstone is not ready" : "Your hearthstone is not ready"); + botAI->GetServices().GetChatService().TellError(player == bot ? "My hearthstone is not ready" : "Your hearthstone is not ready"); return false; } @@ -143,7 +144,7 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player, bool preser } } - botAI->TellError(summoner == bot ? "There are no innkeepers nearby" : "There are no innkeepers near you"); + botAI->GetServices().GetChatService().TellError(summoner == bot ? "There are no innkeepers nearby" : "There are no innkeepers near you"); return false; } @@ -155,7 +156,7 @@ bool SummonAction::Teleport(Player* summoner, Player* player, bool preserveAuras if (player->GetVehicle()) { - botAI->TellError("You cannot summon me while I'm on a vehicle"); + botAI->GetServices().GetChatService().TellError("You cannot summon me while I'm on a vehicle"); return false; } @@ -177,20 +178,20 @@ bool SummonAction::Teleport(Player* summoner, Player* player, bool preserveAuras if (summoner->IsInCombat() && !sPlayerbotAIConfig->allowSummonInCombat) { - botAI->TellError("You cannot summon me while you're in combat"); + botAI->GetServices().GetChatService().TellError("You cannot summon me while you're in combat"); return false; } if (!summoner->IsAlive() && !sPlayerbotAIConfig->allowSummonWhenMasterIsDead) { - botAI->TellError("You cannot summon me while you're dead"); + botAI->GetServices().GetChatService().TellError("You cannot summon me while you're dead"); return false; } if (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST) && !sPlayerbotAIConfig->allowSummonWhenBotIsDead) { - botAI->TellError("You cannot summon me while I'm dead, you need to release my spirit first"); + botAI->GetServices().GetChatService().TellError("You cannot summon me while I'm dead, you need to release my spirit first"); return false; } @@ -202,7 +203,7 @@ bool SummonAction::Teleport(Player* summoner, Player* player, bool preserveAuras { bot->ResurrectPlayer(1.0f, false); bot->SpawnCorpseBones(); - botAI->TellMasterNoFacing("I live, again!"); + botAI->GetServices().GetChatService().TellMasterNoFacing("I live, again!"); botAI->GetAiObjectContext()->GetValue("prioritized targets")->Reset(); } @@ -230,6 +231,6 @@ bool SummonAction::Teleport(Player* summoner, Player* player, bool preserveAuras } if (summoner != player) - botAI->TellError("Not enough place to summon"); + botAI->GetServices().GetChatService().TellError("Not enough place to summon"); return false; } diff --git a/src/Ai/Base/Actions/VehicleActions.cpp b/src/Ai/Base/Actions/VehicleActions.cpp index ee1c6e2dd8..49ef7c7e38 100644 --- a/src/Ai/Base/Actions/VehicleActions.cpp +++ b/src/Ai/Base/Actions/VehicleActions.cpp @@ -95,7 +95,7 @@ bool EnterVehicleAction::EnterVehicle(Unit* vehicleBase, bool moveIfFar) return true; } -bool LeaveVehicleAction::Execute(Event event) +bool LeaveVehicleAction::Execute(Event /*event*/) { Vehicle* myVehicle = bot->GetVehicle(); if (!myVehicle) diff --git a/src/Ai/Base/Actions/WhoAction.cpp b/src/Ai/Base/Actions/WhoAction.cpp index edafdfcb27..fad1a58167 100644 --- a/src/Ai/Base/Actions/WhoAction.cpp +++ b/src/Ai/Base/Actions/WhoAction.cpp @@ -6,9 +6,11 @@ #include "WhoAction.h" #include "AiFactory.h" +#include "Bot/Core/ManagerRegistry.h" #include "Event.h" #include "ItemVisitors.h" #include "Playerbots.h" +#include "BotItemService.h" #ifndef WIN32 inline int strcmpi(char const* s1, char const* s2) @@ -33,7 +35,7 @@ bool WhoAction::Execute(Event event) { out << QuerySkill(text); - if (sRandomPlayerbotMgr->IsRandomBot(bot)) + if (sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) out << QueryTrade(text); } else @@ -74,7 +76,7 @@ std::string const WhoAction::QueryTrade(std::string const text) for (Item* sell : items) { int32 sellPrice = - sell->GetTemplate()->SellPrice * sRandomPlayerbotMgr->GetSellMultiplier(bot) * sell->GetCount(); + sell->GetTemplate()->SellPrice * sManagerRegistry.GetRandomBotManager().GetSellMultiplier(bot) * sell->GetCount(); if (!sellPrice) continue; @@ -117,7 +119,7 @@ std::string const WhoAction::QuerySpec(std::string const text) out << "|h|cffffffff" << chat->FormatRace(bot->getRace()) << " [" << (bot->getGender() == GENDER_MALE ? "M" : "F") << "] " << chat->FormatClass(bot, spec); out << " (|h|cff00ff00" << (uint32)bot->GetLevel() << "|h|cffffffff lvl), "; - out << "|h|cff00ff00" << botAI->GetEquipGearScore(bot/*, false, false*/) << "|h|cffffffff GS ("; + out << "|h|cff00ff00" << botAI->GetServices().GetItemService().GetEquipGearScore(bot/*, false, false*/) << "|h|cffffffff GS ("; ItemCountByQuality visitor; IterateItems(&visitor, ITERATE_ITEMS_IN_EQUIP); diff --git a/src/Ai/Base/Actions/WipeAction.cpp b/src/Ai/Base/Actions/WipeAction.cpp index bbd1997d6c..1fe087d279 100644 --- a/src/Ai/Base/Actions/WipeAction.cpp +++ b/src/Ai/Base/Actions/WipeAction.cpp @@ -8,8 +8,6 @@ bool WipeAction::Execute(Event event) { - Player* master = event.getOwner(); - if (botAI->GetMaster()->GetGUID() != event.getOwner()->GetGUID()) return false; diff --git a/src/Ai/Base/Actions/WorldBuffAction.cpp b/src/Ai/Base/Actions/WorldBuffAction.cpp index 3cd770e4d0..4a536ee6fd 100644 --- a/src/Ai/Base/Actions/WorldBuffAction.cpp +++ b/src/Ai/Base/Actions/WorldBuffAction.cpp @@ -8,9 +8,8 @@ #include "Event.h" #include "Playerbots.h" -bool WorldBuffAction::Execute(Event event) +bool WorldBuffAction::Execute(Event /*event*/) { - std::string const text = event.getParam(); std::vector buffs = NeedWorldBuffs(bot); // Get matching buffs diff --git a/src/Ai/Base/Actions/WtsAction.cpp b/src/Ai/Base/Actions/WtsAction.cpp index a3c77c27f9..5f5c40aacd 100644 --- a/src/Ai/Base/Actions/WtsAction.cpp +++ b/src/Ai/Base/Actions/WtsAction.cpp @@ -6,6 +6,7 @@ #include "WtsAction.h" #include "AiFactory.h" +#include "Bot/Core/ManagerRegistry.h" #include "Event.h" #include "ItemUsageValue.h" #include "ItemVisitors.h" @@ -20,7 +21,7 @@ bool WtsAction::Execute(Event event) std::ostringstream out; std::string const text = event.getParam(); - if (!sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) return false; std::string const link = event.getParam(); @@ -42,7 +43,7 @@ bool WtsAction::Execute(Event event) if (usage == ITEM_USAGE_NONE) continue; - int32 buyPrice = proto->BuyPrice * sRandomPlayerbotMgr->GetBuyMultiplier(bot); + int32 buyPrice = proto->BuyPrice * sManagerRegistry.GetRandomBotManager().GetBuyMultiplier(bot); if (!buyPrice) continue; diff --git a/src/Ai/Base/Strategy/CombatStrategy.h b/src/Ai/Base/Strategy/CombatStrategy.h index 3296d8bc7c..b47c6ef795 100644 --- a/src/Ai/Base/Strategy/CombatStrategy.h +++ b/src/Ai/Base/Strategy/CombatStrategy.h @@ -23,7 +23,7 @@ class AvoidAoeStrategy : public Strategy { public: explicit AvoidAoeStrategy(PlayerbotAI* ai); - const std::string getName() override { return "avoid aoe"; } + std::string const getName() override { return "avoid aoe"; } std::vector getDefaultActions() override; void InitMultipliers(std::vector& multipliers) override; void InitTriggers(std::vector& triggers) override; @@ -33,7 +33,7 @@ class TankFaceStrategy : public Strategy { public: explicit TankFaceStrategy(PlayerbotAI* ai); - const std::string getName() override { return "tank face"; } + std::string const getName() override { return "tank face"; } std::vector getDefaultActions() override; void InitTriggers(std::vector& triggers) override; }; @@ -42,7 +42,7 @@ class CombatFormationStrategy : public Strategy { public: CombatFormationStrategy(PlayerbotAI* ai) : Strategy(ai) {} - const std::string getName() override { return "formation"; } + std::string const getName() override { return "formation"; } std::vector getDefaultActions() override; }; diff --git a/src/Ai/Base/Strategy/ConserveManaStrategy.cpp b/src/Ai/Base/Strategy/ConserveManaStrategy.cpp index adc3cec884..68e4f9770b 100644 --- a/src/Ai/Base/Strategy/ConserveManaStrategy.cpp +++ b/src/Ai/Base/Strategy/ConserveManaStrategy.cpp @@ -4,6 +4,7 @@ */ #include "ConserveManaStrategy.h" +#include "BotRoleService.h" #include "GenericSpellActions.h" #include "LastSpellCastValue.h" @@ -56,7 +57,7 @@ // return 1.0f; // double saveLevel = AI_VALUE(double, "mana save level"); -// if (saveLevel <= 1.0) +// if (saveLevel <= 1.0f) // return 1.0f; // CastSpellAction* spellAction = dynamic_cast(action); @@ -102,14 +103,14 @@ float HealerAutoSaveManaMultiplier::GetValue(Action* action) Unit* target = healingAction->GetTarget(); if (!target) return 1.0f; - bool isTank = target->ToPlayer() ? botAI->IsTank(target->ToPlayer()) : false; + bool isTank = target->ToPlayer() ? BotRoleService::IsTankStatic(target->ToPlayer()) : false; uint8 health = target->GetHealthPct(); HealingManaEfficiency manaEfficiency = healingAction->manaEfficiency; uint8 estAmount = healingAction->estAmount; uint8 lossAmount = 100 - health; if (isTank) { - estAmount /= 1.5; // tanks have more health + estAmount /= 1.5f; // tanks have more health if (health >= sPlayerbotAIConfig->mediumHealth && (lossAmount < estAmount || manaEfficiency <= HealingManaEfficiency::MEDIUM)) return 0.0f; diff --git a/src/Ai/Base/Trigger/CureTriggers.cpp b/src/Ai/Base/Trigger/CureTriggers.cpp index f119441e10..8adfdef6b0 100644 --- a/src/Ai/Base/Trigger/CureTriggers.cpp +++ b/src/Ai/Base/Trigger/CureTriggers.cpp @@ -4,6 +4,7 @@ */ #include "CureTriggers.h" +#include "BotSpellService.h" #include "Playerbots.h" #include "WorldBuffAction.h" @@ -11,7 +12,7 @@ bool NeedCureTrigger::IsActive() { Unit* target = GetTarget(); - return target && target->IsInWorld() && botAI->HasAuraToDispel(target, dispelType); + return target && target->IsInWorld() && botAI->GetServices().GetSpellService().HasAuraToDispel(target, dispelType); } Value* PartyMemberNeedCureTrigger::GetTargetValue() diff --git a/src/Ai/Base/Trigger/GenericTriggers.cpp b/src/Ai/Base/Trigger/GenericTriggers.cpp index 988413b2f3..8446a4627e 100644 --- a/src/Ai/Base/Trigger/GenericTriggers.cpp +++ b/src/Ai/Base/Trigger/GenericTriggers.cpp @@ -1,3 +1,4 @@ +#include "BotSpellService.h" /* * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it * and/or modify it under version 3 of the License, or (at your option), any later version. @@ -7,6 +8,8 @@ #include +#include "Bot/Core/ManagerRegistry.h" + #include "BattlegroundWS.h" #include "CreatureAI.h" #include "GameTime.h" @@ -22,6 +25,7 @@ #include "Timer.h" #include "PlayerbotAI.h" #include "Player.h" +#include "BotItemService.h" bool LowManaTrigger::IsActive() { @@ -162,10 +166,10 @@ bool BuffTrigger::IsActive() return false; if (!SpellTrigger::IsActive()) return false; - Aura* aura = botAI->GetAura(spell, target, checkIsOwner, checkDuration); + Aura* aura = botAI->GetServices().GetSpellService().GetAura(spell, target, checkIsOwner, checkDuration); if (!aura) return true; - if (beforeDuration && aura->GetDuration() < beforeDuration) + if (beforeDuration && aura->GetDuration() < static_cast(beforeDuration)) return true; return false; } @@ -248,7 +252,7 @@ bool AoeTrigger::IsActive() bool NoFoodTrigger::IsActive() { - bool isRandomBot = sRandomPlayerbotMgr->IsRandomBot(bot); + bool isRandomBot = sManagerRegistry.GetRandomBotManager().IsRandomBot(bot); if (isRandomBot && botAI->HasCheat(BotCheatMask::food)) return false; @@ -257,7 +261,7 @@ bool NoFoodTrigger::IsActive() bool NoDrinkTrigger::IsActive() { - bool isRandomBot = sRandomPlayerbotMgr->IsRandomBot(bot); + bool isRandomBot = sManagerRegistry.GetRandomBotManager().IsRandomBot(bot); if (isRandomBot && botAI->HasCheat(BotCheatMask::food)) return false; @@ -291,7 +295,7 @@ bool SpellTrigger::IsActive() { return GetTarget(); } bool SpellCanBeCastTrigger::IsActive() { Unit* target = GetTarget(); - return target && botAI->CanCastSpell(spell, target); + return target && botAI->GetServices().GetSpellService().CanCastSpell(spell, target); } bool SpellNoCooldownTrigger::IsActive() @@ -411,11 +415,11 @@ bool HealerShouldAttackTrigger::IsActive() return true; } -bool ItemCountTrigger::IsActive() { return AI_VALUE2(uint32, "item count", item) < count; } +bool ItemCountTrigger::IsActive() { return static_cast(AI_VALUE2(uint32, "item count", item)) < count; } bool InterruptSpellTrigger::IsActive() { - return SpellTrigger::IsActive() && botAI->IsInterruptableSpellCasting(GetTarget(), getName()); + return SpellTrigger::IsActive() && botAI->GetServices().GetSpellService().IsInterruptableSpellCasting(GetTarget(), getName()); } bool DeflectSpellTrigger::IsActive() @@ -463,11 +467,11 @@ bool DeflectSpellTrigger::IsActive() bool AttackerCountTrigger::IsActive() { return AI_VALUE(uint8, "attacker count") >= amount; } -bool HasAuraTrigger::IsActive() { return botAI->HasAura(getName(), GetTarget(), false, false, -1, true); } +bool HasAuraTrigger::IsActive() { return botAI->GetServices().GetSpellService().HasAura(getName(), GetTarget(), false, false, -1, true); } bool HasAuraStackTrigger::IsActive() { - Aura* aura = botAI->GetAura(getName(), GetTarget(), false, true, stack); + Aura* aura = botAI->GetServices().GetSpellService().GetAura(getName(), GetTarget(), false, true, stack); // sLog->outMessage("playerbot", LOG_LEVEL_DEBUG, "HasAuraStackTrigger::IsActive %s %d", getName(), aura ? // aura->GetStackAmount() : -1); return aura; @@ -497,7 +501,7 @@ bool TimerBGTrigger::IsActive() return false; } -bool HasNoAuraTrigger::IsActive() { return !botAI->HasAura(getName(), GetTarget()); } +bool HasNoAuraTrigger::IsActive() { return !botAI->GetServices().GetSpellService().HasAura(getName(), GetTarget()); } bool TankAssistTrigger::IsActive() { @@ -682,7 +686,7 @@ bool AmmoCountTrigger::IsActive() if (bot->GetUInt32Value(PLAYER_AMMO_ID) != 0) return ItemCountTrigger::IsActive(); // Ammo already equipped - if (botAI->FindAmmo()) + if (botAI->GetServices().GetItemService().FindAmmo()) return true; // Found ammo in inventory but not equipped return ItemCountTrigger::IsActive(); diff --git a/src/Ai/Base/Trigger/HealthTriggers.cpp b/src/Ai/Base/Trigger/HealthTriggers.cpp index 9f0485b779..e8aafcae0b 100644 --- a/src/Ai/Base/Trigger/HealthTriggers.cpp +++ b/src/Ai/Base/Trigger/HealthTriggers.cpp @@ -27,7 +27,7 @@ bool AoeInGroupTrigger::IsActive() int32 member = botAI->GetNearGroupMemberCount(); if (member < 5) return false; - int threshold = member * 0.5; + int threshold = member * 0.5f; if (member <= 5) threshold = 3; else if (member <= 10) diff --git a/src/Ai/Base/Trigger/PvpTriggers.cpp b/src/Ai/Base/Trigger/PvpTriggers.cpp index c837d8b16c..1df6a3a2a5 100644 --- a/src/Ai/Base/Trigger/PvpTriggers.cpp +++ b/src/Ai/Base/Trigger/PvpTriggers.cpp @@ -4,6 +4,7 @@ */ #include "PvpTriggers.h" +#include "BotSpellService.h" #include "BattleGroundTactics.h" #include "BattlegroundEY.h" @@ -309,7 +310,7 @@ bool VehicleNearTrigger::IsActive() return npcs.size(); } -bool InVehicleTrigger::IsActive() { return botAI->IsInVehicle(); } +bool InVehicleTrigger::IsActive() { return botAI->GetServices().GetSpellService().IsInVehicle(); } bool AllianceNoSnowfallGY::IsActive() { diff --git a/src/Ai/Base/Trigger/RangeTriggers.cpp b/src/Ai/Base/Trigger/RangeTriggers.cpp index af29f984d1..7fb0ee1319 100644 --- a/src/Ai/Base/Trigger/RangeTriggers.cpp +++ b/src/Ai/Base/Trigger/RangeTriggers.cpp @@ -11,10 +11,10 @@ #include "ServerFacade.h" #include "SharedDefines.h" -static float GetSpeedInMotion(Unit* target) -{ - return target->GetSpeed(Movement::SelectSpeedType(target->GetUnitMovementFlags())); -} +// static float GetSpeedInMotion(Unit* target) +// { +// return target->GetSpeed(Movement::SelectSpeedType(target->GetUnitMovementFlags())); +// } bool EnemyTooCloseForSpellTrigger::IsActive() { @@ -201,7 +201,6 @@ bool PartyMemberToHealOutOfSpellRangeTrigger::IsActive() if (!target) return false; - float combatReach = bot->GetCombatReach() + target->GetCombatReach(); return target && (sServerFacade->GetDistance2d(bot, target) > (distance + sPlayerbotAIConfig->contactDistance) || !bot->IsWithinLOSInMap(target)); } diff --git a/src/Ai/Base/Trigger/RpgTriggers.cpp b/src/Ai/Base/Trigger/RpgTriggers.cpp index f390359be3..fe348627e7 100644 --- a/src/Ai/Base/Trigger/RpgTriggers.cpp +++ b/src/Ai/Base/Trigger/RpgTriggers.cpp @@ -31,7 +31,7 @@ bool RpgTrigger::IsActive() { return true; } Event RpgTrigger::Check() { - if (!NoRpgTargetTrigger::IsActive() && (AI_VALUE(std::string, "next rpg action") == "choose rpg target") || + if ((!NoRpgTargetTrigger::IsActive() && (AI_VALUE(std::string, "next rpg action") == "choose rpg target")) || !FarFromRpgTargetTrigger::IsActive()) return Trigger::Check(); diff --git a/src/Ai/Base/Value/Arrow.cpp b/src/Ai/Base/Value/Arrow.cpp index 15fc2e2959..b1084b2255 100644 --- a/src/Ai/Base/Value/Arrow.cpp +++ b/src/Ai/Base/Value/Arrow.cpp @@ -4,6 +4,7 @@ */ #include "Arrow.h" +#include "BotRoleService.h" #include "Map.h" #include "Playerbots.h" @@ -18,7 +19,6 @@ WorldLocation ArrowFormation::GetLocationInternal() uint32 tankLines = 1 + tanks.Size() / 6; uint32 meleeLines = 1 + melee.Size() / 6; uint32 rangedLines = 1 + ranged.Size() / 6; - uint32 healerLines = 1 + healers.Size() / 6; float offset = 0.f; Player* master = botAI->GetMaster(); @@ -73,11 +73,11 @@ void ArrowFormation::Build() FormationSlot* ArrowFormation::FindSlot(Player* member) { - if (botAI->IsTank(member)) + if (BotRoleService::IsTankStatic(member)) return &tanks; - else if (botAI->IsHeal(member)) + else if (BotRoleService::IsHealStatic(member)) return &healers; - else if (botAI->IsRanged(member)) + else if (BotRoleService::IsRangedStatic(member)) return &ranged; else return &melee; @@ -140,15 +140,12 @@ UnitPosition MultiLineUnitPlacer::Place(FormationUnit* unit, uint32 index, uint3 if (count <= 6) return placer.Place(unit, index, count); - uint32 lineNo = index / 6; uint32 indexInLine = index % 6; - uint32 lineSize = std::max(count - lineNo * 6, uint32(6)); - float x = cos(orientation) * sPlayerbotAIConfig->followDistance * lineNo; - float y = sin(orientation) * sPlayerbotAIConfig->followDistance * lineNo; + uint32 lineSize = std::max(count - (index / 6) * 6, uint32(6)); return placer.Place(unit, indexInLine, lineSize); } -UnitPosition SingleLineUnitPlacer::Place(FormationUnit* unit, uint32 index, uint32 count) +UnitPosition SingleLineUnitPlacer::Place(FormationUnit* /*unit*/, uint32 index, uint32 count) { float angle = orientation - M_PI / 2.0f; float x = cos(angle) * sPlayerbotAIConfig->followDistance * ((float)index - (float)count / 2); diff --git a/src/Ai/Base/Value/Arrow.h b/src/Ai/Base/Value/Arrow.h index 3e5a76b8fb..9032f67871 100644 --- a/src/Ai/Base/Value/Arrow.h +++ b/src/Ai/Base/Value/Arrow.h @@ -21,6 +21,15 @@ class UnitPosition x = other.x; y = other.y; } + UnitPosition& operator=(UnitPosition const& other) + { + if (this != &other) + { + x = other.x; + y = other.y; + } + return *this; + } float x, y; }; @@ -102,7 +111,7 @@ class ArrowFormation : public MoveAheadFormation { public: ArrowFormation(PlayerbotAI* botAI) - : MoveAheadFormation(botAI, "arrow"), built(false), masterUnit(nullptr), botUnit(nullptr) + : MoveAheadFormation(botAI, "arrow"), masterUnit(nullptr), botUnit(nullptr), built(false) { } diff --git a/src/Ai/Base/Value/AttackerCountValues.cpp b/src/Ai/Base/Value/AttackerCountValues.cpp index 637fd72864..66ab601066 100644 --- a/src/Ai/Base/Value/AttackerCountValues.cpp +++ b/src/Ai/Base/Value/AttackerCountValues.cpp @@ -4,6 +4,7 @@ */ #include "AttackerCountValues.h" +#include "BotRoleService.h" #include "Playerbots.h" #include "SharedDefines.h" @@ -22,9 +23,9 @@ bool HasAggroValue::Calculate() { return true; } - bool isMT = botAI->IsMainTank(bot); + bool isMT = BotRoleService::IsMainTankStatic(bot); if (victim && - (victim->GetGUID() == bot->GetGUID() || (!isMT && victim->ToPlayer() && botAI->IsTank(victim->ToPlayer())))) + (victim->GetGUID() == bot->GetGUID() || (!isMT && victim->ToPlayer() && BotRoleService::IsTankStatic(victim->ToPlayer())))) { return true; } @@ -96,6 +97,8 @@ uint8 BalancePercentValue::Calculate() case CREATURE_ELITE_WORLDBOSS: level *= 20; break; + default: + break; } attackerLevel += level; diff --git a/src/Ai/Base/Value/AttackerWithoutAuraTargetValue.cpp b/src/Ai/Base/Value/AttackerWithoutAuraTargetValue.cpp index c80ba5f68f..5aa634f3e2 100644 --- a/src/Ai/Base/Value/AttackerWithoutAuraTargetValue.cpp +++ b/src/Ai/Base/Value/AttackerWithoutAuraTargetValue.cpp @@ -27,7 +27,7 @@ Unit* AttackerWithoutAuraTargetValue::Calculate() continue; } - if (!botAI->HasAura(qualifier, unit, false, true)) + if (!botAI->GetServices().GetSpellService().HasAura(qualifier, unit, false, true)) { max_health = unit->GetHealth(); result = unit; @@ -60,7 +60,7 @@ Unit* MeleeAttackerWithoutAuraTargetValue::Calculate() continue; } - if (!botAI->HasAura(qualifier, unit, false, true)) + if (!botAI->GetServices().GetSpellService().HasAura(qualifier, unit, false, true)) { max_health = unit->GetHealth(); result = unit; diff --git a/src/Ai/Base/Value/AttackersValue.cpp b/src/Ai/Base/Value/AttackersValue.cpp index f6da0aa7a6..e58ddae33d 100644 --- a/src/Ai/Base/Value/AttackersValue.cpp +++ b/src/Ai/Base/Value/AttackersValue.cpp @@ -4,6 +4,7 @@ */ #include "AttackersValue.h" +#include "BotSpellService.h" #include "CellImpl.h" #include "GridNotifiers.h" @@ -144,7 +145,7 @@ bool AttackersValue::IsPossibleTarget(Unit* attacker, Player* bot, float /*range if (!attacker) return false; - // bool inCannon = botAI->IsInVehicle(false, true); + // bool inCannon = botAI->GetServices().GetSpellService().IsInVehicle(false, true); // bool enemy = botAI->GetAiObjectContext()->GetValue("enemy player target")->Get(); // Validity checks diff --git a/src/Ai/Base/Value/BudgetValues.cpp b/src/Ai/Base/Value/BudgetValues.cpp index daa7aca863..c4464540fc 100644 --- a/src/Ai/Base/Value/BudgetValues.cpp +++ b/src/Ai/Base/Value/BudgetValues.cpp @@ -10,7 +10,7 @@ uint32 MaxGearRepairCostValue::Calculate() { uint32 TotalCost = 0; - for (int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) + for (uint32 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) { uint16 pos = ((INVENTORY_SLOT_BAG_0 << 8) | i); Item* item = bot->GetItemByPos(pos); diff --git a/src/Ai/Base/Value/CcTargetValue.cpp b/src/Ai/Base/Value/CcTargetValue.cpp index c47151ddfd..bab93c9db5 100644 --- a/src/Ai/Base/Value/CcTargetValue.cpp +++ b/src/Ai/Base/Value/CcTargetValue.cpp @@ -1,9 +1,11 @@ +#include "BotSpellService.h" /* * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it * and/or modify it under version 3 of the License, or (at your option), any later version. */ #include "CcTargetValue.h" +#include "BotRoleService.h" #include "Action.h" #include "Playerbots.h" @@ -18,10 +20,10 @@ class FindTargetForCcStrategy : public FindTargetStrategy } public: - void CheckAttacker(Unit* creature, ThreatMgr* threatMgr) override + void CheckAttacker(Unit* creature, ThreatMgr* /*threatMgr*/) override { Player* bot = botAI->GetBot(); - if (!botAI->CanCastSpell(spell, creature)) + if (!botAI->GetServices().GetSpellService().CanCastSpell(spell, creature)) return; if (*botAI->GetAiObjectContext()->GetValue("rti cc target") == creature) @@ -67,7 +69,7 @@ class FindTargetForCcStrategy : public FindTargetStrategy if (!member || !member->IsAlive() || member == bot) continue; - if (!botAI->IsTank(member)) + if (!BotRoleService::IsTankStatic(member)) continue; float distance = sServerFacade->GetDistance2d(member, creature); diff --git a/src/Ai/Base/Value/CraftValue.h b/src/Ai/Base/Value/CraftValue.h index d3311e6c8a..11395b6aca 100644 --- a/src/Ai/Base/Value/CraftValue.h +++ b/src/Ai/Base/Value/CraftValue.h @@ -21,6 +21,16 @@ class CraftData required.insert(other.required.begin(), other.required.end()); obtained.insert(other.obtained.begin(), other.obtained.end()); } + CraftData& operator=(CraftData const& other) + { + if (this != &other) + { + itemId = other.itemId; + required = other.required; + obtained = other.obtained; + } + return *this; + } uint32 itemId; std::map required, obtained; diff --git a/src/Ai/Base/Value/CurrentCcTargetValue.cpp b/src/Ai/Base/Value/CurrentCcTargetValue.cpp index 27b8100264..7895e0b360 100644 --- a/src/Ai/Base/Value/CurrentCcTargetValue.cpp +++ b/src/Ai/Base/Value/CurrentCcTargetValue.cpp @@ -14,9 +14,9 @@ class FindCurrentCcTargetStrategy : public FindTargetStrategy { } - void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override + void CheckAttacker(Unit* attacker, ThreatMgr* /*threatMgr*/) override { - if (botAI->HasAura(spell, attacker)) + if (botAI->GetServices().GetSpellService().HasAura(spell, attacker)) result = attacker; } diff --git a/src/Ai/Base/Value/DpsTargetValue.cpp b/src/Ai/Base/Value/DpsTargetValue.cpp index 55b47d7c01..fb56faede6 100644 --- a/src/Ai/Base/Value/DpsTargetValue.cpp +++ b/src/Ai/Base/Value/DpsTargetValue.cpp @@ -4,6 +4,7 @@ */ #include "DpsTargetValue.h" +#include "BotRoleService.h" #include "PlayerbotAIConfig.h" #include "Playerbots.h" @@ -29,7 +30,6 @@ class FindMaxThreatGapTargetStrategy : public FindTargetStrategy foundHighPriority = true; return; } - Unit* victim = attacker->GetVictim(); if (!result || CalcThreatGap(attacker, threatMgr) > CalcThreatGap(result, &result->GetThreatMgr())) result = attacker; } @@ -53,7 +53,7 @@ class CasterFindTargetSmartStrategy : public FindTargetStrategy result = nullptr; } - void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override + void CheckAttacker(Unit* attacker, ThreatMgr* /*threatMgr*/) override { if (Group* group = botAI->GetBot()->GetGroup()) { @@ -116,7 +116,7 @@ class CasterFindTargetSmartStrategy : public FindTargetStrategy float time = unit->GetHealth() / dps_; float dis = unit->GetDistance(botAI->GetBot()); float attackRange = - botAI->IsRanged(botAI->GetBot()) ? sPlayerbotAIConfig->spellDistance : sPlayerbotAIConfig->meleeDistance; + BotRoleService::IsRangedStatic(botAI->GetBot()) ? sPlayerbotAIConfig->spellDistance : sPlayerbotAIConfig->meleeDistance; attackRange += 5.0f; int level = dis < attackRange ? 10 : 0; if (time >= 5 && time <= 30) @@ -144,7 +144,7 @@ class GeneralFindTargetSmartStrategy : public FindTargetStrategy { } - void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override + void CheckAttacker(Unit* attacker, ThreatMgr* /*threatMgr*/) override { if (Group* group = botAI->GetBot()->GetGroup()) { @@ -195,10 +195,9 @@ class GeneralFindTargetSmartStrategy : public FindTargetStrategy } int32_t GetIntervalLevel(Unit* unit) { - float time = unit->GetHealth() / dps_; float dis = unit->GetDistance(botAI->GetBot()); float attackRange = - botAI->IsRanged(botAI->GetBot()) ? sPlayerbotAIConfig->spellDistance : sPlayerbotAIConfig->meleeDistance; + BotRoleService::IsRangedStatic(botAI->GetBot()) ? sPlayerbotAIConfig->spellDistance : sPlayerbotAIConfig->meleeDistance; attackRange += 5.0f; int level = dis < attackRange ? 10 : 0; return level; @@ -218,7 +217,7 @@ class ComboFindTargetSmartStrategy : public FindTargetStrategy { } - void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override + void CheckAttacker(Unit* attacker, ThreatMgr* /*threatMgr*/) override { if (Group* group = botAI->GetBot()->GetGroup()) { @@ -276,10 +275,9 @@ class ComboFindTargetSmartStrategy : public FindTargetStrategy } int32_t GetIntervalLevel(Unit* unit) { - float time = unit->GetHealth() / dps_; float dis = unit->GetDistance(botAI->GetBot()); float attackRange = - botAI->IsRanged(botAI->GetBot()) ? sPlayerbotAIConfig->spellDistance : sPlayerbotAIConfig->meleeDistance; + BotRoleService::IsRangedStatic(botAI->GetBot()) ? sPlayerbotAIConfig->spellDistance : sPlayerbotAIConfig->meleeDistance; attackRange += 5.0f; int level = dis < attackRange ? 10 : 0; return level; @@ -300,14 +298,14 @@ Unit* DpsTargetValue::Calculate() if (botAI->GetNearGroupMemberCount() > 3) { - if (botAI->IsCaster(bot)) + if (BotRoleService::IsCasterStatic(bot)) { // Caster find target strategy avoids casting spells on enemies // with too low health to ensure the effectiveness of casting CasterFindTargetSmartStrategy strategy(botAI, dps); return TargetValue::FindTarget(&strategy); } - else if (botAI->IsCombo(bot)) + else if (BotRoleService::IsComboStatic(bot)) { ComboFindTargetSmartStrategy strategy(botAI, dps); return TargetValue::FindTarget(&strategy); @@ -322,7 +320,7 @@ class FindMaxHpTargetStrategy : public FindTargetStrategy public: FindMaxHpTargetStrategy(PlayerbotAI* botAI) : FindTargetStrategy(botAI), maxHealth(0) {} - void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override + void CheckAttacker(Unit* attacker, ThreatMgr* /*threatMgr*/) override { if (Group* group = botAI->GetBot()->GetGroup()) { diff --git a/src/Ai/Base/Value/EnemyHealerTargetValue.cpp b/src/Ai/Base/Value/EnemyHealerTargetValue.cpp index 6040c4fcb5..4f800aadba 100644 --- a/src/Ai/Base/Value/EnemyHealerTargetValue.cpp +++ b/src/Ai/Base/Value/EnemyHealerTargetValue.cpp @@ -4,6 +4,7 @@ */ #include "EnemyHealerTargetValue.h" +#include "BotSpellService.h" #include "Playerbots.h" #include "ServerFacade.h" @@ -23,7 +24,7 @@ Unit* EnemyHealerTargetValue::Calculate() if (sServerFacade->GetDistance2d(bot, unit) > botAI->GetRange("spell")) continue; - if (!botAI->IsInterruptableSpellCasting(unit, spell)) + if (!botAI->GetServices().GetSpellService().IsInterruptableSpellCasting(unit, spell)) continue; Spell* spell = unit->GetCurrentSpell(CURRENT_GENERIC_SPELL); diff --git a/src/Ai/Base/Value/EnemyPlayerValue.cpp b/src/Ai/Base/Value/EnemyPlayerValue.cpp index 2325c9c09b..c8a7dba31e 100644 --- a/src/Ai/Base/Value/EnemyPlayerValue.cpp +++ b/src/Ai/Base/Value/EnemyPlayerValue.cpp @@ -4,6 +4,7 @@ */ #include "EnemyPlayerValue.h" +#include "BotSpellService.h" #include "Playerbots.h" #include "ServerFacade.h" @@ -11,7 +12,7 @@ bool NearestEnemyPlayersValue::AcceptUnit(Unit* unit) { - bool inCannon = botAI->IsInVehicle(false, true); + bool inCannon = botAI->GetServices().GetSpellService().IsInVehicle(false, true); Player* enemy = dynamic_cast(unit); if (enemy && botAI->IsOpposing(enemy) && enemy->IsPvP() && !sPlayerbotAIConfig->IsPvpProhibited(enemy->GetZoneId(), enemy->GetAreaId()) && @@ -160,7 +161,7 @@ float EnemyPlayerValue::GetMaxAttackDistance() if (bgType == BATTLEGROUND_IC) { - if (botAI->IsInVehicle(false, true)) + if (botAI->GetServices().GetSpellService().IsInVehicle(false, true)) return 120.0f; } diff --git a/src/Ai/Base/Value/EstimatedLifetimeValue.cpp b/src/Ai/Base/Value/EstimatedLifetimeValue.cpp index dd79207130..2f81e4f0f9 100644 --- a/src/Ai/Base/Value/EstimatedLifetimeValue.cpp +++ b/src/Ai/Base/Value/EstimatedLifetimeValue.cpp @@ -1,4 +1,5 @@ #include "EstimatedLifetimeValue.h" +#include "BotRoleService.h" #include "AiFactory.h" #include "PlayerbotAI.h" @@ -17,7 +18,7 @@ float EstimatedLifetimeValue::Calculate() float dps = AI_VALUE(float, "estimated group dps"); bool aoePenalty = AI_VALUE(uint8, "attacker count") >= 3; if (aoePenalty) - dps *= 0.75; + dps *= 0.75f; float res = target->GetHealth() / dps; // bot->Say(target->GetName() + " lifetime: " + std::to_string(res), LANG_UNIVERSAL); return res; @@ -55,9 +56,9 @@ float EstimatedGroupDpsValue::Calculate() for (Player* player : groupPlayer) { float roleMultiplier; - if (botAI->IsTank(player)) + if (BotRoleService::IsTankStatic(player)) roleMultiplier = 0.3f; - else if (botAI->IsHeal(player)) + else if (BotRoleService::IsHealStatic(player)) roleMultiplier = 0.1f; else roleMultiplier = 1.0f; @@ -68,21 +69,21 @@ float EstimatedGroupDpsValue::Calculate() // bonus for wotlk epic gear if (mixedGearScore >= 300) { - gs_modifier *= 1 + (mixedGearScore - 300) * 0.01; + gs_modifier *= 1 + (mixedGearScore - 300) * 0.01f; } - if (gs_modifier < 0.75) - gs_modifier = 0.75; + if (gs_modifier < 0.75f) + gs_modifier = 0.75f; if (gs_modifier > 4) gs_modifier = 4; totalDps += basicDps * roleMultiplier * gs_modifier; } // Group buff bonus if (groupPlayer.size() >= 25) - totalDps *= 1.2; + totalDps *= 1.2f; else if (groupPlayer.size() >= 10) - totalDps *= 1.1; + totalDps *= 1.1f; else if (groupPlayer.size() >= 5) - totalDps *= 1.05; + totalDps *= 1.05f; return totalDps; } diff --git a/src/Ai/Base/Value/Formations.cpp b/src/Ai/Base/Value/Formations.cpp index 69869889d7..9a4565c3ad 100644 --- a/src/Ai/Base/Value/Formations.cpp +++ b/src/Ai/Base/Value/Formations.cpp @@ -3,7 +3,9 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "Formations.h" +#include "BotRoleService.h" #include "Arrow.h" #include "Event.h" @@ -210,13 +212,15 @@ class CircleFormation : public MoveFormation range = botAI->GetRange("flee"); break; case CLASS_DRUID: - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) range = botAI->GetRange("flee"); break; case CLASS_SHAMAN: - if (botAI->IsHeal(bot)) + if (BotRoleService::IsHealStatic(bot)) range = botAI->GetRange("flee"); break; + default: + break; } float angle = GetFollowAngle(); @@ -307,31 +311,31 @@ class ShieldFormation : public MoveFormation if (!member || member == master) continue; - if (botAI->IsTank(member)) + if (BotRoleService::IsTankStatic(member)) tanks.push_back(member); else dps.push_back(member); } - if (botAI->IsTank(master)) + if (BotRoleService::IsTankStatic(master)) tanks.insert(tanks.begin() + (tanks.size() + 1) / 2, master); else dps.insert(dps.begin() + (dps.size() + 1) / 2, master); - if (botAI->IsTank(bot) && botAI->IsTank(master)) + if (BotRoleService::IsTankStatic(bot) && BotRoleService::IsTankStatic(master)) return MoveLine(tanks, 0.0f, x, y, z, orientation, range); - if (!botAI->IsTank(bot) && !botAI->IsTank(master)) + if (!BotRoleService::IsTankStatic(bot) && !BotRoleService::IsTankStatic(master)) return MoveLine(dps, 0.0f, x, y, z, orientation, range); - if (botAI->IsTank(bot) && !botAI->IsTank(master)) + if (BotRoleService::IsTankStatic(bot) && !BotRoleService::IsTankStatic(master)) { float diff = (tanks.size() % 2 == 0) ? -sPlayerbotAIConfig->tooCloseDistance / 2.0f : 0.0f; return MoveLine(tanks, diff, x + cos(orientation) * range, y + sin(orientation) * range, z, orientation, range); } - if (!botAI->IsTank(bot) && botAI->IsTank(master)) + if (!BotRoleService::IsTankStatic(bot) && BotRoleService::IsTankStatic(master)) { float diff = (dps.size() % 2 == 0) ? -sPlayerbotAIConfig->tooCloseDistance / 2.0f : 0.0f; return MoveLine(dps, diff, x - cos(orientation) * range, y - sin(orientation) * range, z, orientation, @@ -420,7 +424,6 @@ float Formation::GetFollowAngle() { Player* master = GetMaster(); Group* group = bot->GetGroup(); - PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); // If there's no master and no group if (!master && !group) @@ -448,19 +451,19 @@ float Formation::GetFollowAngle() continue; // Put DPS in the middle - if (!botAI->IsTank(member) && !botAI->IsHeal(member)) + if (!BotRoleService::IsTankStatic(member) && !BotRoleService::IsHealStatic(member)) { roster.insert(roster.begin() + roster.size() / 2, member); } // Put Healers in the middle - else if (botAI->IsHeal(member)) + else if (BotRoleService::IsHealStatic(member)) { roster.insert(roster.begin() + roster.size() / 2, member); } // Handle tanks (alternate between front and back) - else if (botAI->IsTank(member)) + else if (BotRoleService::IsTankStatic(member)) { if (left) roster.push_back(member); // Place tank at the back @@ -599,7 +602,7 @@ bool SetFormationAction::Execute(Event event) { std::ostringstream str; str << "Formation: |cff00ff00" << value->Get()->getName(); - botAI->TellMaster(str); + botAI->GetServices().GetChatService().TellMaster(str); return true; } @@ -607,7 +610,7 @@ bool SetFormationAction::Execute(Event event) { WorldLocation loc = value->Get()->GetLocation(); if (!Formation::IsNullLocation(loc)) - botAI->Ping(loc.GetPositionX(), loc.GetPositionY()); + botAI->GetServices().GetChatService().Ping(loc.GetPositionX(), loc.GetPositionY()); return true; } @@ -616,15 +619,15 @@ bool SetFormationAction::Execute(Event event) { std::ostringstream str; str << "Invalid formation: |cffff0000" << formation; - botAI->TellMaster(str); - botAI->TellMaster( + botAI->GetServices().GetChatService().TellMaster(str); + botAI->GetServices().GetChatService().TellMaster( "Please set to any of:|cffffffff chaos (default), near, queue, circle, line, shield, arrow, melee, far"); return false; } std::ostringstream str; str << "Formation set to: " << formation; - botAI->TellMaster(str); + botAI->GetServices().GetChatService().TellMaster(str); return true; } @@ -636,7 +639,7 @@ WorldLocation MoveFormation::MoveLine(std::vector line, float diff, flo return MoveSingleLine(line, diff, cx, cy, cz, orientation, range); } - uint32 lines = ceil((double)line.size() / 5.0); + uint32 lines = ceil((double)line.size() / 5.0f); for (uint32 i = 0; i < lines; i++) { float radius = range * i; diff --git a/src/Ai/Base/Value/GrindTargetValue.cpp b/src/Ai/Base/Value/GrindTargetValue.cpp index 7e6168c776..65656c28da 100644 --- a/src/Ai/Base/Value/GrindTargetValue.cpp +++ b/src/Ai/Base/Value/GrindTargetValue.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "GrindTargetValue.h" #include "NewRpgInfo.h" @@ -30,8 +31,6 @@ Unit* GrindTargetValue::Calculate() Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount) { - uint32 memberCount = 1; - Group* group = bot->GetGroup(); Player* master = GetMaster(); if (master && (master == bot || master->GetMapId() != bot->GetMapId() || master->IsBeingTeleported() || @@ -54,6 +53,7 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount) float distance = 0; Unit* result = nullptr; + Group* group = bot->GetGroup(); std::unordered_map needForQuestMap; for (ObjectGuid const guid : targets) @@ -65,7 +65,6 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount) if (!unit->IsInWorld() || unit->IsDuringRemoveFromWorld()) continue; - auto& rep = bot->ToPlayer()->GetReputationMgr(); if (unit->ToCreature() && !unit->ToCreature()->GetCreatureTemplate()->lootid && bot->GetReactionTo(unit) >= REP_NEUTRAL) continue; @@ -90,7 +89,7 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount) sServerFacade->GetDistance2d(master, unit) > sPlayerbotAIConfig->lootDistance) { if (botAI->HasStrategy("debug grind", BotState::BOT_STATE_NON_COMBAT)) - botAI->TellMaster(chat->FormatWorldobject(unit) + " ignored (far from master)."); + botAI->GetServices().GetChatService().TellMaster(chat->FormatWorldobject(unit) + " ignored (far from master)."); continue; } @@ -170,7 +169,7 @@ bool GrindTargetValue::needForQuest(Unit* target) if (status == QUEST_STATUS_INCOMPLETE) { - const QuestStatusData* questStatus = &bot->getQuestStatusMap()[questId]; + QuestStatusData const* questStatus = &bot->getQuestStatusMap()[questId]; if (questTemplate->GetQuestLevel() > bot->GetLevel() + 5) continue; @@ -184,7 +183,7 @@ bool GrindTargetValue::needForQuest(Unit* target) int required = questTemplate->RequiredNpcOrGoCount[j]; int available = questStatus->CreatureOrGOCount[j]; - if (required && available < required && target->GetEntry() == entry) + if (required && available < required && target->GetEntry() == static_cast(entry)) return true; } } diff --git a/src/Ai/Base/Value/ItemCountValue.cpp b/src/Ai/Base/Value/ItemCountValue.cpp index 2c719d8f4d..5b107171db 100644 --- a/src/Ai/Base/Value/ItemCountValue.cpp +++ b/src/Ai/Base/Value/ItemCountValue.cpp @@ -11,8 +11,6 @@ std::vector InventoryItemValueBase::Find(std::string const qualifier) { std::vector result; - Player* bot = InventoryAction::botAI->GetBot(); - std::vector items = InventoryAction::parseItems(qualifier); for (Item* item : items) result.push_back(item); diff --git a/src/Ai/Base/Value/ItemCountValue.h b/src/Ai/Base/Value/ItemCountValue.h index 6f7c593b23..7e47565fc5 100644 --- a/src/Ai/Base/Value/ItemCountValue.h +++ b/src/Ai/Base/Value/ItemCountValue.h @@ -17,7 +17,7 @@ class InventoryItemValueBase : public InventoryAction public: InventoryItemValueBase(PlayerbotAI* botAI) : InventoryAction(botAI, "empty") {} - bool Execute(Event event) override { return false; } + bool Execute(Event /*event*/) override { return false; } protected: std::vector Find(std::string const qualifier); diff --git a/src/Ai/Base/Value/ItemUsageValue.cpp b/src/Ai/Base/Value/ItemUsageValue.cpp index 25866c8059..ff5c88296f 100644 --- a/src/Ai/Base/Value/ItemUsageValue.cpp +++ b/src/Ai/Base/Value/ItemUsageValue.cpp @@ -16,6 +16,7 @@ #include "RandomItemMgr.h" #include "ServerFacade.h" #include "StatsWeightCalculator.h" +#include "BotItemService.h" ItemUsage ItemUsageValue::Calculate() { @@ -80,7 +81,7 @@ ItemUsage ItemUsageValue::Calculate() return ITEM_USAGE_USE; if (proto->Class == ITEM_CLASS_CONSUMABLE && - (proto->MaxCount == 0 || bot->GetItemCount(itemId, false) < proto->MaxCount)) + (proto->MaxCount == 0 || static_cast(bot->GetItemCount(itemId, false)) < proto->MaxCount)) { std::string const foodType = GetConsumableType(proto, bot->GetPower(POWER_MANA)); @@ -176,6 +177,8 @@ ItemUsage ItemUsageValue::Calculate() case ITEM_SUBCLASS_WEAPON_CROSSBOW: requiredSubClass = ITEM_SUBCLASS_ARROW; break; + default: + break; } } @@ -250,7 +253,7 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, return ITEM_USAGE_NONE; uint16 dest; - InventoryResult result = botAI->CanEquipItem(NULL_SLOT, dest, pItem, true, true); + InventoryResult result = botAI->GetServices().GetItemService().CanEquipItem(NULL_SLOT, dest, pItem, true, true); pItem->RemoveFromUpdateQueueOf(bot); delete pItem; @@ -321,7 +324,7 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, shouldEquip = false; uint8 possibleSlots = 1; - uint8 dstSlot = botAI->FindEquipSlot(itemProto, NULL_SLOT, true); + uint8 dstSlot = botAI->GetServices().GetItemService().FindEquipSlot(itemProto, NULL_SLOT, true); // Check if dest wasn't set correctly by CanEquipItem and use FindEquipSlot instead // This occurs with unique items that are already in the bots bags when CanEquipItem is called if (dest == 0) @@ -615,6 +618,8 @@ bool ItemUsageValue::IsItemNeededForSkill(ItemTemplate const* proto) return botAI->HasSkill(SKILL_COOKING); case 6256: // Fishing Rod return botAI->HasSkill(SKILL_FISHING); + default: + break; } return false; @@ -688,8 +693,12 @@ bool ItemUsageValue::IsItemUsefulForSkill(ItemTemplate const* proto) return botAI->HasSkill(SKILL_ENCHANTING); case ITEM_SUBCLASS_FISHING_MANUAL: return botAI->HasSkill(SKILL_FISHING); + default: + break; } } + default: + break; } return false; @@ -734,7 +743,7 @@ bool ItemUsageValue::HasItemsNeededForSpell(uint32 spellId, ItemTemplate const* for (uint8 i = 0; i < MAX_SPELL_REAGENTS; i++) if (spellInfo->ReagentCount[i] > 0 && spellInfo->Reagent[i]) { - if (proto && proto->ItemId == spellInfo->Reagent[i] && + if (proto && proto->ItemId == static_cast(spellInfo->Reagent[i]) && spellInfo->ReagentCount[i] == 1) // If we only need 1 item then current item does not need to be // checked since we are looting/buying or already have it. continue; @@ -743,7 +752,7 @@ bool ItemUsageValue::HasItemsNeededForSpell(uint32 spellId, ItemTemplate const* uint32 count = AI_VALUE2(uint32, "item count", reqProto->Name1); - if (count < spellInfo->ReagentCount[i]) + if (count < static_cast(spellInfo->ReagentCount[i])) return false; } @@ -833,7 +842,7 @@ std::vector ItemUsageValue::SpellsUsingItem(uint32 itemId, Player* bot) continue; for (uint8 i = 0; i < MAX_SPELL_REAGENTS; i++) - if (spellInfo->ReagentCount[i] > 0 && spellInfo->Reagent[i] == itemId) + if (spellInfo->ReagentCount[i] > 0 && static_cast(spellInfo->Reagent[i]) == itemId) retSpells.push_back(spellId); } @@ -865,8 +874,6 @@ bool ItemUsageValue::SpellGivesSkillUp(uint32 spellId, Player* bot) { uint32 SkillValue = bot->GetPureSkillValue(skill->SkillLine); - uint32 craft_skill_gain = sWorld->getIntConfig(CONFIG_SKILL_GAIN_CRAFTING); - if (SkillGainChance(SkillValue, skill->TrivialSkillLineRankHigh, (skill->TrivialSkillLineRankHigh + skill->TrivialSkillLineRankLow) / 2, skill->TrivialSkillLineRankLow) > 0) diff --git a/src/Ai/Base/Value/LastMovementValue.cpp b/src/Ai/Base/Value/LastMovementValue.cpp index ba54020e39..6a4fe3405d 100644 --- a/src/Ai/Base/Value/LastMovementValue.cpp +++ b/src/Ai/Base/Value/LastMovementValue.cpp @@ -15,11 +15,11 @@ LastMovement::LastMovement(LastMovement& other) taxiMaster(other.taxiMaster), lastFollow(other.lastFollow), lastAreaTrigger(other.lastAreaTrigger), + lastFlee(other.lastFlee), lastMoveToX(other.lastMoveToX), lastMoveToY(other.lastMoveToY), lastMoveToZ(other.lastMoveToZ), - lastMoveToOri(other.lastMoveToOri), - lastFlee(other.lastFlee) + lastMoveToOri(other.lastMoveToOri) { lastMoveShort = other.lastMoveShort; nextTeleport = other.nextTeleport; diff --git a/src/Ai/Base/Value/LeastHpTargetValue.cpp b/src/Ai/Base/Value/LeastHpTargetValue.cpp index 8b9f1a55fc..3517ef8adc 100644 --- a/src/Ai/Base/Value/LeastHpTargetValue.cpp +++ b/src/Ai/Base/Value/LeastHpTargetValue.cpp @@ -13,9 +13,8 @@ class FindLeastHpTargetStrategy : public FindNonCcTargetStrategy public: FindLeastHpTargetStrategy(PlayerbotAI* botAI) : FindNonCcTargetStrategy(botAI), minHealth(0) {} - void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override + void CheckAttacker(Unit* attacker, ThreatMgr* /*threatMgr*/) override { - Player* bot = botAI->GetBot(); if (IsCcTarget(attacker)) return; diff --git a/src/Ai/Base/Value/LootStrategyValue.cpp b/src/Ai/Base/Value/LootStrategyValue.cpp index 6a4f9b9b48..3b0b81a19d 100644 --- a/src/Ai/Base/Value/LootStrategyValue.cpp +++ b/src/Ai/Base/Value/LootStrategyValue.cpp @@ -60,7 +60,7 @@ class DisenchantLootStrategy : public NormalLootStrategy class AllLootStrategy : public LootStrategy { public: - bool CanLoot(ItemTemplate const* proto, AiObjectContext* context) override { return true; } + bool CanLoot(ItemTemplate const* /*proto*/, AiObjectContext* /*context*/) override { return true; } std::string const GetName() override { return "all"; } }; diff --git a/src/Ai/Base/Value/ManaSaveLevelValue.h b/src/Ai/Base/Value/ManaSaveLevelValue.h index f406998f16..d796b7c655 100644 --- a/src/Ai/Base/Value/ManaSaveLevelValue.h +++ b/src/Ai/Base/Value/ManaSaveLevelValue.h @@ -13,7 +13,7 @@ class PlayerbotAI; class ManaSaveLevelValue : public ManualSetValue { public: - ManaSaveLevelValue(PlayerbotAI* botAI) : ManualSetValue(botAI, 1.0, "mana save level") {} + ManaSaveLevelValue(PlayerbotAI* botAI) : ManualSetValue(botAI, 1.0f, "mana save level") {} std::string const Save() { diff --git a/src/Ai/Base/Value/NearestCorpsesValue.cpp b/src/Ai/Base/Value/NearestCorpsesValue.cpp index fec1aed21f..df6c1bb809 100644 --- a/src/Ai/Base/Value/NearestCorpsesValue.cpp +++ b/src/Ai/Base/Value/NearestCorpsesValue.cpp @@ -13,13 +13,12 @@ class AnyDeadUnitInObjectRangeCheck { public: - AnyDeadUnitInObjectRangeCheck(WorldObject const* obj, float range) : i_obj(obj), i_range(range) {} + AnyDeadUnitInObjectRangeCheck(WorldObject const* obj, float /*range*/) : i_obj(obj) {} WorldObject const& GetFocusObject() const { return *i_obj; } bool operator()(Unit* u) { return !u->IsAlive(); } private: WorldObject const* i_obj; - float i_range; }; void NearestCorpsesValue::FindUnits(std::list& targets) @@ -29,4 +28,4 @@ void NearestCorpsesValue::FindUnits(std::list& targets) Cell::VisitObjects(bot, searcher, range); } -bool NearestCorpsesValue::AcceptUnit(Unit* unit) { return true; } +bool NearestCorpsesValue::AcceptUnit(Unit* /*unit*/) { return true; } diff --git a/src/Ai/Base/Value/NearestGameObjects.cpp b/src/Ai/Base/Value/NearestGameObjects.cpp index b5aa2de787..b52289a539 100644 --- a/src/Ai/Base/Value/NearestGameObjects.cpp +++ b/src/Ai/Base/Value/NearestGameObjects.cpp @@ -48,7 +48,7 @@ GuidVector NearestTrapWithDamageValue::Calculate() { continue; } - const GameObjectTemplate* goInfo = go->GetGOInfo(); + GameObjectTemplate const* goInfo = go->GetGOInfo(); if (!goInfo) { continue; @@ -58,7 +58,7 @@ GuidVector NearestTrapWithDamageValue::Calculate() { continue; } - const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo || spellInfo->IsPositive()) { continue; diff --git a/src/Ai/Base/Value/PartyMemberToDispel.cpp b/src/Ai/Base/Value/PartyMemberToDispel.cpp index 91b1955b27..cbf01e5238 100644 --- a/src/Ai/Base/Value/PartyMemberToDispel.cpp +++ b/src/Ai/Base/Value/PartyMemberToDispel.cpp @@ -4,6 +4,7 @@ */ #include "PartyMemberToDispel.h" +#include "BotSpellService.h" #include "Playerbots.h" @@ -11,11 +12,11 @@ class PartyMemberToDispelPredicate : public FindPlayerPredicate, public Playerbo { public: PartyMemberToDispelPredicate(PlayerbotAI* botAI, uint32 dispelType) - : PlayerbotAIAware(botAI), FindPlayerPredicate(), dispelType(dispelType) + : FindPlayerPredicate(), PlayerbotAIAware(botAI), dispelType(dispelType) { } - bool Check(Unit* unit) override { return unit->IsAlive() && botAI->HasAuraToDispel(unit, dispelType); } + bool Check(Unit* unit) override { return unit->IsAlive() && botAI->GetServices().GetSpellService().HasAuraToDispel(unit, dispelType); } private: uint32 dispelType; diff --git a/src/Ai/Base/Value/PartyMemberToHeal.cpp b/src/Ai/Base/Value/PartyMemberToHeal.cpp index 47554c7c2b..97fc6722b4 100644 --- a/src/Ai/Base/Value/PartyMemberToHeal.cpp +++ b/src/Ai/Base/Value/PartyMemberToHeal.cpp @@ -4,6 +4,7 @@ */ #include "PartyMemberToHeal.h" +#include "BotRoleService.h" #include "Playerbots.h" #include "ServerFacade.h" @@ -99,7 +100,7 @@ Unit* PartyMemberToHeal::Calculate() bool PartyMemberToHeal::Check(Unit* player) { // return player && player != bot && player->GetMapId() == bot->GetMapId() && player->IsInWorld() && - // sServerFacade->GetDistance2d(bot, player) < (player->IsPlayer() && botAI->IsTank((Player*)player) ? 50.0f + // sServerFacade->GetDistance2d(bot, player) < (player->IsPlayer() && BotRoleService::IsTankStatic((Player*)player) ? 50.0f // : 40.0f); return player->GetMapId() == bot->GetMapId() && !player->IsCharmed() && bot->GetDistance2d(player) < sPlayerbotAIConfig->healDistance * 2 && bot->IsWithinLOSInMap(player); @@ -132,7 +133,7 @@ Unit* PartyMemberToProtect::Calculate() if (sServerFacade->GetDistance2d(pVictim, unit) > attackDistance) continue; - if (botAI->IsTank((Player*)pVictim) && pVictim->GetHealthPct() > 10) + if (BotRoleService::IsTankStatic((Player*)pVictim) && pVictim->GetHealthPct() > 10) continue; else if (pVictim->GetHealthPct() > 30) continue; diff --git a/src/Ai/Base/Value/PartyMemberValue.cpp b/src/Ai/Base/Value/PartyMemberValue.cpp index 4ae9be08ad..0ffd247f3a 100644 --- a/src/Ai/Base/Value/PartyMemberValue.cpp +++ b/src/Ai/Base/Value/PartyMemberValue.cpp @@ -4,6 +4,7 @@ */ #include "PartyMemberValue.h" +#include "BotRoleService.h" #include "Playerbots.h" #include "ServerFacade.h" @@ -23,7 +24,7 @@ Unit* PartyMemberValue::FindPartyMember(std::vector* party, FindPlayerP return nullptr; } -Unit* PartyMemberValue::FindPartyMember(FindPlayerPredicate& predicate, bool ignoreOutOfGroup) +Unit* PartyMemberValue::FindPartyMember(FindPlayerPredicate& predicate, bool /*ignoreOutOfGroup*/) { Player* master = GetMaster(); // GuidVector nearestPlayers; @@ -73,9 +74,9 @@ Unit* PartyMemberValue::FindPartyMember(FindPlayerPredicate& predicate, bool ign if (!player) continue; - if (botAI->IsHeal(player)) + if (BotRoleService::IsHealStatic(player)) healers.push_back(player); - else if (botAI->IsTank(player)) + else if (BotRoleService::IsTankStatic(player)) tanks.push_back(player); else if (player != master) others.push_back(player); @@ -150,7 +151,7 @@ bool PartyMemberValue::IsTargetOfSpellCast(Player* target, SpellEntryPredicate& class FindMainTankPlayer : public FindPlayerPredicate { public: - FindMainTankPlayer(PlayerbotAI* botAI) : botAI(botAI) {} + FindMainTankPlayer(PlayerbotAI* /*botAI*/) {} virtual bool Check(Unit* unit) { @@ -159,11 +160,8 @@ class FindMainTankPlayer : public FindPlayerPredicate { return false; } - return botAI->IsMainTank(player); + return BotRoleService::IsMainTankStatic(player); } - -private: - PlayerbotAI* botAI; }; Unit* PartyMemberMainTankValue::Calculate() diff --git a/src/Ai/Base/Value/PartyMemberWithoutAuraValue.cpp b/src/Ai/Base/Value/PartyMemberWithoutAuraValue.cpp index f195a899bd..8770365eff 100644 --- a/src/Ai/Base/Value/PartyMemberWithoutAuraValue.cpp +++ b/src/Ai/Base/Value/PartyMemberWithoutAuraValue.cpp @@ -13,7 +13,7 @@ class PlayerWithoutAuraPredicate : public FindPlayerPredicate, public PlayerbotA { public: PlayerWithoutAuraPredicate(PlayerbotAI* botAI, std::string const aura) - : PlayerbotAIAware(botAI), FindPlayerPredicate(), auras(split(aura, ',')) + : FindPlayerPredicate(), PlayerbotAIAware(botAI), auras(split(aura, ',')) { } @@ -25,7 +25,7 @@ class PlayerWithoutAuraPredicate : public FindPlayerPredicate, public PlayerbotA for (std::vector::iterator i = auras.begin(); i != auras.end(); ++i) { - if (botAI->HasAura(*i, unit)) + if (botAI->GetServices().GetSpellService().HasAura(*i, unit)) return false; } diff --git a/src/Ai/Base/Value/PartyMemberWithoutAuraValue.h b/src/Ai/Base/Value/PartyMemberWithoutAuraValue.h index 57bb958c76..2937cc657a 100644 --- a/src/Ai/Base/Value/PartyMemberWithoutAuraValue.h +++ b/src/Ai/Base/Value/PartyMemberWithoutAuraValue.h @@ -17,7 +17,7 @@ class PartyMemberWithoutAuraValue : public PartyMemberValue, public Qualified { public: PartyMemberWithoutAuraValue(PlayerbotAI* botAI, std::string const name = "party member without aura", - float range = sPlayerbotAIConfig->sightDistance) + float /*range*/ = sPlayerbotAIConfig->sightDistance) : PartyMemberValue(botAI, name) { } diff --git a/src/Ai/Base/Value/PartyMemberWithoutItemValue.cpp b/src/Ai/Base/Value/PartyMemberWithoutItemValue.cpp index c9136c61a5..fe8032a459 100644 --- a/src/Ai/Base/Value/PartyMemberWithoutItemValue.cpp +++ b/src/Ai/Base/Value/PartyMemberWithoutItemValue.cpp @@ -11,7 +11,7 @@ class PlayerWithoutItemPredicate : public FindPlayerPredicate, public PlayerbotA { public: PlayerWithoutItemPredicate(PlayerbotAI* botAI, std::string const item) - : PlayerbotAIAware(botAI), FindPlayerPredicate(), item(item) + : FindPlayerPredicate(), PlayerbotAIAware(botAI), item(item) { } diff --git a/src/Ai/Base/Value/PartyMemberWithoutItemValue.h b/src/Ai/Base/Value/PartyMemberWithoutItemValue.h index 9ca128ff8d..240c9827f0 100644 --- a/src/Ai/Base/Value/PartyMemberWithoutItemValue.h +++ b/src/Ai/Base/Value/PartyMemberWithoutItemValue.h @@ -17,7 +17,7 @@ class PartyMemberWithoutItemValue : public PartyMemberValue, public Qualified { public: PartyMemberWithoutItemValue(PlayerbotAI* botAI, std::string const name = "party member without item", - float range = sPlayerbotAIConfig->farDistance) + float /*range*/ = sPlayerbotAIConfig->farDistance) : PartyMemberValue(botAI, name) { } diff --git a/src/Ai/Base/Value/PositionValue.h b/src/Ai/Base/Value/PositionValue.h index 69e8e35943..25ac186ca6 100644 --- a/src/Ai/Base/Value/PositionValue.h +++ b/src/Ai/Base/Value/PositionValue.h @@ -24,6 +24,18 @@ class PositionInfo : x(other.x), y(other.y), z(other.z), mapId(other.mapId), valueSet(other.valueSet) { } + PositionInfo& operator=(PositionInfo const& other) + { + if (this != &other) + { + x = other.x; + y = other.y; + z = other.z; + mapId = other.mapId; + valueSet = other.valueSet; + } + return *this; + } void Set(float newX, float newY, float newZ, uint32 newMapId) { diff --git a/src/Ai/Base/Value/PossibleRpgTargetsValue.cpp b/src/Ai/Base/Value/PossibleRpgTargetsValue.cpp index e9b79debf8..788d4c354e 100644 --- a/src/Ai/Base/Value/PossibleRpgTargetsValue.cpp +++ b/src/Ai/Base/Value/PossibleRpgTargetsValue.cpp @@ -74,7 +74,7 @@ bool PossibleRpgTargetsValue::AcceptUnit(Unit* unit) TravelTarget* travelTarget = context->GetValue("travel target")->Get(); if (travelTarget && travelTarget->getDestination() && - travelTarget->getDestination()->getEntry() == unit->GetEntry()) + travelTarget->getDestination()->getEntry() == static_cast(unit->GetEntry())) return true; if (urand(1, 100) < 25 && unit->IsFriendlyTo(bot)) diff --git a/src/Ai/Base/Value/PossibleTargetsValue.cpp b/src/Ai/Base/Value/PossibleTargetsValue.cpp index 654abd44c4..5479493d82 100644 --- a/src/Ai/Base/Value/PossibleTargetsValue.cpp +++ b/src/Ai/Base/Value/PossibleTargetsValue.cpp @@ -48,10 +48,10 @@ bool PossibleTriggersValue::AcceptUnit(Unit* unit) for (auto i = list.begin(); i != list.end(); ++i) { AuraEffect* aurEff = *i; - const SpellInfo* spellInfo = aurEff->GetSpellInfo(); + SpellInfo const* spellInfo = aurEff->GetSpellInfo(); if (!spellInfo) continue; - const SpellInfo* triggerSpellInfo = + SpellInfo const* triggerSpellInfo = sSpellMgr->GetSpellInfo(spellInfo->Effects[aurEff->GetEffIndex()].TriggerSpell); if (!triggerSpellInfo) continue; diff --git a/src/Ai/Base/Value/PvpValues.cpp b/src/Ai/Base/Value/PvpValues.cpp index e01b56ef2c..bf9c8ac9bd 100644 --- a/src/Ai/Base/Value/PvpValues.cpp +++ b/src/Ai/Base/Value/PvpValues.cpp @@ -24,11 +24,11 @@ Unit* FlagCarrierValue::Calculate() if (!bg) return nullptr; - if ((!sameTeam && bot->GetTeamId() == TEAM_HORDE || (sameTeam && bot->GetTeamId() == TEAM_ALLIANCE)) && + if (((!sameTeam && bot->GetTeamId() == TEAM_HORDE) || (sameTeam && bot->GetTeamId() == TEAM_ALLIANCE)) && !bg->GetFlagPickerGUID(TEAM_HORDE).IsEmpty()) carrier = ObjectAccessor::GetPlayer(bg->GetBgMap(), bg->GetFlagPickerGUID(TEAM_HORDE)); - if ((!sameTeam && bot->GetTeamId() == TEAM_ALLIANCE || (sameTeam && bot->GetTeamId() == TEAM_HORDE)) && + if (((!sameTeam && bot->GetTeamId() == TEAM_ALLIANCE) || (sameTeam && bot->GetTeamId() == TEAM_HORDE)) && !bg->GetFlagPickerGUID(TEAM_ALLIANCE).IsEmpty()) carrier = ObjectAccessor::GetPlayer(bg->GetBgMap(), bg->GetFlagPickerGUID(TEAM_ALLIANCE)); diff --git a/src/Ai/Base/Value/SnareTargetValue.cpp b/src/Ai/Base/Value/SnareTargetValue.cpp index 96e958c2d8..076384f920 100644 --- a/src/Ai/Base/Value/SnareTargetValue.cpp +++ b/src/Ai/Base/Value/SnareTargetValue.cpp @@ -4,16 +4,14 @@ */ #include "SnareTargetValue.h" +#include "BotRoleService.h" #include "Playerbots.h" #include "ServerFacade.h" Unit* SnareTargetValue::Calculate() { - std::string const spell = qualifier; - GuidVector attackers = botAI->GetAiObjectContext()->GetValue("attackers")->Get(); - Unit* target = botAI->GetAiObjectContext()->GetValue("current target")->Get(); for (ObjectGuid const guid : attackers) { Unit* unit = botAI->GetUnit(guid); @@ -38,7 +36,7 @@ Unit* SnareTargetValue::Calculate() bool shouldSnare = true; // do not slow down if bot is melee and mob/bot attack each other - if (chaseTargetPlayer && !botAI->IsRanged(bot) && chaseTargetPlayer == bot) + if (chaseTargetPlayer && !BotRoleService::IsRangedStatic(bot) && chaseTargetPlayer == bot) shouldSnare = false; if (!unit->isMoving()) @@ -47,7 +45,7 @@ Unit* SnareTargetValue::Calculate() if (unit->HasAuraType(SPELL_AURA_MOD_ROOT)) shouldSnare = false; - if (chaseTargetPlayer && shouldSnare && !botAI->IsTank(chaseTargetPlayer)) + if (chaseTargetPlayer && shouldSnare && !BotRoleService::IsTankStatic(chaseTargetPlayer)) { return unit; } diff --git a/src/Ai/Base/Value/SpellIdValue.cpp b/src/Ai/Base/Value/SpellIdValue.cpp index 682c0b3ffd..406c303a2c 100644 --- a/src/Ai/Base/Value/SpellIdValue.cpp +++ b/src/Ai/Base/Value/SpellIdValue.cpp @@ -31,7 +31,7 @@ uint32 SpellIdValue::Calculate() wstrToLower(wnamepart); char firstSymbol = tolower(namepart[0]); - int spellLength = wnamepart.length(); + size_t spellLength = wnamepart.length(); LocaleConstant loc = LOCALE_enUS; @@ -108,7 +108,7 @@ uint32 SpellIdValue::Calculate() for (auto it = spellIds.rbegin(); it != spellIds.rend(); ++it) { auto spellId = *it; - const SpellInfo* pSpellInfo = sSpellMgr->GetSpellInfo(spellId); + SpellInfo const* pSpellInfo = sSpellMgr->GetSpellInfo(spellId); if (!pSpellInfo) continue; @@ -126,7 +126,7 @@ uint32 SpellIdValue::Calculate() spellName = spellName.substr(i, spellName.length() - i); // convert the remaining text to an integer - int id = atoi(spellName.c_str()); + uint32 id = atoi(spellName.c_str()); if (!id) { @@ -154,7 +154,7 @@ uint32 SpellIdValue::Calculate() auto spellId = *it; if (!highestSpellId) highestSpellId = spellId; - if (saveMana == rank) + if (static_cast(saveMana) == rank) return spellId; lowestSpellId = spellId; rank++; @@ -193,7 +193,7 @@ uint32 VehicleSpellIdValue::Calculate() wstrToLower(wnamepart); char firstSymbol = tolower(namepart[0]); - int spellLength = wnamepart.length(); + size_t spellLength = wnamepart.length(); const int loc = LocaleConstant::LOCALE_enUS; diff --git a/src/Ai/Base/Value/Stances.cpp b/src/Ai/Base/Value/Stances.cpp index 2e7a18d2b9..ced60ae26b 100644 --- a/src/Ai/Base/Value/Stances.cpp +++ b/src/Ai/Base/Value/Stances.cpp @@ -3,7 +3,9 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "Stances.h" +#include "BotRoleService.h" #include "Arrow.h" #include "Event.h" @@ -93,7 +95,7 @@ class NearStance : public MoveStance if (member == bot) index = count; - if (member && !botAI->IsRanged(member) && !botAI->IsTank(member)) + if (member && !BotRoleService::IsRangedStatic(member) && !BotRoleService::IsTankStatic(member)) count++; } } @@ -142,7 +144,7 @@ class TurnBackStance : public MoveStance for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { if (Player* member = ref->GetSource()) - if (member != bot && botAI->IsRanged(member)) + if (member != bot && BotRoleService::IsRangedStatic(member)) { angle += target->GetAngle(member); ++count; @@ -177,7 +179,7 @@ class BehindStance : public MoveStance if (member == bot) index = count; - if (!botAI->IsRanged(member) && !botAI->IsTank(member)) + if (!BotRoleService::IsRangedStatic(member) && !BotRoleService::IsTankStatic(member)) ++count; } } @@ -237,7 +239,7 @@ bool SetStanceAction::Execute(Event event) { std::ostringstream str; str << "Stance: |cff00ff00" << value->Get()->getName(); - botAI->TellMaster(str); + botAI->GetServices().GetChatService().TellMaster(str); return true; } @@ -245,7 +247,7 @@ bool SetStanceAction::Execute(Event event) { WorldLocation loc = value->Get()->GetLocation(); if (!Formation::IsNullLocation(loc)) - botAI->Ping(loc.GetPositionX(), loc.GetPositionY()); + botAI->GetServices().GetChatService().Ping(loc.GetPositionX(), loc.GetPositionY()); return true; } @@ -254,13 +256,13 @@ bool SetStanceAction::Execute(Event event) { std::ostringstream str; str << "Invalid stance: |cffff0000" << stance; - botAI->TellMaster(str); - botAI->TellMaster("Please set to any of:|cffffffff near (default), tank, turnback, behind"); + botAI->GetServices().GetChatService().TellMaster(str); + botAI->GetServices().GetChatService().TellMaster("Please set to any of:|cffffffff near (default), tank, turnback, behind"); return false; } std::ostringstream str; str << "Stance set to: " << stance; - botAI->TellMaster(str); + botAI->GetServices().GetChatService().TellMaster(str); return true; } diff --git a/src/Ai/Base/Value/StatsValues.cpp b/src/Ai/Base/Value/StatsValues.cpp index dc3499c683..c5d2247287 100644 --- a/src/Ai/Base/Value/StatsValues.cpp +++ b/src/Ai/Base/Value/StatsValues.cpp @@ -204,17 +204,15 @@ uint8 BagSpaceValue::Calculate() ++totalused; } - uint32 totalfree = 16 - totalused; for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag) { - const Bag* const pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag); + Bag const* const pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag); if (pBag) { ItemTemplate const* pBagProto = pBag->GetTemplate(); if (pBagProto->Class == ITEM_CLASS_CONTAINER && pBagProto->SubClass == ITEM_SUBCLASS_CONTAINER) { total += pBag->GetBagSize(); - totalfree += pBag->GetFreeSlots(); totalused += pBag->GetBagSize() - pBag->GetFreeSlots(); } } diff --git a/src/Ai/Base/Value/TankTargetValue.cpp b/src/Ai/Base/Value/TankTargetValue.cpp index ef2d1e959c..6cd0627ec5 100644 --- a/src/Ai/Base/Value/TankTargetValue.cpp +++ b/src/Ai/Base/Value/TankTargetValue.cpp @@ -4,6 +4,7 @@ */ #include "TankTargetValue.h" +#include "BotRoleService.h" #include "AttackersValue.h" #include "PlayerbotAIConfig.h" @@ -32,7 +33,7 @@ class FindTargetForTankStrategy : public FindNonCcTargetStrategy { // float max_threat = threatMgr->GetThreat(threatMgr->getCurrentVictim()->getTarget()); Unit* victim = threatMgr->getCurrentVictim()->getTarget(); - if (victim && victim->ToPlayer() && botAI->IsMainTank(victim->ToPlayer())) + if (victim && victim->ToPlayer() && BotRoleService::IsMainTankStatic(victim->ToPlayer())) { return; } @@ -53,7 +54,7 @@ class FindTankTargetSmartStrategy : public FindTargetStrategy public: FindTankTargetSmartStrategy(PlayerbotAI* botAI) : FindTargetStrategy(botAI) {} - void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override + void CheckAttacker(Unit* attacker, ThreatMgr* /*threatMgr*/) override { if (Group* group = botAI->GetBot()->GetGroup()) { @@ -75,7 +76,7 @@ class FindTankTargetSmartStrategy : public FindTargetStrategy Player* bot = botAI->GetBot(); // if group has multiple tanks, main tank just focus on the current target Unit* currentTarget = botAI->GetAiObjectContext()->GetValue("current target")->Get(); - if (currentTarget && botAI->IsMainTank(bot) && botAI->GetGroupTankNum(bot) > 1) + if (currentTarget && BotRoleService::IsMainTankStatic(bot) && BotRoleService::GetGroupTankNumStatic(bot) > 1) { if (old_unit == currentTarget) return false; @@ -100,7 +101,7 @@ class FindTankTargetSmartStrategy : public FindTargetStrategy } int32_t GetIntervalLevel(Unit* unit) { - if (!botAI->HasAggro(unit)) + if (!botAI->GetServices().GetRoleService().HasAggro(unit)) { return 2; } diff --git a/src/Ai/Base/Value/TargetValue.cpp b/src/Ai/Base/Value/TargetValue.cpp index 598b78441b..ed1c891051 100644 --- a/src/Ai/Base/Value/TargetValue.cpp +++ b/src/Ai/Base/Value/TargetValue.cpp @@ -4,6 +4,7 @@ */ #include "TargetValue.h" +#include "BotRoleService.h" #include "LastMovementValue.h" #include "ObjectGuid.h" @@ -88,7 +89,7 @@ void FindTargetStrategy::GetPlayerCount(Unit* creature, uint32* tankCount, uint3 if (!player) continue; - if (botAI->IsTank(player)) + if (BotRoleService::IsTankStatic(player)) ++(*tankCount); else ++(*dpsCount); @@ -161,7 +162,7 @@ Unit* FindTargetValue::Calculate() return nullptr; } -void FindBossTargetStrategy::CheckAttacker(Unit* attacker, ThreatMgr* threatManager) +void FindBossTargetStrategy::CheckAttacker(Unit* attacker, ThreatMgr* /*threatManager*/) { UnitAI* unitAI = attacker->GetAI(); BossAI* bossAI = dynamic_cast(unitAI); diff --git a/src/Ai/Base/Value/ThreatValues.cpp b/src/Ai/Base/Value/ThreatValues.cpp index d95b00142c..ef6e73abce 100644 --- a/src/Ai/Base/Value/ThreatValues.cpp +++ b/src/Ai/Base/Value/ThreatValues.cpp @@ -4,6 +4,7 @@ */ #include "ThreatValues.h" +#include "BotRoleService.h" #include "Playerbots.h" #include "ThreatMgr.h" @@ -54,7 +55,7 @@ uint8 ThreatValue::Calculate(Unit* target) if (!player || !player->IsAlive() || player == bot) continue; - if (botAI->IsTank(player)) + if (BotRoleService::IsTankStatic(player)) { hasTank = true; float threat = target->GetThreatMgr().GetThreat(player); diff --git a/src/Ai/Class/Dk/Strategy/GenericDKNonCombatStrategy.cpp b/src/Ai/Class/Dk/Strategy/GenericDKNonCombatStrategy.cpp index edead06755..86fb48d245 100644 --- a/src/Ai/Class/Dk/Strategy/GenericDKNonCombatStrategy.cpp +++ b/src/Ai/Class/Dk/Strategy/GenericDKNonCombatStrategy.cpp @@ -55,7 +55,7 @@ void GenericDKNonCombatStrategy::InitTriggers(std::vector& trigger new TriggerNode("new pet", { NextAction("set pet stance", 60.0f) })); } -void DKBuffDpsStrategy::InitTriggers(std::vector& triggers) +void DKBuffDpsStrategy::InitTriggers(std::vector& /*triggers*/) { } diff --git a/src/Ai/Class/Dk/Trigger/DKTriggers.cpp b/src/Ai/Class/Dk/Trigger/DKTriggers.cpp index 90c1620953..5d61272190 100644 --- a/src/Ai/Class/Dk/Trigger/DKTriggers.cpp +++ b/src/Ai/Class/Dk/Trigger/DKTriggers.cpp @@ -4,6 +4,7 @@ */ #include "DKTriggers.h" +#include "BotSpellService.h" #include @@ -14,8 +15,8 @@ bool DKPresenceTrigger::IsActive() { Unit* target = GetTarget(); - return !botAI->HasAura("blood presence", target) && !botAI->HasAura("unholy presence", target) && - !botAI->HasAura("frost presence", target); + return !botAI->GetServices().GetSpellService().HasAura("blood presence", target) && !botAI->GetServices().GetSpellService().HasAura("unholy presence", target) && + !botAI->GetServices().GetSpellService().HasAura("frost presence", target); } bool PestilenceGlyphTrigger::IsActive() @@ -28,8 +29,8 @@ bool PestilenceGlyphTrigger::IsActive() { return false; } - Aura* blood_plague = botAI->GetAura("blood plague", GetTarget(), true, true); - Aura* frost_fever = botAI->GetAura("frost fever", GetTarget(), true, true); + Aura* blood_plague = botAI->GetServices().GetSpellService().GetAura("blood plague", GetTarget(), true, true); + Aura* frost_fever = botAI->GetServices().GetSpellService().GetAura("frost fever", GetTarget(), true, true); if ((blood_plague && blood_plague->GetDuration() <= 3000) || (frost_fever && frost_fever->GetDuration() <= 3000)) { return true; diff --git a/src/Ai/Class/Druid/Action/DruidActions.cpp b/src/Ai/Class/Druid/Action/DruidActions.cpp index 13336a674a..8890eafb1e 100644 --- a/src/Ai/Class/Druid/Action/DruidActions.cpp +++ b/src/Ai/Class/Druid/Action/DruidActions.cpp @@ -10,6 +10,7 @@ #include "ServerFacade.h" #include "AoeValues.h" #include "TargetValue.h" +#include "BotSpellService.h" std::vector CastAbolishPoisonAction::getAlternatives() { @@ -28,11 +29,11 @@ Value* CastEntanglingRootsCcAction::GetTargetValue() return context->GetValue("cc target", "entangling roots"); } -bool CastEntanglingRootsCcAction::Execute(Event event) { return botAI->CastSpell("entangling roots", GetTarget()); } +bool CastEntanglingRootsCcAction::Execute(Event /*event*/) { return botAI->GetServices().GetSpellService().CastSpell("entangling roots", GetTarget()); } Value* CastHibernateCcAction::GetTargetValue() { return context->GetValue("cc target", "hibernate"); } -bool CastHibernateCcAction::Execute(Event event) { return botAI->CastSpell("hibernate", GetTarget()); } +bool CastHibernateCcAction::Execute(Event /*event*/) { return botAI->GetServices().GetSpellService().CastSpell("hibernate", GetTarget()); } bool CastStarfallAction::isUseful() { if (!CastSpellAction::isUseful()) @@ -53,7 +54,7 @@ bool CastStarfallAction::isUseful() if (aoeCount < 2) { Unit* target = context->GetValue("current target")->Get(); - if (!target || (!botAI->HasAura("moonfire", target) && !botAI->HasAura("insect swarm", target))) + if (!target || (!botAI->GetServices().GetSpellService().HasAura("moonfire", target) && !botAI->GetServices().GetSpellService().HasAura("insect swarm", target))) return false; } @@ -95,7 +96,7 @@ Unit* CastRejuvenationOnNotFullAction::GetTarget() { continue; } - if (botAI->HasAura("rejuvenation", player)) + if (botAI->GetServices().GetSpellService().HasAura("rejuvenation", player)) { continue; } diff --git a/src/Ai/Class/Druid/Action/DruidShapeshiftActions.cpp b/src/Ai/Class/Druid/Action/DruidShapeshiftActions.cpp index 4f4a4e5968..f472011335 100644 --- a/src/Ai/Class/Druid/Action/DruidShapeshiftActions.cpp +++ b/src/Ai/Class/Druid/Action/DruidShapeshiftActions.cpp @@ -4,17 +4,18 @@ */ #include "DruidShapeshiftActions.h" +#include "BotSpellService.h" #include "Playerbots.h" bool CastBearFormAction::isPossible() { - return CastBuffSpellAction::isPossible() && !botAI->HasAura("dire bear form", GetTarget()); + return CastBuffSpellAction::isPossible() && !botAI->GetServices().GetSpellService().HasAura("dire bear form", GetTarget()); } bool CastBearFormAction::isUseful() { - return CastBuffSpellAction::isUseful() && !botAI->HasAura("dire bear form", GetTarget()); + return CastBuffSpellAction::isUseful() && !botAI->GetServices().GetSpellService().HasAura("dire bear form", GetTarget()); } std::vector CastDireBearFormAction::getAlternatives() @@ -29,34 +30,34 @@ bool CastTravelFormAction::isUseful() // useful if no mount or with wsg flag return !bot->IsMounted() && (!firstmount || (bot->HasAura(23333) || bot->HasAura(23335) || bot->HasAura(34976))) && - !botAI->HasAura("dash", bot); + !botAI->GetServices().GetSpellService().HasAura("dash", bot); } bool CastCasterFormAction::isUseful() { - return botAI->HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form", "aquatic form", + return botAI->GetServices().GetSpellService().HasAnyAuraOf(GetTarget(), "dire bear form", "bear form", "cat form", "travel form", "aquatic form", "flight form", "swift flight form", "moonkin form", nullptr) && AI_VALUE2(uint8, "mana", "self target") > sPlayerbotAIConfig->mediumHealth; } -bool CastCasterFormAction::Execute(Event event) +bool CastCasterFormAction::Execute(Event /*event*/) { - botAI->RemoveShapeshift(); + botAI->GetServices().GetSpellService().RemoveShapeshift(); return true; } bool CastCancelTreeFormAction::isUseful() { - return botAI->HasAura(33891, bot); + return botAI->GetServices().GetSpellService().HasAura(33891, bot); } -bool CastCancelTreeFormAction::Execute(Event event) +bool CastCancelTreeFormAction::Execute(Event /*event*/) { - botAI->RemoveAura("tree of life"); + botAI->GetServices().GetSpellService().RemoveAura("tree of life"); return true; } bool CastTreeFormAction::isUseful() { - return GetTarget() && CastSpellAction::isUseful() && !botAI->HasAura(33891, bot); + return GetTarget() && CastSpellAction::isUseful() && !botAI->GetServices().GetSpellService().HasAura(33891, bot); } \ No newline at end of file diff --git a/src/Ai/Class/Druid/Strategy/CatDpsDruidStrategy.cpp b/src/Ai/Class/Druid/Strategy/CatDpsDruidStrategy.cpp index fda1b5f94f..e0db3b478c 100644 --- a/src/Ai/Class/Druid/Strategy/CatDpsDruidStrategy.cpp +++ b/src/Ai/Class/Druid/Strategy/CatDpsDruidStrategy.cpp @@ -311,4 +311,4 @@ void CatDpsDruidStrategy::InitTriggers(std::vector& triggers) ); } -void CatAoeDruidStrategy::InitTriggers(std::vector& triggers) {} +void CatAoeDruidStrategy::InitTriggers(std::vector& /*triggers*/) {} diff --git a/src/Ai/Class/Druid/Strategy/HealDruidStrategy.cpp b/src/Ai/Class/Druid/Strategy/HealDruidStrategy.cpp index 5d2c4ce34c..c56ef392bf 100644 --- a/src/Ai/Class/Druid/Strategy/HealDruidStrategy.cpp +++ b/src/Ai/Class/Druid/Strategy/HealDruidStrategy.cpp @@ -72,7 +72,7 @@ void HealDruidStrategy::InitTriggers(std::vector& triggers) { NextAction("tree form", ACTION_MEDIUM_HEAL + 1.5f), NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 1.4f), NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 1.3f), - NextAction("swiftmend on party", ACTION_MEDIUM_HEAL + 1.2), + NextAction("swiftmend on party", ACTION_MEDIUM_HEAL + 1.2f), NextAction("nourish on party", ACTION_MEDIUM_HEAL + 1.1f), })); diff --git a/src/Ai/Class/Druid/Trigger/DruidTriggers.cpp b/src/Ai/Class/Druid/Trigger/DruidTriggers.cpp index 6cf8552a3a..fa35ebb681 100644 --- a/src/Ai/Class/Druid/Trigger/DruidTriggers.cpp +++ b/src/Ai/Class/Druid/Trigger/DruidTriggers.cpp @@ -4,22 +4,23 @@ */ #include "DruidTriggers.h" +#include "BotSpellService.h" #include "Player.h" #include "Playerbots.h" bool MarkOfTheWildOnPartyTrigger::IsActive() { - return BuffOnPartyTrigger::IsActive() && !botAI->HasAura("gift of the wild", GetTarget()); + return BuffOnPartyTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAura("gift of the wild", GetTarget()); } bool MarkOfTheWildTrigger::IsActive() { - return BuffTrigger::IsActive() && !botAI->HasAura("gift of the wild", GetTarget()); + return BuffTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAura("gift of the wild", GetTarget()); } bool ThornsOnPartyTrigger::IsActive() { - return BuffOnPartyTrigger::IsActive() && !botAI->HasAura("thorns", GetTarget()); + return BuffOnPartyTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAura("thorns", GetTarget()); } bool EntanglingRootsKiteTrigger::IsActive() @@ -27,15 +28,15 @@ bool EntanglingRootsKiteTrigger::IsActive() return DebuffTrigger::IsActive() && AI_VALUE(uint8, "attacker count") < 3 && !GetTarget()->GetPower(POWER_MANA); } -bool ThornsTrigger::IsActive() { return BuffTrigger::IsActive() && !botAI->HasAura("thorns", GetTarget()); } +bool ThornsTrigger::IsActive() { return BuffTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAura("thorns", GetTarget()); } -bool BearFormTrigger::IsActive() { return !botAI->HasAnyAuraOf(bot, "bear form", "dire bear form", nullptr); } +bool BearFormTrigger::IsActive() { return !botAI->GetServices().GetSpellService().HasAnyAuraOf(bot, "bear form", "dire bear form", nullptr); } -bool TreeFormTrigger::IsActive() { return !botAI->HasAura(33891, bot); } +bool TreeFormTrigger::IsActive() { return !botAI->GetServices().GetSpellService().HasAura(33891, bot); } -bool CatFormTrigger::IsActive() { return !botAI->HasAura("cat form", bot); } +bool CatFormTrigger::IsActive() { return !botAI->GetServices().GetSpellService().HasAura("cat form", bot); } -const std::set HurricaneChannelCheckTrigger::HURRICANE_SPELL_IDS = { +std::set const HurricaneChannelCheckTrigger::HURRICANE_SPELL_IDS = { 16914, // Hurricane Rank 1 17401, // Hurricane Rank 2 17402, // Hurricane Rank 3 diff --git a/src/Ai/Class/Druid/Trigger/DruidTriggers.h b/src/Ai/Class/Druid/Trigger/DruidTriggers.h index 5541cc10e3..9a52c1d3e9 100644 --- a/src/Ai/Class/Druid/Trigger/DruidTriggers.h +++ b/src/Ai/Class/Druid/Trigger/DruidTriggers.h @@ -7,6 +7,7 @@ #define _PLAYERBOT_DRUIDTRIGGERS_H #include "CureTriggers.h" +#include "BotSpellService.h" #include "GenericTriggers.h" #include "Player.h" #include "PlayerbotAI.h" @@ -232,8 +233,8 @@ class MangleCatTrigger : public DebuffTrigger MangleCatTrigger(PlayerbotAI* ai) : DebuffTrigger(ai, "mangle (cat)", 1, false, 0.0f) {} bool IsActive() override { - return DebuffTrigger::IsActive() && !botAI->HasAura("mangle (bear)", GetTarget(), false, false, -1, true) - && !botAI->HasAura("trauma", GetTarget(), false, false, -1, true); + return DebuffTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAura("mangle (bear)", GetTarget(), false, false, -1, true) + && !botAI->GetServices().GetSpellService().HasAura("trauma", GetTarget(), false, false, -1, true); } }; @@ -251,12 +252,12 @@ class FerociousBiteTimeTrigger : public Trigger if (cp < 5) return false; - Aura* roar = botAI->GetAura("savage roar", bot); + Aura* roar = botAI->GetServices().GetSpellService().GetAura("savage roar", bot); bool roarCheck = !roar || roar->GetDuration() > 10000; if (!roarCheck) return false; - Aura* rip = botAI->GetAura("rip", target, true); + Aura* rip = botAI->GetServices().GetSpellService().GetAura("rip", target, true); bool ripCheck = !rip || rip->GetDuration() > 10000; if (!ripCheck) return false; @@ -277,7 +278,7 @@ class HurricaneChannelCheckTrigger : public Trigger protected: uint32 minEnemies; - static const std::set HURRICANE_SPELL_IDS; + static std::set const HURRICANE_SPELL_IDS; }; #endif diff --git a/src/Ai/Class/Hunter/Action/HunterActions.cpp b/src/Ai/Class/Hunter/Action/HunterActions.cpp index a1588f8537..2216f42287 100644 --- a/src/Ai/Class/Hunter/Action/HunterActions.cpp +++ b/src/Ai/Class/Hunter/Action/HunterActions.cpp @@ -9,6 +9,7 @@ #include "GenericSpellActions.h" #include "PlayerbotAI.h" #include "Playerbots.h" +#include "BotSpellService.h" bool CastViperStingAction::isUseful() { @@ -18,7 +19,7 @@ bool CastViperStingAction::isUseful() bool CastAspectOfTheCheetahAction::isUseful() { - return !botAI->HasAnyAuraOf(GetTarget(), "aspect of the cheetah", "aspect of the pack", nullptr); + return !botAI->GetServices().GetSpellService().HasAnyAuraOf(GetTarget(), "aspect of the cheetah", "aspect of the pack", nullptr); } bool CastAspectOfTheHawkAction::isUseful() @@ -59,7 +60,7 @@ bool CastImmolationTrapAction::isUseful() Value* CastFreezingTrap::GetTargetValue() { return context->GetValue("cc target", "freezing trap"); } -bool FeedPetAction::Execute(Event event) +bool FeedPetAction::Execute(Event /*event*/) { if (Pet* pet = bot->GetPet()) if (pet->getPetType() == HUNTER_PET && pet->GetHappinessState() != HAPPY) @@ -70,7 +71,7 @@ bool FeedPetAction::Execute(Event event) bool CastAutoShotAction::isUseful() { - if (botAI->IsInVehicle() && !botAI->IsInVehicle(false, false, true)) + if (botAI->GetServices().GetSpellService().IsInVehicle() && !botAI->GetServices().GetSpellService().IsInVehicle(false, false, true)) return false; if (AI_VALUE(Unit*, "current target") && bot->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL) && @@ -99,9 +100,9 @@ bool CastDisengageAction::isUseful() Value* CastScareBeastCcAction::GetTargetValue() { return context->GetValue("cc target", "scare beast"); } -bool CastScareBeastCcAction::Execute(Event event) { return botAI->CastSpell("scare beast", GetTarget()); } +bool CastScareBeastCcAction::Execute(Event /*event*/) { return botAI->GetServices().GetSpellService().CastSpell("scare beast", GetTarget()); } -bool CastWingClipAction::isUseful() { return CastSpellAction::isUseful() && !botAI->HasAura(spell, GetTarget()); } +bool CastWingClipAction::isUseful() { return CastSpellAction::isUseful() && !botAI->GetServices().GetSpellService().HasAura(spell, GetTarget()); } std::vector CastWingClipAction::getPrerequisites() { diff --git a/src/Ai/Class/Hunter/Action/HunterActions.h b/src/Ai/Class/Hunter/Action/HunterActions.h index 8e21135254..38fabfdae6 100644 --- a/src/Ai/Class/Hunter/Action/HunterActions.h +++ b/src/Ai/Class/Hunter/Action/HunterActions.h @@ -10,6 +10,7 @@ #include "Event.h" #include "GenericSpellActions.h" #include "Unit.h" +#include "BotSpellService.h" class PlayerbotAI; class Unit; @@ -352,7 +353,7 @@ class CastExplosiveShotRank4Action : public CastDebuffSpellAction public: CastExplosiveShotRank4Action(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "explosive shot", true, 0.0f) {} - bool Execute(Event event) override { return botAI->CastSpell(60053, GetTarget()); } + bool Execute(Event /*event*/) override { return botAI->GetServices().GetSpellService().CastSpell(60053, GetTarget()); } bool isUseful() override { Unit* target = GetTarget(); @@ -368,7 +369,7 @@ class CastExplosiveShotRank3Action : public CastDebuffSpellAction public: CastExplosiveShotRank3Action(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "explosive shot", true, 0.0f) {} - bool Execute(Event event) override { return botAI->CastSpell(60052, GetTarget()); } + bool Execute(Event /*event*/) override { return botAI->GetServices().GetSpellService().CastSpell(60052, GetTarget()); } bool isUseful() override { Unit* target = GetTarget(); @@ -384,7 +385,7 @@ class CastExplosiveShotRank2Action : public CastDebuffSpellAction public: CastExplosiveShotRank2Action(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "explosive shot", true, 0.0f) {} - bool Execute(Event event) override { return botAI->CastSpell(60051, GetTarget()); } + bool Execute(Event /*event*/) override { return botAI->GetServices().GetSpellService().CastSpell(60051, GetTarget()); } bool isUseful() override { Unit* target = GetTarget(); @@ -400,7 +401,7 @@ class CastExplosiveShotRank1Action : public CastDebuffSpellAction public: CastExplosiveShotRank1Action(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "explosive shot", true, 0.0f) {} - bool Execute(Event event) override { return botAI->CastSpell(53301, GetTarget()); } + bool Execute(Event /*event*/) override { return botAI->GetServices().GetSpellService().CastSpell(53301, GetTarget()); } bool isUseful() override { Unit* target = GetTarget(); diff --git a/src/Ai/Class/Hunter/Strategy/GenericHunterStrategy.cpp b/src/Ai/Class/Hunter/Strategy/GenericHunterStrategy.cpp index 04afc6f766..676da18a9f 100644 --- a/src/Ai/Class/Hunter/Strategy/GenericHunterStrategy.cpp +++ b/src/Ai/Class/Hunter/Strategy/GenericHunterStrategy.cpp @@ -141,7 +141,7 @@ void AoEHunterStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("light aoe", { NextAction("multi-shot", 21.0f) })); } -void HunterBoostStrategy::InitTriggers(std::vector& triggers) +void HunterBoostStrategy::InitTriggers(std::vector& /*triggers*/) { } diff --git a/src/Ai/Class/Hunter/Trigger/HunterTriggers.cpp b/src/Ai/Class/Hunter/Trigger/HunterTriggers.cpp index 091b8d5f72..5645b7775a 100644 --- a/src/Ai/Class/Hunter/Trigger/HunterTriggers.cpp +++ b/src/Ai/Class/Hunter/Trigger/HunterTriggers.cpp @@ -17,7 +17,7 @@ bool KillCommandTrigger::IsActive() { Unit* target = GetTarget(); - return !botAI->HasAura("kill command", target); + return !botAI->GetServices().GetSpellService().HasAura("kill command", target); } bool BlackArrowTrigger::IsActive() @@ -32,16 +32,16 @@ bool BlackArrowTrigger::IsActive() bool HunterAspectOfTheHawkTrigger::IsActive() { Unit* target = GetTarget(); - return SpellTrigger::IsActive() && !botAI->HasAura("aspect of the hawk", target) && - !botAI->HasAura("aspect of the dragonhawk", target) && + return SpellTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAura("aspect of the hawk", target) && + !botAI->GetServices().GetSpellService().HasAura("aspect of the dragonhawk", target) && (!AI_VALUE2(bool, "has mana", "self target") || AI_VALUE2(uint8, "mana", "self target") > 70); } bool HunterNoStingsActiveTrigger::IsActive() { Unit* target = AI_VALUE(Unit*, "current target"); - return DebuffTrigger::IsActive() && target && !botAI->HasAura("serpent sting", target, false, true) && - !botAI->HasAura("scorpid sting", target, false, true) && !botAI->HasAura("viper sting", target, false, true); + return DebuffTrigger::IsActive() && target && !botAI->GetServices().GetSpellService().HasAura("serpent sting", target, false, true) && + !botAI->GetServices().GetSpellService().HasAura("scorpid sting", target, false, true) && !botAI->GetServices().GetSpellService().HasAura("viper sting", target, false, true); return BuffTrigger::IsActive(); } @@ -73,14 +73,14 @@ bool HunterPetNotHappy::IsActive() bool HunterAspectOfTheViperTrigger::IsActive() { - return SpellTrigger::IsActive() && !botAI->HasAura(spell, GetTarget()) && + return SpellTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAura(spell, GetTarget()) && AI_VALUE2(uint8, "mana", "self target") < (sPlayerbotAIConfig->lowMana / 2); ; } bool HunterAspectOfThePackTrigger::IsActive() { - return BuffTrigger::IsActive() && !botAI->HasAura("aspect of the cheetah", GetTarget()); + return BuffTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAura("aspect of the cheetah", GetTarget()); }; bool HunterLowAmmoTrigger::IsActive() @@ -119,9 +119,9 @@ bool NoTrackTrigger::IsActive() "track humanoids" }; - for (auto &track: track_list) + for (auto& track: track_list) { - if (botAI->HasAura(track, bot)) + if (botAI->GetServices().GetSpellService().HasAura(track, bot)) return false; } return true; @@ -136,12 +136,12 @@ bool SerpentStingOnAttackerTrigger::IsActive() { return false; } - return !botAI->HasAura("scorpid sting", target, false, true) && - !botAI->HasAura("viper sting", target, false, true); + return !botAI->GetServices().GetSpellService().HasAura("scorpid sting", target, false, true) && + !botAI->GetServices().GetSpellService().HasAura("viper sting", target, false, true); return BuffTrigger::IsActive(); } -const std::set VolleyChannelCheckTrigger::VOLLEY_SPELL_IDS = { +std::set const VolleyChannelCheckTrigger::VOLLEY_SPELL_IDS = { 1510, // Volley Rank 1 14294, // Volley Rank 2 14295, // Volley Rank 3 diff --git a/src/Ai/Class/Hunter/Trigger/HunterTriggers.h b/src/Ai/Class/Hunter/Trigger/HunterTriggers.h index 6bc5f3c44a..4f4dc67ff1 100644 --- a/src/Ai/Class/Hunter/Trigger/HunterTriggers.h +++ b/src/Ai/Class/Hunter/Trigger/HunterTriggers.h @@ -118,7 +118,7 @@ class LockAndLoadTrigger : public BuffTrigger bool IsActive() override { - return botAI->HasAura("lock and load", botAI->GetBot()); + return botAI->GetServices().GetSpellService().HasAura("lock and load", botAI->GetBot()); } }; @@ -257,7 +257,7 @@ class VolleyChannelCheckTrigger : public Trigger protected: uint32 minEnemies; - static const std::set VOLLEY_SPELL_IDS; + static std::set const VOLLEY_SPELL_IDS; }; #endif diff --git a/src/Ai/Class/Mage/Action/MageActions.cpp b/src/Ai/Class/Mage/Action/MageActions.cpp index 67587e3213..1a690258b2 100644 --- a/src/Ai/Class/Mage/Action/MageActions.cpp +++ b/src/Ai/Class/Mage/Action/MageActions.cpp @@ -1,3 +1,4 @@ +#include "BotRoleService.h" /* * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it * and/or modify it under version 3 of the License, or (at your option), any later version. @@ -114,10 +115,10 @@ Unit* CastFocusMagicOnPartyAction::GetTarget() if (member->getClass() == CLASS_MAGE) return member; - if (!casterDps && botAI->IsCaster(member) && botAI->IsDps(member)) + if (!casterDps && BotRoleService::IsCasterStatic(member) && BotRoleService::IsDpsStatic(member)) casterDps = member; - if (!healer && botAI->IsHeal(member)) + if (!healer && BotRoleService::IsHealStatic(member)) healer = member; if (!target) diff --git a/src/Ai/Class/Mage/Trigger/MageTriggers.cpp b/src/Ai/Class/Mage/Trigger/MageTriggers.cpp index e222107988..d9a0803020 100644 --- a/src/Ai/Class/Mage/Trigger/MageTriggers.cpp +++ b/src/Ai/Class/Mage/Trigger/MageTriggers.cpp @@ -15,7 +15,7 @@ bool NoManaGemTrigger::IsActive() { - static const std::vector gemIds = { + static std::vector const gemIds = { 33312, // Mana Sapphire 22044, // Mana Emerald 8008, // Mana Ruby @@ -34,19 +34,19 @@ bool NoManaGemTrigger::IsActive() bool ArcaneIntellectOnPartyTrigger::IsActive() { - return BuffOnPartyTrigger::IsActive() && !botAI->HasAura("arcane brilliance", GetTarget()); + return BuffOnPartyTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAura("arcane brilliance", GetTarget()); } bool ArcaneIntellectTrigger::IsActive() { - return BuffTrigger::IsActive() && !botAI->HasAura("arcane brilliance", GetTarget()); + return BuffTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAura("arcane brilliance", GetTarget()); } bool MageArmorTrigger::IsActive() { Unit* target = GetTarget(); - return !botAI->HasAura("ice armor", target) && !botAI->HasAura("frost armor", target) && - !botAI->HasAura("molten armor", target) && !botAI->HasAura("mage armor", target); + return !botAI->GetServices().GetSpellService().HasAura("ice armor", target) && !botAI->GetServices().GetSpellService().HasAura("frost armor", target) && + !botAI->GetServices().GetSpellService().HasAura("molten armor", target) && !botAI->GetServices().GetSpellService().HasAura("mage armor", target); } bool FrostNovaOnTargetTrigger::IsActive() @@ -56,7 +56,7 @@ bool FrostNovaOnTargetTrigger::IsActive() { return false; } - return botAI->HasAura(spell, target); + return botAI->GetServices().GetSpellService().HasAura(spell, target); } bool FrostbiteOnTargetTrigger::IsActive() @@ -66,7 +66,7 @@ bool FrostbiteOnTargetTrigger::IsActive() { return false; } - return botAI->HasAura(spell, target); + return botAI->GetServices().GetSpellService().HasAura(spell, target); } bool NoFocusMagicTrigger::IsActive() @@ -103,7 +103,7 @@ bool DeepFreezeCooldownTrigger::IsActive() return SpellCooldownTrigger::IsActive(); } -const std::set FlamestrikeNearbyTrigger::FLAMESTRIKE_SPELL_IDS = {2120, 2121, 8422, 8423, 10215, +std::set const FlamestrikeNearbyTrigger::FLAMESTRIKE_SPELL_IDS = {2120, 2121, 8422, 8423, 10215, 10216, 27086, 42925, 42926}; bool FlamestrikeNearbyTrigger::IsActive() @@ -151,7 +151,7 @@ bool ImprovedScorchTrigger::IsActive() return DebuffTrigger::IsActive(); } -const std::set BlizzardChannelCheckTrigger::BLIZZARD_SPELL_IDS = { +std::set const BlizzardChannelCheckTrigger::BLIZZARD_SPELL_IDS = { 10, // Blizzard Rank 1 6141, // Blizzard Rank 2 8427, // Blizzard Rank 3 diff --git a/src/Ai/Class/Mage/Trigger/MageTriggers.h b/src/Ai/Class/Mage/Trigger/MageTriggers.h index e769916f47..5eae79f326 100644 --- a/src/Ai/Class/Mage/Trigger/MageTriggers.h +++ b/src/Ai/Class/Mage/Trigger/MageTriggers.h @@ -282,7 +282,7 @@ class FlamestrikeNearbyTrigger : public Trigger protected: float radius; - static const std::set FLAMESTRIKE_SPELL_IDS; + static std::set const FLAMESTRIKE_SPELL_IDS; }; class FlamestrikeBlizzardTrigger : public TwoTriggers @@ -301,7 +301,7 @@ class BlizzardChannelCheckTrigger : public Trigger protected: uint32 minEnemies; - static const std::set BLIZZARD_SPELL_IDS; + static std::set const BLIZZARD_SPELL_IDS; }; class BlastWaveOffCdTrigger : public SpellNoCooldownTrigger diff --git a/src/Ai/Class/Paladin/Action/PaladinActions.cpp b/src/Ai/Class/Paladin/Action/PaladinActions.cpp index 190ecb264a..1abda3bc0d 100644 --- a/src/Ai/Class/Paladin/Action/PaladinActions.cpp +++ b/src/Ai/Class/Paladin/Action/PaladinActions.cpp @@ -17,6 +17,7 @@ #include "Config.h" #include "Group.h" #include "ObjectAccessor.h" +#include "BotSpellService.h" using ai::buff::MakeAuraQualifierForBuff; using ai::buff::UpgradeToGroupIfAppropriate; @@ -56,11 +57,6 @@ static inline bool IsOnlyPaladinInGroup(Player* bot) return pals == 1u; } -static inline bool GroupHasTankOfClass(Group* g, uint8 classId) -{ - return GroupHasTankOfClass(g, static_cast(classId)); -} - inline std::string const GetActualBlessingOfMight(Unit* target) { if (!target->ToPlayer()) @@ -94,6 +90,8 @@ inline std::string const GetActualBlessingOfMight(Unit* target) return "blessing of wisdom"; } break; + default: + break; } return "blessing of might"; @@ -132,6 +130,8 @@ inline std::string const GetActualBlessingOfWisdom(Unit* target) return "blessing of might"; } break; + default: + break; } return "blessing of wisdom"; @@ -167,7 +167,7 @@ Value* CastBlessingOnPartyAction::GetTargetValue() return context->GetValue("party member without aura", MakeAuraQualifierForBuff(spell)); } -bool CastBlessingOfMightAction::Execute(Event event) +bool CastBlessingOfMightAction::Execute(Event /*event*/) { Unit* target = GetTarget(); if (!target) @@ -177,7 +177,7 @@ bool CastBlessingOfMightAction::Execute(Event event) auto RP = ai::chat::MakeGroupAnnouncer(bot); castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, castName, /*announceOnMissing=*/true, RP); - return botAI->CastSpell(castName, target); + return botAI->GetServices().GetSpellService().CastSpell(castName, target); } Value* CastBlessingOfMightOnPartyAction::GetTargetValue() @@ -188,7 +188,7 @@ Value* CastBlessingOfMightOnPartyAction::GetTargetValue() ); } -bool CastBlessingOfMightOnPartyAction::Execute(Event event) +bool CastBlessingOfMightOnPartyAction::Execute(Event /*event*/) { Unit* target = GetTarget(); if (!target) @@ -198,10 +198,10 @@ bool CastBlessingOfMightOnPartyAction::Execute(Event event) auto RP = ai::chat::MakeGroupAnnouncer(bot); castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, castName, /*announceOnMissing=*/true, RP); - return botAI->CastSpell(castName, target); + return botAI->GetServices().GetSpellService().CastSpell(castName, target); } -bool CastBlessingOfWisdomAction::Execute(Event event) +bool CastBlessingOfWisdomAction::Execute(Event /*event*/) { Unit* target = GetTarget(); if (!target) @@ -211,7 +211,7 @@ bool CastBlessingOfWisdomAction::Execute(Event event) auto RP = ai::chat::MakeGroupAnnouncer(bot); castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, castName, /*announceOnMissing=*/true, RP); - return botAI->CastSpell(castName, target); + return botAI->GetServices().GetSpellService().CastSpell(castName, target); } Value* CastBlessingOfWisdomOnPartyAction::GetTargetValue() @@ -222,7 +222,7 @@ Value* CastBlessingOfWisdomOnPartyAction::GetTargetValue() ); } -bool CastBlessingOfWisdomOnPartyAction::Execute(Event event) +bool CastBlessingOfWisdomOnPartyAction::Execute(Event /*event*/) { Unit* target = GetTarget(); if (!target) @@ -247,7 +247,7 @@ bool CastBlessingOfWisdomOnPartyAction::Execute(Event event) auto RP = ai::chat::MakeGroupAnnouncer(bot); castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, castName, /*announceOnMissing=*/true, RP); - return botAI->CastSpell(castName, target); + return botAI->GetServices().GetSpellService().CastSpell(castName, target); } Value* CastBlessingOfSanctuaryOnPartyAction::GetTargetValue() @@ -258,7 +258,7 @@ Value* CastBlessingOfSanctuaryOnPartyAction::GetTargetValue() ); } -bool CastBlessingOfSanctuaryOnPartyAction::Execute(Event event) +bool CastBlessingOfSanctuaryOnPartyAction::Execute(Event /*event*/) { if (!bot->HasSpell(SPELL_BLESSING_OF_SANCTUARY)) return false; @@ -275,10 +275,10 @@ bool CastBlessingOfSanctuaryOnPartyAction::Execute(Event event) // Small helpers to check relevant auras const auto HasKingsAura = [&](Unit* u) -> bool { - return botAI->HasAura("blessing of kings", u) || botAI->HasAura("greater blessing of kings", u); + return botAI->GetServices().GetSpellService().HasAura("blessing of kings", u) || botAI->GetServices().GetSpellService().HasAura("greater blessing of kings", u); }; const auto HasSanctAura = [&](Unit* u) -> bool { - return botAI->HasAura("blessing of sanctuary", u) || botAI->HasAura("greater blessing of sanctuary", u); + return botAI->GetServices().GetSpellService().HasAura("blessing of sanctuary", u) || botAI->GetServices().GetSpellService().HasAura("greater blessing of sanctuary", u); }; if (Group* g = bot->GetGroup()) @@ -369,7 +369,7 @@ bool CastBlessingOfSanctuaryOnPartyAction::Execute(Event event) castName = "blessing of sanctuary"; } - bool ok = botAI->CastSpell(castName, target); + bool ok = botAI->GetServices().GetSpellService().CastSpell(castName, target); LOG_DEBUG("playerbots", "[Sanct] Cast {} on {} result={}", castName, target->GetName(), ok); return ok; } @@ -382,7 +382,7 @@ Value* CastBlessingOfKingsOnPartyAction::GetTargetValue() ); } -bool CastBlessingOfKingsOnPartyAction::Execute(Event event) +bool CastBlessingOfKingsOnPartyAction::Execute(Event /*event*/) { Unit* target = GetTarget(); if (!target) @@ -426,8 +426,8 @@ bool CastBlessingOfKingsOnPartyAction::Execute(Event event) target->HasAura(SPELL_BLESSING_OF_SANCTUARY, bot->GetGUID()) || target->HasAura(SPELL_GREATER_BLESSING_OF_SANCTUARY, bot->GetGUID()); const bool hasSanctAny = - botAI->HasAura("blessing of sanctuary", target) || - botAI->HasAura("greater blessing of sanctuary", target); + botAI->GetServices().GetSpellService().HasAura("blessing of sanctuary", target) || + botAI->GetServices().GetSpellService().HasAura("greater blessing of sanctuary", target); if (isTank && hasSanctFromMe) { @@ -470,7 +470,7 @@ bool CastBlessingOfKingsOnPartyAction::Execute(Event event) castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, castName, /*announceOnMissing=*/true, RP); } - return botAI->CastSpell(castName, target); + return botAI->GetServices().GetSpellService().CastSpell(castName, target); } bool CastSealSpellAction::isUseful() { return AI_VALUE2(bool, "combat", "self target"); } @@ -490,16 +490,16 @@ Unit* CastRighteousDefenseAction::GetTarget() bool CastDivineSacrificeAction::isUseful() { return GetTarget() && (GetTarget() != nullptr) && CastSpellAction::isUseful() && - !botAI->HasAura("divine guardian", GetTarget(), false, false, -1, true); + !botAI->GetServices().GetSpellService().HasAura("divine guardian", GetTarget(), false, false, -1, true); } -bool CastCancelDivineSacrificeAction::Execute(Event event) +bool CastCancelDivineSacrificeAction::Execute(Event /*event*/) { - botAI->RemoveAura("divine sacrifice"); + botAI->GetServices().GetSpellService().RemoveAura("divine sacrifice"); return true; } bool CastCancelDivineSacrificeAction::isUseful() { - return botAI->HasAura("divine sacrifice", GetTarget(), false, true, -1, true); + return botAI->GetServices().GetSpellService().HasAura("divine sacrifice", GetTarget(), false, true, -1, true); } diff --git a/src/Ai/Class/Paladin/Strategy/GenericPaladinStrategy.cpp b/src/Ai/Class/Paladin/Strategy/GenericPaladinStrategy.cpp index 62d7dfb10b..ed99c8a92a 100644 --- a/src/Ai/Class/Paladin/Strategy/GenericPaladinStrategy.cpp +++ b/src/Ai/Class/Paladin/Strategy/GenericPaladinStrategy.cpp @@ -59,7 +59,7 @@ void PaladinCureStrategy::InitTriggers(std::vector& triggers) { NextAction("cleanse magic on party", ACTION_DISPEL + 1) })); } -void PaladinBoostStrategy::InitTriggers(std::vector& triggers) +void PaladinBoostStrategy::InitTriggers(std::vector& /*triggers*/) { // triggers.push_back(new TriggerNode("divine favor", { NextAction("divine favor", diff --git a/src/Ai/Class/Paladin/Trigger/PaladinTriggers.cpp b/src/Ai/Class/Paladin/Trigger/PaladinTriggers.cpp index 6c333bc7ee..a278e689c7 100644 --- a/src/Ai/Class/Paladin/Trigger/PaladinTriggers.cpp +++ b/src/Ai/Class/Paladin/Trigger/PaladinTriggers.cpp @@ -4,6 +4,7 @@ */ #include "PaladinTriggers.h" +#include "BotSpellService.h" #include "PaladinActions.h" #include "PlayerbotAIConfig.h" @@ -12,21 +13,21 @@ bool SealTrigger::IsActive() { Unit* target = GetTarget(); - return !botAI->HasAura("seal of justice", target) && !botAI->HasAura("seal of command", target) && - !botAI->HasAura("seal of vengeance", target) && !botAI->HasAura("seal of corruption", target) && - !botAI->HasAura("seal of righteousness", target) && !botAI->HasAura("seal of light", target) && - (!botAI->HasAura("seal of wisdom", target) || AI_VALUE2(uint8, "mana", "self target") > 70); + return !botAI->GetServices().GetSpellService().HasAura("seal of justice", target) && !botAI->GetServices().GetSpellService().HasAura("seal of command", target) && + !botAI->GetServices().GetSpellService().HasAura("seal of vengeance", target) && !botAI->GetServices().GetSpellService().HasAura("seal of corruption", target) && + !botAI->GetServices().GetSpellService().HasAura("seal of righteousness", target) && !botAI->GetServices().GetSpellService().HasAura("seal of light", target) && + (!botAI->GetServices().GetSpellService().HasAura("seal of wisdom", target) || AI_VALUE2(uint8, "mana", "self target") > 70); } bool CrusaderAuraTrigger::IsActive() { Unit* target = GetTarget(); - return AI_VALUE2(bool, "mounted", "self target") && !botAI->HasAura("crusader aura", target); + return AI_VALUE2(bool, "mounted", "self target") && !botAI->GetServices().GetSpellService().HasAura("crusader aura", target); } bool BlessingTrigger::IsActive() { Unit* target = GetTarget(); - return SpellTrigger::IsActive() && !botAI->HasAnyAuraOf(target, "blessing of might", "blessing of wisdom", + return SpellTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAnyAuraOf(target, "blessing of might", "blessing of wisdom", "blessing of kings", "blessing of sanctuary", nullptr); } diff --git a/src/Ai/Class/Paladin/Trigger/PaladinTriggers.h b/src/Ai/Class/Paladin/Trigger/PaladinTriggers.h index 7352dbc812..fa28f866a9 100644 --- a/src/Ai/Class/Paladin/Trigger/PaladinTriggers.h +++ b/src/Ai/Class/Paladin/Trigger/PaladinTriggers.h @@ -21,6 +21,8 @@ inline std::string const GetActualBlessingOfMight(Unit* target) case CLASS_PRIEST: case CLASS_WARLOCK: return "blessing of wisdom"; + default: + break; } return "blessing of might"; @@ -34,6 +36,8 @@ inline std::string const GetActualBlessingOfWisdom(Unit* target) case CLASS_ROGUE: case CLASS_DEATH_KNIGHT: return "blessing of might"; + default: + break; } return "blessing of wisdom"; diff --git a/src/Ai/Class/Priest/Action/PriestActions.cpp b/src/Ai/Class/Priest/Action/PriestActions.cpp index e1c61033a1..c138c67015 100644 --- a/src/Ai/Class/Priest/Action/PriestActions.cpp +++ b/src/Ai/Class/Priest/Action/PriestActions.cpp @@ -4,17 +4,18 @@ */ #include "PriestActions.h" +#include "BotSpellService.h" #include "Event.h" #include "Playerbots.h" -bool CastRemoveShadowformAction::isUseful() { return botAI->HasAura("shadowform", AI_VALUE(Unit*, "self target")); } +bool CastRemoveShadowformAction::isUseful() { return botAI->GetServices().GetSpellService().HasAura("shadowform", AI_VALUE(Unit*, "self target")); } bool CastRemoveShadowformAction::isPossible() { return true; } -bool CastRemoveShadowformAction::Execute(Event event) +bool CastRemoveShadowformAction::Execute(Event /*event*/) { - botAI->RemoveAura("shadowform"); + botAI->GetServices().GetSpellService().RemoveAura("shadowform"); return true; } @@ -38,7 +39,7 @@ Unit* CastPowerWordShieldOnAlmostFullHealthBelowAction::GetTarget() { continue; } - if (botAI->HasAnyAuraOf(player, "weakened soul", "power word: shield", nullptr)) + if (botAI->GetServices().GetSpellService().HasAnyAuraOf(player, "weakened soul", "power word: shield", nullptr)) { continue; } @@ -67,7 +68,7 @@ bool CastPowerWordShieldOnAlmostFullHealthBelowAction::isUseful() { continue; } - if (botAI->HasAnyAuraOf(player, "weakened soul", "power word: shield", nullptr)) + if (botAI->GetServices().GetSpellService().HasAnyAuraOf(player, "weakened soul", "power word: shield", nullptr)) { continue; } @@ -93,7 +94,7 @@ Unit* CastPowerWordShieldOnNotFullAction::GetTarget() { continue; } - if (botAI->HasAnyAuraOf(player, "weakened soul", "power word: shield", nullptr)) + if (botAI->GetServices().GetSpellService().HasAnyAuraOf(player, "weakened soul", "power word: shield", nullptr)) { continue; } diff --git a/src/Ai/Class/Priest/Action/PriestActions.h b/src/Ai/Class/Priest/Action/PriestActions.h index 1b09414d48..4748ff0c3b 100644 --- a/src/Ai/Class/Priest/Action/PriestActions.h +++ b/src/Ai/Class/Priest/Action/PriestActions.h @@ -29,14 +29,14 @@ BUFF_ACTION(CastLevitateAction, "levitate"); BUFF_ACTION(CastDivineSpiritAction, "divine spirit"); BUFF_PARTY_ACTION(CastDivineSpiritOnPartyAction, "divine spirit"); BUFF_PARTY_ACTION(CastPrayerOfSpiritOnPartyAction, "prayer of spirit"); -// disc 2.4.3 +// disc 2.4f.3 SPELL_ACTION(CastMassDispelAction, "mass dispel"); // disc talents BUFF_ACTION(CastPowerInfusionAction, "power infusion"); BUFF_PARTY_ACTION(CastPowerInfusionOnPartyAction, "power infusion"); BUFF_ACTION(CastInnerFocusAction, "inner focus"); -// disc 2.4.3 talents +// disc 2.4f.3 talents BUFF_ACTION(CastPainSuppressionAction, "pain suppression"); PROTECT_ACTION(CastPainSuppressionProtectAction, "pain suppression"); @@ -88,7 +88,7 @@ class CastHolyFireAction : public CastDebuffSpellAction CastHolyFireAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "holy fire", true, 0.0f) {} }; -// shadow 2.4.3 +// shadow 2.4f.3 // BUFF_ACTION(CastShadowfiendAction, "shadowfiend"); SPELL_ACTION(CastShadowWordDeathAction, "shadow word: death"); @@ -109,7 +109,7 @@ DEBUFF_ACTION(CastVampiricEmbraceAction, "vampiric embrace"); BUFF_ACTION(CastShadowformAction, "shadowform"); SPELL_ACTION(CastSilenceAction, "silence"); ENEMY_HEALER_ACTION(CastSilenceOnEnemyHealerAction, "silence"); -// shadow talents 2.4.3 +// shadow talents 2.4f.3 DEBUFF_CHECKISOWNER_ACTION(CastVampiricTouchAction, "vampiric touch"); DEBUFF_ENEMY_ACTION(CastVampiricTouchOnAttackerAction, "vampiric touch"); // racials diff --git a/src/Ai/Class/Priest/Trigger/PriestTriggers.cpp b/src/Ai/Class/Priest/Trigger/PriestTriggers.cpp index 894780ad45..752a5e00a9 100644 --- a/src/Ai/Class/Priest/Trigger/PriestTriggers.cpp +++ b/src/Ai/Class/Priest/Trigger/PriestTriggers.cpp @@ -10,26 +10,26 @@ bool PowerWordFortitudeOnPartyTrigger::IsActive() { - return BuffOnPartyTrigger::IsActive() && !botAI->HasAura("power word : fortitude", GetTarget()) && - !botAI->HasAura("prayer of fortitude", GetTarget()); + return BuffOnPartyTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAura("power word : fortitude", GetTarget()) && + !botAI->GetServices().GetSpellService().HasAura("prayer of fortitude", GetTarget()); } bool PowerWordFortitudeTrigger::IsActive() { - return BuffTrigger::IsActive() && !botAI->HasAura("power word: fortitude", GetTarget()) && - !botAI->HasAura("prayer of fortitude", GetTarget()); + return BuffTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAura("power word: fortitude", GetTarget()) && + !botAI->GetServices().GetSpellService().HasAura("prayer of fortitude", GetTarget()); } bool DivineSpiritOnPartyTrigger::IsActive() { - return BuffOnPartyTrigger::IsActive() && !botAI->HasAura("divine spirit", GetTarget()) && - !botAI->HasAura("prayer of spirit", GetTarget()); + return BuffOnPartyTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAura("divine spirit", GetTarget()) && + !botAI->GetServices().GetSpellService().HasAura("prayer of spirit", GetTarget()); } bool DivineSpiritTrigger::IsActive() { - return BuffTrigger::IsActive() && !botAI->HasAura("divine spirit", GetTarget()) && - !botAI->HasAura("prayer of spirit", GetTarget()); + return BuffTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAura("divine spirit", GetTarget()) && + !botAI->GetServices().GetSpellService().HasAura("prayer of spirit", GetTarget()); } bool PrayerOfFortitudeTrigger::IsActive() @@ -38,7 +38,7 @@ bool PrayerOfFortitudeTrigger::IsActive() if (!target || !target->IsPlayer()) return false; - return BuffOnPartyTrigger::IsActive() && !botAI->HasAura("prayer of fortitude", GetTarget()) && + return BuffOnPartyTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAura("prayer of fortitude", GetTarget()) && botAI->GetBot()->IsInSameGroupWith((Player*)GetTarget()) && botAI->GetBuffedCount((Player*)GetTarget(), "prayer of fortitude") < 4 && !botAI->GetBuffedCount((Player*)GetTarget(), "power word: fortitude"); @@ -50,7 +50,7 @@ bool PrayerOfSpiritTrigger::IsActive() if (!target || !target->IsPlayer()) return false; - return BuffOnPartyTrigger::IsActive() && !botAI->HasAura("prayer of spirit", GetTarget()) && + return BuffOnPartyTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAura("prayer of spirit", GetTarget()) && botAI->GetBot()->IsInSameGroupWith((Player*)GetTarget()) && // botAI->GetManaPercent() > 50 && botAI->GetBuffedCount((Player*)GetTarget(), "prayer of spirit") < 4 && @@ -60,10 +60,10 @@ bool PrayerOfSpiritTrigger::IsActive() bool InnerFireTrigger::IsActive() { Unit* target = GetTarget(); - return SpellTrigger::IsActive() && !botAI->HasAura(spell, target); + return SpellTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAura(spell, target); } -bool ShadowformTrigger::IsActive() { return !botAI->HasAura("shadowform", bot); } +bool ShadowformTrigger::IsActive() { return !botAI->GetServices().GetSpellService().HasAura("shadowform", bot); } bool ShadowfiendTrigger::IsActive() { return BoostTrigger::IsActive() && !bot->HasSpellCooldown(34433); } @@ -78,7 +78,7 @@ bool BindingHealTrigger::IsActive() AI_VALUE2(uint8, "health", "self target") < sPlayerbotAIConfig->mediumHealth; } -const std::set MindSearChannelCheckTrigger::MIND_SEAR_SPELL_IDS = { +std::set const MindSearChannelCheckTrigger::MIND_SEAR_SPELL_IDS = { 48045, // Mind Sear Rank 1 53023 // Mind Sear Rank 2 }; diff --git a/src/Ai/Class/Priest/Trigger/PriestTriggers.h b/src/Ai/Class/Priest/Trigger/PriestTriggers.h index 5ae91985d3..14dd56e153 100644 --- a/src/Ai/Class/Priest/Trigger/PriestTriggers.h +++ b/src/Ai/Class/Priest/Trigger/PriestTriggers.h @@ -114,7 +114,7 @@ class MindSearChannelCheckTrigger : public Trigger protected: uint32 minEnemies; - static const std::set MIND_SEAR_SPELL_IDS; + static std::set const MIND_SEAR_SPELL_IDS; }; #endif diff --git a/src/Ai/Class/Rogue/Action/RogueActions.cpp b/src/Ai/Class/Rogue/Action/RogueActions.cpp index 3019e0ec86..8ad51a36e5 100644 --- a/src/Ai/Class/Rogue/Action/RogueActions.cpp +++ b/src/Ai/Class/Rogue/Action/RogueActions.cpp @@ -4,6 +4,7 @@ */ #include "RogueActions.h" +#include "BotSpellService.h" #include "Event.h" #include "ObjectGuid.h" @@ -22,20 +23,20 @@ bool CastStealthAction::isUseful() bool CastStealthAction::isPossible() { // do not use with WSG flag or EYE flag - return !botAI->HasAura(23333, bot) && !botAI->HasAura(23335, bot) && !botAI->HasAura(34976, bot); + return !botAI->GetServices().GetSpellService().HasAura(23333, bot) && !botAI->GetServices().GetSpellService().HasAura(23335, bot) && !botAI->GetServices().GetSpellService().HasAura(34976, bot); } -bool UnstealthAction::Execute(Event event) +bool UnstealthAction::Execute(Event /*event*/) { - botAI->RemoveAura("stealth"); + botAI->GetServices().GetSpellService().RemoveAura("stealth"); // botAI->ChangeStrategy("+dps,-stealthed", BOT_STATE_COMBAT); return true; } -bool CheckStealthAction::Execute(Event event) +bool CheckStealthAction::Execute(Event /*event*/) { - if (botAI->HasAura("stealth", bot)) + if (botAI->GetServices().GetSpellService().HasAura("stealth", bot)) { botAI->ChangeStrategy("-dps,+stealthed", BOT_STATE_COMBAT); } @@ -50,7 +51,7 @@ bool CheckStealthAction::Execute(Event event) bool CastVanishAction::isUseful() { // do not use with WSG flag or EYE flag - return !botAI->HasAura(23333, bot) && !botAI->HasAura(23335, bot) && !botAI->HasAura(34976, bot); + return !botAI->GetServices().GetSpellService().HasAura(23333, bot) && !botAI->GetServices().GetSpellService().HasAura(23335, bot) && !botAI->GetServices().GetSpellService().HasAura(34976, bot); } bool CastEnvenomAction::isUseful() @@ -61,7 +62,7 @@ bool CastEnvenomAction::isUseful() bool CastEnvenomAction::isPossible() { // alternate to eviscerate if talents unlearned - return botAI->HasAura(58410, bot) /* Master Poisoner */; + return botAI->GetServices().GetSpellService().HasAura(58410, bot) /* Master Poisoner */; } bool CastTricksOfTheTradeOnMainTankAction::isUseful() @@ -69,7 +70,7 @@ bool CastTricksOfTheTradeOnMainTankAction::isUseful() return CastSpellAction::isUseful() && AI_VALUE2(float, "distance", GetTargetName()) < 20.0f; } -bool UseDeadlyPoisonAction::Execute(Event event) +bool UseDeadlyPoisonAction::Execute(Event /*event*/) { std::vector poison_suffixs = {" IX", " VIII", " VII", " VI", " V", " IV", " III", " II", ""}; std::vector items; @@ -109,7 +110,7 @@ bool UseDeadlyPoisonAction::isPossible() return !items.empty(); } -bool UseInstantPoisonAction::Execute(Event event) +bool UseInstantPoisonAction::Execute(Event /*event*/) { std::vector poison_suffixs = {" IX", " VIII", " VII", " VI", " V", " IV", " III", " II", ""}; std::vector items; @@ -148,7 +149,7 @@ bool UseInstantPoisonAction::isPossible() return !items.empty(); } -bool UseInstantPoisonOffHandAction::Execute(Event event) +bool UseInstantPoisonOffHandAction::Execute(Event /*event*/) { std::vector poison_suffixs = {" IX", " VIII", " VII", " VI", " V", " IV", " III", " II", ""}; std::vector items; diff --git a/src/Ai/Class/Rogue/Trigger/RogueTriggers.cpp b/src/Ai/Class/Rogue/Trigger/RogueTriggers.cpp index fd9901552e..76e5aecf49 100644 --- a/src/Ai/Class/Rogue/Trigger/RogueTriggers.cpp +++ b/src/Ai/Class/Rogue/Trigger/RogueTriggers.cpp @@ -11,15 +11,15 @@ // bool AdrenalineRushTrigger::isPossible() // { -// return !botAI->HasAura("stealth", bot); +// return !botAI->GetServices().GetSpellService().HasAura("stealth", bot); // } bool UnstealthTrigger::IsActive() { - if (!botAI->HasAura("stealth", bot)) + if (!botAI->GetServices().GetSpellService().HasAura("stealth", bot)) return false; - return botAI->HasAura("stealth", bot) && !AI_VALUE(uint8, "attacker count") && + return botAI->GetServices().GetSpellService().HasAura("stealth", bot) && !AI_VALUE(uint8, "attacker count") && (AI_VALUE2(bool, "moving", "self target") && ((botAI->GetMaster() && sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "group leader"), 10.0f) && @@ -29,7 +29,7 @@ bool UnstealthTrigger::IsActive() bool StealthTrigger::IsActive() { - if (botAI->HasAura("stealth", bot) || bot->IsInCombat() || bot->HasSpellCooldown(1784)) + if (botAI->GetServices().GetSpellService().HasAura("stealth", bot) || bot->IsInCombat() || bot->HasSpellCooldown(1784)) return false; float distance = 30.f; @@ -73,7 +73,7 @@ bool SprintTrigger::IsActive() return false; float distance = botAI->GetMaster() ? 45.0f : 35.0f; - if (botAI->HasAura("stealth", bot)) + if (botAI->GetServices().GetSpellService().HasAura("stealth", bot)) distance -= 10; bool targeted = false; @@ -106,7 +106,7 @@ bool SprintTrigger::IsActive() bool ExposeArmorTrigger::IsActive() { Unit* target = AI_VALUE(Unit*, "current target"); // Get the bot's current target - return DebuffTrigger::IsActive() && !botAI->HasAura("sunder armor", target, false, false, -1, true) && + return DebuffTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAura("sunder armor", target, false, false, -1, true) && AI_VALUE2(uint8, "combo", "current target") <= 3; } diff --git a/src/Ai/Class/Shaman/Action/ShamanActions.cpp b/src/Ai/Class/Shaman/Action/ShamanActions.cpp index e8ed1234bf..d2f0982d3c 100644 --- a/src/Ai/Class/Shaman/Action/ShamanActions.cpp +++ b/src/Ai/Class/Shaman/Action/ShamanActions.cpp @@ -14,7 +14,7 @@ bool CastTotemAction::isUseful() { return CastBuffSpellAction::isUseful() && !AI_VALUE2(bool, "has totem", name) - && !botAI->HasAura(buff, bot); + && !botAI->GetServices().GetSpellService().HasAura(buff, bot); } bool CastMagmaTotemAction::isUseful() { @@ -71,7 +71,7 @@ bool CastLavaBurstAction::isUseful() // Logic for making a guardian (spirit wolf) use a spell (spirit walk) // There is no existing code for guardians casting spells in the AC/Playerbots repo. -bool CastSpiritWalkAction::Execute(Event event) +bool CastSpiritWalkAction::Execute(Event /*event*/) { constexpr uint32 SPIRIT_WOLF = 29264; constexpr uint32 SPIRIT_WALK_SPELL = 58875; @@ -93,7 +93,7 @@ bool CastSpiritWalkAction::Execute(Event event) // Set Strategy Assigned Totems (Actions) - First, it checks // the highest-rank spell the bot knows for each totem type, // then adds it to the Call of the Elements bar. -bool SetTotemAction::Execute(Event event) +bool SetTotemAction::Execute(Event /*event*/) { uint32 totemSpell = 0; for (size_t i = 0; i < totemSpellIdsCount; ++i) @@ -110,7 +110,7 @@ bool SetTotemAction::Execute(Event event) return false; } - if (const ActionButton* button = bot->GetActionButton(actionButtonId)) + if (ActionButton const* button = bot->GetActionButton(actionButtonId)) { if (button->GetType() == ACTION_BUTTON_SPELL && button->GetAction() == totemSpell) { diff --git a/src/Ai/Class/Shaman/Strategy/TotemsShamanStrategy.h b/src/Ai/Class/Shaman/Strategy/TotemsShamanStrategy.h index 6d36f4a271..0be2250192 100644 --- a/src/Ai/Class/Shaman/Strategy/TotemsShamanStrategy.h +++ b/src/Ai/Class/Shaman/Strategy/TotemsShamanStrategy.h @@ -14,10 +14,10 @@ // This is the header with all of the totem-related constants and arrays used in the Shaman strategies. // Totem Bar Slot Constants -#define TOTEM_BAR_SLOT_FIRE 132 -#define TOTEM_BAR_SLOT_EARTH 133 -#define TOTEM_BAR_SLOT_WATER 134 -#define TOTEM_BAR_SLOT_AIR 135 +constexpr uint32 TOTEM_BAR_SLOT_FIRE = 132; +constexpr uint32 TOTEM_BAR_SLOT_EARTH = 133; +constexpr uint32 TOTEM_BAR_SLOT_WATER = 134; +constexpr uint32 TOTEM_BAR_SLOT_AIR = 135; // Strength of Earth Totem static const uint32 STRENGTH_OF_EARTH_TOTEM[] = { diff --git a/src/Ai/Class/Shaman/Trigger/ShamanTriggers.cpp b/src/Ai/Class/Shaman/Trigger/ShamanTriggers.cpp index 753edb83fd..2c98d96e2d 100644 --- a/src/Ai/Class/Shaman/Trigger/ShamanTriggers.cpp +++ b/src/Ai/Class/Shaman/Trigger/ShamanTriggers.cpp @@ -1,3 +1,4 @@ +#include "BotSpellService.h" /* * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it * and/or modify it under version 3 of the License, or (at your option), any later version. @@ -40,8 +41,8 @@ bool OffHandWeaponNoImbueTrigger::IsActive() bool ShockTrigger::IsActive() { return SpellTrigger::IsActive() && - !botAI->HasAura("flame shock", GetTarget(), false, true) && - !botAI->HasAura("frost shock", GetTarget(), false, true); + !botAI->GetServices().GetSpellService().HasAura("flame shock", GetTarget(), false, true) && + !botAI->GetServices().GetSpellService().HasAura("frost shock", GetTarget(), false, true); } // Checks if the target's health is above 25%/1500 hp. Returns false if either are true. @@ -66,7 +67,7 @@ bool TotemTrigger::IsActive() { return AI_VALUE(uint8, "attacker count") >= attackerCount && !AI_VALUE2(bool, "has totem", name) && - !botAI->HasAura(name, bot); + !botAI->GetServices().GetSpellService().HasAura(name, bot); } bool WaterWalkingTrigger::IsActive() @@ -90,8 +91,8 @@ bool WaterBreathingOnPartyTrigger::IsActive() } // Checks if Chain Lightning is on Cooldown, and prevents activation if it is. -// This is to ensure that Elemental Mastery is used on Lava Burst (2.0 second cast), -// and not on Chain Lightning (1.5 second cast with talents). +// This is to ensure that Elemental Mastery is used on Lava Burst (2.0f second cast), +// and not on Chain Lightning (1.5f second cast with talents). bool ElementalMasteryTrigger::IsActive() { return bot->HasSpellCooldown(421); @@ -131,7 +132,7 @@ bool SpiritWalkTrigger::IsActive() // Fires the trigger if at least 2 of the totem slots are empty or out of range. bool CallOfTheElementsTrigger::IsActive() { - if (!botAI->CanCastSpell(SPELL_CALL_OF_THE_ELEMENTS, bot, true)) + if (!botAI->GetServices().GetSpellService().CanCastSpell(SPELL_CALL_OF_THE_ELEMENTS, bot, true)) { return false; } @@ -284,18 +285,18 @@ static uint32 GetRequiredTotemSpellId(PlayerbotAI* ai, const char* strategies[], } // Get the spellId of the currently summoned totem in the slot -static uint32 GetSummonedTotemSpellId(Player* bot, uint8 slot) -{ - ObjectGuid guid = bot->m_SummonSlot[slot]; - if (guid.IsEmpty()) - return 0; - - Creature* totem = bot->GetMap()->GetCreature(guid); - if (!totem) - return 0; - - return totem->GetUInt32Value(UNIT_CREATED_BY_SPELL); -} +// static uint32 GetSummonedTotemSpellId(Player* bot, uint8 slot) +// { +// ObjectGuid guid = bot->m_SummonSlot[slot]; +// if (guid.IsEmpty()) +// return 0; +// +// Creature* totem = bot->GetMap()->GetCreature(guid); +// if (!totem) +// return 0; +// +// return totem->GetUInt32Value(UNIT_CREATED_BY_SPELL); +// } bool NoEarthTotemTrigger::IsActive() { diff --git a/src/Ai/Class/Shaman/Trigger/ShamanTriggers.h b/src/Ai/Class/Shaman/Trigger/ShamanTriggers.h index abf4ed566c..4f2c779656 100644 --- a/src/Ai/Class/Shaman/Trigger/ShamanTriggers.h +++ b/src/Ai/Class/Shaman/Trigger/ShamanTriggers.h @@ -361,10 +361,9 @@ class SetTotemTrigger : public Trigger public: // Template constructor: infers N (size of the id array) at compile time template - SetTotemTrigger(PlayerbotAI* ai, std::string const& spellName, uint32 requiredSpellId, + SetTotemTrigger(PlayerbotAI* ai, std::string const& spellName, uint32 /*requiredSpellId*/, const uint32 (&ids)[N], int actionButtonId) : Trigger(ai, "set " + spellName) - , requiredSpellId(requiredSpellId) , totemSpellIds(ids) , totemSpellIdsCount(N) , actionButtonId(actionButtonId) @@ -372,7 +371,6 @@ class SetTotemTrigger : public Trigger bool IsActive() override; private: - uint32 requiredSpellId; uint32 const* totemSpellIds; size_t totemSpellIdsCount; int actionButtonId; diff --git a/src/Ai/Class/Warlock/Action/WarlockActions.cpp b/src/Ai/Class/Warlock/Action/WarlockActions.cpp index e899c7a2f6..d526e09a14 100644 --- a/src/Ai/Class/Warlock/Action/WarlockActions.cpp +++ b/src/Ai/Class/Warlock/Action/WarlockActions.cpp @@ -4,6 +4,7 @@ */ #include "WarlockActions.h" +#include "BotRoleService.h" #include #include @@ -18,6 +19,7 @@ #include "Timer.h" #include #include +#include "BotSpellService.h" const int ITEM_SOUL_SHARD = 6265; @@ -116,7 +118,7 @@ bool CastSoulshatterAction::isUseful() } // Checks if the bot has enough bag space to create a soul shard, then does so -bool CreateSoulShardAction::Execute(Event event) +bool CreateSoulShardAction::Execute(Event /*event*/) { Player* bot = botAI->GetBot(); if (!bot) @@ -160,7 +162,7 @@ bool CastCreateSoulstoneAction::isUseful() return false; // List of all Soulstone item IDs - static const std::vector soulstoneIds = { + static std::vector const soulstoneIds = { 5232, // Minor Soulstone 16892, // Lesser Soulstone 16893, // Soulstone @@ -188,7 +190,7 @@ bool CastCreateSoulstoneAction::isUseful() return hasSpace; } -bool DestroySoulShardAction::Execute(Event event) +bool DestroySoulShardAction::Execute(Event /*event*/) { // Look for the first soul shard in any bag and destroy it for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) @@ -226,7 +228,7 @@ bool DestroySoulShardAction::Execute(Event event) // Checks if the target has a soulstone aura static bool HasSoulstoneAura(Unit* unit) { - static const std::vector soulstoneAuraIds = {20707, 20762, 20763, 20764, 20765, 27239, 47883}; + static std::vector const soulstoneAuraIds = {20707, 20762, 20763, 20764, 20765, 27239, 47883}; for (uint32 spellId : soulstoneAuraIds) if (unit->HasAura(spellId)) return true; @@ -234,7 +236,7 @@ static bool HasSoulstoneAura(Unit* unit) } // Use the soulstone item on the bot itself with nc strategy "ss self" -bool UseSoulstoneSelfAction::Execute(Event event) +bool UseSoulstoneSelfAction::Execute(Event /*event*/) { std::vector items = AI_VALUE2(std::vector, "inventory items", "soulstone"); if (items.empty()) @@ -266,7 +268,7 @@ void CleanupSoulstoneReservations() } // Use the soulstone item on the bot's master with nc strategy "ss master" -bool UseSoulstoneMasterAction::Execute(Event event) +bool UseSoulstoneMasterAction::Execute(Event /*event*/) { CleanupSoulstoneReservations(); @@ -285,7 +287,7 @@ bool UseSoulstoneMasterAction::Execute(Event event) if (soulstoneReservations.count(master->GetGUID()) && soulstoneReservations[master->GetGUID()] > now) return false; // Already being soulstoned - soulstoneReservations[master->GetGUID()] = now + 2500; // Reserve for 2.5 seconds + soulstoneReservations[master->GetGUID()] = now + 2500; // Reserve for 2.5f seconds } float distance = sServerFacade->GetDistance2d(bot, master); @@ -300,7 +302,7 @@ bool UseSoulstoneMasterAction::Execute(Event event) } // Use the soulstone item on a tank in the group with nc strategy "ss tank" -bool UseSoulstoneTankAction::Execute(Event event) +bool UseSoulstoneTankAction::Execute(Event /*event*/) { CleanupSoulstoneReservations(); @@ -318,7 +320,7 @@ bool UseSoulstoneTankAction::Execute(Event event) for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) { Player* member = gref->GetSource(); - if (member && member->IsAlive() && botAI->IsTank(member) && botAI->IsMainTank(member) && + if (member && member->IsAlive() && BotRoleService::IsTankStatic(member) && BotRoleService::IsMainTankStatic(member) && !HasSoulstoneAura(member)) { std::lock_guard lock(soulstoneReservationsMutex); @@ -329,7 +331,7 @@ bool UseSoulstoneTankAction::Execute(Event event) if (distance < 30.0f && bot->IsWithinLOSInMap(member)) { chosenTank = member; - soulstoneReservations[chosenTank->GetGUID()] = now + 2500; // Reserve for 2.5 seconds + soulstoneReservations[chosenTank->GetGUID()] = now + 2500; // Reserve for 2.5f seconds break; } } @@ -341,7 +343,7 @@ bool UseSoulstoneTankAction::Execute(Event event) for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) { Player* member = gref->GetSource(); - if (member && member->IsAlive() && botAI->IsTank(member) && !HasSoulstoneAura(member)) + if (member && member->IsAlive() && BotRoleService::IsTankStatic(member) && !HasSoulstoneAura(member)) { std::lock_guard lock(soulstoneReservationsMutex); if (soulstoneReservations.count(member->GetGUID()) && @@ -352,7 +354,7 @@ bool UseSoulstoneTankAction::Execute(Event event) if (distance < 30.0f && bot->IsWithinLOSInMap(member)) { chosenTank = member; - soulstoneReservations[chosenTank->GetGUID()] = now + 2500; // Reserve for 2.5 seconds + soulstoneReservations[chosenTank->GetGUID()] = now + 2500; // Reserve for 2.5f seconds break; } } @@ -368,7 +370,7 @@ bool UseSoulstoneTankAction::Execute(Event event) } // Use the soulstone item on a healer in the group with nc strategy "ss healer" -bool UseSoulstoneHealerAction::Execute(Event event) +bool UseSoulstoneHealerAction::Execute(Event /*event*/) { CleanupSoulstoneReservations(); @@ -384,7 +386,7 @@ bool UseSoulstoneHealerAction::Execute(Event event) for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) { Player* member = gref->GetSource(); - if (member && member->IsAlive() && botAI->IsHeal(member) && !HasSoulstoneAura(member)) + if (member && member->IsAlive() && BotRoleService::IsHealStatic(member) && !HasSoulstoneAura(member)) { { std::lock_guard lock(soulstoneReservationsMutex); @@ -396,7 +398,7 @@ bool UseSoulstoneHealerAction::Execute(Event event) if (distance < 30.0f && bot->IsWithinLOSInMap(member)) { healer = member; - soulstoneReservations[healer->GetGUID()] = now + 2500; // Reserve for 2.5 seconds + soulstoneReservations[healer->GetGUID()] = now + 2500; // Reserve for 2.5f seconds break; } } @@ -411,7 +413,7 @@ bool UseSoulstoneHealerAction::Execute(Event event) return UseItem(items[0], ObjectGuid::Empty, nullptr, healer); } -const std::vector CastCreateFirestoneAction::firestoneSpellIds = { +std::vector const CastCreateFirestoneAction::firestoneSpellIds = { 60220, // Create Firestone (Rank 7) 27250, // Rank 5 17953, // Rank 4 @@ -425,12 +427,12 @@ CastCreateFirestoneAction::CastCreateFirestoneAction(PlayerbotAI* botAI) { } -bool CastCreateFirestoneAction::Execute(Event event) +bool CastCreateFirestoneAction::Execute(Event /*event*/) { for (uint32 spellId : firestoneSpellIds) { if (bot->HasSpell(spellId)) - return botAI->CastSpell(spellId, bot); + return botAI->GetServices().GetSpellService().CastSpell(spellId, bot); } return false; } diff --git a/src/Ai/Class/Warlock/Action/WarlockActions.h b/src/Ai/Class/Warlock/Action/WarlockActions.h index 787b518d69..398d874774 100644 --- a/src/Ai/Class/Warlock/Action/WarlockActions.h +++ b/src/Ai/Class/Warlock/Action/WarlockActions.h @@ -71,7 +71,7 @@ class CastCreateFirestoneAction : public CastBuffSpellAction bool isUseful() override; private: - static const std::vector firestoneSpellIds; + static std::vector const firestoneSpellIds; }; class CastCreateSpellstoneAction : public CastBuffSpellAction @@ -243,7 +243,7 @@ class CastCorruptionAction : public CastDebuffSpellAction bool isUseful() override { // Bypass TTL check and prevent casting if Seed of Corruption is present - return CastAuraSpellAction::isUseful() && !botAI->HasAura("seed of corruption", GetTarget(), false, true); + return CastAuraSpellAction::isUseful() && !botAI->GetServices().GetSpellService().HasAura("seed of corruption", GetTarget(), false, true); } }; @@ -254,7 +254,7 @@ class CastCorruptionOnAttackerAction : public CastDebuffSpellOnAttackerAction bool isUseful() override { // Bypass TTL check and prevent casting if Seed of Corruption is present - return CastAuraSpellAction::isUseful() && !botAI->HasAura("seed of corruption", GetTarget(), false, true); + return CastAuraSpellAction::isUseful() && !botAI->GetServices().GetSpellService().HasAura("seed of corruption", GetTarget(), false, true); } }; diff --git a/src/Ai/Class/Warlock/Strategy/GenericWarlockStrategy.cpp b/src/Ai/Class/Warlock/Strategy/GenericWarlockStrategy.cpp index 1759776580..30a3b3fe73 100644 --- a/src/Ai/Class/Warlock/Strategy/GenericWarlockStrategy.cpp +++ b/src/Ai/Class/Warlock/Strategy/GenericWarlockStrategy.cpp @@ -126,12 +126,12 @@ void AoEWarlockStrategy::InitTriggers(std::vector& triggers) ); } -void WarlockBoostStrategy::InitTriggers(std::vector& triggers) +void WarlockBoostStrategy::InitTriggers(std::vector& /*triggers*/) { // Placeholder for future boost triggers } -void WarlockPetStrategy::InitTriggers(std::vector& triggers) +void WarlockPetStrategy::InitTriggers(std::vector& /*triggers*/) { // Placeholder for future pet triggers } diff --git a/src/Ai/Class/Warlock/Strategy/TankWarlockStrategy.cpp b/src/Ai/Class/Warlock/Strategy/TankWarlockStrategy.cpp index c54fd59684..643c521e23 100644 --- a/src/Ai/Class/Warlock/Strategy/TankWarlockStrategy.cpp +++ b/src/Ai/Class/Warlock/Strategy/TankWarlockStrategy.cpp @@ -42,6 +42,6 @@ std::vector TankWarlockStrategy::getDefaultActions() }; } -void TankWarlockStrategy::InitTriggers(std::vector& triggers) +void TankWarlockStrategy::InitTriggers(std::vector& /*triggers*/) { } diff --git a/src/Ai/Class/Warlock/Trigger/WarlockTriggers.cpp b/src/Ai/Class/Warlock/Trigger/WarlockTriggers.cpp index 04592c9b41..8df4e016ce 100644 --- a/src/Ai/Class/Warlock/Trigger/WarlockTriggers.cpp +++ b/src/Ai/Class/Warlock/Trigger/WarlockTriggers.cpp @@ -4,6 +4,7 @@ */ #include "WarlockTriggers.h" +#include "BotSpellService.h" #include "GenericTriggers.h" #include "Playerbots.h" #include "PlayerbotAI.h" @@ -17,7 +18,7 @@ uint32 GetSoulShardCount(Player* bot) } // List of all Soulstone item IDs -static const std::vector soulstoneItemIds = { +static std::vector const soulstoneItemIds = { 5232, // Minor Soulstone 16892, // Lesser Soulstone 16893, // Soulstone @@ -69,14 +70,14 @@ bool FearTrigger::IsActive() bool DemonArmorTrigger::IsActive() { Unit* target = GetTarget(); - return !botAI->HasAura("demon skin", target) && !botAI->HasAura("demon armor", target) && - !botAI->HasAura("fel armor", target); + return !botAI->GetServices().GetSpellService().HasAura("demon skin", target) && !botAI->GetServices().GetSpellService().HasAura("demon armor", target) && + !botAI->GetServices().GetSpellService().HasAura("fel armor", target); } bool SoulLinkTrigger::IsActive() { Unit* target = GetTarget(); - return !botAI->HasAura("soul link", target); + return !botAI->GetServices().GetSpellService().HasAura("soul link", target); } bool DemonicEmpowermentTrigger::IsActive() @@ -84,12 +85,12 @@ bool DemonicEmpowermentTrigger::IsActive() Pet* pet = bot->GetPet(); if (!pet) return false; - return !botAI->HasAura("demonic empowerment", pet); + return !botAI->GetServices().GetSpellService().HasAura("demonic empowerment", pet); } bool DecimationTrigger::IsActive() { - Aura* aura = botAI->GetAura(getName(), GetTarget(), false, true); + Aura* aura = botAI->GetServices().GetSpellService().GetAura(getName(), GetTarget(), false, true); return aura && aura->GetDuration() > 3000; } @@ -110,7 +111,7 @@ bool LifeTapTrigger::IsActive() // Checks if the Life Tap Glyph buff is active bool LifeTapGlyphBuffTrigger::IsActive() { - if (!botAI->HasAura(63320, bot)) + if (!botAI->GetServices().GetSpellService().HasAura(63320, bot)) return false; return BuffTrigger::IsActive(); @@ -191,9 +192,9 @@ bool WrongPetTrigger::IsActive() // Step 1: Count how many pet strategies are currently enabled for this bot. // While doing so, also remember which pet strategy is the only enabled one (if that's the case). int enabledCount = 0; - const WarlockPetDef* enabledPet = + WarlockPetDef const* enabledPet = nullptr; // Pointer to the pet definition of the enabled strategy, if only one is enabled - for (const WarlockPetDef& pd : pets) + for (WarlockPetDef const& pd : pets) { if (botAI->HasStrategy(pd.strategy, BOT_STATE_NON_COMBAT)) { @@ -235,7 +236,7 @@ bool WrongPetTrigger::IsActive() return false; } -const std::set RainOfFireChannelCheckTrigger::RAIN_OF_FIRE_SPELL_IDS = { +std::set const RainOfFireChannelCheckTrigger::RAIN_OF_FIRE_SPELL_IDS = { 5740, // Rain of Fire Rank 1 6219, // Rain of Fire Rank 2 11677, // Rain of Fire Rank 3 diff --git a/src/Ai/Class/Warlock/Trigger/WarlockTriggers.h b/src/Ai/Class/Warlock/Trigger/WarlockTriggers.h index 8512549012..cd3761023b 100644 --- a/src/Ai/Class/Warlock/Trigger/WarlockTriggers.h +++ b/src/Ai/Class/Warlock/Trigger/WarlockTriggers.h @@ -74,7 +74,7 @@ class SoulstoneTrigger : public Trigger bool IsActive() override { - static const std::vector soulstoneSpellIds = {20707, 20762, 20763, 20764, 20765, 27239, 47883}; + static std::vector const soulstoneSpellIds = {20707, 20762, 20763, 20764, 20765, 27239, 47883}; if (AI_VALUE2(uint32, "item count", "soulstone") == 0) return false; @@ -163,7 +163,7 @@ class CorruptionTrigger : public DebuffTrigger CorruptionTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "corruption", 1, true, 0.5f) {} bool IsActive() override { - return BuffTrigger::IsActive() && !botAI->HasAura("seed of corruption", GetTarget(), false, true); + return BuffTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAura("seed of corruption", GetTarget(), false, true); } }; @@ -173,7 +173,7 @@ class CorruptionOnAttackerTrigger : public DebuffOnAttackerTrigger CorruptionOnAttackerTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "corruption", true) {} bool IsActive() override { - return BuffTrigger::IsActive() && !botAI->HasAura("seed of corruption", GetTarget(), false, true); + return BuffTrigger::IsActive() && !botAI->GetServices().GetSpellService().HasAura("seed of corruption", GetTarget(), false, true); } }; @@ -345,7 +345,7 @@ class RainOfFireChannelCheckTrigger : public Trigger protected: uint32 minEnemies; - static const std::set RAIN_OF_FIRE_SPELL_IDS; + static std::set const RAIN_OF_FIRE_SPELL_IDS; }; #endif diff --git a/src/Ai/Class/Warrior/Action/WarriorActions.cpp b/src/Ai/Class/Warrior/Action/WarriorActions.cpp index 9733226a95..7ead1e7cd5 100644 --- a/src/Ai/Class/Warrior/Action/WarriorActions.cpp +++ b/src/Ai/Class/Warrior/Action/WarriorActions.cpp @@ -4,12 +4,15 @@ */ #include "WarriorActions.h" +#include "BotRoleService.h" #include "Playerbots.h" +#include "BotSpellService.h" +#include "BotItemService.h" bool CastSunderArmorAction::isUseful() { - Aura* aura = botAI->GetAura("sunder armor", GetTarget(), false, true); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("sunder armor", GetTarget(), false, true); return !aura || aura->GetStackAmount() < 5 || aura->GetDuration() <= 6000; } @@ -36,29 +39,29 @@ Unit* CastVigilanceAction::GetTarget() continue; // Check if member has Vigilance applied by the bot - if (!currentVigilanceTarget && botAI->HasAura("vigilance", member, false, true)) + if (!currentVigilanceTarget && botAI->GetServices().GetSpellService().HasAura("vigilance", member, false, true)) { currentVigilanceTarget = member; } // Identify Main Tank - if (!mainTank && botAI->IsMainTank(member)) + if (!mainTank && BotRoleService::IsMainTankStatic(member)) { mainTank = member; } // Identify Assist Tanks - if (assistTank1 == nullptr && botAI->IsAssistTankOfIndex(member, 0)) + if (assistTank1 == nullptr && BotRoleService::IsAssistTankOfIndexStatic(member, 0)) { assistTank1 = member; } - else if (assistTank2 == nullptr && botAI->IsAssistTankOfIndex(member, 1)) + else if (assistTank2 == nullptr && BotRoleService::IsAssistTankOfIndexStatic(member, 1)) { assistTank2 = member; } // Determine Highest Gear Score - uint32 gearScore = botAI->GetEquipGearScore(member/*, false, false*/); + uint32 gearScore = botAI->GetServices().GetItemService().GetEquipGearScore(member/*, false, false*/); if (gearScore > highestGearScore) { highestGearScore = gearScore; @@ -93,13 +96,13 @@ Unit* CastVigilanceAction::GetTarget() return nullptr; } -bool CastVigilanceAction::Execute(Event event) +bool CastVigilanceAction::Execute(Event /*event*/) { Unit* target = GetTarget(); if (!target || target == bot) return false; - return botAI->CastSpell("vigilance", target); + return botAI->GetServices().GetSpellService().CastSpell("vigilance", target); } bool CastRetaliationAction::isUseful() @@ -139,7 +142,7 @@ bool CastRetaliationAction::isUseful() else if (attacker->IsPlayer()) { Player* playerAttacker = attacker->ToPlayer(); - if (playerAttacker && botAI->IsMelee(playerAttacker)) // Reuse existing Player melee check + if (playerAttacker && BotRoleService::IsMeleeStatic(playerAttacker)) // Reuse existing Player melee check { ++meleeAttackers; } @@ -151,7 +154,7 @@ bool CastRetaliationAction::isUseful() } // Only cast Retaliation if there are at least 2 melee attackers and the buff is not active - return meleeAttackers >= 2 && !botAI->HasAura("retaliation", bot); + return meleeAttackers >= 2 && !botAI->GetServices().GetSpellService().HasAura("retaliation", bot); } Unit* CastShatteringThrowAction::GetTarget() @@ -234,11 +237,11 @@ bool CastShatteringThrowAction::isPossible() return true; } -bool CastShatteringThrowAction::Execute(Event event) +bool CastShatteringThrowAction::Execute(Event /*event*/) { Unit* target = GetTarget(); if (!target) return false; - return botAI->CastSpell("shattering throw", target); + return botAI->GetServices().GetSpellService().CastSpell("shattering throw", target); } diff --git a/src/Ai/Class/Warrior/Action/WarriorActions.h b/src/Ai/Class/Warrior/Action/WarriorActions.h index ea72fb269a..78c72b8523 100644 --- a/src/Ai/Class/Warrior/Action/WarriorActions.h +++ b/src/Ai/Class/Warrior/Action/WarriorActions.h @@ -7,6 +7,7 @@ #define _PLAYERBOT_WARRIORACTIONS_H #include "AiObject.h" +#include "BotRoleService.h" #include "GenericSpellActions.h" #include "Player.h" #include "PlayerbotAI.h" @@ -40,7 +41,7 @@ class CastDemoralizingShoutWithoutLifeTimeCheckAction : public CastMeleeDebuffSp MELEE_ACTION(CastChallengingShoutAction, "challenging shout"); DEBUFF_ACTION_R(CastIntimidatingShoutAction, "intimidating shout", 8.0f); -// shouts 2.4.3 +// shouts 2.4f.3 BUFF_ACTION(CastCommandingShoutAction, "commanding shout"); // arms @@ -61,7 +62,7 @@ SNARE_ACTION(CastHamstringAction, "hamstring"); MELEE_ACTION(CastOverpowerAction, "overpower"); MELEE_ACTION(CastMockingBlowAction, "mocking blow"); // BUFF_ACTION(CastRetaliationAction, "retaliation"); -// arms 3.3.5 +// arms 3.3f.5 SPELL_ACTION(CastHeroicThrowAction, "heroic throw"); SNARE_ACTION(CastHeroicThrowSnareAction, "heroic throw"); // DEBUFF_ACTION(CastShatteringThrowAction, "shattering throw"); @@ -69,7 +70,7 @@ SNARE_ACTION(CastHeroicThrowSnareAction, "heroic throw"); // arms talents MELEE_ACTION(CastMortalStrikeAction, "mortal strike"); BUFF_ACTION(CastSweepingStrikesAction, "sweeping strikes"); -// arms talents 3.3.5 +// arms talents 3.3f.5 MELEE_ACTION(CastBladestormAction, "bladestorm"); // fury @@ -83,9 +84,9 @@ BUFF_ACTION(CastBerserkerRageAction, "berserker rage"); MELEE_ACTION(CastWhirlwindAction, "whirlwind"); MELEE_ACTION(CastPummelAction, "pummel"); ENEMY_HEALER_ACTION(CastPummelOnEnemyHealerAction, "pummel"); -// fury 2.4.3 +// fury 2.4f.3 MELEE_ACTION(CastVictoryRushAction, "victory rush"); -// fury 3.3.5 +// fury 3.3f.5 BUFF_ACTION(CastEnragedRegenerationAction, "enraged regeneration"); BUFF_ACTION(CastHeroicFuryAction, "heroic fury"); @@ -94,7 +95,7 @@ BUFF_ACTION(CastDeathWishAction, "death wish"); BUFF_ACTION(CastRecklessnessAction, "recklessness"); MELEE_ACTION(CastBloodthirstAction, "bloodthirst"); DEBUFF_ACTION_R(CastPiercingHowlAction, "piercing howl", 8.0f); -// fury talents 2.4.3 +// fury talents 2.4f.3 BUFF_ACTION(CastRampageAction, "rampage"); // protection @@ -106,11 +107,11 @@ ENEMY_HEALER_ACTION(CastShieldBashOnEnemyHealerAction, "shield bash"); MELEE_ACTION(CastRevengeAction, "revenge"); BUFF_ACTION(CastShieldBlockAction, "shield block"); DEBUFF_ACTION_U(CastDisarmAction, "disarm", - GetTarget() && GetTarget()->IsPlayer() ? !botAI->IsRanged((Player*)GetTarget()) + GetTarget() && GetTarget()->IsPlayer() ? !BotRoleService::IsRangedStatic((Player*)GetTarget()) : CastDebuffSpellAction::isUseful()); DEBUFF_ENEMY_ACTION(CastDisarmOnAttackerAction, "disarm"); BUFF_ACTION(CastShieldWallAction, "shield wall"); -// protection 2.4.3 +// protection 2.4f.3 PROTECT_ACTION(CastInterveneAction, "intervene"); BUFF_ACTION(CastSpellReflectionAction, "spell reflection"); @@ -118,9 +119,9 @@ BUFF_ACTION(CastSpellReflectionAction, "spell reflection"); BUFF_ACTION(CastLastStandAction, "last stand"); MELEE_ACTION(CastShieldSlamAction, "shield slam"); MELEE_ACTION(CastConcussionBlowAction, "concussion blow"); -// protection talents 2.4.3 +// protection talents 2.4f.3 MELEE_ACTION(CastDevastateAction, "devastate"); -// protection talents 3.3.5 +// protection talents 3.3f.5 DEBUFF_ACTION_R(CastShockwaveAction, "shockwave", 8.0f); SNARE_ACTION(CastShockwaveSnareAction, "shockwave"); diff --git a/src/Ai/Class/Warrior/Strategy/ArmsWarriorStrategy.cpp b/src/Ai/Class/Warrior/Strategy/ArmsWarriorStrategy.cpp index eba5677e7f..ef9f62639e 100644 --- a/src/Ai/Class/Warrior/Strategy/ArmsWarriorStrategy.cpp +++ b/src/Ai/Class/Warrior/Strategy/ArmsWarriorStrategy.cpp @@ -23,7 +23,7 @@ class ArmsWarriorStrategyActionNodeFactory : public NamedObjectFactoryHasAura("vigilance", member, false, true)) + if (!currentVigilanceTarget && botAI->GetServices().GetSpellService().HasAura("vigilance", member, false, true)) { currentVigilanceTarget = member; } // Identify Main Tank - if (!mainTank && botAI->IsMainTank(member)) + if (!mainTank && BotRoleService::IsMainTankStatic(member)) { mainTank = member; } // Identify Assist Tanks - if (assistTank1 == nullptr && botAI->IsAssistTankOfIndex(member, 0)) + if (assistTank1 == nullptr && BotRoleService::IsAssistTankOfIndexStatic(member, 0)) { assistTank1 = member; } - else if (assistTank2 == nullptr && botAI->IsAssistTankOfIndex(member, 1)) + else if (assistTank2 == nullptr && BotRoleService::IsAssistTankOfIndexStatic(member, 1)) { assistTank2 = member; } // Determine Highest Gear Score - uint32 gearScore = botAI->GetEquipGearScore(member/*, false, false*/); + uint32 gearScore = botAI->GetServices().GetItemService().GetEquipGearScore(member/*, false, false*/); if (gearScore > highestGearScore) { highestGearScore = gearScore; diff --git a/src/Ai/Dungeon/AzjolNerub/Action/AzjolNerubActions.cpp b/src/Ai/Dungeon/AzjolNerub/Action/AzjolNerubActions.cpp index e31ffd00f5..b166e3c67d 100644 --- a/src/Ai/Dungeon/AzjolNerub/Action/AzjolNerubActions.cpp +++ b/src/Ai/Dungeon/AzjolNerub/Action/AzjolNerubActions.cpp @@ -1,9 +1,10 @@ #include "Playerbots.h" +#include "BotRoleService.h" #include "AzjolNerubActions.h" #include "AzjolNerubStrategy.h" -bool AttackWebWrapAction::isUseful() { return !botAI->IsHeal(bot); } -bool AttackWebWrapAction::Execute(Event event) +bool AttackWebWrapAction::isUseful() { return !BotRoleService::IsHealStatic(bot); } +bool AttackWebWrapAction::Execute(Event /*event*/) { Unit* webWrap = nullptr; @@ -28,8 +29,8 @@ bool AttackWebWrapAction::Execute(Event event) return Attack(webWrap); } -bool WatchersTargetAction::isUseful() { return !botAI->IsHeal(bot); } -bool WatchersTargetAction::Execute(Event event) +bool WatchersTargetAction::isUseful() { return !BotRoleService::IsHealStatic(bot); } +bool WatchersTargetAction::Execute(Event /*event*/) { // Always prioritise web wraps Unit* currTarget = AI_VALUE(Unit*, "current target"); @@ -71,6 +72,8 @@ bool WatchersTargetAction::Execute(Event event) case NPC_WATCHER_WARRIOR: priorityTargets[3] = npc; break; + default: + break; } } @@ -95,7 +98,7 @@ bool WatchersTargetAction::Execute(Event event) } bool AnubarakDodgePoundAction::isUseful() { return !AI_VALUE2(bool, "behind", "current target"); } -bool AnubarakDodgePoundAction::Execute(Event event) +bool AnubarakDodgePoundAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "anub'arak"); if (!boss) { return false; } diff --git a/src/Ai/Dungeon/AzjolNerub/Multiplier/AzjolNerubMultipliers.cpp b/src/Ai/Dungeon/AzjolNerub/Multiplier/AzjolNerubMultipliers.cpp index 6cd6673dc5..bf385437e4 100644 --- a/src/Ai/Dungeon/AzjolNerub/Multiplier/AzjolNerubMultipliers.cpp +++ b/src/Ai/Dungeon/AzjolNerub/Multiplier/AzjolNerubMultipliers.cpp @@ -1,3 +1,4 @@ +#include "BotRoleService.h" #include "AzjolNerubMultipliers.h" #include "AzjolNerubActions.h" #include "GenericSpellActions.h" @@ -8,7 +9,7 @@ float KrikthirMultiplier::GetValue(Action* action) { - if (!botAI->IsDps(bot)) { return 1.0f; } + if (!BotRoleService::IsDpsStatic(bot)) { return 1.0f; } // Target is not findable from threat table using AI_VALUE2(), // therefore need to search manually for the unit name @@ -34,6 +35,8 @@ float KrikthirMultiplier::GetValue(Action* action) case NPC_WATCHER_WARRIOR: watcher = unit; continue; + default: + break; } } diff --git a/src/Ai/Dungeon/AzjolNerub/Strategy/AzjolNerubStrategy.cpp b/src/Ai/Dungeon/AzjolNerub/Strategy/AzjolNerubStrategy.cpp index dbbb8f5bf5..7f7b78b74e 100644 --- a/src/Ai/Dungeon/AzjolNerub/Strategy/AzjolNerubStrategy.cpp +++ b/src/Ai/Dungeon/AzjolNerub/Strategy/AzjolNerubStrategy.cpp @@ -1,7 +1,7 @@ #include "AzjolNerubStrategy.h" #include "AzjolNerubMultipliers.h" -void WotlkDungeonANStrategy::InitTriggers(std::vector &triggers) +void WotlkDungeonANStrategy::InitTriggers(std::vector& triggers) { // Krik'thir the Gatewatcher // TODO: Add CC trigger while web wraps are casting? @@ -24,7 +24,7 @@ void WotlkDungeonANStrategy::InitTriggers(std::vector &triggers) { NextAction("dodge pound", ACTION_MOVE + 5) })); } -void WotlkDungeonANStrategy::InitMultipliers(std::vector &multipliers) +void WotlkDungeonANStrategy::InitMultipliers(std::vector& multipliers) { multipliers.push_back(new KrikthirMultiplier(botAI)); } diff --git a/src/Ai/Dungeon/AzjolNerub/Strategy/AzjolNerubStrategy.h b/src/Ai/Dungeon/AzjolNerub/Strategy/AzjolNerubStrategy.h index 70196e6e41..3357000917 100644 --- a/src/Ai/Dungeon/AzjolNerub/Strategy/AzjolNerubStrategy.h +++ b/src/Ai/Dungeon/AzjolNerub/Strategy/AzjolNerubStrategy.h @@ -10,8 +10,8 @@ class WotlkDungeonANStrategy : public Strategy public: WotlkDungeonANStrategy(PlayerbotAI* ai) : Strategy(ai) {} virtual std::string const getName() override { return "azjol'nerub"; } - virtual void InitTriggers(std::vector &triggers) override; - virtual void InitMultipliers(std::vector &multipliers) override; + virtual void InitTriggers(std::vector& triggers) override; + virtual void InitMultipliers(std::vector& multipliers) override; }; #endif diff --git a/src/Ai/Dungeon/AzjolNerub/Trigger/AzjolNerubTriggers.cpp b/src/Ai/Dungeon/AzjolNerub/Trigger/AzjolNerubTriggers.cpp index f9ef4ff953..262dc615b1 100644 --- a/src/Ai/Dungeon/AzjolNerub/Trigger/AzjolNerubTriggers.cpp +++ b/src/Ai/Dungeon/AzjolNerub/Trigger/AzjolNerubTriggers.cpp @@ -1,3 +1,4 @@ +#include "BotRoleService.h" #include "Playerbots.h" #include "AzjolNerubTriggers.h" #include "AiObject.h" @@ -5,7 +6,7 @@ bool KrikthirWebWrapTrigger::IsActive() { - if (!botAI->IsDps(bot)) { return false; } + if (!BotRoleService::IsDpsStatic(bot)) { return false; } // Target is not findable from threat table using AI_VALUE2(), // therefore need to search manually for the unit name @@ -25,7 +26,7 @@ bool KrikthirWebWrapTrigger::IsActive() bool KrikthirWatchersTrigger::IsActive() { - if (!botAI->IsDps(bot)) { return false; } + if (!BotRoleService::IsDpsStatic(bot)) { return false; } // Target is not findable from threat table using AI_VALUE2(), // therefore need to search manually for the unit name diff --git a/src/Ai/Dungeon/CullingOfStratholme/Action/CullingOfStratholmeActions.cpp b/src/Ai/Dungeon/CullingOfStratholme/Action/CullingOfStratholmeActions.cpp index 15121f4a2e..0e7eb49494 100644 --- a/src/Ai/Dungeon/CullingOfStratholme/Action/CullingOfStratholmeActions.cpp +++ b/src/Ai/Dungeon/CullingOfStratholme/Action/CullingOfStratholmeActions.cpp @@ -2,7 +2,7 @@ #include "CullingOfStratholmeActions.h" #include "CullingOfStratholmeStrategy.h" -bool ExplodeGhoulSpreadAction::Execute(Event event) +bool ExplodeGhoulSpreadAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "salramm the fleshcrafter"); if (!boss) { return false; } @@ -29,7 +29,7 @@ bool EpochStackAction::isUseful() { // Minimum hunter range is 5, but values too close to this seem to cause issues.. // Hunter bots will try and melee in between ranged attacks, or just melee entirely at 5 as they are in range. - // 7.5 or 8.0 solves this for this boss. + // 7.5f or 8.0f solves this for this boss. // Unfortunately at this range the boss will charge. So I guess just don't stack as a hunter.. // if (bot->getClass() == CLASS_HUNTER) // { @@ -38,7 +38,7 @@ bool EpochStackAction::isUseful() // else return !(bot->getClass() == CLASS_HUNTER) && AI_VALUE2(float, "distance", "current target") > 5.0f; } -bool EpochStackAction::Execute(Event event) +bool EpochStackAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "chrono-lord epoch"); if (!boss) { return false; } diff --git a/src/Ai/Dungeon/CullingOfStratholme/Strategy/CullingOfStratholmeStrategy.cpp b/src/Ai/Dungeon/CullingOfStratholme/Strategy/CullingOfStratholmeStrategy.cpp index 90765bded4..7de8c83e86 100644 --- a/src/Ai/Dungeon/CullingOfStratholme/Strategy/CullingOfStratholmeStrategy.cpp +++ b/src/Ai/Dungeon/CullingOfStratholme/Strategy/CullingOfStratholmeStrategy.cpp @@ -1,7 +1,7 @@ #include "CullingOfStratholmeStrategy.h" #include "CullingOfStratholmeMultipliers.h" -void WotlkDungeonCoSStrategy::InitTriggers(std::vector &triggers) +void WotlkDungeonCoSStrategy::InitTriggers(std::vector& triggers) { // Meathook // Can tank this in a fixed position to allow healer to LoS the stun, probably not necessary @@ -20,7 +20,7 @@ void WotlkDungeonCoSStrategy::InitTriggers(std::vector &triggers) // Infinite Corruptor (Heroic only) } -void WotlkDungeonCoSStrategy::InitMultipliers(std::vector &multipliers) +void WotlkDungeonCoSStrategy::InitMultipliers(std::vector& multipliers) { multipliers.push_back(new EpochMultiplier(botAI)); } diff --git a/src/Ai/Dungeon/CullingOfStratholme/Strategy/CullingOfStratholmeStrategy.h b/src/Ai/Dungeon/CullingOfStratholme/Strategy/CullingOfStratholmeStrategy.h index bff1267435..775fc4009f 100644 --- a/src/Ai/Dungeon/CullingOfStratholme/Strategy/CullingOfStratholmeStrategy.h +++ b/src/Ai/Dungeon/CullingOfStratholme/Strategy/CullingOfStratholmeStrategy.h @@ -10,8 +10,8 @@ class WotlkDungeonCoSStrategy : public Strategy public: WotlkDungeonCoSStrategy(PlayerbotAI* ai) : Strategy(ai) {} virtual std::string const getName() override { return "culling of stratholme"; } - virtual void InitTriggers(std::vector &triggers) override; - virtual void InitMultipliers(std::vector &multipliers) override; + virtual void InitTriggers(std::vector& triggers) override; + virtual void InitMultipliers(std::vector& multipliers) override; }; #endif diff --git a/src/Ai/Dungeon/CullingOfStratholme/Trigger/CullingOfStratholmeTriggers.cpp b/src/Ai/Dungeon/CullingOfStratholme/Trigger/CullingOfStratholmeTriggers.cpp index cb67ad077f..6366fbcc2a 100644 --- a/src/Ai/Dungeon/CullingOfStratholme/Trigger/CullingOfStratholmeTriggers.cpp +++ b/src/Ai/Dungeon/CullingOfStratholme/Trigger/CullingOfStratholmeTriggers.cpp @@ -1,4 +1,5 @@ #include "Playerbots.h" +#include "BotRoleService.h" #include "CullingOfStratholmeTriggers.h" #include "AiObject.h" #include "AiObjectContext.h" @@ -27,5 +28,5 @@ bool ExplodeGhoulTrigger::IsActive() bool EpochRangedTrigger::IsActive() { - return !botAI->IsMelee(bot) && AI_VALUE2(Unit*, "find target", "chrono-lord epoch"); + return !BotRoleService::IsMeleeStatic(bot) && AI_VALUE2(Unit*, "find target", "chrono-lord epoch"); } diff --git a/src/Ai/Dungeon/DraktharonKeep/Action/DrakTharonKeepActions.cpp b/src/Ai/Dungeon/DraktharonKeep/Action/DrakTharonKeepActions.cpp index 8cbbe49c2a..7097c5dc97 100644 --- a/src/Ai/Dungeon/DraktharonKeep/Action/DrakTharonKeepActions.cpp +++ b/src/Ai/Dungeon/DraktharonKeep/Action/DrakTharonKeepActions.cpp @@ -1,8 +1,9 @@ #include "Playerbots.h" +#include "BotRoleService.h" #include "DrakTharonKeepActions.h" #include "DrakTharonKeepStrategy.h" -bool CorpseExplodeSpreadAction::Execute(Event event) +bool CorpseExplodeSpreadAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "trollgore"); if (!boss) { return false; } @@ -25,7 +26,7 @@ bool CorpseExplodeSpreadAction::Execute(Event event) return false; } -bool AvoidArcaneFieldAction::Execute(Event event) +bool AvoidArcaneFieldAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "novos the summoner"); if (!boss) { return false; } @@ -44,7 +45,7 @@ bool NovosDefaultPositionAction::isUseful() float threshold = 15.0f; return bot->GetDistance(NOVOS_PARTY_POSITION) > threshold; } -bool NovosDefaultPositionAction::Execute(Event event) +bool NovosDefaultPositionAction::Execute(Event /*event*/) { float clusterDistance = 4.0f; // Only reposition if we're not killing anything @@ -59,7 +60,7 @@ bool NovosDefaultPositionAction::Execute(Event event) return false; } -bool NovosTargetPriorityAction::Execute(Event event) +bool NovosTargetPriorityAction::Execute(Event /*event*/) { // TODO: This can be improved, some parts are still buggy. // But it works for now and this fight is very easy @@ -76,9 +77,9 @@ bool NovosTargetPriorityAction::Execute(Event event) Player* groupMember = botAI->GetPlayer(member); if (!groupMember) { continue; } - if (botAI->IsDps(groupMember)) + if (BotRoleService::IsDpsStatic(groupMember)) { - if (botAI->IsMelee(groupMember)) + if (BotRoleService::IsMeleeStatic(groupMember)) { // Found our first melee dps, grab handle and break stairsDps = groupMember; @@ -109,7 +110,7 @@ bool NovosTargetPriorityAction::Execute(Event event) // Tank priority: // Hulking Corpse -> Crystal Handler - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) { if (creatureId == NPC_HULKING_CORPSE) { @@ -142,7 +143,7 @@ bool NovosTargetPriorityAction::Execute(Event event) } // All other dps priority: // Crystal Handler -> Hulking Corpse - else if (botAI->IsDps(bot)) + else if (BotRoleService::IsDpsStatic(bot)) { if (creatureId == NPC_CRYSTAL_HANDLER) { diff --git a/src/Ai/Dungeon/DraktharonKeep/Multiplier/DrakTharonKeepMultipliers.cpp b/src/Ai/Dungeon/DraktharonKeep/Multiplier/DrakTharonKeepMultipliers.cpp index 8fd119dc4e..f6b2193de5 100644 --- a/src/Ai/Dungeon/DraktharonKeep/Multiplier/DrakTharonKeepMultipliers.cpp +++ b/src/Ai/Dungeon/DraktharonKeep/Multiplier/DrakTharonKeepMultipliers.cpp @@ -1,4 +1,5 @@ #include "DrakTharonKeepMultipliers.h" +#include "BotRoleService.h" #include "DrakTharonKeepActions.h" #include "GenericSpellActions.h" #include "ChooseTargetActions.h" @@ -43,7 +44,7 @@ float TharonjaMultiplier::GetValue(Action* action) } // Tanks should only taunt, no slaying strike - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) { if (dynamic_cast(action)) { diff --git a/src/Ai/Dungeon/DraktharonKeep/Strategy/DrakTharonKeepStrategy.cpp b/src/Ai/Dungeon/DraktharonKeep/Strategy/DrakTharonKeepStrategy.cpp index 791a0ea71e..9a040b58df 100644 --- a/src/Ai/Dungeon/DraktharonKeep/Strategy/DrakTharonKeepStrategy.cpp +++ b/src/Ai/Dungeon/DraktharonKeep/Strategy/DrakTharonKeepStrategy.cpp @@ -1,7 +1,7 @@ #include "DrakTharonKeepStrategy.h" #include "DrakTharonKeepMultipliers.h" -void WotlkDungeonDTKStrategy::InitTriggers(std::vector &triggers) +void WotlkDungeonDTKStrategy::InitTriggers(std::vector& triggers) { // Trollgore triggers.push_back(new TriggerNode("corpse explode", @@ -33,7 +33,7 @@ void WotlkDungeonDTKStrategy::InitTriggers(std::vector &triggers) { NextAction("slaying strike", ACTION_NORMAL + 2) })); } -void WotlkDungeonDTKStrategy::InitMultipliers(std::vector &multipliers) +void WotlkDungeonDTKStrategy::InitMultipliers(std::vector& multipliers) { multipliers.push_back(new NovosMultiplier(botAI)); multipliers.push_back(new TharonjaMultiplier(botAI)); diff --git a/src/Ai/Dungeon/DraktharonKeep/Strategy/DrakTharonKeepStrategy.h b/src/Ai/Dungeon/DraktharonKeep/Strategy/DrakTharonKeepStrategy.h index 41ef0ced6f..18ea91360a 100644 --- a/src/Ai/Dungeon/DraktharonKeep/Strategy/DrakTharonKeepStrategy.h +++ b/src/Ai/Dungeon/DraktharonKeep/Strategy/DrakTharonKeepStrategy.h @@ -10,8 +10,8 @@ class WotlkDungeonDTKStrategy : public Strategy public: WotlkDungeonDTKStrategy(PlayerbotAI* ai) : Strategy(ai) {} virtual std::string const getName() override { return "drak'tharon keep"; } - virtual void InitTriggers(std::vector &triggers) override; - virtual void InitMultipliers(std::vector &multipliers) override; + virtual void InitTriggers(std::vector& triggers) override; + virtual void InitMultipliers(std::vector& multipliers) override; }; #endif diff --git a/src/Ai/Dungeon/ForgeOfSouls/Action/ForgeOfSoulsActions.cpp b/src/Ai/Dungeon/ForgeOfSouls/Action/ForgeOfSoulsActions.cpp index e72fe98d5f..3e4d4ed066 100644 --- a/src/Ai/Dungeon/ForgeOfSouls/Action/ForgeOfSoulsActions.cpp +++ b/src/Ai/Dungeon/ForgeOfSouls/Action/ForgeOfSoulsActions.cpp @@ -1,9 +1,11 @@ #include "Playerbots.h" +#include "BotSpellService.h" +#include "BotRoleService.h" #include "ForgeOfSoulsActions.h" #include "ForgeOfSoulsStrategy.h" #include "SharedDefines.h" -bool MoveFromBronjahmAction::Execute(Event event) +bool MoveFromBronjahmAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "bronjahm"); if (!boss) @@ -17,13 +19,11 @@ bool MoveFromBronjahmAction::Execute(Event event) return false; } -bool AttackCorruptedSoulFragmentAction::Execute(Event event) +bool AttackCorruptedSoulFragmentAction::Execute(Event /*event*/) { - Unit* currentTarget = AI_VALUE(Unit*, "current target"); GuidVector targets = AI_VALUE(GuidVector, "possible targets"); // If no valid skull target, search for corrupted soul fragment - Unit* empoweredPrince = nullptr; for (auto i = targets.begin(); i != targets.end(); ++i) { Unit* unit = botAI->GetUnit(*i); @@ -32,38 +32,35 @@ bool AttackCorruptedSoulFragmentAction::Execute(Event event) if (unit->GetEntry() == NPC_CORRUPTED_SOUL_FRAGMENT) { - empoweredPrince = unit; - - // Mark corrupted soul fragment with skull if in group and not already marked - if (Group* group = bot->GetGroup()) + // Mark corrupted soul fragment with skull if in group and not already marked + if (Group* group = bot->GetGroup()) + { + ObjectGuid currentSkullGuid = group->GetTargetIcon(7); + if (currentSkullGuid.IsEmpty() || currentSkullGuid != unit->GetGUID()) { - ObjectGuid currentSkullGuid = group->GetTargetIcon(7); - if (currentSkullGuid.IsEmpty() || currentSkullGuid != unit->GetGUID()) - { - group->SetTargetIcon(7, bot->GetGUID(), unit->GetGUID()); // 7 = skull - } + group->SetTargetIcon(7, bot->GetGUID(), unit->GetGUID()); // 7 = skull } - break; + } + break; } - } return false; } -bool BronjahmGroupPositionAction::Execute(Event event) +bool BronjahmGroupPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "bronjahm"); if (!boss) return false; - Aura* aura = botAI->GetAura("soulstorm", boss); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("soulstorm", boss); bool hasAura = aura; Unit* corruptedSoul = bot->FindNearestCreature(NPC_CORRUPTED_SOUL_FRAGMENT, 50.0f); bool activeSoulExists = corruptedSoul && corruptedSoul->IsAlive(); - if (botAI->IsTank(bot) && botAI->HasAggro(boss)) + if (BotRoleService::IsTankStatic(bot) && botAI->GetServices().GetRoleService().HasAggro(boss)) { // If any corrupted soul exists, handle positioning carefully if (activeSoulExists) @@ -123,7 +120,7 @@ bool BronjahmGroupPositionAction::Execute(Event event) if (bot->GetExactDist2d(boss) > maxMovement && !activeSoulExists && (hasAura || boss->FindCurrentSpellBySpellId(SPELL_SOULSTORM_VISUAL) || boss->FindCurrentSpellBySpellId(SPELL_SOULSTORM_VISUAL2))) { - if (botAI->IsRanged(bot)) + if (BotRoleService::IsRangedStatic(bot)) { return Move(bot->GetAngle(boss), fmin(bot->GetExactDist2d(boss) - 6.5f, maxMovement)); } @@ -141,16 +138,16 @@ bool BronjahmGroupPositionAction::Execute(Event event) bool BronjahmGroupPositionAction::isUseful() { return true; } -bool DevourerOfSoulsAction::Execute(Event event) +bool DevourerOfSoulsAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "devourer of souls"); if (!boss) return false; - Aura* aura = botAI->GetAura("mirrored soul", boss); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("mirrored soul", boss); bool hasAura = aura; - if (!botAI->IsTank(bot) && !botAI->IsHeal(bot) && hasAura) + if (!BotRoleService::IsTankStatic(bot) && !BotRoleService::IsHealStatic(bot) && hasAura) { // Calculate the opposite direction float angle = bot->GetAngle(boss); diff --git a/src/Ai/Dungeon/ForgeOfSouls/Multiplier/ForgeOfSoulsMultipliers.cpp b/src/Ai/Dungeon/ForgeOfSouls/Multiplier/ForgeOfSoulsMultipliers.cpp index 7873e7c1d1..8a41bc3083 100644 --- a/src/Ai/Dungeon/ForgeOfSouls/Multiplier/ForgeOfSoulsMultipliers.cpp +++ b/src/Ai/Dungeon/ForgeOfSouls/Multiplier/ForgeOfSoulsMultipliers.cpp @@ -1,3 +1,4 @@ +#include "BotRoleService.h" #include "ForgeOfSoulsMultipliers.h" #include "ForgeOfSoulsActions.h" #include "GenericSpellActions.h" @@ -41,7 +42,7 @@ float AttackFragmentMultiplier::GetValue(Action* action) } } - if (fragment && botAI->IsDps(bot) && dynamic_cast(action)) + if (fragment && BotRoleService::IsDpsStatic(bot) && dynamic_cast(action)) return 0.0f; return 1.0f; diff --git a/src/Ai/Dungeon/ForgeOfSouls/Strategy/ForgeOfSoulsStrategy.h b/src/Ai/Dungeon/ForgeOfSouls/Strategy/ForgeOfSoulsStrategy.h index 79d58334a9..811b63f2b1 100644 --- a/src/Ai/Dungeon/ForgeOfSouls/Strategy/ForgeOfSoulsStrategy.h +++ b/src/Ai/Dungeon/ForgeOfSouls/Strategy/ForgeOfSoulsStrategy.h @@ -8,8 +8,8 @@ class WotlkDungeonFoSStrategy : public Strategy public: WotlkDungeonFoSStrategy(PlayerbotAI* ai) : Strategy(ai) {} std::string const getName() override { return "forge of souls"; } - void InitTriggers(std::vector &triggers) override; - void InitMultipliers(std::vector &multipliers) override; + void InitTriggers(std::vector& triggers) override; + void InitMultipliers(std::vector& multipliers) override; }; diff --git a/src/Ai/Dungeon/Gundrak/Action/GundrakActions.cpp b/src/Ai/Dungeon/Gundrak/Action/GundrakActions.cpp index 4e9910993d..544739eb4c 100644 --- a/src/Ai/Dungeon/Gundrak/Action/GundrakActions.cpp +++ b/src/Ai/Dungeon/Gundrak/Action/GundrakActions.cpp @@ -1,8 +1,9 @@ #include "Playerbots.h" +#include "BotRoleService.h" #include "GundrakActions.h" #include "GundrakStrategy.h" -bool AvoidPoisonNovaAction::Execute(Event event) +bool AvoidPoisonNovaAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "slad'ran"); if (!boss) { return false; } @@ -19,12 +20,11 @@ bool AvoidPoisonNovaAction::Execute(Event event) return false; } -bool AttackSnakeWrapAction::Execute(Event event) +bool AttackSnakeWrapAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "slad'ran"); if (!boss) { return false; } - Unit* snakeWrap = nullptr; // Target is not findable from threat table using AI_VALUE2(), // therefore need to search manually for the unit name GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); @@ -45,7 +45,7 @@ bool AttackSnakeWrapAction::Execute(Event event) return false; } -bool AvoidWhirlingSlashAction::Execute(Event event) +bool AvoidWhirlingSlashAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "gal'darah"); if (!boss) { return false; } @@ -56,7 +56,7 @@ bool AvoidWhirlingSlashAction::Execute(Event event) if (distance < radius + distanceExtra) { - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) { // The boss chases tank during this, leads to jittery stutter-stepping // by the tank if we don't pre-move additional range. 2*radius seems ok diff --git a/src/Ai/Dungeon/Gundrak/Multiplier/GundrakMultipliers.cpp b/src/Ai/Dungeon/Gundrak/Multiplier/GundrakMultipliers.cpp index 2d44042551..3c8fd84bc1 100644 --- a/src/Ai/Dungeon/Gundrak/Multiplier/GundrakMultipliers.cpp +++ b/src/Ai/Dungeon/Gundrak/Multiplier/GundrakMultipliers.cpp @@ -1,3 +1,4 @@ +#include "BotRoleService.h" #include "GundrakMultipliers.h" #include "GundrakActions.h" #include "GenericSpellActions.h" @@ -19,7 +20,7 @@ float SladranMultiplier::GetValue(Action* action) } } - if (!botAI->IsDps(bot)) { return 1.0f; } + if (!BotRoleService::IsDpsStatic(bot)) { return 1.0f; } if (action->getThreatType() == Action::ActionThreatType::Aoe) { diff --git a/src/Ai/Dungeon/Gundrak/Strategy/GundrakStrategy.cpp b/src/Ai/Dungeon/Gundrak/Strategy/GundrakStrategy.cpp index c8244e2ca9..9cdd7a5aaa 100644 --- a/src/Ai/Dungeon/Gundrak/Strategy/GundrakStrategy.cpp +++ b/src/Ai/Dungeon/Gundrak/Strategy/GundrakStrategy.cpp @@ -1,7 +1,7 @@ #include "GundrakStrategy.h" #include "GundrakMultipliers.h" -void WotlkDungeonGDStrategy::InitTriggers(std::vector &triggers) +void WotlkDungeonGDStrategy::InitTriggers(std::vector& triggers) { // Moorabi @@ -22,7 +22,7 @@ void WotlkDungeonGDStrategy::InitTriggers(std::vector &triggers) // Eck the Ferocious (Heroic only) } -void WotlkDungeonGDStrategy::InitMultipliers(std::vector &multipliers) +void WotlkDungeonGDStrategy::InitMultipliers(std::vector& multipliers) { multipliers.push_back(new SladranMultiplier(botAI)); multipliers.push_back(new GaldarahMultiplier(botAI)); diff --git a/src/Ai/Dungeon/Gundrak/Strategy/GundrakStrategy.h b/src/Ai/Dungeon/Gundrak/Strategy/GundrakStrategy.h index 47f8617f17..3b145e5cd7 100644 --- a/src/Ai/Dungeon/Gundrak/Strategy/GundrakStrategy.h +++ b/src/Ai/Dungeon/Gundrak/Strategy/GundrakStrategy.h @@ -10,8 +10,8 @@ class WotlkDungeonGDStrategy : public Strategy public: WotlkDungeonGDStrategy(PlayerbotAI* ai) : Strategy(ai) {} virtual std::string const getName() override { return "gundrak"; } - virtual void InitTriggers(std::vector &triggers) override; - virtual void InitMultipliers(std::vector &multipliers) override; + virtual void InitTriggers(std::vector& triggers) override; + virtual void InitMultipliers(std::vector& multipliers) override; }; #endif diff --git a/src/Ai/Dungeon/Gundrak/Trigger/GundrakTriggers.cpp b/src/Ai/Dungeon/Gundrak/Trigger/GundrakTriggers.cpp index b3e98cfdb6..74038aaf5c 100644 --- a/src/Ai/Dungeon/Gundrak/Trigger/GundrakTriggers.cpp +++ b/src/Ai/Dungeon/Gundrak/Trigger/GundrakTriggers.cpp @@ -1,3 +1,4 @@ +#include "BotRoleService.h" #include "Playerbots.h" #include "GundrakTriggers.h" #include "AiObject.h" @@ -13,7 +14,7 @@ bool SladranPoisonNovaTrigger::IsActive() bool SladranSnakeWrapTrigger::IsActive() { - if (!botAI->IsDps(bot)) { return false; } + if (!BotRoleService::IsDpsStatic(bot)) { return false; } // Target is not findable from threat table using AI_VALUE2(), // therefore need to search manually for the unit name diff --git a/src/Ai/Dungeon/HallsOfLightning/Action/HallsOfLightningActions.cpp b/src/Ai/Dungeon/HallsOfLightning/Action/HallsOfLightningActions.cpp index 047e0aee13..b6705656ff 100644 --- a/src/Ai/Dungeon/HallsOfLightning/Action/HallsOfLightningActions.cpp +++ b/src/Ai/Dungeon/HallsOfLightning/Action/HallsOfLightningActions.cpp @@ -2,7 +2,7 @@ #include "HallsOfLightningActions.h" #include "HallsOfLightningStrategy.h" -bool BjarngrimTargetAction::Execute(Event event) +bool BjarngrimTargetAction::Execute(Event /*event*/) { Unit* target = nullptr; @@ -35,7 +35,7 @@ bool BjarngrimTargetAction::Execute(Event event) return Attack(target); } -bool AvoidWhirlwindAction::Execute(Event event) +bool AvoidWhirlwindAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "general bjarngrim"); if (!boss) { return false; } @@ -52,7 +52,7 @@ bool AvoidWhirlwindAction::Execute(Event event) return false; } -bool VolkhanTargetAction::Execute(Event event) +bool VolkhanTargetAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "volkhan"); if (!boss || AI_VALUE(Unit*, "current target") == boss) @@ -63,7 +63,7 @@ bool VolkhanTargetAction::Execute(Event event) return Attack(boss); } -bool StaticOverloadSpreadAction::Execute(Event event) +bool StaticOverloadSpreadAction::Execute(Event /*event*/) { float radius = 8.0f; float distanceExtra = 2.0f; @@ -86,7 +86,7 @@ bool StaticOverloadSpreadAction::Execute(Event event) return false; } -bool BallLightningSpreadAction::Execute(Event event) +bool BallLightningSpreadAction::Execute(Event /*event*/) { float radius = 6.0f; float distanceExtra = 1.0f; @@ -108,14 +108,14 @@ bool BallLightningSpreadAction::Execute(Event event) } bool IonarTankPositionAction::isUseful() { return bot->GetExactDist2d(IONAR_TANK_POSITION) > 10.0f; } -bool IonarTankPositionAction::Execute(Event event) +bool IonarTankPositionAction::Execute(Event /*event*/) { return MoveTo(bot->GetMapId(), IONAR_TANK_POSITION.GetPositionX(), IONAR_TANK_POSITION.GetPositionY(), IONAR_TANK_POSITION.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT); } bool DispersePositionAction::isUseful() { return bot->GetExactDist2d(DISPERSE_POSITION) > 8.0f; } -bool DispersePositionAction::Execute(Event event) +bool DispersePositionAction::Execute(Event /*event*/) { return MoveTo(bot->GetMapId(), DISPERSE_POSITION.GetPositionX(), DISPERSE_POSITION.GetPositionY(), DISPERSE_POSITION.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT); @@ -125,7 +125,7 @@ bool LokenStackAction::isUseful() { // Minimum hunter range is 5, but values too close to this seem to cause issues.. // Hunter bots will try and melee in between ranged attacks, or just melee entirely at 5 as they are in range. - // 6.5 or 7.0 solves this for this boss. + // 6.5f or 7.0f solves this for this boss. if (bot->getClass() == CLASS_HUNTER) { return AI_VALUE2(float, "distance", "current target") > 6.5f; @@ -133,7 +133,7 @@ bool LokenStackAction::isUseful() // else return AI_VALUE2(float, "distance", "current target") > 2.0f; } -bool LokenStackAction::Execute(Event event) +bool LokenStackAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "loken"); if (!boss) { return false; } @@ -152,7 +152,7 @@ bool LokenStackAction::Execute(Event event) return false; } -bool AvoidLightningNovaAction::Execute(Event event) +bool AvoidLightningNovaAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "loken"); if (!boss) { return false; } diff --git a/src/Ai/Dungeon/HallsOfLightning/Multiplier/HallsOfLightningMultipliers.cpp b/src/Ai/Dungeon/HallsOfLightning/Multiplier/HallsOfLightningMultipliers.cpp index 3bf9fcfd63..8e39232494 100644 --- a/src/Ai/Dungeon/HallsOfLightning/Multiplier/HallsOfLightningMultipliers.cpp +++ b/src/Ai/Dungeon/HallsOfLightning/Multiplier/HallsOfLightningMultipliers.cpp @@ -1,4 +1,5 @@ #include "HallsOfLightningMultipliers.h" +#include "BotRoleService.h" #include "HallsOfLightningActions.h" #include "GenericSpellActions.h" #include "ChooseTargetActions.h" @@ -11,7 +12,7 @@ float BjarngrimMultiplier::GetValue(Action* action) { Unit* boss = AI_VALUE2(Unit*, "find target", "general bjarngrim"); - if (!boss || botAI->IsHeal(bot)) { return 1.0f; } + if (!boss || BotRoleService::IsHealStatic(bot)) { return 1.0f; } if (boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_WHIRLWIND_BJARNGRIM)) { @@ -37,7 +38,7 @@ float BjarngrimMultiplier::GetValue(Action* action) } } - if (!boss_add || botAI->IsTank(bot)) { return 1.0f; } + if (!boss_add || BotRoleService::IsTankStatic(bot)) { return 1.0f; } if (dynamic_cast(action)) { @@ -55,7 +56,7 @@ float BjarngrimMultiplier::GetValue(Action* action) float VolkhanMultiplier::GetValue(Action* action) { Unit* boss = AI_VALUE2(Unit*, "find target", "volkhan"); - if (!boss || botAI->IsTank(bot) || botAI->IsHeal(bot)) { return 1.0f; } + if (!boss || BotRoleService::IsTankStatic(bot) || BotRoleService::IsHealStatic(bot)) { return 1.0f; } if (dynamic_cast(action)) { diff --git a/src/Ai/Dungeon/HallsOfLightning/Strategy/HallsOfLightningStrategy.cpp b/src/Ai/Dungeon/HallsOfLightning/Strategy/HallsOfLightningStrategy.cpp index 1266fb0b9c..1bb4f2c6f2 100644 --- a/src/Ai/Dungeon/HallsOfLightning/Strategy/HallsOfLightningStrategy.cpp +++ b/src/Ai/Dungeon/HallsOfLightning/Strategy/HallsOfLightningStrategy.cpp @@ -1,7 +1,7 @@ #include "HallsOfLightningStrategy.h" #include "HallsOfLightningMultipliers.h" -void WotlkDungeonHoLStrategy::InitTriggers(std::vector &triggers) +void WotlkDungeonHoLStrategy::InitTriggers(std::vector& triggers) { // General Bjarngrim triggers.push_back(new TriggerNode("stormforged lieutenant", @@ -31,7 +31,7 @@ void WotlkDungeonHoLStrategy::InitTriggers(std::vector &triggers) { NextAction("loken stack", ACTION_MOVE + 4) })); } -void WotlkDungeonHoLStrategy::InitMultipliers(std::vector &multipliers) +void WotlkDungeonHoLStrategy::InitMultipliers(std::vector& multipliers) { multipliers.push_back(new BjarngrimMultiplier(botAI)); multipliers.push_back(new VolkhanMultiplier(botAI)); diff --git a/src/Ai/Dungeon/HallsOfLightning/Strategy/HallsOfLightningStrategy.h b/src/Ai/Dungeon/HallsOfLightning/Strategy/HallsOfLightningStrategy.h index 6463033b3a..9c8da0933d 100644 --- a/src/Ai/Dungeon/HallsOfLightning/Strategy/HallsOfLightningStrategy.h +++ b/src/Ai/Dungeon/HallsOfLightning/Strategy/HallsOfLightningStrategy.h @@ -10,8 +10,8 @@ class WotlkDungeonHoLStrategy : public Strategy public: WotlkDungeonHoLStrategy(PlayerbotAI* ai) : Strategy(ai) {} virtual std::string const getName() override { return "halls of lightning"; } - virtual void InitTriggers(std::vector &triggers) override; - virtual void InitMultipliers(std::vector &multipliers) override; + virtual void InitTriggers(std::vector& triggers) override; + virtual void InitMultipliers(std::vector& multipliers) override; }; #endif diff --git a/src/Ai/Dungeon/HallsOfLightning/Trigger/HallsOfLightningTriggers.cpp b/src/Ai/Dungeon/HallsOfLightning/Trigger/HallsOfLightningTriggers.cpp index da88807caf..53f58dffb0 100644 --- a/src/Ai/Dungeon/HallsOfLightning/Trigger/HallsOfLightningTriggers.cpp +++ b/src/Ai/Dungeon/HallsOfLightning/Trigger/HallsOfLightningTriggers.cpp @@ -1,11 +1,12 @@ #include "Playerbots.h" +#include "BotRoleService.h" #include "HallsOfLightningTriggers.h" #include "AiObject.h" #include "AiObjectContext.h" bool StormforgedLieutenantTrigger::IsActive() { - if (!botAI->IsDps(bot)) { return false; } + if (!BotRoleService::IsDpsStatic(bot)) { return false; } // Target is not findable from threat table using AI_VALUE2(), // therefore need to search manually for the unit name @@ -34,7 +35,7 @@ bool BjarngrimWhirlwindTrigger::IsActive() bool VolkhanTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "volkhan"); - return boss && !botAI->IsTank(bot) && !botAI->IsHeal(bot); + return boss && !BotRoleService::IsTankStatic(bot) && !BotRoleService::IsHealStatic(bot); } bool IonarStaticOverloadTrigger::IsActive() @@ -53,7 +54,7 @@ bool IonarStaticOverloadTrigger::IsActive() bool IonarBallLightningTrigger::IsActive() { - if (botAI->IsMelee(bot)) { return false; } + if (BotRoleService::IsMeleeStatic(bot)) { return false; } Unit* boss = AI_VALUE2(Unit*, "find target", "ionar"); if (!boss) { return false; } @@ -63,7 +64,7 @@ bool IonarBallLightningTrigger::IsActive() bool IonarTankAggroTrigger::IsActive() { - if (!botAI->IsTank(bot)) { return false; } + if (!BotRoleService::IsTankStatic(bot)) { return false; } Unit* boss = AI_VALUE2(Unit*, "find target", "ionar"); if (!boss) { return false; } @@ -81,7 +82,7 @@ bool IonarDisperseTrigger::IsActive() bool LokenRangedTrigger::IsActive() { - return !botAI->IsMelee(bot) && AI_VALUE2(Unit*, "find target", "loken"); + return !BotRoleService::IsMeleeStatic(bot) && AI_VALUE2(Unit*, "find target", "loken"); } bool LokenLightningNovaTrigger::IsActive() diff --git a/src/Ai/Dungeon/HallsOfStone/Action/HallsOfStoneActions.cpp b/src/Ai/Dungeon/HallsOfStone/Action/HallsOfStoneActions.cpp index 30562f090d..83fba872ab 100644 --- a/src/Ai/Dungeon/HallsOfStone/Action/HallsOfStoneActions.cpp +++ b/src/Ai/Dungeon/HallsOfStone/Action/HallsOfStoneActions.cpp @@ -2,7 +2,7 @@ #include "HallsOfStoneActions.h" #include "HallsOfStoneStrategy.h" -bool ShatterSpreadAction::Execute(Event event) +bool ShatterSpreadAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "krystallus"); if (!boss) { return false; } @@ -35,7 +35,7 @@ bool ShatterSpreadAction::Execute(Event event) return false; } -bool AvoidLightningRingAction::Execute(Event event) +bool AvoidLightningRingAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "sjonnir the ironshaper"); if (!boss) { return false; } diff --git a/src/Ai/Dungeon/HallsOfStone/Strategy/HallsOfStoneStrategy.cpp b/src/Ai/Dungeon/HallsOfStone/Strategy/HallsOfStoneStrategy.cpp index 47006a9e44..8d26094374 100644 --- a/src/Ai/Dungeon/HallsOfStone/Strategy/HallsOfStoneStrategy.cpp +++ b/src/Ai/Dungeon/HallsOfStone/Strategy/HallsOfStoneStrategy.cpp @@ -1,7 +1,7 @@ #include "HallsOfStoneStrategy.h" #include "HallsOfStoneMultipliers.h" -void WotlkDungeonHoSStrategy::InitTriggers(std::vector &triggers) +void WotlkDungeonHoSStrategy::InitTriggers(std::vector& triggers) { // Maiden of Grief // TODO: Jump into damage during shock of sorrow? @@ -22,7 +22,7 @@ void WotlkDungeonHoSStrategy::InitTriggers(std::vector &triggers) { NextAction("avoid lightning ring", ACTION_RAID + 5) })); } -void WotlkDungeonHoSStrategy::InitMultipliers(std::vector &multipliers) +void WotlkDungeonHoSStrategy::InitMultipliers(std::vector& multipliers) { multipliers.push_back(new KrystallusMultiplier(botAI)); multipliers.push_back(new SjonnirMultiplier(botAI)); diff --git a/src/Ai/Dungeon/HallsOfStone/Strategy/HallsOfStoneStrategy.h b/src/Ai/Dungeon/HallsOfStone/Strategy/HallsOfStoneStrategy.h index ee26aa2cd2..1a03024f79 100644 --- a/src/Ai/Dungeon/HallsOfStone/Strategy/HallsOfStoneStrategy.h +++ b/src/Ai/Dungeon/HallsOfStone/Strategy/HallsOfStoneStrategy.h @@ -10,8 +10,8 @@ class WotlkDungeonHoSStrategy : public Strategy public: WotlkDungeonHoSStrategy(PlayerbotAI* ai) : Strategy(ai) {} virtual std::string const getName() override { return "halls of stone"; } - virtual void InitTriggers(std::vector &triggers) override; - virtual void InitMultipliers(std::vector &multipliers) override; + virtual void InitTriggers(std::vector& triggers) override; + virtual void InitMultipliers(std::vector& multipliers) override; }; #endif diff --git a/src/Ai/Dungeon/Nexus/Action/NexusActions.cpp b/src/Ai/Dungeon/Nexus/Action/NexusActions.cpp index f61a95aef4..b5d61d05ce 100644 --- a/src/Ai/Dungeon/Nexus/Action/NexusActions.cpp +++ b/src/Ai/Dungeon/Nexus/Action/NexusActions.cpp @@ -1,8 +1,9 @@ #include "Playerbots.h" +#include "BotRoleService.h" #include "NexusActions.h" #include "NexusStrategy.h" -bool MoveFromWhirlwindAction::Execute(Event event) +bool MoveFromWhirlwindAction::Execute(Event /*event*/) { Unit* boss = nullptr; uint8 faction = bot->GetTeamId(); @@ -52,7 +53,7 @@ bool MoveFromWhirlwindAction::Execute(Event event) return MoveAway(boss, targetDist - bossDistance); } -bool FirebombSpreadAction::Execute(Event event) +bool FirebombSpreadAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "grand magus telestra"); float radius = 5.0f; @@ -73,8 +74,8 @@ bool FirebombSpreadAction::Execute(Event event) return false; } -bool TelestraSplitTargetAction::isUseful() { return !botAI->IsHeal(bot); } -bool TelestraSplitTargetAction::Execute(Event event) +bool TelestraSplitTargetAction::isUseful() { return !BotRoleService::IsHealStatic(bot); } +bool TelestraSplitTargetAction::Execute(Event /*event*/) { GuidVector attackers = AI_VALUE(GuidVector, "attackers"); Unit* splitTargets[3] = {nullptr, nullptr, nullptr}; @@ -98,6 +99,8 @@ bool TelestraSplitTargetAction::Execute(Event event) case NPC_FIRE_MAGUS: splitTargets[2] = unit; break; + default: + break; } } @@ -120,8 +123,8 @@ bool TelestraSplitTargetAction::Execute(Event event) return false; } -bool ChaoticRiftTargetAction::isUseful() { return !botAI->IsHeal(bot); } -bool ChaoticRiftTargetAction::Execute(Event event) +bool ChaoticRiftTargetAction::isUseful() { return !BotRoleService::IsHealStatic(bot); } +bool ChaoticRiftTargetAction::Execute(Event /*event*/) { Unit* chaoticRift = nullptr; @@ -152,7 +155,7 @@ bool DodgeSpikesAction::isUseful() return bot->GetExactDist2d(boss) > 0.5f; } -bool DodgeSpikesAction::Execute(Event event) +bool DodgeSpikesAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper"); if (!boss) { return false; } @@ -160,7 +163,7 @@ bool DodgeSpikesAction::Execute(Event event) return Move(bot->GetAngle(boss), bot->GetExactDist2d(boss) - 0.3f); } -bool IntenseColdJumpAction::Execute(Event event) +bool IntenseColdJumpAction::Execute(Event /*event*/) { // This needs improving but maybe it should be done in the playerbot core. // Jump doesn't seem to support zero offset (eg. jump on the spot) so need to add a tiny delta. diff --git a/src/Ai/Dungeon/Nexus/Multiplier/NexusMultipliers.cpp b/src/Ai/Dungeon/Nexus/Multiplier/NexusMultipliers.cpp index 7adc79b455..72a819425e 100644 --- a/src/Ai/Dungeon/Nexus/Multiplier/NexusMultipliers.cpp +++ b/src/Ai/Dungeon/Nexus/Multiplier/NexusMultipliers.cpp @@ -1,4 +1,5 @@ #include "NexusMultipliers.h" +#include "BotRoleService.h" #include "NexusActions.h" #include "GenericSpellActions.h" #include "ChooseTargetActions.h" @@ -87,7 +88,7 @@ float OrmorokMultiplier::GetValue(Action* action) // This boss is annoying and shuffles around a lot. Don't let tank move once fight has started. // Extra checks are to allow the tank to close distance and engage the boss initially if (dynamic_cast(action) && !dynamic_cast(action) - && botAI->IsTank(bot) && bot->IsWithinMeleeRange(boss) + && BotRoleService::IsTankStatic(bot) && bot->IsWithinMeleeRange(boss) && AI_VALUE2(bool, "facing", "current target")) { return 0.0f; diff --git a/src/Ai/Dungeon/Nexus/Strategy/NexusStrategy.cpp b/src/Ai/Dungeon/Nexus/Strategy/NexusStrategy.cpp index e633ba9822..4304d03370 100644 --- a/src/Ai/Dungeon/Nexus/Strategy/NexusStrategy.cpp +++ b/src/Ai/Dungeon/Nexus/Strategy/NexusStrategy.cpp @@ -1,7 +1,7 @@ #include "NexusStrategy.h" #include "NexusMultipliers.h" -void WotlkDungeonNexStrategy::InitTriggers(std::vector &triggers) +void WotlkDungeonNexStrategy::InitTriggers(std::vector& triggers) { // Horde Commander (Alliance N)/Commander Kolurg (Alliance H) // or @@ -43,7 +43,7 @@ void WotlkDungeonNexStrategy::InitTriggers(std::vector &triggers) // TODO: Add frost resist aura for paladins? } -void WotlkDungeonNexStrategy::InitMultipliers(std::vector &multipliers) +void WotlkDungeonNexStrategy::InitMultipliers(std::vector& multipliers) { multipliers.push_back(new FactionCommanderMultiplier(botAI)); multipliers.push_back(new TelestraMultiplier(botAI)); diff --git a/src/Ai/Dungeon/Nexus/Strategy/NexusStrategy.h b/src/Ai/Dungeon/Nexus/Strategy/NexusStrategy.h index c78225b93b..abe3384d29 100644 --- a/src/Ai/Dungeon/Nexus/Strategy/NexusStrategy.h +++ b/src/Ai/Dungeon/Nexus/Strategy/NexusStrategy.h @@ -10,8 +10,8 @@ class WotlkDungeonNexStrategy : public Strategy public: WotlkDungeonNexStrategy(PlayerbotAI* ai) : Strategy(ai) {} virtual std::string const getName() override { return "nexus"; } - virtual void InitTriggers(std::vector &triggers) override; - virtual void InitMultipliers(std::vector &multipliers) override; + virtual void InitTriggers(std::vector& triggers) override; + virtual void InitMultipliers(std::vector& multipliers) override; }; #endif diff --git a/src/Ai/Dungeon/Nexus/Trigger/NexusTriggers.cpp b/src/Ai/Dungeon/Nexus/Trigger/NexusTriggers.cpp index 02be3f70e3..255e471b84 100644 --- a/src/Ai/Dungeon/Nexus/Trigger/NexusTriggers.cpp +++ b/src/Ai/Dungeon/Nexus/Trigger/NexusTriggers.cpp @@ -1,4 +1,6 @@ #include "Playerbots.h" +#include "BotSpellService.h" +#include "BotRoleService.h" #include "NexusTriggers.h" #include "AiObject.h" #include "AiObjectContext.h" @@ -46,7 +48,7 @@ bool FactionCommanderWhirlwindTrigger::IsActive() bool TelestraFirebombTrigger::IsActive() { - if (botAI->IsMelee(bot)) { return false; } + if (BotRoleService::IsMeleeStatic(bot)) { return false; } Unit* boss = AI_VALUE2(Unit*, "find target", "grand magus telestra"); // Avoid split phase with the fake Telestra units, only match the true boss id @@ -69,7 +71,7 @@ bool ChaoticRiftTrigger::IsActive() bool OrmorokSpikesTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper"); - if (!boss || !botAI->IsTank(bot)) { return false; } + if (!boss || !BotRoleService::IsTankStatic(bot)) { return false; } GuidVector objects = AI_VALUE(GuidVector, "closest game objects"); for (auto i = objects.begin(); i != objects.end(); ++i) @@ -86,7 +88,7 @@ bool OrmorokSpikesTrigger::IsActive() bool OrmorokStackTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "ormorok the tree-shaper"); - return (boss && !botAI->IsTank(bot)); + return (boss && !BotRoleService::IsTankStatic(bot)); } bool IntenseColdTrigger::IsActive() @@ -95,13 +97,13 @@ bool IntenseColdTrigger::IsActive() // but too many stacks is deadly. Assuming 3-5 is a good number to clear int stackThreshold = 5; Unit* boss = AI_VALUE2(Unit*, "find target", "keristrasza"); - return boss && botAI->GetAura("intense cold", bot, false, false, stackThreshold); + return boss && botAI->GetServices().GetSpellService().GetAura("intense cold", bot, false, false, stackThreshold); } bool KeristraszaPositioningTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "keristrasza"); // Include healers here for now, otherwise they stand in things - return boss && !botAI->IsTank(bot) && !botAI->IsRangedDps(bot); - // return boss && botAI->IsMelee(bot) && !botAI->IsTank(bot); + return boss && !BotRoleService::IsTankStatic(bot) && !BotRoleService::IsRangedDpsStatic(bot); + // return boss && BotRoleService::IsMeleeStatic(bot) && !BotRoleService::IsTankStatic(bot); } diff --git a/src/Ai/Dungeon/Oculus/Action/OculusActions.cpp b/src/Ai/Dungeon/Oculus/Action/OculusActions.cpp index 414acc4bc9..0530e3f327 100644 --- a/src/Ai/Dungeon/Oculus/Action/OculusActions.cpp +++ b/src/Ai/Dungeon/Oculus/Action/OculusActions.cpp @@ -1,12 +1,15 @@ #include "Playerbots.h" +#include "BotSpellService.h" +#include "BotRoleService.h" #include "OculusActions.h" #include "OculusStrategy.h" #include "LastSpellCastValue.h" -bool AvoidUnstableSphereAction::Execute(Event event) +bool AvoidUnstableSphereAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "drakos the interrogator"); - if (!boss) { return false; } + if (!boss) + return false; float radius = 12.0f; float extraDistance = 1.0f; @@ -34,19 +37,21 @@ bool AvoidUnstableSphereAction::Execute(Event event) } bool MountDrakeAction::isPossible() { return bot->GetMapId() == OCULUS_MAP_ID; } -bool MountDrakeAction::Execute(Event event) +bool MountDrakeAction::Execute(Event /*event*/) { std::map drakeAssignments; // Composition can be adjusted - both 3/1/1 and 2/2/1 are good default comps // {Amber, Emerald, Ruby} std::vector composition = {2, 2, 1}; // std::vector composition = {3, 1, 1}; - int32 myIndex = botAI->GetGroupSlotIndex(bot); + int32 myIndex = botAI->GetServices().GetRoleService().GetGroupSlotIndex(bot); Player* master = botAI->GetMaster(); - if (!master) { return false; } + if (!master) + return false; Unit* vehicle = master->GetVehicleBase(); - if (!vehicle) { return false; } + if (!vehicle) + return false; // Subtract the player's chosen mount type from the composition so player can play whichever they prefer switch (vehicle->GetEntry()) @@ -60,6 +65,8 @@ bool MountDrakeAction::Execute(Event event) case NPC_RUBY_DRAKE: composition[2]--; break; + default: + break; } std::vector players = botAI->GetPlayersInGroup(); @@ -72,7 +79,7 @@ bool MountDrakeAction::Execute(Event event) if (!session || !session->IsBot()) continue; - int slot = botAI->GetGroupSlotIndex(player); + int slot = botAI->GetServices().GetRoleService().GetGroupSlotIndex(player); if (slot < 0) continue; @@ -91,9 +98,10 @@ bool MountDrakeAction::Execute(Event event) for (uint32 itemId : DRAKE_ITEMS) { Item* item = bot->GetItemByEntry(itemId); - if (!item) { continue; } + if (!item) + continue; - if (itemId == drakeAssignments[myIndex]) + if (itemId == static_cast(drakeAssignments[myIndex])) { // Use our assigned drake return UseItemAuto(item); @@ -109,7 +117,7 @@ bool MountDrakeAction::Execute(Event event) return false; } -bool DismountDrakeAction::Execute(Event event) +bool DismountDrakeAction::Execute(Event /*event*/) { if (bot->GetVehicle()) { @@ -119,13 +127,15 @@ bool DismountDrakeAction::Execute(Event event) return false; } -bool OccFlyDrakeAction::Execute(Event event) +bool OccFlyDrakeAction::Execute(Event /*event*/) { Player* master = botAI->GetMaster(); - if (!master) { return false; } + if (!master) + return false; Unit* masterVehicle = master->GetVehicleBase(); Unit* vehicleBase = bot->GetVehicleBase(); - if (!vehicleBase || !masterVehicle) { return false; } + if (!vehicleBase || !masterVehicle) + return false; MotionMaster* mm = vehicleBase->GetMotionMaster(); Unit* boss = AI_VALUE2(Unit*, "find target", "ley-guardian eregos"); @@ -151,7 +161,7 @@ bool OccFlyDrakeAction::Execute(Event event) if (vehicleBase->GetExactDist(masterVehicle) > 20.0f) { // 3/4 of a circle, with frontal cone 90 deg unobstructed - float angle = botAI->GetGroupSlotIndex(bot) * (2*M_PI - M_PI_2)/5 + M_PI_2; + float angle = botAI->GetServices().GetRoleService().GetGroupSlotIndex(bot) * (2*M_PI - M_PI_2)/5 + M_PI_2; vehicleBase->SetCanFly(true); mm->MoveFollow(masterVehicle, 15.0f, angle); vehicleBase->SendMovementFlagUpdate(); @@ -160,10 +170,11 @@ bool OccFlyDrakeAction::Execute(Event event) return false; } -bool OccDrakeAttackAction::Execute(Event event) +bool OccDrakeAttackAction::Execute(Event /*event*/) { vehicleBase = bot->GetVehicleBase(); - if (!vehicleBase) { return false; } + if (!vehicleBase) + return false; Unit* target = AI_VALUE(Unit*, "current target"); @@ -173,14 +184,16 @@ bool OccDrakeAttackAction::Execute(Event event) for (auto& npc : npcs) { Unit* unit = botAI->GetUnit(npc); - if (!unit || !unit->IsInCombat()) { continue; } + if (!unit || !unit->IsInCombat()) + continue; target = unit; break; } } // Check this again to see if a target was assigned - if (!target) { return false; } + if (!target) + return false; switch (vehicleBase->GetEntry()) { @@ -198,8 +211,8 @@ bool OccDrakeAttackAction::Execute(Event event) bool OccDrakeAttackAction::CastDrakeSpellAction(Unit* target, uint32 spellId, uint32 cooldown) { - if (botAI->CanCastVehicleSpell(spellId, target)) - if (botAI->CastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CanCastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CastVehicleSpell(spellId, target)) { vehicleBase->AddSpellCooldown(spellId, 0, cooldown); return true; @@ -259,7 +272,8 @@ bool OccDrakeAttackAction::EmeraldDrakeAction(Unit* target) } Unit* drake = unit->GetVehicleBase(); - if (!drake || drake->IsFullHealth()) { continue; } + if (!drake || drake->IsFullHealth()) + continue; if (!healingTarget || drake->GetHealthPct() < healingTarget->GetHealthPct() - 15.0f) { @@ -319,12 +333,13 @@ bool OccDrakeAttackAction::RubyDrakeAction(Unit* target) return CastDrakeSpellAction(target, SPELL_SEARING_WRATH, 0); } -bool AvoidArcaneExplosionAction::Execute(Event event) +bool AvoidArcaneExplosionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "mage-lord urom"); - if (!boss) { return false; } + if (!boss) + return false; - const Position* closestPos = nullptr; + Position const* closestPos = nullptr; for (auto& position : uromSafePositions) { @@ -334,12 +349,13 @@ bool AvoidArcaneExplosionAction::Execute(Event event) } } - if (!closestPos) { return false; } + if (!closestPos) + return false; return MoveNear(bot->GetMapId(), closestPos->GetPositionX(), closestPos->GetPositionY(), closestPos->GetPositionZ(), 2.0f, MovementPriority::MOVEMENT_COMBAT); } -bool TimeBombSpreadAction::Execute(Event event) +bool TimeBombSpreadAction::Execute(Event /*event*/) { float radius = 10.0f; float distanceExtra = 2.0f; diff --git a/src/Ai/Dungeon/Oculus/Multiplier/OculusMultipliers.cpp b/src/Ai/Dungeon/Oculus/Multiplier/OculusMultipliers.cpp index 7be543ee70..f21ea61ef1 100644 --- a/src/Ai/Dungeon/Oculus/Multiplier/OculusMultipliers.cpp +++ b/src/Ai/Dungeon/Oculus/Multiplier/OculusMultipliers.cpp @@ -1,4 +1,5 @@ #include "OculusMultipliers.h" +#include "BotRoleService.h" #include "OculusActions.h" #include "GenericSpellActions.h" #include "ChooseTargetActions.h" @@ -10,9 +11,9 @@ float MountingDrakeMultiplier::GetValue(Action* action) { // P.I.T.A bug where the bots will somehow interrupt their item spell use, - // even though the 0.5 sec cast goes off, it puts the drake essence on 15 sec cd + // even though the 0.5f sec cast goes off, it puts the drake essence on 15 sec cd // and no drake comes down. - // It seems like this is due to moving/other actions being processed during the 0.5 secs. + // It seems like this is due to moving/other actions being processed during the 0.5f secs. // If we suppress everything, they seem to mount properly. A bit of a ham-fisted solution but it works Player* master = botAI->GetMaster(); if (!master) { return 1.0f; } @@ -63,7 +64,7 @@ float UromMultiplier::GetValue(Action* action) } // Don't bother avoiding Frostbomb for melee - if (botAI->IsMelee(bot)) + if (BotRoleService::IsMeleeStatic(bot)) { if (dynamic_cast(action)) { @@ -103,7 +104,7 @@ float EregosMultiplier::GetValue(Action* action) Unit* boss = AI_VALUE2(Unit*, "find target", "ley-guardian eregos"); if (!boss) { return 1.0f; } - if (boss->HasAura(SPELL_PLANAR_SHIFT && dynamic_cast(action))) + if (boss->HasAura(SPELL_PLANAR_SHIFT) && dynamic_cast(action)) { return 0.0f; } diff --git a/src/Ai/Dungeon/Oculus/Strategy/OculusStrategy.cpp b/src/Ai/Dungeon/Oculus/Strategy/OculusStrategy.cpp index 4c558469ee..2e53716dcb 100644 --- a/src/Ai/Dungeon/Oculus/Strategy/OculusStrategy.cpp +++ b/src/Ai/Dungeon/Oculus/Strategy/OculusStrategy.cpp @@ -1,7 +1,7 @@ #include "OculusStrategy.h" #include "OculusMultipliers.h" -void WotlkDungeonOccStrategy::InitTriggers(std::vector &triggers) +void WotlkDungeonOccStrategy::InitTriggers(std::vector& triggers) { // Drakos the Interrogator // TODO: May need work, TBA. @@ -32,7 +32,7 @@ void WotlkDungeonOccStrategy::InitTriggers(std::vector &triggers) // Ley-Guardian Eregos } -void WotlkDungeonOccStrategy::InitMultipliers(std::vector &multipliers) +void WotlkDungeonOccStrategy::InitMultipliers(std::vector& multipliers) { multipliers.push_back(new MountingDrakeMultiplier(botAI)); multipliers.push_back(new OccFlyingMultiplier(botAI)); diff --git a/src/Ai/Dungeon/Oculus/Strategy/OculusStrategy.h b/src/Ai/Dungeon/Oculus/Strategy/OculusStrategy.h index 2accfd8f4d..3d464008f6 100644 --- a/src/Ai/Dungeon/Oculus/Strategy/OculusStrategy.h +++ b/src/Ai/Dungeon/Oculus/Strategy/OculusStrategy.h @@ -10,8 +10,8 @@ class WotlkDungeonOccStrategy : public Strategy public: WotlkDungeonOccStrategy(PlayerbotAI* ai) : Strategy(ai) {} virtual std::string const getName() override { return "oculus"; } - virtual void InitTriggers(std::vector &triggers) override; - virtual void InitMultipliers(std::vector &multipliers) override; + virtual void InitTriggers(std::vector& triggers) override; + virtual void InitMultipliers(std::vector& multipliers) override; }; #endif diff --git a/src/Ai/Dungeon/Oculus/Trigger/OculusTriggers.cpp b/src/Ai/Dungeon/Oculus/Trigger/OculusTriggers.cpp index 9cc8288f36..7b1200653b 100644 --- a/src/Ai/Dungeon/Oculus/Trigger/OculusTriggers.cpp +++ b/src/Ai/Dungeon/Oculus/Trigger/OculusTriggers.cpp @@ -1,4 +1,5 @@ #include "Playerbots.h" +#include "BotRoleService.h" #include "OculusTriggers.h" #include "AiObject.h" #include "AiObjectContext.h" @@ -8,8 +9,8 @@ bool DrakosUnstableSphereTrigger::IsActive() { // Doesn't seem to be much point trying to get melee to dodge this, // they get hit anyway and it just causes a lot of running around and chaos - // if (botAI->IsMelee(bot)) { return false; } - if (botAI->IsTank(bot)) { return false; } + // if (BotRoleService::IsMeleeStatic(bot)) { return false; } + if (BotRoleService::IsTankStatic(bot)) { return false; } GuidVector targets = AI_VALUE(GuidVector, "nearest hostile npcs"); for (auto& target : targets) diff --git a/src/Ai/Dungeon/Oculus/Trigger/OculusTriggers.h b/src/Ai/Dungeon/Oculus/Trigger/OculusTriggers.h index 8d2d55d167..52bd70d0b5 100644 --- a/src/Ai/Dungeon/Oculus/Trigger/OculusTriggers.h +++ b/src/Ai/Dungeon/Oculus/Trigger/OculusTriggers.h @@ -58,8 +58,8 @@ enum OculusIDs #define SPELL_EMPOWERED_ARCANE_EXPLOSION DUNGEON_MODE(bot, SPELL_EMPOWERED_ARCANE_EXPLOSION_N, SPELL_EMPOWERED_ARCANE_EXPLOSION_H) #define SPELL_TIME_BOMB DUNGEON_MODE(bot, SPELL_TIME_BOMB_N, SPELL_TIME_BOMB_H) -const std::vector DRAKE_ITEMS = {ITEM_AMBER_ESSENCE, ITEM_EMERALD_ESSENCE, ITEM_RUBY_ESSENCE}; -const std::vector DRAKE_SPELLS = {SPELL_AMBER_ESSENCE, SPELL_EMERALD_ESSENCE, SPELL_RUBY_ESSENCE}; +std::vector const DRAKE_ITEMS = {ITEM_AMBER_ESSENCE, ITEM_EMERALD_ESSENCE, ITEM_RUBY_ESSENCE}; +std::vector const DRAKE_SPELLS = {SPELL_AMBER_ESSENCE, SPELL_EMERALD_ESSENCE, SPELL_RUBY_ESSENCE}; const uint32 OCULUS_MAP_ID = 578; // const float uromCoords[4][4] = diff --git a/src/Ai/Dungeon/OldKingdom/Action/OldKingdomActions.cpp b/src/Ai/Dungeon/OldKingdom/Action/OldKingdomActions.cpp index 916bf29200..49589d210f 100644 --- a/src/Ai/Dungeon/OldKingdom/Action/OldKingdomActions.cpp +++ b/src/Ai/Dungeon/OldKingdom/Action/OldKingdomActions.cpp @@ -1,8 +1,9 @@ #include "Playerbots.h" +#include "BotRoleService.h" #include "OldKingdomActions.h" #include "OldKingdomStrategy.h" -bool AttackNadoxGuardianAction::Execute(Event event) +bool AttackNadoxGuardianAction::Execute(Event /*event*/) { Unit* target = AI_VALUE2(Unit*, "find target", "ahn'kahar guardian"); if (!target || AI_VALUE(Unit*, "current target") == target) @@ -12,7 +13,7 @@ bool AttackNadoxGuardianAction::Execute(Event event) return Attack(target); } -bool AttackJedogaVolunteerAction::Execute(Event event) +bool AttackJedogaVolunteerAction::Execute(Event /*event*/) { Unit* target = nullptr; // Target is not findable from threat table using AI_VALUE2(), @@ -40,7 +41,7 @@ bool AttackJedogaVolunteerAction::Execute(Event event) return Attack(target); } -bool AvoidShadowCrashAction::Execute(Event event) +bool AvoidShadowCrashAction::Execute(Event /*event*/) { // Could check all enemy units in range as it's possible to pull multiple of these mobs. // They should really be killed 1 by 1, multipulls are messy so we just handle singles for now @@ -67,7 +68,7 @@ bool AvoidShadowCrashAction::Execute(Event event) } // Otherwise ranged members passively spread, to avoid AoE overlap - if (botAI->IsMelee(bot)) { return false; } + if (BotRoleService::IsMeleeStatic(bot)) { return false; } GuidVector members = AI_VALUE(GuidVector, "group members"); for (auto& member : members) diff --git a/src/Ai/Dungeon/OldKingdom/Strategy/OldKingdomStrategy.cpp b/src/Ai/Dungeon/OldKingdom/Strategy/OldKingdomStrategy.cpp index 484ea6ac48..a7e75bda0b 100644 --- a/src/Ai/Dungeon/OldKingdom/Strategy/OldKingdomStrategy.cpp +++ b/src/Ai/Dungeon/OldKingdom/Strategy/OldKingdomStrategy.cpp @@ -1,7 +1,7 @@ #include "OldKingdomStrategy.h" #include "OldKingdomMultipliers.h" -void WotlkDungeonOKStrategy::InitTriggers(std::vector &triggers) +void WotlkDungeonOKStrategy::InitTriggers(std::vector& triggers) { // Elder Nadox triggers.push_back(new TriggerNode("nadox guardian", @@ -27,7 +27,7 @@ void WotlkDungeonOKStrategy::InitTriggers(std::vector &triggers) // TODO: once I get to heroics } -void WotlkDungeonOKStrategy::InitMultipliers(std::vector &multipliers) +void WotlkDungeonOKStrategy::InitMultipliers(std::vector& multipliers) { multipliers.push_back(new ElderNadoxMultiplier(botAI)); multipliers.push_back(new JedogaShadowseekerMultiplier(botAI)); diff --git a/src/Ai/Dungeon/OldKingdom/Strategy/OldKingdomStrategy.h b/src/Ai/Dungeon/OldKingdom/Strategy/OldKingdomStrategy.h index 75c475559e..37e1749269 100644 --- a/src/Ai/Dungeon/OldKingdom/Strategy/OldKingdomStrategy.h +++ b/src/Ai/Dungeon/OldKingdom/Strategy/OldKingdomStrategy.h @@ -10,8 +10,8 @@ class WotlkDungeonOKStrategy : public Strategy public: WotlkDungeonOKStrategy(PlayerbotAI* ai) : Strategy(ai) {} virtual std::string const getName() override { return "old kingdom"; } - virtual void InitTriggers(std::vector &triggers) override; - virtual void InitMultipliers(std::vector &multipliers) override; + virtual void InitTriggers(std::vector& triggers) override; + virtual void InitMultipliers(std::vector& multipliers) override; }; #endif diff --git a/src/Ai/Dungeon/OldKingdom/Trigger/OldKingdomTriggers.cpp b/src/Ai/Dungeon/OldKingdom/Trigger/OldKingdomTriggers.cpp index 6b72f656a9..2f4a3464f6 100644 --- a/src/Ai/Dungeon/OldKingdom/Trigger/OldKingdomTriggers.cpp +++ b/src/Ai/Dungeon/OldKingdom/Trigger/OldKingdomTriggers.cpp @@ -1,11 +1,12 @@ #include "Playerbots.h" +#include "BotRoleService.h" #include "OldKingdomTriggers.h" #include "AiObject.h" #include "AiObjectContext.h" bool NadoxGuardianTrigger::IsActive() { - if (botAI->IsHeal(bot)) { return false; } + if (BotRoleService::IsHealStatic(bot)) { return false; } Unit* boss = AI_VALUE2(Unit*, "find target", "elder nadox"); Unit* guardian = AI_VALUE2(Unit*, "find target", "ahn'kahar guardian"); @@ -38,5 +39,5 @@ bool ShadowCrashTrigger::IsActive() Unit* unit = AI_VALUE2(Unit*, "find target", "forgotten one"); if (!unit) { return false; } - return !botAI->IsMelee(bot); + return !BotRoleService::IsMeleeStatic(bot); } diff --git a/src/Ai/Dungeon/PitOfSaron/Action/PitOfSaronActions.cpp b/src/Ai/Dungeon/PitOfSaron/Action/PitOfSaronActions.cpp index f34b98462e..02b13df792 100644 --- a/src/Ai/Dungeon/PitOfSaron/Action/PitOfSaronActions.cpp +++ b/src/Ai/Dungeon/PitOfSaron/Action/PitOfSaronActions.cpp @@ -1,9 +1,10 @@ #include "Playerbots.h" +#include "BotRoleService.h" #include "PitOfSaronActions.h" #include "PitOfSaronStrategy.h" #include "SharedDefines.h" -bool IckAndKrickAction::Execute(Event event) +bool IckAndKrickAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "Ick"); if (!boss) @@ -24,10 +25,10 @@ bool IckAndKrickAction::Execute(Event event) } } - bool pursuit = bot->HasAura(SPELL_PURSUIT) || (!botAI->IsTank(bot) && boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_PURSUIT)); + bool pursuit = bot->HasAura(SPELL_PURSUIT) || (!BotRoleService::IsTankStatic(bot) && boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_PURSUIT)); bool poisonNova = boss->HasUnitState(UNIT_STATE_CASTING) && (boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA_POS) || boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA_POS_HC)); - bool explosiveBarrage = orb || boss->HasUnitState(UNIT_STATE_CASTING) && (boss->FindCurrentSpellBySpellId(SPELL_EXPLOSIVE_BARRAGE_ICK) || boss->FindCurrentSpellBySpellId(SPELL_EXPLOSIVE_BARRAGE_KRICK)); - bool isTank = botAI->IsTank(bot); + bool explosiveBarrage = orb || (boss->HasUnitState(UNIT_STATE_CASTING) && (boss->FindCurrentSpellBySpellId(SPELL_EXPLOSIVE_BARRAGE_ICK) || boss->FindCurrentSpellBySpellId(SPELL_EXPLOSIVE_BARRAGE_KRICK))); + bool isTank = BotRoleService::IsTankStatic(bot); if (pursuit && Pursuit(pursuit, boss)) return true; @@ -49,7 +50,7 @@ bool IckAndKrickAction::Execute(Event event) bool IckAndKrickAction::TankPosition(Unit* boss) { - if (botAI->HasAggro(boss)) + if (botAI->GetServices().GetRoleService().HasAggro(boss)) { float distance = bot->GetExactDist2d(ICKANDKRICK_TANK_POSITION.GetPositionX(), ICKANDKRICK_TANK_POSITION.GetPositionY()); if (distance > 7.0f) @@ -75,7 +76,7 @@ bool IckAndKrickAction::TankPosition(Unit* boss) bool IckAndKrickAction::Pursuit(bool pursuit, Unit* boss) { // Only execute this action when pursuit is active and for non-tank players - if (!pursuit || botAI->IsTank(bot)) + if (!pursuit || BotRoleService::IsTankStatic(bot)) return false; // Get the tank position as a reference point @@ -136,7 +137,7 @@ bool IckAndKrickAction::PoisonNova(bool poisonNova, Unit* boss) return false; } -bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss) +bool IckAndKrickAction::ExplosiveBarrage(bool /*explosiveBarrage*/, Unit* boss) { std::vector orbs; Unit* closestOrb = nullptr; @@ -223,7 +224,7 @@ bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss) score += minOrbDist * 2.0f; // Weight orb distance more heavily // Check distance from other players - for (const Position& playerPos : playerPositions) + for (Position const& playerPos : playerPositions) { float playerDist = sqrt(pow(potentialPos.m_positionX - playerPos.m_positionX, 2) + pow(potentialPos.m_positionY - playerPos.m_positionY, 2)); @@ -269,7 +270,7 @@ bool IckAndKrickAction::ExplosiveBarrage(bool explosiveBarrage, Unit* boss) return false; } -bool TyrannusAction::Execute(Event event) +bool TyrannusAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "scourgelord tyrannus"); if (!boss) @@ -277,7 +278,7 @@ bool TyrannusAction::Execute(Event event) bool rangedSpread = false; - if (botAI->IsRanged(bot) && boss->HealthBelowPct(99)) + if (BotRoleService::IsRangedStatic(bot) && boss->HealthBelowPct(99)) rangedSpread = true; if (rangedSpread && RangedSpread(rangedSpread)) @@ -292,7 +293,7 @@ bool TyrannusAction::RangedSpread(bool rangedSpread) float moveIncrement = 3.0f; GuidVector members = AI_VALUE(GuidVector, "group members"); - if (botAI->IsRanged(bot) && rangedSpread) + if (BotRoleService::IsRangedStatic(bot) && rangedSpread) { // Ranged: spread from other members for (auto& member : members) diff --git a/src/Ai/Dungeon/PitOfSaron/Multiplier/PitOfSaronMultipliers.cpp b/src/Ai/Dungeon/PitOfSaron/Multiplier/PitOfSaronMultipliers.cpp index be36e480f2..966c4cd953 100644 --- a/src/Ai/Dungeon/PitOfSaron/Multiplier/PitOfSaronMultipliers.cpp +++ b/src/Ai/Dungeon/PitOfSaron/Multiplier/PitOfSaronMultipliers.cpp @@ -1,4 +1,5 @@ #include "PitOfSaronMultipliers.h" +#include "BotRoleService.h" #include "PitOfSaronActions.h" #include "GenericSpellActions.h" #include "ChooseTargetActions.h" @@ -18,16 +19,16 @@ float IckAndKrickMultiplier::GetValue(Action* action) if (boss->HasUnitState(UNIT_STATE_CASTING) && (boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA_POS) || boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA_POS_HC)) && bot->GetExactDist2d(boss) < 20.0f) return 0.0f; // Cancel all other actions when we need to handle Poison Nova - if (bot->GetExactDist2d(boss) < 15.0f && bot->HasAura(SPELL_PURSUIT) && !botAI->IsTank(bot)) + if (bot->GetExactDist2d(boss) < 15.0f && bot->HasAura(SPELL_PURSUIT) && !BotRoleService::IsTankStatic(bot)) return 0.0f; // Cancel all other actions when we need to handle Pursuit - if (!botAI->IsHeal(bot) && boss->HasUnitState(UNIT_STATE_CASTING) && (boss->FindCurrentSpellBySpellId(SPELL_EXPLOSIVE_BARRAGE_ICK) || boss->FindCurrentSpellBySpellId(SPELL_EXPLOSIVE_BARRAGE_KRICK))) + if (!BotRoleService::IsHealStatic(bot) && boss->HasUnitState(UNIT_STATE_CASTING) && (boss->FindCurrentSpellBySpellId(SPELL_EXPLOSIVE_BARRAGE_ICK) || boss->FindCurrentSpellBySpellId(SPELL_EXPLOSIVE_BARRAGE_KRICK))) return 0.0f; // Cancel all other actions when we need to handle Explosive Barrage return 1.0f; } -float GarfrostMultiplier::GetValue(Action* action) +float GarfrostMultiplier::GetValue(Action* /*action*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "garfrost"); if (!boss) diff --git a/src/Ai/Dungeon/PitOfSaron/Strategy/PitOfSaronStrategy.h b/src/Ai/Dungeon/PitOfSaron/Strategy/PitOfSaronStrategy.h index 4098378d73..969bea3a05 100644 --- a/src/Ai/Dungeon/PitOfSaron/Strategy/PitOfSaronStrategy.h +++ b/src/Ai/Dungeon/PitOfSaron/Strategy/PitOfSaronStrategy.h @@ -8,8 +8,8 @@ class WotlkDungeonPoSStrategy : public Strategy public: WotlkDungeonPoSStrategy(PlayerbotAI* ai) : Strategy(ai) {} std::string const getName() override { return "pit of saron"; } - void InitTriggers(std::vector &triggers) override; - void InitMultipliers(std::vector &multipliers) override; + void InitTriggers(std::vector& triggers) override; + void InitMultipliers(std::vector& multipliers) override; }; diff --git a/src/Ai/Dungeon/TrialOfTheChampion/Action/TrialOfTheChampionActions.cpp b/src/Ai/Dungeon/TrialOfTheChampion/Action/TrialOfTheChampionActions.cpp index 92b687b292..96077c5537 100644 --- a/src/Ai/Dungeon/TrialOfTheChampion/Action/TrialOfTheChampionActions.cpp +++ b/src/Ai/Dungeon/TrialOfTheChampion/Action/TrialOfTheChampionActions.cpp @@ -1,4 +1,5 @@ #include "Playerbots.h" +#include "BotSpellService.h" #include "TrialOfTheChampionActions.h" #include "TrialOfTheChampionStrategy.h" #include "NearestNpcsValue.h" @@ -9,7 +10,7 @@ #include "GenericActions.h" #include -bool ToCLanceAction::Execute(Event event) +bool ToCLanceAction::Execute(Event /*event*/) { // If already has lance equipped, do nothing if (bot->HasItemOrGemWithIdEquipped(ITEM_LANCE, 1)) @@ -58,10 +59,6 @@ bool ToCLanceAction::Execute(Event event) // If we found the lance, equip it if (lanceItem) { - // Store the lance's current position - uint8 srcBag = lanceItem->GetBagSlot(); - uint8 srcSlot = lanceItem->GetSlot(); - // First unequip current weapon if it exists if (oldWeapon) { @@ -84,7 +81,7 @@ bool ToCLanceAction::Execute(Event event) if (!lanceRack->IsWithinDistInMap(bot, INTERACTION_DISTANCE)) return MoveTo(lanceRack, INTERACTION_DISTANCE); - botAI->RemoveShapeshift(); + botAI->GetServices().GetSpellService().RemoveShapeshift(); bot->GetMotionMaster()->Clear(); bot->StopMoving(); lanceRack->Use(bot); @@ -105,7 +102,7 @@ bool ToCUELanceAction::Execute(Event event) return false; } -bool ToCMountedAction::Execute(Event event) +bool ToCMountedAction::Execute(Event /*event*/) { Unit* vehicleBase = bot->GetVehicleBase(); Vehicle* vehicle = bot->GetVehicle(); @@ -132,11 +129,11 @@ bool ToCMountedAction::Execute(Event event) break; } - Aura* defendBot = botAI->GetAura("defend", bot, false, true); + Aura* defendBot = botAI->GetServices().GetSpellService().GetAura("defend", bot, false, true); if (!defendBot || defendBot->GetStackAmount() < 3) { uint32 spellId = AI_VALUE2(uint32, "vehicle spell id", "Defend"); - if (botAI->CanCastVehicleSpell(spellId, target) && botAI->CastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CanCastVehicleSpell(spellId, target) && botAI->GetServices().GetSpellService().CastVehicleSpell(spellId, target)) { vehicleBase->AddSpellCooldown(spellId, 0, 1000); return true; @@ -149,20 +146,20 @@ bool ToCMountedAction::Execute(Event event) if (target->GetDistance2d(bot) > 5.0f) { uint32 spellId = AI_VALUE2(uint32, "vehicle spell id", "Charge"); - if (botAI->CanCastVehicleSpell(spellId, target) && botAI->CastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CanCastVehicleSpell(spellId, target) && botAI->GetServices().GetSpellService().CastVehicleSpell(spellId, target)) { vehicleBase->AddSpellCooldown(spellId, 0, 1000); return true; } } - Aura* defendTarget = botAI->GetAura("defend", target, false, false); + Aura* defendTarget = botAI->GetServices().GetSpellService().GetAura("defend", target, false, false); if (!defendTarget) {} else { uint32 spellId = AI_VALUE2(uint32, "vehicle spell id", "Shield-Breaker"); - if (botAI->CanCastVehicleSpell(spellId, target) && botAI->CastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CanCastVehicleSpell(spellId, target) && botAI->GetServices().GetSpellService().CastVehicleSpell(spellId, target)) { vehicleBase->AddSpellCooldown(spellId, 0, 1000); return true; @@ -170,7 +167,7 @@ bool ToCMountedAction::Execute(Event event) } uint32 spellId = AI_VALUE2(uint32, "vehicle spell id", "Thrust"); - if (botAI->CanCastVehicleSpell(spellId, target) && botAI->CastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CanCastVehicleSpell(spellId, target) && botAI->GetServices().GetSpellService().CastVehicleSpell(spellId, target)) { vehicleBase->AddSpellCooldown(spellId, 0, 1000); return true; @@ -179,7 +176,7 @@ bool ToCMountedAction::Execute(Event event) return false; } -bool ToCMountAction::Execute(Event event) +bool ToCMountAction::Execute(Event /*event*/) { // do not switch vehicles yet if (bot->GetVehicle()) @@ -229,7 +226,7 @@ bool ToCMountAction::EnterVehicle(Unit* vehicleBase, bool moveIfFar) if (dist > INTERACTION_DISTANCE) return MoveTo(vehicleBase); - botAI->RemoveShapeshift(); + botAI->GetServices().GetSpellService().RemoveShapeshift(); bot->GetMotionMaster()->Clear(); bot->StopMoving(); @@ -244,7 +241,7 @@ bool ToCMountAction::EnterVehicle(Unit* vehicleBase, bool moveIfFar) return true; } -bool ToCEadricAction::Execute(Event event) +bool ToCEadricAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "eadric the pure"); if (!boss) diff --git a/src/Ai/Dungeon/TrialOfTheChampion/Strategy/TrialOfTheChampionStrategy.cpp b/src/Ai/Dungeon/TrialOfTheChampion/Strategy/TrialOfTheChampionStrategy.cpp index bbccca71f3..f453b2891d 100644 --- a/src/Ai/Dungeon/TrialOfTheChampion/Strategy/TrialOfTheChampionStrategy.cpp +++ b/src/Ai/Dungeon/TrialOfTheChampion/Strategy/TrialOfTheChampionStrategy.cpp @@ -1,7 +1,7 @@ #include "TrialOfTheChampionStrategy.h" #include "TrialOfTheChampionMultipliers.h" -void WotlkDungeonToCStrategy::InitTriggers(std::vector &triggers) +void WotlkDungeonToCStrategy::InitTriggers(std::vector& triggers) { triggers.push_back(new TriggerNode("toc lance", { NextAction("toc lance", ACTION_RAID + 5) })); @@ -16,6 +16,6 @@ void WotlkDungeonToCStrategy::InitTriggers(std::vector &triggers) } -void WotlkDungeonToCStrategy::InitMultipliers(std::vector &multipliers) +void WotlkDungeonToCStrategy::InitMultipliers(std::vector& /*multipliers*/) { } diff --git a/src/Ai/Dungeon/TrialOfTheChampion/Strategy/TrialOfTheChampionStrategy.h b/src/Ai/Dungeon/TrialOfTheChampion/Strategy/TrialOfTheChampionStrategy.h index 12416d5adc..a343a68dbe 100644 --- a/src/Ai/Dungeon/TrialOfTheChampion/Strategy/TrialOfTheChampionStrategy.h +++ b/src/Ai/Dungeon/TrialOfTheChampion/Strategy/TrialOfTheChampionStrategy.h @@ -11,8 +11,8 @@ class WotlkDungeonToCStrategy : public Strategy public: WotlkDungeonToCStrategy(PlayerbotAI* ai) : Strategy(ai) {} virtual std::string const getName() override { return "trial of the champion"; } - virtual void InitTriggers(std::vector &triggers) override; - virtual void InitMultipliers(std::vector &multipliers) override; + virtual void InitTriggers(std::vector& triggers) override; + virtual void InitMultipliers(std::vector& multipliers) override; }; #endif diff --git a/src/Ai/Dungeon/TrialOfTheChampion/Trigger/TrialOfTheChampionTriggers.h b/src/Ai/Dungeon/TrialOfTheChampion/Trigger/TrialOfTheChampionTriggers.h index 0daaf914a8..9b830ea930 100644 --- a/src/Ai/Dungeon/TrialOfTheChampion/Trigger/TrialOfTheChampionTriggers.h +++ b/src/Ai/Dungeon/TrialOfTheChampion/Trigger/TrialOfTheChampionTriggers.h @@ -73,7 +73,7 @@ enum TocC_IDs }; -const std::vector availableTargets = { +std::vector const availableTargets = { NPC_MOKRA, NPC_ERESSEA, NPC_RUNOK, NPC_ZULTORE, NPC_VISCERI, NPC_AMBROSE, NPC_COLOSOS, NPC_JAELYNE, NPC_LANA, NPC_JACOB, NPC_STORMWIND_MINION, NPC_GNOMEREGAN_MINION, NPC_EXODAR_MINION, NPC_DARNASSUS_MINION, NPC_IRONFORGE_MINION, diff --git a/src/Ai/Dungeon/UtgardeKeep/Action/UtgardeKeepActions.cpp b/src/Ai/Dungeon/UtgardeKeep/Action/UtgardeKeepActions.cpp index 20a5f27b49..724bb1c1db 100644 --- a/src/Ai/Dungeon/UtgardeKeep/Action/UtgardeKeepActions.cpp +++ b/src/Ai/Dungeon/UtgardeKeep/Action/UtgardeKeepActions.cpp @@ -1,9 +1,10 @@ #include "Playerbots.h" +#include "BotRoleService.h" #include "UtgardeKeepActions.h" #include "UtgardeKeepStrategy.h" -bool AttackFrostTombAction::isUseful() { return !botAI->IsHeal(bot); } -bool AttackFrostTombAction::Execute(Event event) +bool AttackFrostTombAction::isUseful() { return !BotRoleService::IsHealStatic(bot); } +bool AttackFrostTombAction::Execute(Event /*event*/) { Unit* frostTomb = nullptr; @@ -28,7 +29,7 @@ bool AttackFrostTombAction::Execute(Event event) } // TODO: Possibly add player stacking behaviour close to tank, to prevent Skarvald charging ranged -bool AttackDalronnAction::Execute(Event event) +bool AttackDalronnAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "dalronn the controller"); if (!boss) { return false; } @@ -40,7 +41,7 @@ bool AttackDalronnAction::Execute(Event event) return Attack(boss); } -bool IngvarStopCastingAction::Execute(Event event) +bool IngvarStopCastingAction::Execute(Event /*event*/) { // Doesn't work, this action gets queued behind the current spell instead of interrupting it Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); @@ -64,7 +65,7 @@ bool IngvarStopCastingAction::Execute(Event event) } bool IngvarDodgeSmashAction::isUseful() { return !AI_VALUE2(bool, "behind", "current target"); } -bool IngvarDodgeSmashAction::Execute(Event event) +bool IngvarDodgeSmashAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); if (!boss) { return false; } @@ -78,7 +79,7 @@ bool IngvarDodgeSmashAction::Execute(Event event) } bool IngvarSmashReturnAction::isUseful() { return AI_VALUE2(bool, "behind", "current target"); } -bool IngvarSmashReturnAction::Execute(Event event) +bool IngvarSmashReturnAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); if (!boss) { return false; } diff --git a/src/Ai/Dungeon/UtgardeKeep/Multiplier/UtgardeKeepMultipliers.cpp b/src/Ai/Dungeon/UtgardeKeep/Multiplier/UtgardeKeepMultipliers.cpp index e9a411dac8..b46e29a238 100644 --- a/src/Ai/Dungeon/UtgardeKeep/Multiplier/UtgardeKeepMultipliers.cpp +++ b/src/Ai/Dungeon/UtgardeKeep/Multiplier/UtgardeKeepMultipliers.cpp @@ -1,4 +1,5 @@ #include "UtgardeKeepMultipliers.h" +#include "BotRoleService.h" #include "UtgardeKeepActions.h" #include "GenericSpellActions.h" #include "ChooseTargetActions.h" @@ -42,7 +43,7 @@ float SkarvaldAndDalronnMultiplier::GetValue(Action* action) float IngvarThePlundererMultiplier::GetValue(Action* action) { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); - bool isTank = botAI->IsTank(bot); + bool isTank = BotRoleService::IsTankStatic(bot); if (!boss) { return 1.0f; } // Prevent movement actions overriding current movement, we're probably dodging a slam diff --git a/src/Ai/Dungeon/UtgardeKeep/Strategy/UtgardeKeepStrategy.cpp b/src/Ai/Dungeon/UtgardeKeep/Strategy/UtgardeKeepStrategy.cpp index 562cb8ec5f..5487cfc76e 100644 --- a/src/Ai/Dungeon/UtgardeKeep/Strategy/UtgardeKeepStrategy.cpp +++ b/src/Ai/Dungeon/UtgardeKeep/Strategy/UtgardeKeepStrategy.cpp @@ -1,7 +1,7 @@ #include "UtgardeKeepStrategy.h" #include "UtgardeKeepMultipliers.h" -void WotlkDungeonUKStrategy::InitTriggers(std::vector &triggers) +void WotlkDungeonUKStrategy::InitTriggers(std::vector& triggers) { // Prince Keleseth triggers.push_back(new TriggerNode("keleseth frost tomb", @@ -34,7 +34,7 @@ void WotlkDungeonUKStrategy::InitTriggers(std::vector &triggers) } -void WotlkDungeonUKStrategy::InitMultipliers(std::vector &multipliers) +void WotlkDungeonUKStrategy::InitMultipliers(std::vector& multipliers) { multipliers.push_back(new PrinceKelesethMultiplier(botAI)); multipliers.push_back(new SkarvaldAndDalronnMultiplier(botAI)); diff --git a/src/Ai/Dungeon/UtgardeKeep/Strategy/UtgardeKeepStrategy.h b/src/Ai/Dungeon/UtgardeKeep/Strategy/UtgardeKeepStrategy.h index 41ce1e76e6..f97cf74ed8 100644 --- a/src/Ai/Dungeon/UtgardeKeep/Strategy/UtgardeKeepStrategy.h +++ b/src/Ai/Dungeon/UtgardeKeep/Strategy/UtgardeKeepStrategy.h @@ -10,8 +10,8 @@ class WotlkDungeonUKStrategy : public Strategy public: WotlkDungeonUKStrategy(PlayerbotAI* ai) : Strategy(ai) {} virtual std::string const getName() override { return "utgarde keep"; } - virtual void InitTriggers(std::vector &triggers) override; - virtual void InitMultipliers(std::vector &multipliers) override; + virtual void InitTriggers(std::vector& triggers) override; + virtual void InitMultipliers(std::vector& multipliers) override; }; #endif diff --git a/src/Ai/Dungeon/UtgardeKeep/Trigger/UtgardeKeepTriggers.cpp b/src/Ai/Dungeon/UtgardeKeep/Trigger/UtgardeKeepTriggers.cpp index e19234bd0e..235a26857c 100644 --- a/src/Ai/Dungeon/UtgardeKeep/Trigger/UtgardeKeepTriggers.cpp +++ b/src/Ai/Dungeon/UtgardeKeep/Trigger/UtgardeKeepTriggers.cpp @@ -1,4 +1,5 @@ #include "Playerbots.h" +#include "BotRoleService.h" #include "UtgardeKeepTriggers.h" #include "AiObject.h" #include "AiObjectContext.h" @@ -23,7 +24,7 @@ bool DalronnDpsTrigger::IsActive() if (!boss || !boss->isTargetableForAttack()) { return false; } // This doesn't cause issues with healers currently and they will continue to heal even when included here - return !botAI->IsTank(bot); + return !BotRoleService::IsTankStatic(bot); } bool IngvarStaggeringRoarTrigger::IsActive() @@ -53,7 +54,7 @@ bool IngvarDreadfulRoarTrigger::IsActive() bool IngvarSmashTankTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); - if (!boss || !botAI->IsTank(bot)) { return false; } + if (!boss || !BotRoleService::IsTankStatic(bot)) { return false; } if (boss->FindCurrentSpellBySpellId(SPELL_SMASH) || boss->FindCurrentSpellBySpellId(SPELL_DARK_SMASH)) @@ -66,10 +67,10 @@ bool IngvarSmashTankTrigger::IsActive() bool IngvarSmashTankReturnTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); - // if (!boss || !botAI->IsTank(bot) || boss->HasUnitState(UNIT_STATE_CASTING)) + // if (!boss || !BotRoleService::IsTankStatic(bot) || boss->HasUnitState(UNIT_STATE_CASTING)) // Ignore casting state as Ingvar will sometimes chain-cast a roar after a smash.. // We don't want this to prevent our tank from repositioning properly. - if (!boss || !botAI->IsTank(bot)) { return false; } + if (!boss || !BotRoleService::IsTankStatic(bot)) { return false; } return true; } @@ -77,7 +78,7 @@ bool IngvarSmashTankReturnTrigger::IsActive() bool NotBehindIngvarTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); - if (!boss || botAI->IsTank(bot)) { return false; } + if (!boss || BotRoleService::IsTankStatic(bot)) { return false; } return AI_VALUE2(bool, "behind", "current target"); } diff --git a/src/Ai/Dungeon/UtgardePinnacle/Action/UtgardePinnacleActions.cpp b/src/Ai/Dungeon/UtgardePinnacle/Action/UtgardePinnacleActions.cpp index f084ca15b6..628969f633 100644 --- a/src/Ai/Dungeon/UtgardePinnacle/Action/UtgardePinnacleActions.cpp +++ b/src/Ai/Dungeon/UtgardePinnacle/Action/UtgardePinnacleActions.cpp @@ -1,8 +1,9 @@ #include "Playerbots.h" +#include "BotRoleService.h" #include "UtgardePinnacleActions.h" #include "UtgardePinnacleStrategy.h" -bool AvoidFreezingCloudAction::Execute(Event event) +bool AvoidFreezingCloudAction::Execute(Event /*event*/) { Unit* closestTrigger = nullptr; GuidVector objects = AI_VALUE(GuidVector, "nearest hostile npcs"); @@ -36,7 +37,7 @@ bool AvoidFreezingCloudAction::Execute(Event event) return false; } -bool AvoidSkadiWhirlwindAction::Execute(Event event) +bool AvoidSkadiWhirlwindAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "skadi the ruthless"); if (!boss) { return false; } @@ -47,7 +48,7 @@ bool AvoidSkadiWhirlwindAction::Execute(Event event) if (distance < radius + distanceExtra) { - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) { // The boss chases tank during this, leads to jittery stutter-stepping // by the tank if we don't pre-move additional range. 2*radius seems ok diff --git a/src/Ai/Dungeon/UtgardePinnacle/Strategy/UtgardePinnacleStrategy.cpp b/src/Ai/Dungeon/UtgardePinnacle/Strategy/UtgardePinnacleStrategy.cpp index fe104f34fb..9355a9a58f 100644 --- a/src/Ai/Dungeon/UtgardePinnacle/Strategy/UtgardePinnacleStrategy.cpp +++ b/src/Ai/Dungeon/UtgardePinnacle/Strategy/UtgardePinnacleStrategy.cpp @@ -1,7 +1,7 @@ #include "UtgardePinnacleStrategy.h" #include "UtgardePinnacleMultipliers.h" -void WotlkDungeonUPStrategy::InitTriggers(std::vector &triggers) +void WotlkDungeonUPStrategy::InitTriggers(std::vector& triggers) { // Svala Sorrowgrave @@ -20,7 +20,7 @@ void WotlkDungeonUPStrategy::InitTriggers(std::vector &triggers) { NextAction("stop attack", ACTION_RAID + 5) })); } -void WotlkDungeonUPStrategy::InitMultipliers(std::vector &multipliers) +void WotlkDungeonUPStrategy::InitMultipliers(std::vector& multipliers) { multipliers.push_back(new SkadiMultiplier(botAI)); multipliers.push_back(new YmironMultiplier(botAI)); diff --git a/src/Ai/Dungeon/UtgardePinnacle/Strategy/UtgardePinnacleStrategy.h b/src/Ai/Dungeon/UtgardePinnacle/Strategy/UtgardePinnacleStrategy.h index 8ce816f808..b6fd3898fe 100644 --- a/src/Ai/Dungeon/UtgardePinnacle/Strategy/UtgardePinnacleStrategy.h +++ b/src/Ai/Dungeon/UtgardePinnacle/Strategy/UtgardePinnacleStrategy.h @@ -10,8 +10,8 @@ class WotlkDungeonUPStrategy : public Strategy public: WotlkDungeonUPStrategy(PlayerbotAI* ai) : Strategy(ai) {} virtual std::string const getName() override { return "utgarde pinnacle"; } - virtual void InitTriggers(std::vector &triggers) override; - virtual void InitMultipliers(std::vector &multipliers) override; + virtual void InitTriggers(std::vector& triggers) override; + virtual void InitMultipliers(std::vector& multipliers) override; }; #endif diff --git a/src/Ai/Dungeon/VioletHold/Action/VioletHoldActions.cpp b/src/Ai/Dungeon/VioletHold/Action/VioletHoldActions.cpp index b590642d8b..558bfe7061 100644 --- a/src/Ai/Dungeon/VioletHold/Action/VioletHoldActions.cpp +++ b/src/Ai/Dungeon/VioletHold/Action/VioletHoldActions.cpp @@ -1,8 +1,9 @@ #include "Playerbots.h" +#include "BotRoleService.h" #include "VioletHoldActions.h" #include "VioletHoldStrategy.h" -bool AttackErekemAction::Execute(Event event) +bool AttackErekemAction::Execute(Event /*event*/) { // Focus boss first, adds after Unit* boss = AI_VALUE2(Unit*, "find target", "erekem"); @@ -15,7 +16,7 @@ bool AttackErekemAction::Execute(Event event) return false; } -bool AttackIchorGlobuleAction::Execute(Event event) +bool AttackIchorGlobuleAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "ichoron"); if (!boss) { return false; } @@ -23,7 +24,7 @@ bool AttackIchorGlobuleAction::Execute(Event event) Unit* currentTarget = AI_VALUE(Unit*, "current target"); // Tank prioritise boss if it's up - if (botAI->IsTank(bot) && !boss->HasAura(SPELL_DRAINED)) + if (BotRoleService::IsTankStatic(bot) && !boss->HasAura(SPELL_DRAINED)) { if (AI_VALUE(Unit*, "current target") != boss) { @@ -60,7 +61,7 @@ bool AttackIchorGlobuleAction::Execute(Event event) return false; } -bool AttackVoidSentryAction::Execute(Event event) +bool AttackVoidSentryAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "zuramat the obliterator"); if (!boss) { return false; } @@ -96,7 +97,7 @@ bool AttackVoidSentryAction::Execute(Event event) return false; } -bool StopAttackAction::Execute(Event event) +bool StopAttackAction::Execute(Event /*event*/) { return bot->AttackStop(); } diff --git a/src/Ai/Dungeon/VioletHold/Multiplier/VioletHoldMultipliers.cpp b/src/Ai/Dungeon/VioletHold/Multiplier/VioletHoldMultipliers.cpp index 3d3a13cd0f..cbc8c6b77b 100644 --- a/src/Ai/Dungeon/VioletHold/Multiplier/VioletHoldMultipliers.cpp +++ b/src/Ai/Dungeon/VioletHold/Multiplier/VioletHoldMultipliers.cpp @@ -1,3 +1,4 @@ +#include "BotRoleService.h" #include "VioletHoldMultipliers.h" #include "VioletHoldActions.h" #include "GenericSpellActions.h" @@ -9,7 +10,7 @@ float ErekemMultiplier::GetValue(Action* action) { Unit* boss = AI_VALUE2(Unit*, "find target", "erekem"); - if (!boss || !botAI->IsDps(bot)) { return 1.0f; } + if (!boss || !BotRoleService::IsDpsStatic(bot)) { return 1.0f; } if (dynamic_cast(action)) { diff --git a/src/Ai/Dungeon/VioletHold/Strategy/VioletHoldStrategy.cpp b/src/Ai/Dungeon/VioletHold/Strategy/VioletHoldStrategy.cpp index ffc00e3063..98bebd20bc 100644 --- a/src/Ai/Dungeon/VioletHold/Strategy/VioletHoldStrategy.cpp +++ b/src/Ai/Dungeon/VioletHold/Strategy/VioletHoldStrategy.cpp @@ -1,7 +1,7 @@ #include "VioletHoldStrategy.h" #include "VioletHoldMultipliers.h" -void WotlkDungeonVHStrategy::InitTriggers(std::vector &triggers) +void WotlkDungeonVHStrategy::InitTriggers(std::vector& triggers) { // Erekem // This boss has many purgable buffs, purging/dispels could be merged into generic strats though @@ -32,7 +32,7 @@ void WotlkDungeonVHStrategy::InitTriggers(std::vector &triggers) { NextAction("rear flank", ACTION_MOVE + 5) })); } -void WotlkDungeonVHStrategy::InitMultipliers(std::vector &multipliers) +void WotlkDungeonVHStrategy::InitMultipliers(std::vector& multipliers) { multipliers.push_back(new ErekemMultiplier(botAI)); multipliers.push_back(new IchoronMultiplier(botAI)); diff --git a/src/Ai/Dungeon/VioletHold/Strategy/VioletHoldStrategy.h b/src/Ai/Dungeon/VioletHold/Strategy/VioletHoldStrategy.h index a06f64ce39..a814f63ae6 100644 --- a/src/Ai/Dungeon/VioletHold/Strategy/VioletHoldStrategy.h +++ b/src/Ai/Dungeon/VioletHold/Strategy/VioletHoldStrategy.h @@ -10,8 +10,8 @@ class WotlkDungeonVHStrategy : public Strategy public: WotlkDungeonVHStrategy(PlayerbotAI* ai) : Strategy(ai) {} virtual std::string const getName() override { return "violet hold"; } - virtual void InitTriggers(std::vector &triggers) override; - virtual void InitMultipliers(std::vector &multipliers) override; + virtual void InitTriggers(std::vector& triggers) override; + virtual void InitMultipliers(std::vector& multipliers) override; }; #endif diff --git a/src/Ai/Dungeon/VioletHold/Trigger/VioletHoldTriggers.cpp b/src/Ai/Dungeon/VioletHold/Trigger/VioletHoldTriggers.cpp index 87284b0e49..e29bb5e8f3 100644 --- a/src/Ai/Dungeon/VioletHold/Trigger/VioletHoldTriggers.cpp +++ b/src/Ai/Dungeon/VioletHold/Trigger/VioletHoldTriggers.cpp @@ -1,4 +1,5 @@ #include "Playerbots.h" +#include "BotRoleService.h" #include "VioletHoldTriggers.h" #include "AiObject.h" #include "AiObjectContext.h" @@ -8,7 +9,7 @@ bool ErekemTargetTrigger::IsActive() Unit* boss = AI_VALUE2(Unit*, "find target", "erekem"); if (!boss) { return false; } - return botAI->IsDps(bot); + return BotRoleService::IsDpsStatic(bot); } bool IchoronTargetTrigger::IsActive() @@ -16,7 +17,7 @@ bool IchoronTargetTrigger::IsActive() Unit* boss = AI_VALUE2(Unit*, "find target", "ichoron"); if (!boss) { return false; } - return !botAI->IsHeal(bot); + return !BotRoleService::IsHealStatic(bot); } bool VoidShiftTrigger::IsActive() @@ -24,7 +25,7 @@ bool VoidShiftTrigger::IsActive() Unit* boss = AI_VALUE2(Unit*, "find target", "zuramat the obliterator"); if (!boss) { return false; } - return bot->HasAura(SPELL_VOID_SHIFTED) && !botAI->IsHeal(bot); + return bot->HasAura(SPELL_VOID_SHIFTED) && !BotRoleService::IsHealStatic(bot); } bool ShroudOfDarknessTrigger::IsActive() @@ -41,6 +42,6 @@ bool CyanigosaPositioningTrigger::IsActive() if (!boss) { return false; } // Include healers here for now, otherwise they stand in things - return !botAI->IsTank(bot) && !botAI->IsRangedDps(bot); - // return botAI->IsMelee(bot) && !botAI->IsTank(bot); + return !BotRoleService::IsTankStatic(bot) && !BotRoleService::IsRangedDpsStatic(bot); + // return BotRoleService::IsMeleeStatic(bot) && !BotRoleService::IsTankStatic(bot); } diff --git a/src/Ai/Raid/Aq20/Action/RaidAq20Actions.cpp b/src/Ai/Raid/Aq20/Action/RaidAq20Actions.cpp index 1bf33147f8..323e875230 100644 --- a/src/Ai/Raid/Aq20/Action/RaidAq20Actions.cpp +++ b/src/Ai/Raid/Aq20/Action/RaidAq20Actions.cpp @@ -3,7 +3,7 @@ #include "Playerbots.h" #include "RaidAq20Utils.h" -bool Aq20UseCrystalAction::Execute(Event event) +bool Aq20UseCrystalAction::Execute(Event /*event*/) { if (Unit* boss = AI_VALUE2(Unit*, "find target", "ossirian the unscarred")) { diff --git a/src/Ai/Raid/Aq20/Strategy/RaidAq20Strategy.h b/src/Ai/Raid/Aq20/Strategy/RaidAq20Strategy.h index 97ff7453a4..3af9110351 100644 --- a/src/Ai/Raid/Aq20/Strategy/RaidAq20Strategy.h +++ b/src/Ai/Raid/Aq20/Strategy/RaidAq20Strategy.h @@ -11,7 +11,7 @@ class RaidAq20Strategy : public Strategy RaidAq20Strategy(PlayerbotAI* ai) : Strategy(ai) {} virtual std::string const getName() override { return "aq20"; } virtual void InitTriggers(std::vector& triggers) override; - // virtual void InitMultipliers(std::vector &multipliers) override; + // virtual void InitMultipliers(std::vector& multipliers) override; }; #endif diff --git a/src/Ai/Raid/BlackwingLair/Action/RaidBwlActions.cpp b/src/Ai/Raid/BlackwingLair/Action/RaidBwlActions.cpp index 54b21791e8..019bd7f67d 100644 --- a/src/Ai/Raid/BlackwingLair/Action/RaidBwlActions.cpp +++ b/src/Ai/Raid/BlackwingLair/Action/RaidBwlActions.cpp @@ -1,8 +1,9 @@ #include "RaidBwlActions.h" #include "Playerbots.h" +#include "BotSpellService.h" -bool BwlOnyxiaScaleCloakAuraCheckAction::Execute(Event event) +bool BwlOnyxiaScaleCloakAuraCheckAction::Execute(Event /*event*/) { bot->AddAura(22683, bot); return true; @@ -10,7 +11,7 @@ bool BwlOnyxiaScaleCloakAuraCheckAction::Execute(Event event) bool BwlOnyxiaScaleCloakAuraCheckAction::isUseful() { return !bot->HasAura(22683); } -bool BwlTurnOffSuppressionDeviceAction::Execute(Event event) +bool BwlTurnOffSuppressionDeviceAction::Execute(Event /*event*/) { GuidVector gos = AI_VALUE(GuidVector, "nearest game objects"); for (GuidVector::iterator i = gos.begin(); i != gos.end(); i++) @@ -29,4 +30,4 @@ bool BwlTurnOffSuppressionDeviceAction::Execute(Event event) return true; } -bool BwlUseHourglassSandAction::Execute(Event event) { return botAI->CastSpell(23645, bot); } +bool BwlUseHourglassSandAction::Execute(Event /*event*/) { return botAI->GetServices().GetSpellService().CastSpell(23645, bot); } diff --git a/src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.h b/src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.h index 27bced8ea4..a73dcf4cb9 100644 --- a/src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.h +++ b/src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.h @@ -12,7 +12,7 @@ class RaidBwlStrategy : public Strategy RaidBwlStrategy(PlayerbotAI* ai) : Strategy(ai) {} virtual std::string const getName() override { return "bwl"; } virtual void InitTriggers(std::vector& triggers) override; - // virtual void InitMultipliers(std::vector &multipliers) override; + // virtual void InitMultipliers(std::vector& multipliers) override; }; #endif \ No newline at end of file diff --git a/src/Ai/Raid/EyeOfEternity/Action/RaidEoEActions.cpp b/src/Ai/Raid/EyeOfEternity/Action/RaidEoEActions.cpp index 3547388426..5b16e0409a 100644 --- a/src/Ai/Raid/EyeOfEternity/Action/RaidEoEActions.cpp +++ b/src/Ai/Raid/EyeOfEternity/Action/RaidEoEActions.cpp @@ -1,8 +1,10 @@ +#include "BotSpellService.h" +#include "BotRoleService.h" #include "Playerbots.h" #include "RaidEoEActions.h" #include "RaidEoETriggers.h" -bool MalygosPositionAction::Execute(Event event) +bool MalygosPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "malygos"); if (!boss) { return false; } @@ -27,7 +29,7 @@ bool MalygosPositionAction::Execute(Event event) } // Position tank - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) { if (bot->GetDistance2d(MALYGOS_MAINTANK_POSITION.first, MALYGOS_MAINTANK_POSITION.second) > distance) { @@ -65,7 +67,7 @@ bool MalygosPositionAction::Execute(Event event) return false; } -bool MalygosTargetAction::Execute(Event event) +bool MalygosTargetAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "malygos"); if (!boss) { return false; } @@ -74,7 +76,7 @@ bool MalygosTargetAction::Execute(Event event) if (phase == 1) { - if (botAI->IsHeal(bot)) { return false; } + if (BotRoleService::IsHealStatic(bot)) { return false; } // Init this as boss by default, if no better target is found just fall back to Malygos Unit* newTarget = boss; @@ -91,7 +93,7 @@ bool MalygosTargetAction::Execute(Event event) // } // } - // if (spark && botAI->IsRangedDps(bot)) + // if (spark && BotRoleService::IsRangedDpsStatic(bot)) // { // newTarget = spark; // } @@ -105,7 +107,7 @@ bool MalygosTargetAction::Execute(Event event) } else if (phase == 2) { - if (botAI->IsHeal(bot)) { return false; } + if (BotRoleService::IsHealStatic(bot)) { return false; } Unit* newTarget = nullptr; Unit* nexusLord = nullptr; @@ -127,7 +129,7 @@ bool MalygosTargetAction::Execute(Event event) } } - if (botAI->IsRangedDps(bot) && scionOfEternity) + if (BotRoleService::IsRangedDpsStatic(bot) && scionOfEternity) { newTarget = scionOfEternity; } @@ -171,7 +173,7 @@ bool MalygosTargetAction::Execute(Event event) // if (spark->GetDistance2d(MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second) > 3.0f) // { // bot->Yell("GRIPPING SPARK", LANG_UNIVERSAL); -// return botAI->CastSpell("death grip", spark); +// return botAI->GetServices().GetSpellService().CastSpell("death grip", spark); // } // return false; @@ -192,7 +194,7 @@ bool MalygosTargetAction::Execute(Event event) // } // } -// return botAI->CanCastSpell(spell, spark); +// return botAI->GetServices().GetSpellService().CanCastSpell(spell, spark); // } // bool PullPowerSparkAction::isUseful() @@ -229,7 +231,7 @@ bool EoEFlyDrakeAction::isPossible() Unit* vehicleBase = bot->GetVehicleBase(); return (vehicleBase && vehicleBase->GetEntry() == NPC_WYRMREST_SKYTALON); } -bool EoEFlyDrakeAction::Execute(Event event) +bool EoEFlyDrakeAction::Execute(Event /*event*/) { Player* master = botAI->GetMaster(); if (!master) { return false; } @@ -263,7 +265,7 @@ bool EoEFlyDrakeAction::Execute(Event event) uint8 numPlayers; bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL ? numPlayers = 25 : numPlayers = 10; // 3/4 of a circle, with frontal cone 90 deg unobstructed - float angle = botAI->GetGroupSlotIndex(bot) * (2*M_PI - M_PI_2)/numPlayers + M_PI_2; + float angle = botAI->GetServices().GetRoleService().GetGroupSlotIndex(bot) * (2*M_PI - M_PI_2)/numPlayers + M_PI_2; // float angle = M_PI; vehicleBase->SetCanFly(true); mm->MoveFollow(masterVehicle, 3.0f, angle); @@ -279,7 +281,7 @@ bool EoEDrakeAttackAction::isPossible() return (vehicleBase && vehicleBase->GetEntry() == NPC_WYRMREST_SKYTALON); } -bool EoEDrakeAttackAction::Execute(Event event) +bool EoEDrakeAttackAction::Execute(Event /*event*/) { vehicleBase = bot->GetVehicleBase(); if (!vehicleBase) @@ -353,8 +355,8 @@ bool EoEDrakeAttackAction::Execute(Event event) bool EoEDrakeAttackAction::CastDrakeSpellAction(Unit* target, uint32 spellId, uint32 cooldown) { - if (botAI->CanCastVehicleSpell(spellId, target)) - if (botAI->CastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CanCastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CastVehicleSpell(spellId, target)) { vehicleBase->AddSpellCooldown(spellId, 0, cooldown); return true; @@ -367,8 +369,6 @@ bool EoEDrakeAttackAction::DrakeDpsAction(Unit* target) Unit* vehicleBase = bot->GetVehicleBase(); if (!vehicleBase) { return false; } - Vehicle* veh = bot->GetVehicle(); - uint8 comboPoints = vehicleBase->GetComboPoints(target); if (comboPoints >= 2) { @@ -396,9 +396,9 @@ bool EoEDrakeAttackAction::DrakeHealAction() else { // "Revivify" may be bugged server-side: - // "botAI->CanCastVehicleSpell()" returns SPELL_FAILED_BAD_TARGETS when targeting drakes. + // "botAI->GetServices().GetSpellService().CanCastVehicleSpell()" returns SPELL_FAILED_BAD_TARGETS when targeting drakes. // Forcing the cast attempt seems to succeed, not sure what's going on here. // return CastDrakeSpellAction(target, SPELL_REVIVIFY, 0); - return botAI->CastVehicleSpell(SPELL_REVIVIFY, vehicleBase); + return botAI->GetServices().GetSpellService().CastVehicleSpell(SPELL_REVIVIFY, vehicleBase); } } diff --git a/src/Ai/Raid/EyeOfEternity/Action/RaidEoEActions.h b/src/Ai/Raid/EyeOfEternity/Action/RaidEoEActions.h index c6fe064c05..f45e37351d 100644 --- a/src/Ai/Raid/EyeOfEternity/Action/RaidEoEActions.h +++ b/src/Ai/Raid/EyeOfEternity/Action/RaidEoEActions.h @@ -7,8 +7,8 @@ #include "PlayerbotAI.h" #include "Playerbots.h" -const std::pair MALYGOS_MAINTANK_POSITION = {757.0f, 1337.0f}; -const std::pair MALYGOS_STACK_POSITION = {755.0f, 1301.0f}; +std::pair const MALYGOS_MAINTANK_POSITION = {757.0f, 1337.0f}; +std::pair const MALYGOS_STACK_POSITION = {755.0f, 1301.0f}; class MalygosPositionAction : public MovementAction { diff --git a/src/Ai/Raid/EyeOfEternity/Multiplier/RaidEoEMultipliers.cpp b/src/Ai/Raid/EyeOfEternity/Multiplier/RaidEoEMultipliers.cpp index a2d8a3f1df..a03ed3e94b 100644 --- a/src/Ai/Raid/EyeOfEternity/Multiplier/RaidEoEMultipliers.cpp +++ b/src/Ai/Raid/EyeOfEternity/Multiplier/RaidEoEMultipliers.cpp @@ -1,3 +1,4 @@ +#include "BotRoleService.h" #include "RaidEoEMultipliers.h" #include "ChooseTargetActions.h" @@ -29,17 +30,17 @@ float MalygosMultiplier::GetValue(Action* action) return 0.0f; } - if (botAI->IsDps(bot) && dynamic_cast(action)) + if (BotRoleService::IsDpsStatic(bot) && dynamic_cast(action)) { return 0.0f; } - if (botAI->IsRangedDps(bot) && dynamic_cast(action)) + if (BotRoleService::IsRangedDpsStatic(bot) && dynamic_cast(action)) { return 0.0f; } - if (!botAI->IsMainTank(bot) && dynamic_cast(action)) + if (!BotRoleService::IsMainTankStatic(bot) && dynamic_cast(action)) { return 0.0f; } @@ -51,7 +52,7 @@ float MalygosMultiplier::GetValue(Action* action) } else if (phase == 2) { - if (botAI->IsDps(bot) && dynamic_cast(action)) + if (BotRoleService::IsDpsStatic(bot) && dynamic_cast(action)) { return 0.0f; } diff --git a/src/Ai/Raid/EyeOfEternity/Strategy/RaidEoEStrategy.cpp b/src/Ai/Raid/EyeOfEternity/Strategy/RaidEoEStrategy.cpp index 3c0ff7ffd8..482e9e567c 100644 --- a/src/Ai/Raid/EyeOfEternity/Strategy/RaidEoEStrategy.cpp +++ b/src/Ai/Raid/EyeOfEternity/Strategy/RaidEoEStrategy.cpp @@ -15,7 +15,7 @@ void RaidEoEStrategy::InitTriggers(std::vector& triggers) { NextAction("eoe drake attack", ACTION_NORMAL + 5) })); } -void RaidEoEStrategy::InitMultipliers(std::vector &multipliers) +void RaidEoEStrategy::InitMultipliers(std::vector& multipliers) { multipliers.push_back(new MalygosMultiplier(botAI)); } diff --git a/src/Ai/Raid/EyeOfEternity/Strategy/RaidEoEStrategy.h b/src/Ai/Raid/EyeOfEternity/Strategy/RaidEoEStrategy.h index eb7a147bd6..5c96d36a33 100644 --- a/src/Ai/Raid/EyeOfEternity/Strategy/RaidEoEStrategy.h +++ b/src/Ai/Raid/EyeOfEternity/Strategy/RaidEoEStrategy.h @@ -10,8 +10,8 @@ class RaidEoEStrategy : public Strategy public: RaidEoEStrategy(PlayerbotAI* ai) : Strategy(ai) {} virtual std::string const getName() override { return "wotlk-eoe"; } - virtual void InitTriggers(std::vector &triggers) override; - virtual void InitMultipliers(std::vector &multipliers) override; + virtual void InitTriggers(std::vector& triggers) override; + virtual void InitMultipliers(std::vector& multipliers) override; }; #endif diff --git a/src/Ai/Raid/GruulsLair/Action/RaidGruulsLairActions.cpp b/src/Ai/Raid/GruulsLair/Action/RaidGruulsLairActions.cpp index 1a98135cac..44b5ef8e25 100644 --- a/src/Ai/Raid/GruulsLair/Action/RaidGruulsLairActions.cpp +++ b/src/Ai/Raid/GruulsLair/Action/RaidGruulsLairActions.cpp @@ -1,4 +1,6 @@ +#include "BotSpellService.h" #include "RaidGruulsLairActions.h" +#include "BotRoleService.h" #include "RaidGruulsLairHelpers.h" #include "CreatureAI.h" #include "Playerbots.h" @@ -9,7 +11,7 @@ using namespace GruulsLairHelpers; // High King Maulgar Actions // Main tank on Maulgar -bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event) +bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event /*event*/) { Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); @@ -21,7 +23,7 @@ bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event) if (maulgar->GetVictim() == bot) { - const Location& tankPosition = GruulsLairLocations::MaulgarTankPosition; + Location const& tankPosition = GruulsLairLocations::MaulgarTankPosition; const float maxDistance = 3.0f; float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y); @@ -52,7 +54,7 @@ bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event) } // First offtank on Olm -bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event) +bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event /*event*/) { Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner"); @@ -64,7 +66,7 @@ bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event) if (olm->GetVictim() == bot) { - const Location& tankPosition = GruulsLairLocations::OlmTankPosition; + Location const& tankPosition = GruulsLairLocations::OlmTankPosition; const float maxDistance = 3.0f; const float olmTankLeeway = 30.0f; @@ -92,7 +94,7 @@ bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event) } // Second offtank on Blindeye -bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event) +bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event /*event*/) { Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); @@ -104,7 +106,7 @@ bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event) if (blindeye->GetVictim() == bot) { - const Location& tankPosition = GruulsLairLocations::BlindeyeTankPosition; + Location const& tankPosition = GruulsLairLocations::BlindeyeTankPosition; const float maxDistance = 3.0f; float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y); @@ -135,18 +137,18 @@ bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event) } // Mage with highest max HP on Krosh -bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event) +bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event /*event*/) { Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); MarkTargetWithTriangle(bot, krosh); SetRtiTarget(botAI, "triangle", krosh); - if (krosh->HasAura(SPELL_SPELL_SHIELD) && botAI->CanCastSpell("spellsteal", krosh)) - return botAI->CastSpell("spellsteal", krosh); + if (krosh->HasAura(SPELL_SPELL_SHIELD) && botAI->GetServices().GetSpellService().CanCastSpell("spellsteal", krosh)) + return botAI->GetServices().GetSpellService().CastSpell("spellsteal", krosh); - if (!bot->HasAura(SPELL_SPELL_SHIELD) && botAI->CanCastSpell("fire ward", bot)) - return botAI->CastSpell("fire ward", bot); + if (!bot->HasAura(SPELL_SPELL_SHIELD) && botAI->GetServices().GetSpellService().CanCastSpell("fire ward", bot)) + return botAI->GetServices().GetSpellService().CastSpell("fire ward", bot); if (bot->GetTarget() != krosh->GetGUID()) { @@ -156,7 +158,7 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event) if (krosh->GetVictim() == bot) { - const Location& tankPosition = GruulsLairLocations::KroshTankPosition; + Location const& tankPosition = GruulsLairLocations::KroshTankPosition; float distanceToKrosh = krosh->GetExactDist2d(tankPosition.x, tankPosition.y); const float minDistance = 16.0f; const float maxDistance = 29.0f; @@ -189,7 +191,7 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event) } // Moonkin with highest max HP on Kiggler -bool HighKingMaulgarMoonkinTankAttackKigglerAction::Execute(Event event) +bool HighKingMaulgarMoonkinTankAttackKigglerAction::Execute(Event /*event*/) { Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed"); @@ -212,7 +214,7 @@ bool HighKingMaulgarMoonkinTankAttackKigglerAction::Execute(Event event) return false; } -bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event event) +bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event /*event*/) { // Target priority 1: Blindeye Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); @@ -264,7 +266,7 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event event) // Target priority 3a: Krosh (ranged only) Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); - if (krosh && krosh->IsAlive() && botAI->IsRanged(bot)) + if (krosh && krosh->IsAlive() && BotRoleService::IsRangedStatic(bot)) { Position safePos; if (TryGetNewSafePosition(botAI, bot, safePos)) @@ -336,9 +338,9 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event event) } // Avoid Whirlwind and Blast Wave and generally try to stay near the center of the room -bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event) +bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event /*event*/) { - const Location& fightCenter = GruulsLairLocations::MaulgarRoomCenter; + Location const& fightCenter = GruulsLairLocations::MaulgarRoomCenter; const float maxDistanceFromFight = 50.0f; float distToFight = bot->GetExactDist2d(fightCenter.x, fightCenter.y); @@ -370,7 +372,7 @@ bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event) } // Run away from Maulgar during Whirlwind (logic for after all other ogres are dead) -bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event event) +bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event /*event*/) { Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); @@ -403,13 +405,13 @@ bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event event) return false; } -bool HighKingMaulgarBanishFelstalkerAction::Execute(Event event) +bool HighKingMaulgarBanishFelstalkerAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) return false; - const GuidVector& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); std::vector felStalkers; for (auto const& npc : npcs) { @@ -436,11 +438,11 @@ bool HighKingMaulgarBanishFelstalkerAction::Execute(Event event) } } - if (warlockIndex >= 0 && warlockIndex < felStalkers.size()) + if (warlockIndex >= 0 && static_cast(warlockIndex) < felStalkers.size()) { Unit* assignedFelStalker = felStalkers[warlockIndex]; - if (!assignedFelStalker->HasAura(SPELL_BANISH) && botAI->CanCastSpell(SPELL_BANISH, assignedFelStalker, true)) - return botAI->CastSpell("banish", assignedFelStalker); + if (!assignedFelStalker->HasAura(SPELL_BANISH) && botAI->GetServices().GetSpellService().CanCastSpell(SPELL_BANISH, assignedFelStalker, true)) + return botAI->GetServices().GetSpellService().CastSpell("banish", assignedFelStalker); } return false; @@ -448,7 +450,7 @@ bool HighKingMaulgarBanishFelstalkerAction::Execute(Event event) // Hunter 1: Misdirect Olm to first offtank and have pet attack Blindeye // Hunter 2: Misdirect Blindeye to second offtank -bool HighKingMaulgarMisdirectOlmAndBlindeyeAction::Execute(Event event) +bool HighKingMaulgarMisdirectOlmAndBlindeyeAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) @@ -482,14 +484,14 @@ bool HighKingMaulgarMisdirectOlmAndBlindeyeAction::Execute(Event event) Player* member = ref->GetSource(); if (!member || !member->IsAlive()) continue; - else if (botAI->IsAssistTankOfIndex(member, 0)) olmTank = member; - else if (botAI->IsAssistTankOfIndex(member, 1)) blindeyeTank = member; + else if (BotRoleService::IsAssistTankOfIndexStatic(member, 0)) olmTank = member; + else if (BotRoleService::IsAssistTankOfIndexStatic(member, 1)) blindeyeTank = member; } switch (hunterIndex) { case 0: - botAI->CastSpell("misdirection", olmTank); + botAI->GetServices().GetSpellService().CastSpell("misdirection", olmTank); if (bot->HasAura(SPELL_MISDIRECTION)) { Pet* pet = bot->GetPet(); @@ -508,14 +510,14 @@ bool HighKingMaulgarMisdirectOlmAndBlindeyeAction::Execute(Event event) } pet->ToCreature()->AI()->AttackStart(blindeye); } - return botAI->CastSpell("steady shot", olm); + return botAI->GetServices().GetSpellService().CastSpell("steady shot", olm); } break; case 1: - botAI->CastSpell("misdirection", blindeyeTank); + botAI->GetServices().GetSpellService().CastSpell("misdirection", blindeyeTank); if (bot->HasAura(SPELL_MISDIRECTION)) - return botAI->CastSpell("steady shot", blindeye); + return botAI->GetServices().GetSpellService().CastSpell("steady shot", blindeye); break; default: @@ -528,7 +530,7 @@ bool HighKingMaulgarMisdirectOlmAndBlindeyeAction::Execute(Event event) // Gruul the Dragonkiller Actions // Position in center of the room -bool GruulTheDragonkillerMainTankPositionBossAction::Execute(Event event) +bool GruulTheDragonkillerMainTankPositionBossAction::Execute(Event /*event*/) { Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); @@ -537,7 +539,7 @@ bool GruulTheDragonkillerMainTankPositionBossAction::Execute(Event event) if (gruul->GetVictim() == bot) { - const Location& tankPosition = GruulsLairLocations::GruulTankPosition; + Location const& tankPosition = GruulsLairLocations::GruulTankPosition; const float maxDistance = 3.0f; float dX = tankPosition.x - bot->GetPositionX(); @@ -546,7 +548,6 @@ bool GruulTheDragonkillerMainTankPositionBossAction::Execute(Event event) if (distanceToTankPosition > maxDistance) { - float step = std::min(maxDistance, distanceToTankPosition); float moveX = bot->GetPositionX() + (dX / distanceToTankPosition) * maxDistance; float moveY = bot->GetPositionY() + (dY / distanceToTankPosition) * maxDistance; const float moveZ = tankPosition.z; @@ -569,7 +570,7 @@ bool GruulTheDragonkillerMainTankPositionBossAction::Execute(Event event) // Ranged will take initial positions around the middle of the room, 25-40 yards from center // Ranged should spread out 10 yards from each other -bool GruulTheDragonkillerSpreadRangedAction::Execute(Event event) +bool GruulTheDragonkillerSpreadRangedAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) @@ -585,7 +586,7 @@ bool GruulTheDragonkillerSpreadRangedAction::Execute(Event event) hasReachedInitialPosition.clear(); } - const Location& tankPosition = GruulsLairLocations::GruulTankPosition; + Location const& tankPosition = GruulsLairLocations::GruulTankPosition; const float centerX = tankPosition.x; const float centerY = tankPosition.y; float centerZ = bot->GetPositionZ(); @@ -662,7 +663,7 @@ bool GruulTheDragonkillerSpreadRangedAction::Execute(Event event) } // Try to get away from other group members when Ground Slam is cast -bool GruulTheDragonkillerShatterSpreadAction::Execute(Event event) +bool GruulTheDragonkillerShatterSpreadAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) diff --git a/src/Ai/Raid/GruulsLair/Multiplier/RaidGruulsLairMultipliers.cpp b/src/Ai/Raid/GruulsLair/Multiplier/RaidGruulsLairMultipliers.cpp index 5ca2de9327..c739260edd 100644 --- a/src/Ai/Raid/GruulsLair/Multiplier/RaidGruulsLairMultipliers.cpp +++ b/src/Ai/Raid/GruulsLair/Multiplier/RaidGruulsLairMultipliers.cpp @@ -1,4 +1,5 @@ #include "RaidGruulsLairMultipliers.h" +#include "BotRoleService.h" #include "RaidGruulsLairActions.h" #include "RaidGruulsLairHelpers.h" #include "ChooseTargetActions.h" @@ -80,7 +81,7 @@ float GruulTheDragonkillerMainTankMovementMultiplier::GetValue(Action* action) if (!gruul) return 1.0f; - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) { if (gruul->GetVictim() == bot && dynamic_cast(action)) return 0.0f; diff --git a/src/Ai/Raid/GruulsLair/Strategy/RaidGruulsLairStrategy.h b/src/Ai/Raid/GruulsLair/Strategy/RaidGruulsLairStrategy.h index ba6f33f076..b18426e93c 100644 --- a/src/Ai/Raid/GruulsLair/Strategy/RaidGruulsLairStrategy.h +++ b/src/Ai/Raid/GruulsLair/Strategy/RaidGruulsLairStrategy.h @@ -12,7 +12,7 @@ class RaidGruulsLairStrategy : public Strategy std::string const getName() override { return "gruulslair"; } void InitTriggers(std::vector& triggers) override; - void InitMultipliers(std::vector &multipliers) override; + void InitMultipliers(std::vector& multipliers) override; }; #endif diff --git a/src/Ai/Raid/GruulsLair/Trigger/RaidGruulsLairTriggers.cpp b/src/Ai/Raid/GruulsLair/Trigger/RaidGruulsLairTriggers.cpp index 35d9f9a1da..a45114e5e6 100644 --- a/src/Ai/Raid/GruulsLair/Trigger/RaidGruulsLairTriggers.cpp +++ b/src/Ai/Raid/GruulsLair/Trigger/RaidGruulsLairTriggers.cpp @@ -1,4 +1,6 @@ +#include "BotSpellService.h" #include "RaidGruulsLairTriggers.h" +#include "BotRoleService.h" #include "RaidGruulsLairHelpers.h" #include "Playerbots.h" @@ -10,21 +12,21 @@ bool HighKingMaulgarIsMainTankTrigger::IsActive() { Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); - return botAI->IsMainTank(bot) && maulgar && maulgar->IsAlive(); + return BotRoleService::IsMainTankStatic(bot) && maulgar && maulgar->IsAlive(); } bool HighKingMaulgarIsFirstAssistTankTrigger::IsActive() { Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner"); - return botAI->IsAssistTankOfIndex(bot, 0) && olm && olm->IsAlive(); + return BotRoleService::IsAssistTankOfIndexStatic(bot, 0) && olm && olm->IsAlive(); } bool HighKingMaulgarIsSecondAssistTankTrigger::IsActive() { Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); - return botAI->IsAssistTankOfIndex(bot, 1) && blindeye && blindeye->IsAlive(); + return BotRoleService::IsAssistTankOfIndexStatic(bot, 1) && blindeye && blindeye->IsAlive(); } bool HighKingMaulgarIsMageTankTrigger::IsActive() @@ -49,17 +51,17 @@ bool HighKingMaulgarDeterminingKillOrderTrigger::IsActive() Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); - return (botAI->IsDps(bot) || botAI->IsTank(bot)) && - !(botAI->IsMainTank(bot) && maulgar && maulgar->IsAlive()) && - !(botAI->IsAssistTankOfIndex(bot, 0) && olm && olm->IsAlive()) && - !(botAI->IsAssistTankOfIndex(bot, 1) && blindeye && blindeye->IsAlive()) && + return (BotRoleService::IsDpsStatic(bot) || BotRoleService::IsTankStatic(bot)) && + !(BotRoleService::IsMainTankStatic(bot) && maulgar && maulgar->IsAlive()) && + !(BotRoleService::IsAssistTankOfIndexStatic(bot, 0) && olm && olm->IsAlive()) && + !(BotRoleService::IsAssistTankOfIndexStatic(bot, 1) && blindeye && blindeye->IsAlive()) && !(IsKroshMageTank(botAI, bot) && krosh && krosh->IsAlive()) && !(IsKigglerMoonkinTank(botAI, bot) && kiggler && kiggler->IsAlive()); } bool HighKingMaulgarHealerInDangerTrigger::IsActive() { - return botAI->IsHeal(bot) && IsAnyOgreBossAlive(botAI); + return BotRoleService::IsHealStatic(bot) && IsAnyOgreBossAlive(botAI); } bool HighKingMaulgarBossChannelingWhirlwindTrigger::IsActive() @@ -67,7 +69,7 @@ bool HighKingMaulgarBossChannelingWhirlwindTrigger::IsActive() Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); return maulgar && maulgar->IsAlive() && maulgar->HasAura(SPELL_WHIRLWIND) && - !botAI->IsMainTank(bot); + !BotRoleService::IsMainTankStatic(bot); } bool HighKingMaulgarWildFelstalkerSpawnedTrigger::IsActive() @@ -113,19 +115,19 @@ bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive() Player* member = ref->GetSource(); if (!member || !member->IsAlive()) continue; - else if (botAI->IsAssistTankOfIndex(member, 0)) olmTank = member; - else if (botAI->IsAssistTankOfIndex(member, 1)) blindeyeTank = member; + else if (BotRoleService::IsAssistTankOfIndexStatic(member, 0)) olmTank = member; + else if (BotRoleService::IsAssistTankOfIndexStatic(member, 1)) blindeyeTank = member; } switch (hunterIndex) { case 0: return olm && olm->IsAlive() && olm->GetHealthPct() > 98.0f && - olmTank && olmTank->IsAlive() && botAI->CanCastSpell("misdirection", olmTank); + olmTank && olmTank->IsAlive() && botAI->GetServices().GetSpellService().CanCastSpell("misdirection", olmTank); case 1: return blindeye && blindeye->IsAlive() && blindeye->GetHealthPct() > 90.0f && - blindeyeTank && blindeyeTank->IsAlive() && botAI->CanCastSpell("misdirection", blindeyeTank); + blindeyeTank && blindeyeTank->IsAlive() && botAI->GetServices().GetSpellService().CanCastSpell("misdirection", blindeyeTank); default: break; @@ -140,14 +142,14 @@ bool GruulTheDragonkillerBossEngagedByMainTankTrigger::IsActive() { Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); - return gruul && gruul->IsAlive() && botAI->IsMainTank(bot); + return gruul && gruul->IsAlive() && BotRoleService::IsMainTankStatic(bot); } bool GruulTheDragonkillerBossEngagedByRangeTrigger::IsActive() { Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); - return gruul && gruul->IsAlive() && botAI->IsRanged(bot); + return gruul && gruul->IsAlive() && BotRoleService::IsRangedStatic(bot); } bool GruulTheDragonkillerIncomingShatterTrigger::IsActive() diff --git a/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.cpp b/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.cpp index 0c8a23a19c..274c6a28fc 100644 --- a/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.cpp +++ b/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.cpp @@ -1,4 +1,5 @@ #include "RaidGruulsLairHelpers.h" +#include "BotRoleService.h" #include "AiFactory.h" #include "GroupReference.h" #include "Playerbots.h" @@ -80,7 +81,7 @@ namespace GruulsLairHelpers MarkTargetWithIcon(bot, target, RtiTargetValue::triangleIndex); } - void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target) + void SetRtiTarget(PlayerbotAI* botAI, std::string const& rtiName, Unit* target) { if (!target) return; @@ -95,7 +96,7 @@ namespace GruulsLairHelpers } } - bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot) + bool IsKroshMageTank(PlayerbotAI* /*botAI*/, Player* bot) { Group* group = bot->GetGroup(); if (!group) @@ -123,7 +124,7 @@ namespace GruulsLairHelpers return highestHpMage == bot; } - bool IsKigglerMoonkinTank(PlayerbotAI* botAI, Player* bot) + bool IsKigglerMoonkinTank(PlayerbotAI* /*botAI*/, Player* bot) { Group* group = bot->GetGroup(); if (!group) @@ -171,7 +172,7 @@ namespace GruulsLairHelpers } Unit* maulgar = botAI->GetAiObjectContext()->GetValue("find target", "high king maulgar")->Get(); - if (botAI->IsRanged(bot) && maulgar && maulgar->IsAlive()) + if (BotRoleService::IsRangedStatic(bot) && maulgar && maulgar->IsAlive()) { float dist = sqrt(pow(pos.GetPositionX() - maulgar->GetPositionX(), 2) + pow(pos.GetPositionY() - maulgar->GetPositionY(), 2)); if (dist < MAULGAR_SAFE_DISTANCE) diff --git a/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.h b/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.h index c7becc8362..203cfb3fb6 100644 --- a/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.h +++ b/src/Ai/Raid/GruulsLair/Util/RaidGruulsLairHelpers.h @@ -37,7 +37,7 @@ namespace GruulsLairHelpers void MarkTargetWithCircle(Player* bot, Unit* target); void MarkTargetWithDiamond(Player* bot, Unit* target); void MarkTargetWithTriangle(Player* bot, Unit* target); - void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target); + void SetRtiTarget(PlayerbotAI* botAI, std::string const& rtiName, Unit* target); bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot); bool IsKigglerMoonkinTank(PlayerbotAI* botAI, Player* bot); bool IsPositionSafe(PlayerbotAI* botAI, Player* bot, Position pos); diff --git a/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp b/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp index 14c8ada9ac..f0d9edddc9 100644 --- a/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp +++ b/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp @@ -1,4 +1,6 @@ +#include "BotSpellService.h" #include "RaidIccActions.h" +#include "BotRoleService.h" #include "NearestNpcsValue.h" #include "ObjectAccessor.h" #include "RaidIccStrategy.h" @@ -11,23 +13,24 @@ #include #include "RaidIccTriggers.h" #include "Multiplier.h" +#include "BotItemService.h" // Lord Marrowgwar -bool IccLmTankPositionAction::Execute(Event event) +bool IccLmTankPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "lord marrowgar"); if (!boss) return false; - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) return false; - const bool isBossInBoneStorm = botAI->GetAura("Bone Storm", boss) != nullptr; + const bool isBossInBoneStorm = botAI->GetServices().GetSpellService().GetAura("Bone Storm", boss) != nullptr; if (isBossInBoneStorm) return false; - if (botAI->HasAggro(boss) && botAI->IsMainTank(bot) && boss->GetVictim() == bot) + if (botAI->GetServices().GetRoleService().HasAggro(boss) && BotRoleService::IsMainTankStatic(bot) && boss->GetVictim() == bot) { const float maxDistanceThreshold = 3.0f; const float distance = bot->GetExactDist2d(ICC_LM_TANK_POSITION.GetPositionX(), ICC_LM_TANK_POSITION.GetPositionY()); @@ -36,7 +39,7 @@ bool IccLmTankPositionAction::Execute(Event event) return MoveTowardPosition(ICC_LM_TANK_POSITION, maxDistanceThreshold); } - if (botAI->IsAssistTank(bot)) + if (BotRoleService::IsAssistTankStatic(bot)) { const float maxDistanceThreshold = 3.0f; const float distance = bot->GetExactDist2d(ICC_LM_TANK_POSITION.GetPositionX(), ICC_LM_TANK_POSITION.GetPositionY()); @@ -54,7 +57,7 @@ bool IccLmTankPositionAction::Execute(Event event) return false; } -bool IccLmTankPositionAction::MoveTowardPosition(const Position& position, float incrementSize) +bool IccLmTankPositionAction::MoveTowardPosition(Position const& position, float incrementSize) { // Calculate direction vector const float dirX = position.GetPositionX() - bot->GetPositionX(); @@ -73,10 +76,10 @@ bool IccLmTankPositionAction::MoveTowardPosition(const Position& position, float MovementPriority::MOVEMENT_COMBAT); } -bool IccSpikeAction::Execute(Event event) +bool IccSpikeAction::Execute(Event /*event*/) { // If we're impaled, we can't do anything - if (botAI->GetAura("Impaled", bot)) + if (botAI->GetServices().GetSpellService().GetAura("Impaled", bot)) return false; // Find the boss @@ -84,8 +87,8 @@ bool IccSpikeAction::Execute(Event event) if (!boss) return false; - const bool isBossInBoneStorm = botAI->GetAura("Bone Storm", boss) != nullptr; - const bool shouldMoveToSafePosition = boss->isInFront(bot) && !botAI->IsTank(bot) && !isBossInBoneStorm; + const bool isBossInBoneStorm = botAI->GetServices().GetSpellService().GetAura("Bone Storm", boss) != nullptr; + const bool shouldMoveToSafePosition = boss->isInFront(bot) && !BotRoleService::IsTankStatic(bot) && !isBossInBoneStorm; if (shouldMoveToSafePosition) { @@ -99,7 +102,7 @@ bool IccSpikeAction::Execute(Event event) return false; } - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) return false; return HandleSpikeTargeting(boss); @@ -107,7 +110,7 @@ bool IccSpikeAction::Execute(Event event) bool IccSpikeAction::HandleSpikeTargeting(Unit* boss) { - static const std::array spikeEntries = {NPC_SPIKE1, NPC_SPIKE2, NPC_SPIKE3}; + static std::array const spikeEntries = {NPC_SPIKE1, NPC_SPIKE2, NPC_SPIKE3}; const GuidVector spikes = AI_VALUE(GuidVector, "possible targets no los"); Unit* priorityTarget = nullptr; @@ -147,7 +150,7 @@ bool IccSpikeAction::HandleSpikeTargeting(Unit* boss) return false; } -bool IccSpikeAction::MoveTowardPosition(const Position& position, float incrementSize) +bool IccSpikeAction::MoveTowardPosition(Position const& position, float incrementSize) { // Calculate direction vector const float dirX = position.GetPositionX() - bot->GetPositionX(); @@ -183,7 +186,7 @@ void IccSpikeAction::UpdateRaidTargetIcon(Unit* target) } // Lady Deathwhisper -bool IccDarkReckoningAction::Execute(Event event) +bool IccDarkReckoningAction::Execute(Event /*event*/) { constexpr float SAFE_DISTANCE_THRESHOLD = 2.0f; @@ -201,7 +204,7 @@ bool IccDarkReckoningAction::Execute(Event event) return false; } -bool IccRangedPositionLadyDeathwhisperAction::Execute(Event event) +bool IccRangedPositionLadyDeathwhisperAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "lady deathwhisper"); if (!boss) @@ -214,7 +217,7 @@ bool IccRangedPositionLadyDeathwhisperAction::Execute(Event event) if (currentDistance < minDistance || currentDistance > maxDistance) return false; - if (!botAI->IsRanged(bot) && !botAI->IsHeal(bot)) + if (!BotRoleService::IsRangedStatic(bot) && !BotRoleService::IsHealStatic(bot)) return false; return MaintainRangedSpacing(); @@ -225,7 +228,7 @@ bool IccRangedPositionLadyDeathwhisperAction::MaintainRangedSpacing() const float safeSpacingRadius = 3.0f; const float moveIncrement = 2.0f; const float maxMoveDistance = 5.0f; // Limit maximum movement distance - const bool isRanged = botAI->IsRanged(bot) || botAI->IsHeal(bot); + const bool isRanged = BotRoleService::IsRangedStatic(bot) || BotRoleService::IsHealStatic(bot); if (!isRanged) return false; @@ -301,20 +304,20 @@ bool IccRangedPositionLadyDeathwhisperAction::MaintainRangedSpacing() return false; // Everyone is properly spaced } -bool IccAddsLadyDeathwhisperAction::Execute(Event event) +bool IccAddsLadyDeathwhisperAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "lady deathwhisper"); if (!boss) return false; - if (botAI->HasAura("Dominate Mind", bot, false, false) && !bot->HasAura(SPELL_CYCLONE)) + if (botAI->GetServices().GetSpellService().HasAura("Dominate Mind", bot, false, false) && !bot->HasAura(SPELL_CYCLONE)) bot->AddAura(SPELL_CYCLONE, bot); - else if (bot->HasAura(SPELL_CYCLONE) && !botAI->HasAura("Dominate Mind", bot, false, false)) + else if (bot->HasAura(SPELL_CYCLONE) && !botAI->GetServices().GetSpellService().HasAura("Dominate Mind", bot, false, false)) bot->RemoveAura(SPELL_CYCLONE); const uint32 shadeEntryId = NPC_SHADE; - if (botAI->IsTank(bot) && boss && boss->HealthBelowPct(95) && boss->GetVictim() == bot) + if (BotRoleService::IsTankStatic(bot) && boss && boss->HealthBelowPct(95) && boss->GetVictim() == bot) { // Check if the bot is not the victim of a shade if (IsTargetedByShade(shadeEntryId)) @@ -331,7 +334,7 @@ bool IccAddsLadyDeathwhisperAction::Execute(Event event) } } - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) return false; return HandleAddTargeting(boss); @@ -349,7 +352,7 @@ bool IccAddsLadyDeathwhisperAction::IsTargetedByShade(uint32 shadeEntry) return false; } -bool IccAddsLadyDeathwhisperAction::MoveTowardPosition(const Position& position, float incrementSize) +bool IccAddsLadyDeathwhisperAction::MoveTowardPosition(Position const& position, float incrementSize) { // Calculate direction vector const float dirX = position.GetPositionX() - bot->GetPositionX(); @@ -419,7 +422,7 @@ void IccAddsLadyDeathwhisperAction::UpdateRaidTargetIcon(Unit* target) } } -bool IccShadeLadyDeathwhisperAction::Execute(Event event) +bool IccShadeLadyDeathwhisperAction::Execute(Event /*event*/) { static constexpr uint32 VENGEFUL_SHADE_ID = NPC_SHADE; static constexpr float SAFE_DISTANCE = 12.0f; @@ -473,28 +476,28 @@ bool IccShadeLadyDeathwhisperAction::Execute(Event event) return false; } -bool IccRottingFrostGiantTankPositionAction::Execute(Event event) +bool IccRottingFrostGiantTankPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "rotting frost giant"); if (!boss) return false; - Aura* aura = botAI->GetAura("death plague", bot, false, false); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("death plague", bot, false, false); if (aura) bot->RemoveAura(aura->GetId()); /* TODO: code works for handling plague, but atm script is bugged and one bot can have 2 plagues at the same time or when cured, which should not happen, and it is immpossible to handle plague atm the legit way. - const bool hasCure = botAI->GetAura("recently infected", bot) != nullptr; + const bool hasCure = botAI->GetServices().GetSpellService().GetAura("recently infected", bot) != nullptr; // Tank behavior - unchanged - if (botAI->IsTank(bot) && botAI->HasAggro(boss) && !isInfected) + if (BotRoleService::IsTankStatic(bot) && botAI->GetServices().GetRoleService().HasAggro(boss) && !isInfected) if (bot->GetExactDist2d(ICC_ROTTING_FROST_GIANT_TANK_POSITION) > 5.0f) return MoveTo(bot->GetMapId(), ICC_ROTTING_FROST_GIANT_TANK_POSITION.GetPositionX(), ICC_ROTTING_FROST_GIANT_TANK_POSITION.GetPositionY(), ICC_ROTTING_FROST_GIANT_TANK_POSITION.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_NORMAL); - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) return false; // Handle infected bot behavior - move near a non-infected, non-cured bot @@ -512,7 +515,7 @@ bool IccRottingFrostGiantTankPositionAction::Execute(Event event) if (!member || !member->IsAlive() || member == bot) continue; - const bool memberIsInfected = botAI->GetAura("death plague", member) != nullptr; + const bool memberIsInfected = botAI->GetServices().GetSpellService().GetAura("death plague", member) != nullptr; if (memberIsInfected) { @@ -526,8 +529,8 @@ bool IccRottingFrostGiantTankPositionAction::Execute(Event event) if (!potentialTarget || !potentialTarget->IsAlive() || potentialTarget == member) continue; - const bool targetIsInfected = botAI->GetAura("death plague", potentialTarget) != nullptr; - const bool targetHasCure = botAI->GetAura("recently infected", potentialTarget) != nullptr; + const bool targetIsInfected = botAI->GetServices().GetSpellService().GetAura("death plague", potentialTarget) != nullptr; + const bool targetHasCure = botAI->GetServices().GetSpellService().GetAura("recently infected", potentialTarget) != nullptr; if (!targetIsInfected && !targetHasCure) { @@ -557,8 +560,8 @@ bool IccRottingFrostGiantTankPositionAction::Execute(Event event) if (!member || !member->IsAlive() || member == bot) continue; - const bool memberHasCure = botAI->GetAura("recently infected", member) != nullptr; - const bool memberIsInfected = botAI->GetAura("death plague", member) != nullptr; + const bool memberHasCure = botAI->GetServices().GetSpellService().GetAura("recently infected", member) != nullptr; + const bool memberIsInfected = botAI->GetServices().GetSpellService().GetAura("death plague", member) != nullptr; if (!memberIsInfected && !memberHasCure) { @@ -566,7 +569,7 @@ bool IccRottingFrostGiantTankPositionAction::Execute(Event event) float score = bot->GetExactDist2d(member); // Prefer ranged targets - if (botAI->IsRanged(bot)) + if (BotRoleService::IsRangedStatic(bot)) { score *= 0.7f; // Bonus for ranged targets } @@ -581,7 +584,7 @@ bool IccRottingFrostGiantTankPositionAction::Execute(Event event) // Sort targets by score (lowest/best first) std::sort(viableTargets.begin(), viableTargets.end(), - [](const std::pair& a, const std::pair& b) + [](std::pair const& a, std::pair const& b) { return a.second < b.second; }); // Choose the best target @@ -617,7 +620,7 @@ bool IccRottingFrostGiantTankPositionAction::Execute(Event event) } // For ranged bots, only spread from non-infected bots - if (botAI->IsRanged(bot)) + if (BotRoleService::IsRangedStatic(bot)) { const float safeSpacingRadius = 11.0f; const float moveIncrement = 2.0f; @@ -637,7 +640,7 @@ bool IccRottingFrostGiantTankPositionAction::Execute(Event event) continue; // Only spread from non-infected bots (can stay near infected or cured bots) - const bool memberIsInfected = botAI->GetAura("death plague", member) != nullptr; + const bool memberIsInfected = botAI->GetServices().GetSpellService().GetAura("death plague", member) != nullptr; if (memberIsInfected) continue; @@ -696,7 +699,7 @@ bool IccRottingFrostGiantTankPositionAction::Execute(Event event) } //Gunship -bool IccCannonFireAction::Execute(Event event) +bool IccCannonFireAction::Execute(Event /*event*/) { Unit* vehicleBase = bot->GetVehicleBase(); Vehicle* vehicle = bot->GetVehicle(); @@ -746,7 +749,7 @@ bool IccCannonFireAction::TryCastCannonSpell(uint32 spellId, Unit* target, Unit* { static constexpr uint32 cooldownMs = 1000; - if (botAI->CanCastVehicleSpell(spellId, target) && botAI->CastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CanCastVehicleSpell(spellId, target) && botAI->GetServices().GetSpellService().CastVehicleSpell(spellId, target)) { vehicleBase->AddSpellCooldown(spellId, 0, cooldownMs); return true; @@ -755,7 +758,7 @@ bool IccCannonFireAction::TryCastCannonSpell(uint32 spellId, Unit* target, Unit* return false; } -bool IccGunshipEnterCannonAction::Execute(Event event) +bool IccGunshipEnterCannonAction::Execute(Event /*event*/) { // Do not switch vehicles if already in one if (bot->GetVehicle()) @@ -838,7 +841,7 @@ bool IccGunshipEnterCannonAction::EnterVehicle(Unit* vehicleBase, bool moveIfFar return MoveTo(vehicleBase); // Prepare for entering vehicle - botAI->RemoveShapeshift(); + botAI->GetServices().GetSpellService().RemoveShapeshift(); bot->GetMotionMaster()->Clear(); bot->StopMoving(); @@ -855,7 +858,7 @@ bool IccGunshipEnterCannonAction::EnterVehicle(Unit* vehicleBase, bool moveIfFar return true; } -bool IccGunshipTeleportAllyAction::Execute(Event event) +bool IccGunshipTeleportAllyAction::Execute(Event /*event*/) { static constexpr float MAX_WAITING_DISTANCE = 45.0f; static constexpr float MAX_ATTACK_DISTANCE = 15.0f; @@ -881,14 +884,14 @@ bool IccGunshipTeleportAllyAction::Execute(Event event) UpdateBossSkullIcon(boss, SKULL_ICON_INDEX); // Teleport non-tank bots to attack position if not already there - if (!botAI->IsAssistTank(bot) && bot->GetExactDist2d(ICC_GUNSHIP_TELEPORT_ALLY) > MAX_ATTACK_DISTANCE) + if (!BotRoleService::IsAssistTankStatic(bot) && bot->GetExactDist2d(ICC_GUNSHIP_TELEPORT_ALLY) > MAX_ATTACK_DISTANCE) return TeleportTo(ICC_GUNSHIP_TELEPORT_ALLY); } return false; } -bool IccGunshipTeleportAllyAction::TeleportTo(const Position& position) +bool IccGunshipTeleportAllyAction::TeleportTo(Position const& position) { return bot->TeleportTo(bot->GetMapId(), position.GetPositionX(), position.GetPositionY(), position.GetPositionZ(), bot->GetOrientation()); @@ -922,7 +925,7 @@ void IccGunshipTeleportAllyAction::UpdateBossSkullIcon(Unit* boss, uint8_t SKULL } } -bool IccGunshipTeleportHordeAction::Execute(Event event) +bool IccGunshipTeleportHordeAction::Execute(Event /*event*/) { static constexpr float MAX_WAITING_DISTANCE = 45.0f; static constexpr float MAX_ATTACK_DISTANCE = 15.0f; @@ -948,14 +951,14 @@ bool IccGunshipTeleportHordeAction::Execute(Event event) UpdateBossSkullIcon(boss, SKULL_ICON_INDEX); // Teleport non-tank bots to attack position if not already there - if (!botAI->IsAssistTank(bot) && bot->GetExactDist2d(ICC_GUNSHIP_TELEPORT_HORDE) > MAX_ATTACK_DISTANCE) + if (!BotRoleService::IsAssistTankStatic(bot) && bot->GetExactDist2d(ICC_GUNSHIP_TELEPORT_HORDE) > MAX_ATTACK_DISTANCE) return TeleportTo(ICC_GUNSHIP_TELEPORT_HORDE); } return false; } -bool IccGunshipTeleportHordeAction::TeleportTo(const Position& position) +bool IccGunshipTeleportHordeAction::TeleportTo(Position const& position) { return bot->TeleportTo(bot->GetMapId(), position.GetPositionX(), position.GetPositionY(), position.GetPositionZ(), bot->GetOrientation()); @@ -990,7 +993,7 @@ void IccGunshipTeleportHordeAction::UpdateBossSkullIcon(Unit* boss, uint8_t SKUL } //DBS -bool IccDbsTankPositionAction::Execute(Event event) +bool IccDbsTankPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "deathbringer saurfang"); if (!boss) @@ -999,7 +1002,7 @@ bool IccDbsTankPositionAction::Execute(Event event) Unit* beast = AI_VALUE2(Unit*, "find target", "blood beast"); // Handle tank positioning - if (botAI->IsTank(bot) && !beast) + if (BotRoleService::IsTankStatic(bot) && !beast) { if (bot->GetExactDist2d(ICC_DBS_TANK_POSITION) > 5.0f) return MoveTo(bot->GetMapId(), ICC_DBS_TANK_POSITION.GetPositionX(), ICC_DBS_TANK_POSITION.GetPositionY(), @@ -1007,18 +1010,18 @@ bool IccDbsTankPositionAction::Execute(Event event) MovementPriority::MOVEMENT_NORMAL); // Early return if this tank has Rune of Blood - if (botAI->GetAura("Rune of Blood", bot)) + if (botAI->GetServices().GetSpellService().GetAura("Rune of Blood", bot)) return true; } - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) { if (CrowdControlBloodBeasts()) return true; } // Handle ranged and healer positioning - if (botAI->IsRanged(bot) || botAI->IsHeal(bot)) + if (BotRoleService::IsRangedStatic(bot) || BotRoleService::IsHealStatic(bot)) { // Handle evasion from blood beasts if (EvadeBloodBeasts()) @@ -1033,12 +1036,10 @@ bool IccDbsTankPositionAction::Execute(Event event) bool IccDbsTankPositionAction::CrowdControlBloodBeasts() { - const std::array bloodBeastEntries = {NPC_BLOOD_BEAST1, NPC_BLOOD_BEAST2, NPC_BLOOD_BEAST3, + std::array const bloodBeastEntries = {NPC_BLOOD_BEAST1, NPC_BLOOD_BEAST2, NPC_BLOOD_BEAST3, NPC_BLOOD_BEAST4}; const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - bool appliedCC = false; - for (auto const& npc : npcs) { Unit* unit = botAI->GetUnit(npc); @@ -1056,74 +1057,44 @@ bool IccDbsTankPositionAction::CrowdControlBloodBeasts() switch (bot->getClass()) { case CLASS_MAGE: - if (!botAI->HasAura("Frost Nova", unit)) - { - botAI->CastSpell("Frost Nova", unit); - appliedCC = true; - } + if (!botAI->GetServices().GetSpellService().HasAura("Frost Nova", unit)) + botAI->GetServices().GetSpellService().CastSpell("Frost Nova", unit); break; case CLASS_DRUID: - if (!botAI->HasAura("Entangling Roots", unit)) - { - botAI->CastSpell("Entangling Roots", unit); - appliedCC = true; - } + if (!botAI->GetServices().GetSpellService().HasAura("Entangling Roots", unit)) + botAI->GetServices().GetSpellService().CastSpell("Entangling Roots", unit); break; case CLASS_PALADIN: - if (!botAI->HasAura("Hammer of Justice", unit)) - { - botAI->CastSpell("Hammer of Justice", unit); - appliedCC = true; - } + if (!botAI->GetServices().GetSpellService().HasAura("Hammer of Justice", unit)) + botAI->GetServices().GetSpellService().CastSpell("Hammer of Justice", unit); break; case CLASS_WARRIOR: - if (!botAI->HasAura("Hamstring", unit)) - { - botAI->CastSpell("Hamstring", unit); - appliedCC = true; - } + if (!botAI->GetServices().GetSpellService().HasAura("Hamstring", unit)) + botAI->GetServices().GetSpellService().CastSpell("Hamstring", unit); break; case CLASS_HUNTER: - if (!botAI->HasAura("Concussive Shot", unit)) - { - botAI->CastSpell("Concussive Shot", unit); - appliedCC = true; - } + if (!botAI->GetServices().GetSpellService().HasAura("Concussive Shot", unit)) + botAI->GetServices().GetSpellService().CastSpell("Concussive Shot", unit); break; case CLASS_ROGUE: - if (!botAI->HasAura("Kidney Shot", unit)) - { - botAI->CastSpell("Kidney Shot", unit); - appliedCC = true; - } + if (!botAI->GetServices().GetSpellService().HasAura("Kidney Shot", unit)) + botAI->GetServices().GetSpellService().CastSpell("Kidney Shot", unit); break; case CLASS_SHAMAN: - if (!botAI->HasAura("Frost Shock", unit)) - { - botAI->CastSpell("Frost Shock", unit); - appliedCC = true; - } + if (!botAI->GetServices().GetSpellService().HasAura("Frost Shock", unit)) + botAI->GetServices().GetSpellService().CastSpell("Frost Shock", unit); break; case CLASS_DEATH_KNIGHT: - if (!botAI->HasAura("Chains of Ice", unit)) - { - botAI->CastSpell("Chains of Ice", unit); - appliedCC = true; - } + if (!botAI->GetServices().GetSpellService().HasAura("Chains of Ice", unit)) + botAI->GetServices().GetSpellService().CastSpell("Chains of Ice", unit); break; case CLASS_PRIEST: - if (!botAI->HasAura("Psychic Scream", unit)) - { - botAI->CastSpell("Psychic Scream", unit); - appliedCC = true; - } + if (!botAI->GetServices().GetSpellService().HasAura("Psychic Scream", unit)) + botAI->GetServices().GetSpellService().CastSpell("Psychic Scream", unit); break; case CLASS_WARLOCK: - if (!botAI->HasAura("Fear", unit)) - { - botAI->CastSpell("Fear", unit); - appliedCC = true; - } + if (!botAI->GetServices().GetSpellService().HasAura("Fear", unit)) + botAI->GetServices().GetSpellService().CastSpell("Fear", unit); break; default: break; @@ -1136,7 +1107,7 @@ bool IccDbsTankPositionAction::CrowdControlBloodBeasts() bool IccDbsTankPositionAction::EvadeBloodBeasts() { const float evasionDistance = 12.0f; - const std::array bloodBeastEntries = {NPC_BLOOD_BEAST1, NPC_BLOOD_BEAST2, NPC_BLOOD_BEAST3, NPC_BLOOD_BEAST4}; + std::array const bloodBeastEntries = {NPC_BLOOD_BEAST1, NPC_BLOOD_BEAST2, NPC_BLOOD_BEAST3, NPC_BLOOD_BEAST4}; // Get the nearest hostile NPCs const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); @@ -1181,7 +1152,7 @@ bool IccDbsTankPositionAction::PositionInRangedFormation() if (!member || !member->IsAlive()) continue; - if ((botAI->IsRanged(member) || botAI->IsHeal(member)) && !botAI->IsTank(member)) + if ((BotRoleService::IsRangedStatic(member) || BotRoleService::IsHealStatic(member)) && !BotRoleService::IsTankStatic(member)) { if (member == bot) { @@ -1231,14 +1202,14 @@ bool IccDbsTankPositionAction::PositionInRangedFormation() return false; } -bool IccAddsDbsAction::Execute(Event event) +bool IccAddsDbsAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "deathbringer saurfang"); if (!boss) return false; // This action is only for melee - if (!botAI->IsMelee(bot)) + if (!BotRoleService::IsMeleeStatic(bot)) return false; Unit* priorityTarget = FindPriorityTarget(boss); @@ -1259,7 +1230,7 @@ Unit* IccAddsDbsAction::FindPriorityTarget(Unit* boss) // First check for alive adds for (uint32_t entry : addEntries) { - for (const ObjectGuid& guid : targets) + for (ObjectGuid const& guid : targets) { Unit* unit = botAI->GetUnit(guid); if (unit && unit->IsAlive() && unit->GetEntry() == entry) @@ -1295,7 +1266,7 @@ void IccAddsDbsAction::UpdateSkullMarker(Unit* priorityTarget) } // Festergut -bool IccFestergutGroupPositionAction::Execute(Event event) +bool IccFestergutGroupPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "festergut"); if (!boss) @@ -1304,7 +1275,7 @@ bool IccFestergutGroupPositionAction::Execute(Event event) bot->SetTarget(boss->GetGUID()); // Handle tank positioning - if ((botAI->HasAggro(boss) && botAI->IsMainTank(bot)) || botAI->IsAssistTank(bot)) + if ((botAI->GetServices().GetRoleService().HasAggro(boss) && BotRoleService::IsMainTankStatic(bot)) || BotRoleService::IsAssistTankStatic(bot)) { if (bot->GetExactDist2d(ICC_FESTERGUT_TANK_POSITION) > 5.0f) return MoveTo(bot->GetMapId(), ICC_FESTERGUT_TANK_POSITION.GetPositionX(), @@ -1337,7 +1308,7 @@ bool IccFestergutGroupPositionAction::HasSporesInGroup() bool IccFestergutGroupPositionAction::PositionNonTankMembers() { // Only position ranged and healers without spores - if (!(botAI->IsRanged(bot) || botAI->IsHeal(bot))) + if (!(BotRoleService::IsRangedStatic(bot) || BotRoleService::IsHealStatic(bot))) return false; Group* group = bot->GetGroup(); @@ -1394,16 +1365,16 @@ int IccFestergutGroupPositionAction::CalculatePositionIndex(Group* group) for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) { Player* member = itr->GetSource(); - if (!member || !member->IsAlive() || botAI->IsTank(member)) + if (!member || !member->IsAlive() || BotRoleService::IsTankStatic(member)) continue; ObjectGuid memberGuid = member->GetGUID(); - if (botAI->IsHeal(member)) + if (BotRoleService::IsHealStatic(member)) { healerGuids.push_back(memberGuid); } - else if (botAI->IsRanged(member)) + else if (BotRoleService::IsRangedStatic(member)) { if (member->getClass() == CLASS_HUNTER) { @@ -1467,7 +1438,6 @@ int IccFestergutGroupPositionAction::CalculatePositionIndex(Group* group) else { // Fill remaining spots in second row - int spotsInFirstRow = 6; int spotsInSecondRow = healerSpotsUsed - 6; int remainingInSecondRow = 6 - spotsInSecondRow; @@ -1511,10 +1481,9 @@ int IccFestergutGroupPositionAction::CalculatePositionIndex(Group* group) return -1; } -bool IccFestergutSporeAction::Execute(Event event) +bool IccFestergutSporeAction::Execute(Event /*event*/) { constexpr float POSITION_TOLERANCE = 4.0f; - constexpr float SPREAD_RADIUS = 2.0f; // Check if bot has spore bool hasSpore = bot->HasAura(SPELL_GAS_SPORE); // gas spore @@ -1583,24 +1552,24 @@ IccFestergutSporeAction::SporeInfo IccFestergutSporeAction::FindSporedPlayers() return info; } -Position IccFestergutSporeAction::DetermineTargetPosition(bool hasSpore, const SporeInfo& sporeInfo, const Position& spreadRangedPos) +Position IccFestergutSporeAction::DetermineTargetPosition(bool hasSpore, SporeInfo const& sporeInfo, Position const& spreadRangedPos) { // No spores at all if (sporeInfo.sporedPlayers.empty()) - return botAI->IsMelee(bot) ? ICC_FESTERGUT_MELEE_SPORE : spreadRangedPos; + return BotRoleService::IsMeleeStatic(bot) ? ICC_FESTERGUT_MELEE_SPORE : spreadRangedPos; // Bot has no spore, go to standard position if (!hasSpore) - return botAI->IsMelee(bot) ? ICC_FESTERGUT_MELEE_SPORE : spreadRangedPos; + return BotRoleService::IsMeleeStatic(bot) ? ICC_FESTERGUT_MELEE_SPORE : spreadRangedPos; // Check if main tank has spore bool mainTankHasSpore = CheckMainTankSpore(); // Determine position based on spore logic - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) return ICC_FESTERGUT_MELEE_SPORE; - if (bot->GetGUID() == sporeInfo.lowestGuid && !botAI->IsTank(bot) && !mainTankHasSpore) + if (bot->GetGUID() == sporeInfo.lowestGuid && !BotRoleService::IsTankStatic(bot) && !mainTankHasSpore) return ICC_FESTERGUT_MELEE_SPORE; return spreadRangedPos; @@ -1616,7 +1585,7 @@ bool IccFestergutSporeAction::CheckMainTankSpore() if (!unit) continue; - if (botAI->IsMainTank(unit->ToPlayer()) && unit->HasAura(SPELL_GAS_SPORE)) + if (BotRoleService::IsMainTankStatic(unit->ToPlayer()) && unit->HasAura(SPELL_GAS_SPORE)) return true; } @@ -1624,7 +1593,7 @@ bool IccFestergutSporeAction::CheckMainTankSpore() } // Rotface -bool IccRotfaceTankPositionAction::Execute(Event event) +bool IccRotfaceTankPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "rotface"); if (!boss) @@ -1636,11 +1605,11 @@ bool IccRotfaceTankPositionAction::Execute(Event event) MarkBossWithSkull(boss); // Main tank positioning and melee positioning - if ((botAI->IsMainTank(bot) || botAI->IsMelee(bot)) && !botAI->IsAssistTank(bot) && !victimOfSmallOoze) + if ((BotRoleService::IsMainTankStatic(bot) || BotRoleService::IsMeleeStatic(bot)) && !BotRoleService::IsAssistTankStatic(bot) && !victimOfSmallOoze) return PositionMainTankAndMelee(boss); // Assist tank positioning for big ooze - if (botAI->IsAssistTank(bot)) + if (BotRoleService::IsAssistTankStatic(bot)) return HandleAssistTankPositioning(boss); return false; @@ -1660,16 +1629,14 @@ void IccRotfaceTankPositionAction::MarkBossWithSkull(Unit* boss) bool IccRotfaceTankPositionAction::PositionMainTankAndMelee(Unit* boss) { - bool isBossCasting = false; - if (boss && boss->HasUnitState(UNIT_STATE_CASTING) && boss->GetCurrentSpell(SPELL_SLIME_SPRAY)) - bool isBossCasting = true; + bool isBossCasting = boss && boss->HasUnitState(UNIT_STATE_CASTING) && boss->GetCurrentSpell(SPELL_SLIME_SPRAY); - if (bot->GetExactDist2d(ICC_ROTFACE_CENTER_POSITION) > 7.0f && botAI->HasAggro(boss) && botAI->IsMainTank(bot)) + if (bot->GetExactDist2d(ICC_ROTFACE_CENTER_POSITION) > 7.0f && botAI->GetServices().GetRoleService().HasAggro(boss) && BotRoleService::IsMainTankStatic(bot)) MoveTo(bot->GetMapId(), ICC_ROTFACE_CENTER_POSITION.GetPositionX(), ICC_ROTFACE_CENTER_POSITION.GetPositionY(), ICC_ROTFACE_CENTER_POSITION.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT); - if (boss && isBossCasting && !botAI->IsTank(bot)) + if (boss && isBossCasting && !BotRoleService::IsTankStatic(bot)) { float x = boss->GetPositionX(); float y = boss->GetPositionY(); @@ -1685,7 +1652,7 @@ bool IccRotfaceTankPositionAction::PositionMainTankAndMelee(Unit* boss) return false; } - if (!isBossCasting && (bot->GetExactDist2d(ICC_ROTFACE_CENTER_POSITION) < 2.0f || bot->GetExactDist2d(ICC_ROTFACE_CENTER_POSITION) > 7.0f) && !botAI->IsTank(bot)) + if (!isBossCasting && (bot->GetExactDist2d(ICC_ROTFACE_CENTER_POSITION) < 2.0f || bot->GetExactDist2d(ICC_ROTFACE_CENTER_POSITION) > 7.0f) && !BotRoleService::IsTankStatic(bot)) { MoveTo(bot->GetMapId(), ICC_ROTFACE_CENTER_POSITION.GetPositionX(), ICC_ROTFACE_CENTER_POSITION.GetPositionY(), bot->GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT); @@ -1700,7 +1667,7 @@ bool IccRotfaceTankPositionAction::HandleAssistTankPositioning(Unit* boss) return HandleBigOozePositioning(boss); } -bool IccRotfaceTankPositionAction::HandleBigOozePositioning(Unit* boss) +bool IccRotfaceTankPositionAction::HandleBigOozePositioning(Unit* /*boss*/) { // Find all big oozes GuidVector bigOozes = AI_VALUE(GuidVector, "nearest hostile npcs"); @@ -1722,7 +1689,7 @@ bool IccRotfaceTankPositionAction::HandleBigOozePositioning(Unit* boss) // Taunt if not targeting us if (bigOoze->GetVictim() != bot && bigOoze->IsAlive() && bigOoze->IsVisible()) { - if (botAI->CastSpell("taunt", bigOoze)) + if (botAI->GetServices().GetSpellService().CastSpell("taunt", bigOoze)) return true; bot->SetTarget(bigOoze->GetGUID()); bot->SetFacingToObject(bigOoze); @@ -1799,7 +1766,7 @@ bool IccRotfaceTankPositionAction::HandleBigOozePositioning(Unit* boss) for (auto const& puddleGuid : puddles) { Unit* puddle = botAI->GetUnit(puddleGuid); - if (puddle && botAI->GetAura("Ooze Flood", puddle)) + if (puddle && botAI->GetServices().GetSpellService().GetAura("Ooze Flood", puddle)) { float puddleDistance = std::sqrt(std::pow(newX - puddle->GetPositionX(), 2) + std::pow(newY - puddle->GetPositionY(), 2)); @@ -1824,34 +1791,18 @@ bool IccRotfaceTankPositionAction::HandleBigOozePositioning(Unit* boss) return false; } -bool IccRotfaceGroupPositionAction::Execute(Event event) +bool IccRotfaceGroupPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "rotface"); if (!boss) return false; - const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - bool floodPresent = false; - - for (auto const& npc : npcs) - { - Unit* unit = botAI->GetUnit(npc); - if (!unit || !botAI->HasAura("Ooze Flood", unit)) - continue; - - float puddleDistance = bot->GetExactDist2d(unit); - - if (puddleDistance < 30.0f) - floodPresent = true; - } - - Unit* bigOoze = AI_VALUE2(Unit*, "find target", "big ooze"); - bool hasOozeFlood = botAI->HasAura("Ooze Flood", bot); + bool hasOozeFlood = botAI->GetServices().GetSpellService().HasAura("Ooze Flood", bot); Unit* smallOoze = AI_VALUE2(Unit*, "find target", "little ooze"); - bool hasMutatedInfection = botAI->HasAura("Mutated Infection", bot); + bool hasMutatedInfection = botAI->GetServices().GetSpellService().HasAura("Mutated Infection", bot); // Handle puddle avoidance - if (!botAI->IsTank(bot) && HandlePuddleAvoidance(boss)) + if (!BotRoleService::IsTankStatic(bot) && HandlePuddleAvoidance(boss)) return true; // Handle little ooze or mutated infection @@ -1859,12 +1810,9 @@ bool IccRotfaceGroupPositionAction::Execute(Event event) return true; // Position ranged and healers - if (/*!floodPresent && */ !((smallOoze && smallOoze->GetVictim() == bot) || hasMutatedInfection) && !hasOozeFlood && PositionRangedAndHealers(boss, smallOoze)) + if (!((smallOoze && smallOoze->GetVictim() == bot) || hasMutatedInfection) && !hasOozeFlood && PositionRangedAndHealers(boss, smallOoze)) return true; - //if (!hasOozeFlood && bigOoze && bigOoze->IsAlive() && MoveAwayFromBigOoze(bigOoze)) - //return true; - return false; } @@ -1875,7 +1823,7 @@ bool IccRotfaceGroupPositionAction::HandlePuddleAvoidance(Unit* boss) for (auto const& npc : npcs) { Unit* unit = botAI->GetUnit(npc); - if (!unit || !botAI->HasAura("Ooze Flood", unit)) + if (!unit || !botAI->GetServices().GetSpellService().HasAura("Ooze Flood", unit)) continue; float puddleDistance = bot->GetExactDist2d(unit); @@ -1891,7 +1839,7 @@ bool IccRotfaceGroupPositionAction::HandlePuddleAvoidance(Unit* boss) return false; } -bool IccRotfaceGroupPositionAction::MoveAwayFromPuddle(Unit* boss, Unit* puddle, float puddleDistance) +bool IccRotfaceGroupPositionAction::MoveAwayFromPuddle(Unit* boss, Unit* puddle, float /*puddleDistance*/) { if (!boss || !puddle) return false; @@ -1960,7 +1908,7 @@ bool IccRotfaceGroupPositionAction::MoveAwayFromPuddle(Unit* boss, Unit* puddle, bool IccRotfaceGroupPositionAction::HandleOozeTargeting() { Unit* smallOoze = AI_VALUE2(Unit*, "find target", "little ooze"); - bool hasMutatedInfection = botAI->HasAura("Mutated Infection", bot); + bool hasMutatedInfection = botAI->GetServices().GetSpellService().HasAura("Mutated Infection", bot); if ((smallOoze && smallOoze->GetVictim() == bot) || hasMutatedInfection) return HandleOozeMemberPositioning(); @@ -2011,13 +1959,11 @@ bool IccRotfaceGroupPositionAction::HandleOozeMemberPositioning() bool IccRotfaceGroupPositionAction::PositionRangedAndHealers(Unit* boss,Unit *smallOoze) { // Only for ranged and healers - if (!(botAI->IsRanged(bot) || botAI->IsHeal(bot))) + if (!(BotRoleService::IsRangedStatic(bot) || BotRoleService::IsHealStatic(bot))) return false; Difficulty diff = bot->GetRaidDifficulty(); - bool isBossCasting = false; - if (boss && boss->HasUnitState(UNIT_STATE_CASTING) && boss->GetCurrentSpell(SPELL_SLIME_SPRAY)) - bool isBossCasting = true; + bool isBossCasting = boss && boss->HasUnitState(UNIT_STATE_CASTING) && boss->GetCurrentSpell(SPELL_SLIME_SPRAY); bool isHeroic = (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC); @@ -2052,7 +1998,7 @@ bool IccRotfaceGroupPositionAction::PositionRangedAndHealers(Unit* boss,Unit *sm return FindAndMoveFromClosestMember(boss, smallOoze); } -bool IccRotfaceGroupPositionAction::FindAndMoveFromClosestMember(Unit* boss, Unit* smallOoze) +bool IccRotfaceGroupPositionAction::FindAndMoveFromClosestMember(Unit* /*boss*/, Unit* smallOoze) { const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); @@ -2061,7 +2007,7 @@ bool IccRotfaceGroupPositionAction::FindAndMoveFromClosestMember(Unit* boss, Uni for (auto const& npc : npcs) { Unit* unit = botAI->GetUnit(npc); - if (!unit || !botAI->HasAura("Ooze Flood", unit)) + if (!unit || !botAI->GetServices().GetSpellService().HasAura("Ooze Flood", unit)) continue; puddle = unit; @@ -2073,9 +2019,8 @@ bool IccRotfaceGroupPositionAction::FindAndMoveFromClosestMember(Unit* boss, Uni const float maxMoveDistance = 12.0f; // Limit maximum movement distance const float puddleSafeDistance = 30.0f; // Minimum distance to stay away from puddle const float minCenterDistance = 20.0f; // Minimum distance from center position - const bool isRanged = botAI->IsRanged(bot) || botAI->IsHeal(bot); - // Ranged: spread from other members + // Spread from other members const GuidVector members = AI_VALUE(GuidVector, "group members"); // Calculate a combined vector representing all nearby members' positions @@ -2087,7 +2032,7 @@ bool IccRotfaceGroupPositionAction::FindAndMoveFromClosestMember(Unit* boss, Uni { Unit* member = botAI->GetUnit(memberGuid); if (!member || !member->IsAlive() || member == bot || (smallOoze && smallOoze->GetVictim() == member) || - (member->IsPlayer() && botAI->IsAssistTank(static_cast(member)))) + (member->IsPlayer() && BotRoleService::IsAssistTankStatic(static_cast(member)))) continue; const float distance = bot->GetExactDist2d(member); @@ -2175,10 +2120,10 @@ bool IccRotfaceGroupPositionAction::FindAndMoveFromClosestMember(Unit* boss, Uni return false; // Everyone is properly spaced } -bool IccRotfaceMoveAwayFromExplosionAction::Execute(Event event) +bool IccRotfaceMoveAwayFromExplosionAction::Execute(Event /*event*/) { // Skip if main tank or ooze flood - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) return false; botAI->Reset(); @@ -2201,7 +2146,7 @@ bool IccRotfaceMoveAwayFromExplosionAction::MoveToRandomSafeLocation() for (auto const& npc : npcs) { Unit* puddle = botAI->GetUnit(npc); - if (!puddle || !botAI->HasAura("Ooze Flood", puddle)) + if (!puddle || !botAI->GetServices().GetSpellService().HasAura("Ooze Flood", puddle)) continue; float puddleDistance = @@ -2238,7 +2183,6 @@ bool IccRotfaceMoveAwayFromExplosionAction::MoveToRandomSafeLocation() // Move in increments of 5.0f towards the calculated position float currentX = bot->GetPositionX(); float currentY = bot->GetPositionY(); - float currentZ = bot->GetPositionZ(); float directionX = moveX - currentX; float directionY = moveY - currentY; @@ -2258,7 +2202,7 @@ bool IccRotfaceMoveAwayFromExplosionAction::MoveToRandomSafeLocation() } // Proffesor Putricide -bool IccPutricideGrowingOozePuddleAction::Execute(Event event) +bool IccPutricideGrowingOozePuddleAction::Execute(Event /*event*/) { Unit* closestPuddle = FindClosestThreateningPuddle(); if (!closestPuddle) @@ -2281,7 +2225,6 @@ Unit* IccPutricideGrowingOozePuddleAction::FindClosestThreateningPuddle() Unit* closestPuddle = nullptr; float closestDistance = FLT_MAX; - float closestSafeDistance = BASE_RADIUS; for (auto const& npc : npcs) { @@ -2298,7 +2241,6 @@ Unit* IccPutricideGrowingOozePuddleAction::FindClosestThreateningPuddle() if (currentDistance < safeDistance && currentDistance < closestDistance) { closestDistance = currentDistance; - closestSafeDistance = safeDistance; closestPuddle = unit; } } @@ -2356,7 +2298,7 @@ Position IccPutricideGrowingOozePuddleAction::CalculateSafeMovePosition(Unit* cl if (!IsPositionTooCloseToOtherPuddles(testX, testY, closestPuddle) && bot->IsWithinLOS(testX, testY, botZ)) { // If main tank, add 6f to calculated position in the direction away from the puddle - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) { float awayDx = testX - closestPuddle->GetPositionX(); float awayDy = testY - closestPuddle->GetPositionY(); @@ -2376,7 +2318,7 @@ Position IccPutricideGrowingOozePuddleAction::CalculateSafeMovePosition(Unit* cl // Fallback position if no safe position found float fallbackX = botX + dx * moveDistance; float fallbackY = botY + dy * moveDistance; - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) { float awayDx = fallbackX - closestPuddle->GetPositionX(); float awayDy = fallbackY - closestPuddle->GetPositionY(); @@ -2416,7 +2358,7 @@ bool IccPutricideGrowingOozePuddleAction::IsPositionTooCloseToOtherPuddles(float return false; } -bool IccPutricideVolatileOozeAction::Execute(Event event) +bool IccPutricideVolatileOozeAction::Execute(Event /*event*/) { static const float STACK_DISTANCE = 7.0f; @@ -2429,13 +2371,13 @@ bool IccPutricideVolatileOozeAction::Execute(Event event) return false; // Main tank handling - if (botAI->IsMainTank(bot) && bot->GetExactDist2d(ICC_PUTRICIDE_TANK_POSITION) > 20.0f && + if (BotRoleService::IsMainTankStatic(bot) && bot->GetExactDist2d(ICC_PUTRICIDE_TANK_POSITION) > 20.0f && !boss->HealthBelowPct(36) && boss->GetVictim() == bot) return MoveTo(bot->GetMapId(), ICC_PUTRICIDE_TANK_POSITION.GetPositionX(), ICC_PUTRICIDE_TANK_POSITION.GetPositionY(), ICC_PUTRICIDE_TANK_POSITION.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT, true, false); // Skip if we have forbidden auras - if (botAI->HasAura("Gaseous Bloat", bot) || botAI->HasAura("Unbound Plague", bot)) + if (botAI->GetServices().GetSpellService().HasAura("Gaseous Bloat", bot) || botAI->GetServices().GetSpellService().HasAura("Unbound Plague", bot)) return false; // Find all alive oozes @@ -2461,7 +2403,7 @@ bool IccPutricideVolatileOozeAction::Execute(Event event) MarkOozeWithSkull(ooze); // Melee handling (non-tanks) - if (botAI->IsMelee(bot) && !botAI->IsMainTank(bot)) + if (BotRoleService::IsMeleeStatic(bot) && !BotRoleService::IsMainTankStatic(bot)) { bot->SetTarget(ooze->GetGUID()); bot->SetFacingToObject(ooze); @@ -2470,7 +2412,7 @@ bool IccPutricideVolatileOozeAction::Execute(Event event) } // Ranged/healer handling - if (botAI->IsRanged(bot) || botAI->IsHeal(bot)) + if (BotRoleService::IsRangedStatic(bot) || BotRoleService::IsHealStatic(bot)) { Unit* stackTarget = FindAuraTarget(); if (stackTarget && bot->GetDistance2d(stackTarget) > STACK_DISTANCE) @@ -2479,7 +2421,7 @@ bool IccPutricideVolatileOozeAction::Execute(Event event) stackTarget->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); } - if (ooze && !botAI->IsHeal(bot) && stackTarget && bot->GetDistance2d(stackTarget) <= STACK_DISTANCE) + if (ooze && !BotRoleService::IsHealStatic(bot) && stackTarget && bot->GetDistance2d(stackTarget) <= STACK_DISTANCE) { bot->SetTarget(ooze->GetGUID()); bot->SetFacingToObject(ooze); @@ -2522,14 +2464,14 @@ Unit* IccPutricideVolatileOozeAction::FindAuraTarget() if (!member || !member->IsAlive() || member == bot) continue; - if (botAI->HasAura("Volatile Ooze Adhesive", member)) + if (botAI->GetServices().GetSpellService().HasAura("Volatile Ooze Adhesive", member)) return member; } return nullptr; } -bool IccPutricideGasCloudAction::Execute(Event event) +bool IccPutricideGasCloudAction::Execute(Event /*event*/) { Unit* gasCloud = AI_VALUE2(Unit*, "find target", "gas cloud"); if (!gasCloud) @@ -2540,16 +2482,16 @@ bool IccPutricideGasCloudAction::Execute(Event event) return false; // Tank positioning logic - if (botAI->IsTank(bot) && bot->GetExactDist2d(ICC_PUTRICIDE_TANK_POSITION) > 20.0f && !boss->HealthBelowPct(36) && + if (BotRoleService::IsTankStatic(bot) && bot->GetExactDist2d(ICC_PUTRICIDE_TANK_POSITION) > 20.0f && !boss->HealthBelowPct(36) && boss->GetVictim() == bot) return MoveTo(bot->GetMapId(), ICC_PUTRICIDE_TANK_POSITION.GetPositionX(), ICC_PUTRICIDE_TANK_POSITION.GetPositionY(), ICC_PUTRICIDE_TANK_POSITION.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT, true, false); - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) return false; - bool hasGaseousBloat = botAI->HasAura("Gaseous Bloat", bot); + bool hasGaseousBloat = botAI->GetServices().GetSpellService().HasAura("Gaseous Bloat", bot); Unit* volatileOoze = AI_VALUE2(Unit*, "find target", "volatile ooze"); // Find all alive gasCloud @@ -2583,7 +2525,7 @@ bool IccPutricideGasCloudAction::Execute(Event event) bool IccPutricideGasCloudAction::HandleGaseousBloatMovement(Unit* gasCloud) { - bool hasGaseousBloat = botAI->HasAura("Gaseous Bloat", bot); + bool hasGaseousBloat = botAI->GetServices().GetSpellService().HasAura("Gaseous Bloat", bot); if (!hasGaseousBloat) return false; @@ -2736,7 +2678,7 @@ bool IccPutricideGasCloudAction::HandleGaseousBloatMovement(Unit* gasCloud) return false; } -bool IccPutricideGasCloudAction::FindSafeMovementPosition(const Position& botPos, const Position& cloudPos, float dx, +bool IccPutricideGasCloudAction::FindSafeMovementPosition(Position const& botPos, Position const& cloudPos, float dx, float dy, int numAngles, Position& resultPos) { float bestScore = 0.0f; @@ -2800,13 +2742,12 @@ bool IccPutricideGasCloudAction::FindSafeMovementPosition(const Position& botPos return foundPath; } -Position IccPutricideGasCloudAction::CalculateEmergencyPosition(const Position& botPos, float dx, float dy) +Position IccPutricideGasCloudAction::CalculateEmergencyPosition(Position const& botPos, float dx, float dy) { // For emergency, still try to avoid corners but prioritize getting away from immediate danger Position bestPos = Position(botPos.GetPositionX() + dx * 15.0f, botPos.GetPositionY() + dy * 15.0f, botPos.GetPositionZ()); float bestFreedom = 0.0f; - static const float MOVEMENT_INCREMENT = 5.0f; // Fixed movement increment // Try fewer directions for emergency but still avoid corners for (int i = 0; i < 8; i++) @@ -2851,7 +2792,7 @@ Position IccPutricideGasCloudAction::CalculateEmergencyPosition(const Position& bool IccPutricideGasCloudAction::HandleGroupAuraSituation(Unit* gasCloud) { Group* group = bot->GetGroup(); - if (!group || botAI->IsHeal(bot)) + if (!group || BotRoleService::IsHealStatic(bot)) return false; // Mark gas cloud with skull if no volatile ooze is present or alive @@ -2889,7 +2830,7 @@ bool IccPutricideGasCloudAction::HandleGroupAuraSituation(Unit* gasCloud) bot->SetFacingToObject(gasCloud); // Attack logic for group with Gaseous Bloat - if (botAI->IsRanged(bot)) + if (BotRoleService::IsRangedStatic(bot)) { // For ranged attackers, maintain optimal distance (15-25 yards) if (currentDist > 25.0f) @@ -2913,7 +2854,7 @@ bool IccPutricideGasCloudAction::HandleGroupAuraSituation(Unit* gasCloud) return Attack(gasCloud); } } - else if (botAI->IsMelee(bot) && !botAI->IsTank(bot)) + else if (BotRoleService::IsMeleeStatic(bot) && !BotRoleService::IsTankStatic(bot)) { // For melee attackers, move to attack range (0-5 yards) if (currentDist > 5.0f) @@ -2936,13 +2877,13 @@ bool IccPutricideGasCloudAction::GroupHasGaseousBloat(Group* group) for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) { Player* member = itr->GetSource(); - if (member && botAI->HasAura("Gaseous Bloat", member)) + if (member && botAI->GetServices().GetSpellService().HasAura("Gaseous Bloat", member)) return true; } return false; } -bool IccPutricideAvoidMalleableGooAction::Execute(Event event) +bool IccPutricideAvoidMalleableGooAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide"); if (!boss) @@ -2964,9 +2905,9 @@ bool IccPutricideAvoidMalleableGooAction::Execute(Event event) return HandleBossPositioning(boss); } -bool IccPutricideAvoidMalleableGooAction::HandleTankPositioning(Unit* boss) +bool IccPutricideAvoidMalleableGooAction::HandleTankPositioning(Unit* /*boss*/) { - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) return false; Unit* bomb = bot->FindNearestCreature(NPC_CHOKING_GAS_BOMB, 100.0f); @@ -2987,7 +2928,7 @@ bool IccPutricideAvoidMalleableGooAction::HandleUnboundPlague(Unit* boss) if (boss && boss->HealthBelowPct(35)) return false; - if (!botAI->HasAura("Unbound Plague", bot)) + if (!botAI->GetServices().GetSpellService().HasAura("Unbound Plague", bot)) return false; Group* group = bot->GetGroup(); @@ -3045,7 +2986,7 @@ bool IccPutricideAvoidMalleableGooAction::HandleUnboundPlague(Unit* boss) bool IccPutricideAvoidMalleableGooAction::HandleBossPositioning(Unit* boss) { - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) return false; // If boss is close to putricide_bad_position, all non-tank bots should be 1f in front of boss @@ -3065,14 +3006,14 @@ bool IccPutricideAvoidMalleableGooAction::HandleBossPositioning(Unit* boss) if (bot->GetExactDist2d(targetX, targetY) > 0.5f) { bot->SetFacingToObject(boss); - return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, botAI->IsRanged(bot), + return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, BotRoleService::IsRangedStatic(bot), MovementPriority::MOVEMENT_COMBAT); } return false; } float distToBoss = bot->GetExactDist2d(boss); - bool isRanged = botAI->IsRanged(bot); + bool isRanged = BotRoleService::IsRangedStatic(bot); // Calculate desired position in front of boss float desiredDistance = @@ -3080,7 +3021,7 @@ bool IccPutricideAvoidMalleableGooAction::HandleBossPositioning(Unit* boss) // Check if we need to move if ((std::abs(distToBoss - desiredDistance) > 0.5f || !boss->isInFront(bot)) && - (!isRanged || (isRanged && !botAI->IsTank(bot)))) + (!isRanged || (isRanged && !BotRoleService::IsTankStatic(bot)))) { Position targetPos = CalculateBossPosition(boss, desiredDistance); @@ -3119,7 +3060,7 @@ Position IccPutricideAvoidMalleableGooAction::CalculateBossPosition(Unit* boss, boss->GetPositionY() + sin(bossOrientation) * distance, boss->GetPositionZ()); } -bool IccPutricideAvoidMalleableGooAction::HasObstacleBetween(const Position& from, const Position& to) +bool IccPutricideAvoidMalleableGooAction::HasObstacleBetween(Position const& from, Position const& to) { GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); for (auto const& npc : npcs) @@ -3137,7 +3078,7 @@ bool IccPutricideAvoidMalleableGooAction::HasObstacleBetween(const Position& fro return false; } -bool IccPutricideAvoidMalleableGooAction::IsOnPath(const Position& from, const Position& to, const Position& point, +bool IccPutricideAvoidMalleableGooAction::IsOnPath(Position const& from, Position const& to, Position const& point, float threshold) { float pathX = to.GetPositionX() - from.GetPositionX(); @@ -3165,7 +3106,7 @@ bool IccPutricideAvoidMalleableGooAction::IsOnPath(const Position& from, const P return distToPath < threshold; } -Position IccPutricideAvoidMalleableGooAction::CalculateArcPoint(const Position& current, const Position& target, const Position& center) +Position IccPutricideAvoidMalleableGooAction::CalculateArcPoint(Position const& current, Position const& target, Position const& center) { // Calculate vectors from center to current position and target float currentX = current.GetPositionX() - center.GetPositionX(); @@ -3211,7 +3152,7 @@ Position IccPutricideAvoidMalleableGooAction::CalculateArcPoint(const Position& current.GetPositionZ()); } -Position IccPutricideAvoidMalleableGooAction::CalculateIncrementalMove(const Position& current, const Position& target, +Position IccPutricideAvoidMalleableGooAction::CalculateIncrementalMove(Position const& current, Position const& target, float maxDistance) { float dx = target.GetPositionX() - current.GetPositionX(); @@ -3229,9 +3170,9 @@ Position IccPutricideAvoidMalleableGooAction::CalculateIncrementalMove(const Pos } // BPC -bool IccBpcKelesethTankAction::Execute(Event event) +bool IccBpcKelesethTankAction::Execute(Event /*event*/) { - if (!botAI->IsAssistTank(bot)) + if (!BotRoleService::IsAssistTankStatic(bot)) return false; // Handle boss positioning @@ -3274,7 +3215,7 @@ bool IccBpcKelesethTankAction::Execute(Event event) } // Positioning logic - only execute if no nucleus needs collecting - if (botAI->HasAura("Invocation of Blood", boss) && bot->GetExactDist2d(ICC_BPC_MT_POSITION) > 15.0f && isBossVictim) + if (botAI->GetServices().GetSpellService().HasAura("Invocation of Blood", boss) && bot->GetExactDist2d(ICC_BPC_MT_POSITION) > 15.0f && isBossVictim) { // Calculate direction vector float dirX = ICC_BPC_MT_POSITION.GetPositionX() - bot->GetPositionX(); @@ -3310,10 +3251,10 @@ bool IccBpcKelesethTankAction::Execute(Event event) return false; } -bool IccBpcMainTankAction::Execute(Event event) +bool IccBpcMainTankAction::Execute(Event /*event*/) { // Main tank specific behavior (higher priority) - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) { // Get target princes auto* valanar = AI_VALUE2(Unit*, "find target", "prince valanar"); @@ -3372,7 +3313,7 @@ bool IccBpcMainTankAction::Execute(Event event) } // Target marking for all tanks, called after main tank priority actions - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) MarkEmpoweredPrince(); return false; @@ -3384,7 +3325,7 @@ void IccBpcMainTankAction::MarkEmpoweredPrince() // Find empowered prince (Invocation of Blood) Unit* empoweredPrince = nullptr; - const GuidVector& targets = AI_VALUE(GuidVector, "possible targets"); + GuidVector const& targets = AI_VALUE(GuidVector, "possible targets"); for (auto const& targetGuid : targets) { @@ -3392,7 +3333,7 @@ void IccBpcMainTankAction::MarkEmpoweredPrince() if (!unit || !unit->IsAlive()) continue; - if (botAI->HasAura("Invocation of Blood", unit)) + if (botAI->GetServices().GetSpellService().HasAura("Invocation of Blood", unit)) { const uint32 entry = unit->GetEntry(); if (entry == NPC_PRINCE_KELESETH || entry == NPC_PRINCE_VALANAR || entry == NPC_PRINCE_TALDARAM) @@ -3427,7 +3368,7 @@ void IccBpcMainTankAction::MarkEmpoweredPrince() } } -bool IccBpcEmpoweredVortexAction::Execute(Event event) +bool IccBpcEmpoweredVortexAction::Execute(Event /*event*/) { Unit* valanar = AI_VALUE2(Unit*, "find target", "prince valanar"); if (!valanar) @@ -3459,7 +3400,7 @@ bool IccBpcEmpoweredVortexAction::MaintainRangedSpacing() const float safeSpacingRadius = 7.0f; const float moveIncrement = 2.0f; const float maxMoveDistance = 5.0f; - const bool isRanged = botAI->IsRanged(bot) || botAI->IsHeal(bot); + const bool isRanged = BotRoleService::IsRangedStatic(bot) || BotRoleService::IsHealStatic(bot); if (!isRanged) return false; @@ -3538,7 +3479,7 @@ bool IccBpcEmpoweredVortexAction::HandleEmpoweredVortexSpread() const float safeSpacingRadius = 13.0f; const float moveIncrement = 2.0f; const float maxMoveDistance = 5.0f; - const bool isTank = botAI->IsTank(bot); + const bool isTank = BotRoleService::IsTankStatic(bot); if (isTank) return false; @@ -3612,10 +3553,10 @@ bool IccBpcEmpoweredVortexAction::HandleEmpoweredVortexSpread() return false; // Everyone is properly spaced } -bool IccBpcKineticBombAction::Execute(Event event) +bool IccBpcKineticBombAction::Execute(Event /*event*/) { // Early exit if not ranged DPS - if (!botAI->IsRangedDps(bot)) + if (!BotRoleService::IsRangedDpsStatic(bot)) return false; // Static constants @@ -3657,16 +3598,15 @@ bool IccBpcKineticBombAction::Execute(Event event) Unit* IccBpcKineticBombAction::FindOptimalKineticBomb() { - static constexpr float MAX_HEIGHT_DIFF = 20.0f; static constexpr std::array KINETIC_BOMB_ENTRIES = {NPC_KINETIC_BOMB1, NPC_KINETIC_BOMB2, NPC_KINETIC_BOMB3, NPC_KINETIC_BOMB4}; - const GuidVector targets = AI_VALUE(GuidVector, "possible targets"); + GuidVector const targets = AI_VALUE(GuidVector, "possible targets"); if (targets.empty()) return nullptr; - const float botZ = 361.18222f; Group* group = bot->GetGroup(); + float botZ = bot->GetPositionZ(); // Gather all valid kinetic bombs std::vector kineticBombs; @@ -3700,7 +3640,7 @@ Unit* IccBpcKineticBombAction::FindOptimalKineticBomb() for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) { Player* member = itr->GetSource(); - if (member && member->IsAlive() && botAI->IsRangedDps(member)) + if (member && member->IsAlive() && BotRoleService::IsRangedDpsStatic(member)) rangedDps.push_back(member); } // Sort by GUID for deterministic assignment @@ -3748,7 +3688,7 @@ bool IccBpcKineticBombAction::IsBombAlreadyHandled(Unit* bomb, Group* group) for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) { Player* member = itr->GetSource(); - if (!member || member == bot || !member->IsAlive() || !botAI->IsRangedDps(member)) + if (!member || member == bot || !member->IsAlive() || !BotRoleService::IsRangedDpsStatic(member)) continue; if (member->GetTarget() == bomb->GetGUID() && member->GetDistance(bomb) < bot->GetDistance(bomb)) @@ -3758,7 +3698,7 @@ bool IccBpcKineticBombAction::IsBombAlreadyHandled(Unit* bomb, Group* group) return false; } -bool IccBpcBallOfFlameAction::Execute(Event event) +bool IccBpcBallOfFlameAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "prince taldaram"); if (!boss) @@ -3773,7 +3713,7 @@ bool IccBpcBallOfFlameAction::Execute(Event event) if (flame2 && (flame2->GetDistance2d(boss) > 2.0f) && !(flame2->GetDistance2d(boss) > 10.0f) && !infernoFlame && bot->getClass() != CLASS_HUNTER) { - if (!botAI->IsTank(bot) && !(flame2->GetVictim() == bot)) + if (!BotRoleService::IsTankStatic(bot) && !(flame2->GetVictim() == bot)) { float targetX = flame2->GetPositionX(); float targetY = flame2->GetPositionY(); @@ -3787,7 +3727,7 @@ bool IccBpcBallOfFlameAction::Execute(Event event) // Normalize and scale to 5 units (or remaining distance if less than 5) float step = std::min(5.0f, distance); - if (distance > 0.1) + if (distance > 0.1f) { dx = dx / distance * step; dy = dy / distance * step; @@ -3839,15 +3779,15 @@ bool IccBpcBallOfFlameAction::Execute(Event event) } // Blood Queen Lana'thel -bool IccBqlGroupPositionAction::Execute(Event event) +bool IccBqlGroupPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "blood-queen lana'thel"); if (!boss) return false; - Aura* frenzyAura = botAI->GetAura("Frenzied Bloodthirst", bot); - Aura* shadowAura = botAI->GetAura("Swarming Shadows", bot); - bool isTank = botAI->IsTank(bot); + Aura* frenzyAura = botAI->GetServices().GetSpellService().GetAura("Frenzied Bloodthirst", bot); + Aura* shadowAura = botAI->GetServices().GetSpellService().GetAura("Swarming Shadows", bot); + bool isTank = BotRoleService::IsTankStatic(bot); // Handle tank positioning if (isTank && HandleTankPosition(boss, frenzyAura, shadowAura)) return true; @@ -3869,7 +3809,7 @@ bool IccBqlGroupPositionAction::HandleTankPosition(Unit* boss, Aura* frenzyAura, return false; // Main tank positioning - if (botAI->IsMainTank(bot) && botAI->HasAggro(boss)) + if (BotRoleService::IsMainTankStatic(bot) && botAI->GetServices().GetRoleService().HasAggro(boss)) { if (bot->GetExactDist2d(ICC_BQL_TANK_POSITION) > 3.0f) { @@ -3880,7 +3820,7 @@ bool IccBqlGroupPositionAction::HandleTankPosition(Unit* boss, Aura* frenzyAura, } // Assist tank positioning - if (botAI->IsAssistTank(bot) && !botAI->GetAura("Blood Mirror", bot)) + if (BotRoleService::IsAssistTankStatic(bot) && !botAI->GetServices().GetSpellService().GetAura("Blood Mirror", bot)) { if (Unit* mainTank = AI_VALUE(Unit*, "main tank")) { @@ -3898,8 +3838,8 @@ bool IccBqlGroupPositionAction::HandleShadowsMovement() const float ARC_STEP = 0.05f; const float CURVE_SPACING = 15.0f; const int MAX_CURVES = 3; - const float maxClosestDist = botAI->IsMelee(bot) ? 25.0f : 20.0f; - const Position& center = ICC_BQL_CENTER_POSITION; + const float maxClosestDist = BotRoleService::IsMeleeStatic(bot) ? 25.0f : 20.0f; + Position const& center = ICC_BQL_CENTER_POSITION; const float OUTER_CURVE_PREFERENCE = 200.0f; // Strong preference for outer curves const float CURVE_SWITCH_PENALTY = 50.0f; // Penalty for switching curves const float DISTANCE_PENALTY_FACTOR = 100.0f; // Penalty per yard moved from current position @@ -3920,7 +3860,7 @@ bool IccBqlGroupPositionAction::HandleShadowsMovement() GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); Unit* shadows[100]{}; // Reasonable max estimate int shadowCount = 0; - for (int i = 0; i < npcs.size() && shadowCount < 100; i++) + for (size_t i = 0; i < npcs.size() && shadowCount < 100; i++) { Unit* unit = botAI->GetUnit(npcs[i]); if (unit && unit->IsAlive() && unit->GetEntry() == NPC_SWARMING_SHADOWS) @@ -3928,7 +3868,7 @@ bool IccBqlGroupPositionAction::HandleShadowsMovement() } // Helper lambda to check if a position is inside a shadow - auto IsPositionInShadow = [&](const Position& pos) -> bool + auto IsPositionInShadow = [&](Position const& pos) -> bool { for (int i = 0; i < shadowCount; ++i) { @@ -4021,7 +3961,6 @@ bool IccBqlGroupPositionAction::HandleShadowsMovement() // Find closest safe point by searching in both directions from closest point Position safeMoveTarget = closestPoint; - float safeMoveTargetDist = FLT_MAX; bool foundSafe = closestIsSafe; // Only search for safe spots if the closest point isn't already safe @@ -4092,7 +4031,6 @@ bool IccBqlGroupPositionAction::HandleShadowsMovement() if (foundSafe) { // If we found a safe point, penalize based on travel distance along the curve to reach it - float stepsToCurve = minDist / 2.0f; // Approximate steps to reach the curve float safeDist = bot->GetExactDist2d(safeMoveTarget); // Add distance penalty based on how far we need to move along the curve @@ -4280,7 +4218,7 @@ bool IccBqlGroupPositionAction::HandleShadowsMovement() return false; } -Position IccBqlGroupPositionAction::AdjustControlPoint(const Position& wall, const Position& center, float factor) +Position IccBqlGroupPositionAction::AdjustControlPoint(Position const& wall, Position const& center, float factor) { float dx = wall.GetPositionX() - center.GetPositionX(); float dy = wall.GetPositionY() - center.GetPositionY(); @@ -4315,8 +4253,8 @@ bool IccBqlGroupPositionAction::HandleGroupPosition(Unit* boss, Aura* frenzyAura return false; GuidVector members = AI_VALUE(GuidVector, "group members"); - bool isRanged = botAI->IsRanged(bot); - bool isMelee = botAI->IsMelee(bot); + bool isRanged = BotRoleService::IsRangedStatic(bot); + bool isMelee = BotRoleService::IsMeleeStatic(bot); if (isRanged && bot->GetExactDist2d(boss->GetPositionX(), boss->GetPositionY()) > 35.0f) MoveTo(boss, 5.0f, MovementPriority::MOVEMENT_FORCED); @@ -4342,9 +4280,9 @@ bool IccBqlGroupPositionAction::HandleGroupPosition(Unit* boss, Aura* frenzyAura Player* player = member->ToPlayer(); if (!player) continue; - if (botAI->IsRanged(player)) + if (BotRoleService::IsRangedStatic(player)) rangedBots.push_back(player); - if (botAI->IsHeal(player)) + if (BotRoleService::IsHealStatic(player)) healers.push_back(player); } // Remove duplicates (healer can be ranged) @@ -4373,7 +4311,6 @@ bool IccBqlGroupPositionAction::HandleGroupPosition(Unit* boss, Aura* frenzyAura rangedBots.erase(std::remove(rangedBots.begin(), rangedBots.end(), h), rangedBots.end()); // Distribute remaining ranged evenly - size_t totalRanged = leftSide.size() + rightSide.size() + rangedBots.size(); size_t leftCount = leftSide.size(); size_t rightCount = rightSide.size(); for (Player* p : rangedBots) @@ -4432,7 +4369,7 @@ bool IccBqlGroupPositionAction::HandleGroupPosition(Unit* boss, Aura* frenzyAura // Spread from other assigned members on the same side and from swarming shadows float totalX = 0.0f, totalY = 0.0f; int nearbyCount = 0; - const std::vector& mySide = isLeft ? leftSide : rightSide; + std::vector const& mySide = isLeft ? leftSide : rightSide; for (Player* member : mySide) { if (!member || !member->IsAlive() || member == bot) @@ -4532,18 +4469,18 @@ bool IccBqlGroupPositionAction::HandleGroupPosition(Unit* boss, Aura* frenzyAura // Find all swarming shadows GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); std::vector swarmingShadows; - for (int i = 0; i < npcs.size(); ++i) + for (size_t i = 0; i < npcs.size(); ++i) { Unit* unit = botAI->GetUnit(npcs[i]); if (unit && unit->IsAlive() && unit->GetEntry() == NPC_SWARMING_SHADOWS) swarmingShadows.push_back(unit); } - for (int i = 0; i < members.size(); i++) + for (size_t i = 0; i < members.size(); i++) { Unit* member = botAI->GetUnit(members[i]); - if (!member || !member->IsAlive() || member == bot || botAI->GetAura("Frenzied Bloodthirst", member) || - botAI->GetAura("Uncontrollable Frenzy", member)) + if (!member || !member->IsAlive() || member == bot || botAI->GetServices().GetSpellService().GetAura("Frenzied Bloodthirst", member) || + botAI->GetServices().GetSpellService().GetAura("Uncontrollable Frenzy", member)) continue; float distance = bot->GetExactDist2d(member); @@ -4601,10 +4538,10 @@ bool IccBqlGroupPositionAction::HandleGroupPosition(Unit* boss, Aura* frenzyAura return false; } -bool IccBqlPactOfDarkfallenAction::Execute(Event event) +bool IccBqlPactOfDarkfallenAction::Execute(Event /*event*/) { // Check if bot has Pact of the Darkfallen - if (!botAI->GetAura("Pact of the Darkfallen", bot)) + if (!botAI->GetServices().GetSpellService().GetAura("Pact of the Darkfallen", bot)) return false; Group* group = bot->GetGroup(); if (!group) @@ -4619,10 +4556,10 @@ bool IccBqlPactOfDarkfallenAction::Execute(Event event) Player* member = itr->GetSource(); if (!member || member == bot) continue; - if (botAI->GetAura("Pact of the Darkfallen", member)) + if (botAI->GetServices().GetSpellService().GetAura("Pact of the Darkfallen", member)) { playersWithAura.push_back(member); - if (botAI->IsTank(member)) + if (BotRoleService::IsTankStatic(member)) tankWithAura = member; } } @@ -4635,7 +4572,7 @@ bool IccBqlPactOfDarkfallenAction::Execute(Event event) if (tankWithAura) { // If there's a tank with aura, everyone moves to the tank (including the tank itself for center positioning) - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) { // If current bot is the tank, stay put or move slightly for better positioning targetPos.Relocate(bot); @@ -4667,7 +4604,7 @@ bool IccBqlPactOfDarkfallenAction::Execute(Event event) } void IccBqlPactOfDarkfallenAction::CalculateCenterPosition(Position& targetPos, - const std::vector& playersWithAura) + std::vector const& playersWithAura) { float sumX = bot->GetPositionX(); float sumY = bot->GetPositionY(); @@ -4686,7 +4623,7 @@ void IccBqlPactOfDarkfallenAction::CalculateCenterPosition(Position& targetPos, targetPos.Relocate(sumX / totalPlayers, sumY / totalPlayers, sumZ / totalPlayers); } -bool IccBqlPactOfDarkfallenAction::MoveToTargetPosition(const Position& targetPos, int auraCount) +bool IccBqlPactOfDarkfallenAction::MoveToTargetPosition(Position const& targetPos, int auraCount) { const float POSITION_TOLERANCE = 0.1f; float distance = bot->GetDistance(targetPos); @@ -4720,10 +4657,10 @@ bool IccBqlPactOfDarkfallenAction::MoveToTargetPosition(const Position& targetPo return false; } -bool IccBqlVampiricBiteAction::Execute(Event event) +bool IccBqlVampiricBiteAction::Execute(Event /*event*/) { // Only act when bot has Frenzied Bloodthirst - if (!botAI->GetAura("Frenzied Bloodthirst", bot)) + if (!botAI->GetServices().GetSpellService().GetAura("Frenzied Bloodthirst", bot)) return false; const float BITE_RANGE = 2.0f; @@ -4766,7 +4703,7 @@ Player* IccBqlVampiricBiteAction::FindBestBiteTarget(Group* group) if (!member || !member->IsAlive() || member == bot) continue; - if (botAI->GetAura("Frenzied Bloodthirst", member) && member->GetTarget()) + if (botAI->GetServices().GetSpellService().GetAura("Frenzied Bloodthirst", member) && member->GetTarget()) { currentlyTargetedPlayers.insert(member->GetTarget()); } @@ -4783,9 +4720,9 @@ Player* IccBqlVampiricBiteAction::FindBestBiteTarget(Group* group) continue; float distance = bot->GetDistance(member); - if (botAI->IsDps(member)) + if (BotRoleService::IsDpsStatic(member)) dpsTargets.push_back({member, distance}); - else if (botAI->IsHeal(member)) + else if (BotRoleService::IsHealStatic(member)) healTargets.push_back({member, distance}); } @@ -4804,9 +4741,9 @@ Player* IccBqlVampiricBiteAction::FindBestBiteTarget(Group* group) bool IccBqlVampiricBiteAction::IsInvalidTarget(Player* player) { - return botAI->GetAura("Frenzied Bloodthirst", player) || botAI->GetAura("Essence of the Blood Queen", player) || - botAI->GetAura("Uncontrollable Frenzy", player) || botAI->GetAura("Swarming Shadows", player) || - botAI->IsTank(player); + return botAI->GetServices().GetSpellService().GetAura("Frenzied Bloodthirst", player) || botAI->GetServices().GetSpellService().GetAura("Essence of the Blood Queen", player) || + botAI->GetServices().GetSpellService().GetAura("Uncontrollable Frenzy", player) || botAI->GetServices().GetSpellService().GetAura("Swarming Shadows", player) || + BotRoleService::IsTankStatic(player); } bool IccBqlVampiricBiteAction::MoveTowardsTarget(Player* target) @@ -4853,11 +4790,11 @@ bool IccBqlVampiricBiteAction::CastVampiricBite(Player* target) if (IsInvalidTarget(target) || !target->IsAlive()) return false; - return botAI->CanCastSpell("Vampiric Bite", target) && botAI->CastSpell("Vampiric Bite", target); + return botAI->GetServices().GetSpellService().CanCastSpell("Vampiric Bite", target) && botAI->GetServices().GetSpellService().CastSpell("Vampiric Bite", target); } // Sister Svalna -bool IccValkyreSpearAction::Execute(Event event) +bool IccValkyreSpearAction::Execute(Event /*event*/) { // Find the nearest spear Creature* spear = bot->FindNearestCreature(NPC_SPEAR, 100.0f); @@ -4869,7 +4806,7 @@ bool IccValkyreSpearAction::Execute(Event event) return MoveTo(spear, INTERACTION_DISTANCE); // Remove shapeshift forms - botAI->RemoveShapeshift(); + botAI->GetServices().GetSpellService().RemoveShapeshift(); // Stop movement and click the spear bot->GetMotionMaster()->Clear(); @@ -4883,23 +4820,23 @@ bool IccValkyreSpearAction::Execute(Event event) return false; } -bool IccSisterSvalnaAction::Execute(Event event) +bool IccSisterSvalnaAction::Execute(Event /*event*/) { Unit* svalna = AI_VALUE2(Unit*, "find target", "sister svalna"); if (!svalna || !svalna->HasAura(SPELL_AETHER_SHIELD)) // Check for Aether Shield aura return false; // Check if bot has the spear item - if (!botAI->HasItemInInventory(ITEM_SPEAR)) + if (!botAI->GetServices().GetItemService().HasItemInInventory(ITEM_SPEAR)) return false; // Get all items from inventory - std::vector items = botAI->GetInventoryItems(); + std::vector items = botAI->GetServices().GetItemService().GetInventoryItems(); for (Item* item : items) { if (item->GetEntry() == ITEM_SPEAR) // Spear ID { - botAI->ImbueItem(item, svalna); // Use spear on Svalna + botAI->GetServices().GetItemService().ImbueItem(item, svalna); // Use spear on Svalna return false; } } @@ -4908,7 +4845,7 @@ bool IccSisterSvalnaAction::Execute(Event event) } // VDW -bool IccValithriaGroupAction::Execute(Event event) +bool IccValithriaGroupAction::Execute(Event /*event*/) { // Helper lambda to find nearest creature of given entries auto findNearestCreature = [this](std::initializer_list entries, float range) -> Creature* @@ -4945,7 +4882,7 @@ bool IccValithriaGroupAction::Execute(Event event) } // Tank behavior - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) { for (auto const& targetGuid : AI_VALUE(GuidVector, "possible targets")) { @@ -4957,7 +4894,7 @@ bool IccValithriaGroupAction::Execute(Event event) // Skip if unit is already attacking any tank if (Unit* victim = unit->GetVictim()) { - if (victim->IsPlayer() && botAI->IsTank(static_cast(victim))) + if (victim->IsPlayer() && BotRoleService::IsTankStatic(static_cast(victim))) { continue; } @@ -4976,14 +4913,14 @@ bool IccValithriaGroupAction::Execute(Event event) } // Healer movement logic - if (botAI->IsHeal(bot) && bot->GetExactDist2d(ICC_VDW_HEAL_POSITION) > 30.0f && !portal) + if (BotRoleService::IsHealStatic(bot) && bot->GetExactDist2d(ICC_VDW_HEAL_POSITION) > 30.0f && !portal) return MoveTo(bot->GetMapId(), ICC_VDW_HEAL_POSITION.GetPositionX(), ICC_VDW_HEAL_POSITION.GetPositionY(), ICC_VDW_HEAL_POSITION.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_NORMAL); // Avoidance behaviors if (manaVoid && bot->GetExactDist2d(manaVoid) < 10.0f && - !(botAI->GetAura("Twisted Nightmares", bot) || botAI->GetAura("Emerald Vigor", bot))) + !(botAI->GetServices().GetSpellService().GetAura("Twisted Nightmares", bot) || botAI->GetServices().GetSpellService().GetAura("Emerald Vigor", bot))) { botAI->Reset(); FleePosition(manaVoid->GetPosition(), 11.0f, 250U); @@ -4998,13 +4935,13 @@ bool IccValithriaGroupAction::Execute(Event event) } } - if (worm && worm->IsAlive() && worm->GetVictim() == bot && !botAI->IsTank(bot)) + if (worm && worm->IsAlive() && worm->GetVictim() == bot && !BotRoleService::IsTankStatic(bot)) { botAI->Reset(); FleePosition(worm->GetPosition(), 10.0f, 250U); } - if (zombie && zombie->IsAlive() && zombie->GetVictim() == bot && !botAI->IsTank(bot) && + if (zombie && zombie->IsAlive() && zombie->GetVictim() == bot && !BotRoleService::IsTankStatic(bot) && bot->GetExactDist2d(zombie) < 20.0f) { botAI->Reset(); @@ -5012,41 +4949,41 @@ bool IccValithriaGroupAction::Execute(Event event) } // Crowd control logic - if (zombie && !botAI->IsMainTank(bot) && !botAI->IsHeal(bot) && zombie->GetVictim() != bot) + if (zombie && !BotRoleService::IsMainTankStatic(bot) && !BotRoleService::IsHealStatic(bot) && zombie->GetVictim() != bot) { switch (bot->getClass()) { case CLASS_MAGE: - if (!botAI->HasAura("Frost Nova", zombie)) - botAI->CastSpell("Frost Nova", zombie); + if (!botAI->GetServices().GetSpellService().HasAura("Frost Nova", zombie)) + botAI->GetServices().GetSpellService().CastSpell("Frost Nova", zombie); break; case CLASS_DRUID: - if (!botAI->HasAura("Entangling Roots", zombie)) - botAI->CastSpell("Entangling Roots", zombie); + if (!botAI->GetServices().GetSpellService().HasAura("Entangling Roots", zombie)) + botAI->GetServices().GetSpellService().CastSpell("Entangling Roots", zombie); break; case CLASS_PALADIN: - if (!botAI->HasAura("Hammer of Justice", zombie)) - botAI->CastSpell("Hammer of Justice", zombie); + if (!botAI->GetServices().GetSpellService().HasAura("Hammer of Justice", zombie)) + botAI->GetServices().GetSpellService().CastSpell("Hammer of Justice", zombie); break; case CLASS_WARRIOR: - if (!botAI->HasAura("Hamstring", zombie)) - botAI->CastSpell("Hamstring", zombie); + if (!botAI->GetServices().GetSpellService().HasAura("Hamstring", zombie)) + botAI->GetServices().GetSpellService().CastSpell("Hamstring", zombie); break; case CLASS_HUNTER: - if (!botAI->HasAura("Concussive Shot", zombie)) - botAI->CastSpell("Concussive Shot", zombie); + if (!botAI->GetServices().GetSpellService().HasAura("Concussive Shot", zombie)) + botAI->GetServices().GetSpellService().CastSpell("Concussive Shot", zombie); break; case CLASS_ROGUE: - if (!botAI->HasAura("Kidney Shot", zombie)) - botAI->CastSpell("Kidney Shot", zombie); + if (!botAI->GetServices().GetSpellService().HasAura("Kidney Shot", zombie)) + botAI->GetServices().GetSpellService().CastSpell("Kidney Shot", zombie); break; case CLASS_SHAMAN: - if (!botAI->HasAura("Frost Shock", zombie)) - botAI->CastSpell("Frost Shock", zombie); + if (!botAI->GetServices().GetSpellService().HasAura("Frost Shock", zombie)) + botAI->GetServices().GetSpellService().CastSpell("Frost Shock", zombie); break; case CLASS_DEATH_KNIGHT: - if (!botAI->HasAura("Chains of Ice", zombie)) - botAI->CastSpell("Chains of Ice", zombie); + if (!botAI->GetServices().GetSpellService().HasAura("Chains of Ice", zombie)) + botAI->GetServices().GetSpellService().CastSpell("Chains of Ice", zombie); break; default: break; @@ -5063,7 +5000,7 @@ bool IccValithriaGroupAction::Execute(Event event) return Handle10ManGroupLogic(); } -bool IccValithriaGroupAction::MoveTowardsPosition(const Position& pos, float increment) +bool IccValithriaGroupAction::MoveTowardsPosition(Position const& pos, float increment) { float dx = pos.GetPositionX() - bot->GetPositionX(); float dy = pos.GetPositionY() - bot->GetPositionY(); @@ -5109,9 +5046,9 @@ bool IccValithriaGroupAction::Handle25ManGroupLogic() { if (Player* member = itr->GetSource()) { - if (member->IsAlive() && !botAI->IsHeal(member)) + if (member->IsAlive() && !BotRoleService::IsHealStatic(member)) { - if (botAI->IsTank(member)) + if (BotRoleService::IsTankStatic(member)) { tanks.push_back(member); } @@ -5157,11 +5094,11 @@ bool IccValithriaGroupAction::Handle25ManGroupLogic() bool inGroup2 = std::any_of(group2.begin(), group2.end(), [this](Player* p) { return p == bot; }); // Marking logic for tanks and DPS - if (botAI->IsTank(bot) || botAI->IsDps(bot)) + if (BotRoleService::IsTankStatic(bot) || BotRoleService::IsDpsStatic(bot)) HandleMarkingLogic(inGroup1, inGroup2, group1Pos, group2Pos); // Movement logic for non-healers - if (!botAI->IsHeal(bot)) + if (!BotRoleService::IsHealStatic(bot)) { if (inGroup1) { @@ -5185,16 +5122,16 @@ bool IccValithriaGroupAction::Handle25ManGroupLogic() return false; } -bool IccValithriaGroupAction::HandleMarkingLogic(bool inGroup1, bool inGroup2, const Position& group1Pos, - const Position& group2Pos) +bool IccValithriaGroupAction::HandleMarkingLogic(bool inGroup1, bool inGroup2, Position const& group1Pos, + Position const& group2Pos) { static constexpr uint8_t SKULL_ICON_INDEX = 7; static constexpr uint8_t CROSS_ICON_INDEX = 6; - static const std::array addPriority = {NPC_BLAZING_SKELETON, NPC_SUPPRESSER, + static std::array const addPriority = {NPC_BLAZING_SKELETON, NPC_SUPPRESSER, NPC_RISEN_ARCHMAGE, NPC_BLISTERING_ZOMBIE, NPC_GLUTTONOUS_ABOMINATION, NPC_ROT_WORM}; - const Position* groupPos = nullptr; + Position const* groupPos = nullptr; uint8_t iconIndex = 0; std::string rtiValue; @@ -5269,7 +5206,7 @@ bool IccValithriaGroupAction::HandleMarkingLogic(bool inGroup1, bool inGroup2, c bool IccValithriaGroupAction::Handle10ManGroupLogic() { static constexpr uint8_t DEFAULT_ICON_INDEX = 7; - static const std::array addPriority = {NPC_BLAZING_SKELETON, NPC_SUPPRESSER, + static std::array const addPriority = {NPC_BLAZING_SKELETON, NPC_SUPPRESSER, NPC_RISEN_ARCHMAGE, NPC_BLISTERING_ZOMBIE, NPC_GLUTTONOUS_ABOMINATION, NPC_ROT_WORM}; @@ -5318,10 +5255,10 @@ bool IccValithriaGroupAction::Handle10ManGroupLogic() return false; } -bool IccValithriaPortalAction::Execute(Event event) +bool IccValithriaPortalAction::Execute(Event /*event*/) { // Only healers should take portals, and not if already inside - if (!botAI->IsHeal(bot) || bot->HasAura(SPELL_DREAM_STATE)) + if (!BotRoleService::IsHealStatic(bot) || bot->HasAura(SPELL_DREAM_STATE)) return false; // Gather all portals (pre-effect and real) using nearest npcs @@ -5358,7 +5295,7 @@ bool IccValithriaPortalAction::Execute(Event event) for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) { Player* member = itr->GetSource(); - if (member && member->IsAlive() && botAI->IsHeal(member)) + if (member && member->IsAlive() && BotRoleService::IsHealStatic(member)) healers.push_back(member); } std::sort(healers.begin(), healers.end(), [](Player* a, Player* b) { return a->GetGUID() < b->GetGUID(); }); @@ -5392,7 +5329,7 @@ bool IccValithriaPortalAction::Execute(Event event) MovementPriority::MOVEMENT_NORMAL); } // Remove shapeshift forms - botAI->RemoveShapeshift(); + botAI->GetServices().GetSpellService().RemoveShapeshift(); // Try to click the real portal if it is close enough Creature* nearestRealPortal = nullptr; @@ -5409,7 +5346,7 @@ bool IccValithriaPortalAction::Execute(Event event) if (nearestRealPortal) { - botAI->RemoveShapeshift(); + botAI->GetServices().GetSpellService().RemoveShapeshift(); bot->GetMotionMaster()->Clear(); bot->StopMoving(); bot->SetFacingToObject(nearestRealPortal); @@ -5440,7 +5377,7 @@ bool IccValithriaPortalAction::Execute(Event event) if (nearestRealPortal) { - botAI->RemoveShapeshift(); + botAI->GetServices().GetSpellService().RemoveShapeshift(); bot->GetMotionMaster()->Clear(); bot->StopMoving(); bot->SetFacingToObject(nearestRealPortal); @@ -5451,10 +5388,10 @@ bool IccValithriaPortalAction::Execute(Event event) return false; } -bool IccValithriaHealAction::Execute(Event event) +bool IccValithriaHealAction::Execute(Event /*event*/) { // Early validation checks - if (!botAI->IsHeal(bot) || bot->GetHealthPct() < 50.0f) + if (!BotRoleService::IsHealStatic(bot) || bot->GetHealthPct() < 50.0f) return false; // Handle movement speed when not in dream state @@ -5492,19 +5429,19 @@ bool IccValithriaHealAction::Execute(Event event) // Apply Rejuvenation if missing if (!valithria->HasAura(SPELL_REJUVENATION, bot->GetGUID())) - return botAI->CastSpell(SPELL_REJUVENATION, valithria); + return botAI->GetServices().GetSpellService().CastSpell(SPELL_REJUVENATION, valithria); // Apply Regrowth if missing if (!valithria->HasAura(SPELL_REGROWTH, bot->GetGUID())) - return botAI->CastSpell(SPELL_REGROWTH, valithria); + return botAI->GetServices().GetSpellService().CastSpell(SPELL_REGROWTH, valithria); // Stack Lifebloom to maximum stacks Aura* lifebloom = valithria->GetAura(SPELL_LIFEBLOOM, bot->GetGUID()); if (!lifebloom || lifebloom->GetStackAmount() < LIFEBLOOM_MAX_STACKS) - return botAI->CastSpell(SPELL_LIFEBLOOM, valithria); + return botAI->GetServices().GetSpellService().CastSpell(SPELL_LIFEBLOOM, valithria); // All HoTs active with full stacks - cast Wild Growth - return botAI->CastSpell(SPELL_WILD_GROWTH, valithria); + return botAI->GetServices().GetSpellService().CastSpell(SPELL_WILD_GROWTH, valithria); } case CLASS_SHAMAN: { @@ -5512,8 +5449,8 @@ bool IccValithriaHealAction::Execute(Event event) constexpr uint32 SPELL_HEALING_WAVE = 49273; // Cast Healing Wave if Riptide is active, otherwise apply Riptide - return valithria->HasAura(SPELL_RIPTIDE, bot->GetGUID()) ? botAI->CastSpell(SPELL_HEALING_WAVE, valithria) - : botAI->CastSpell(SPELL_RIPTIDE, valithria); + return valithria->HasAura(SPELL_RIPTIDE, bot->GetGUID()) ? botAI->GetServices().GetSpellService().CastSpell(SPELL_HEALING_WAVE, valithria) + : botAI->GetServices().GetSpellService().CastSpell(SPELL_RIPTIDE, valithria); } case CLASS_PRIEST: { @@ -5521,8 +5458,8 @@ bool IccValithriaHealAction::Execute(Event event) constexpr uint32 SPELL_GREATER_HEAL = 48063; // Cast Greater Heal if Renew is active, otherwise apply Renew - return valithria->HasAura(SPELL_RENEW, bot->GetGUID()) ? botAI->CastSpell(SPELL_GREATER_HEAL, valithria) - : botAI->CastSpell(SPELL_RENEW, valithria); + return valithria->HasAura(SPELL_RENEW, bot->GetGUID()) ? botAI->GetServices().GetSpellService().CastSpell(SPELL_GREATER_HEAL, valithria) + : botAI->GetServices().GetSpellService().CastSpell(SPELL_RENEW, valithria); } case CLASS_PALADIN: { @@ -5531,8 +5468,8 @@ bool IccValithriaHealAction::Execute(Event event) // Cast Holy Light if Beacon is active, otherwise apply Beacon of Light return valithria->HasAura(SPELL_BEACON_OF_LIGHT, bot->GetGUID()) - ? botAI->CastSpell(SPELL_HOLY_LIGHT, valithria) - : botAI->CastSpell(SPELL_BEACON_OF_LIGHT, valithria); + ? botAI->GetServices().GetSpellService().CastSpell(SPELL_HOLY_LIGHT, valithria) + : botAI->GetServices().GetSpellService().CastSpell(SPELL_BEACON_OF_LIGHT, valithria); } default: return false; @@ -5541,7 +5478,7 @@ bool IccValithriaHealAction::Execute(Event event) return false; } -bool IccValithriaDreamCloudAction::Execute(Event event) +bool IccValithriaDreamCloudAction::Execute(Event /*event*/) { // Only execute if we're in dream state if (!bot->HasAura(SPELL_DREAM_STATE)) @@ -5572,7 +5509,6 @@ bool IccValithriaDreamCloudAction::Execute(Event event) auto it = std::find(dreamBots.begin(), dreamBots.end(), bot); if (it == dreamBots.end()) return false; - size_t myIndex = std::distance(dreamBots.begin(), it); // Check if all dream bots are stacked within 3f of the current leader (lowest guid) constexpr float STACK_RADIUS = 2.0f; @@ -5612,7 +5548,7 @@ bool IccValithriaDreamCloudAction::Execute(Event event) std::vector dreamClouds; std::vector nightmareClouds; - for (int i = 0; i < npcs.size(); ++i) + for (size_t i = 0; i < npcs.size(); ++i) { Unit* unit = botAI->GetUnit(npcs[i]); if (unit && unit->IsAlive()) @@ -5824,18 +5760,18 @@ bool IccValithriaDreamCloudAction::Execute(Event event) } // Sindragosa -bool IccSindragosaGroupPositionAction::Execute(Event event) +bool IccSindragosaGroupPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); if (!boss || boss->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY)) return false; - Aura* aura = botAI->GetAura("mystic buffet", bot, false, true); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("mystic buffet", bot, false, true); - if (aura && aura->GetStackAmount() >= 6 && botAI->IsMainTank(bot)) + if (aura && aura->GetStackAmount() >= 6 && BotRoleService::IsMainTankStatic(bot)) return false; - if (botAI->IsTank(bot) && boss->GetVictim() == bot) + if (BotRoleService::IsTankStatic(bot) && boss->GetVictim() == bot) return HandleTankPositioning(boss); if (boss && boss->GetVictim() != bot) @@ -5965,7 +5901,7 @@ bool IccSindragosaGroupPositionAction::HandleNonTankPositioning() size_t membersWithoutAura = 0; for (Player* member : raidMembers) { - if (!botAI->GetAura("mystic buffet", member)) + if (!botAI->GetServices().GetSpellService().GetAura("mystic buffet", member)) membersWithoutAura++; } @@ -5975,11 +5911,11 @@ bool IccSindragosaGroupPositionAction::HandleNonTankPositioning() return false; double percentageWithoutAura = static_cast(membersWithoutAura) / totalMembers; - bool raidClear = (percentageWithoutAura >= 0.6); // 60% or more don't have aura 1111 + bool raidClear = (percentageWithoutAura >= 0.6f); // 60% or more don't have aura 1111 - if (raidClear && botAI->IsTank(bot)) + if (raidClear && BotRoleService::IsTankStatic(bot)) { - static const std::array tombEntries = {NPC_TOMB1, NPC_TOMB2, NPC_TOMB3, NPC_TOMB4}; + static std::array const tombEntries = {NPC_TOMB1, NPC_TOMB2, NPC_TOMB3, NPC_TOMB4}; const GuidVector tombGuids = AI_VALUE(GuidVector, "possible targets no los"); Unit* nearestTomb = nullptr; @@ -6034,7 +5970,7 @@ bool IccSindragosaGroupPositionAction::HandleNonTankPositioning() } context->GetValue("rti")->Set("skull"); - if (botAI->IsRanged(bot)) + if (BotRoleService::IsRangedStatic(bot)) { const float TOLERANCE = 9.0f; const float MAX_STEP = 5.0f; @@ -6062,7 +5998,7 @@ bool IccSindragosaGroupPositionAction::HandleNonTankPositioning() } } -bool IccSindragosaGroupPositionAction::MoveIncrementallyToPosition(const Position& targetPos, float maxStep) +bool IccSindragosaGroupPositionAction::MoveIncrementallyToPosition(Position const& targetPos, float maxStep) { // Calculate direction vector to target float dirX = targetPos.GetPositionX() - bot->GetPositionX(); @@ -6083,14 +6019,14 @@ bool IccSindragosaGroupPositionAction::MoveIncrementallyToPosition(const Positio MovementPriority::MOVEMENT_COMBAT); } -bool IccSindragosaTankSwapPositionAction::Execute(Event event) +bool IccSindragosaTankSwapPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); if (!boss) return false; // Only for assist tank - if (!botAI->IsAssistTank(bot)) + if (!BotRoleService::IsAssistTankStatic(bot)) return false; float distToTankPos = bot->GetExactDist2d(ICC_SINDRAGOSA_TANK_POSITION); @@ -6107,9 +6043,9 @@ bool IccSindragosaTankSwapPositionAction::Execute(Event event) return false; } -bool IccSindragosaFrostBeaconAction::Execute(Event event) +bool IccSindragosaFrostBeaconAction::Execute(Event /*event*/) { - const Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); + Unit const* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); if (!boss) return false; @@ -6133,21 +6069,21 @@ void IccSindragosaFrostBeaconAction::HandleSupportActions() for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) { Player* member = itr->GetSource(); - if (!member || !member->IsAlive() || !botAI->IsTank(member)) + if (!member || !member->IsAlive() || !BotRoleService::IsTankStatic(member)) { continue; } - if (botAI->GetAura("Frost Breath", member) && !member->HasAura(HAND_OF_FREEDOM_SPELL_ID)) + if (botAI->GetServices().GetSpellService().GetAura("Frost Breath", member) && !member->HasAura(HAND_OF_FREEDOM_SPELL_ID)) { - botAI->CastSpell(HAND_OF_FREEDOM_SPELL_ID, member); + botAI->GetServices().GetSpellService().CastSpell(HAND_OF_FREEDOM_SPELL_ID, member); break; } } } // Healer support - Apply HoTs to beaconed players - if (botAI->IsHeal(bot) && !bot->HasAura(FROST_BEACON_AURA_ID)) + if (BotRoleService::IsHealStatic(bot) && !bot->HasAura(FROST_BEACON_AURA_ID)) { const auto members = AI_VALUE(GuidVector, "group members"); for (auto const& memberGuid : members) @@ -6177,13 +6113,13 @@ void IccSindragosaFrostBeaconAction::HandleSupportActions() if (!member->HasAura(spellId)) { - botAI->CastSpell(spellId, member); + botAI->GetServices().GetSpellService().CastSpell(spellId, member); } } } } -bool IccSindragosaFrostBeaconAction::HandleBeaconedPlayer(const Unit* boss) +bool IccSindragosaFrostBeaconAction::HandleBeaconedPlayer(Unit const* boss) { // Phase 3 positioning (below 35% health, not flying) if (boss->HealthBelowPct(35) && !IsBossFlying(boss)) @@ -6213,7 +6149,7 @@ bool IccSindragosaFrostBeaconAction::HandleBeaconedPlayer(const Unit* boss) } std::sort(beaconedPlayers.begin(), beaconedPlayers.end(), - [](const Player* a, const Player* b) { return a->GetGUID() < b->GetGUID(); }); + [](Player const* a, Player const* b) { return a->GetGUID() < b->GetGUID(); }); // Find this bot's index const auto it = std::find(beaconedPlayers.begin(), beaconedPlayers.end(), bot); @@ -6244,14 +6180,14 @@ bool IccSindragosaFrostBeaconAction::HandleBeaconedPlayer(const Unit* boss) } // Get tomb position and move if needed - static constexpr std::array tombPositions = { + static constexpr std::array tombPositions = { &ICC_SINDRAGOSA_THOMB1_POSITION, &ICC_SINDRAGOSA_THOMB2_POSITION, &ICC_SINDRAGOSA_THOMB3_POSITION}; - const Position& tombPosition = *tombPositions[std::min(spot, tombPositions.size() - 1)]; + Position const& tombPosition = *tombPositions[std::min(spot, tombPositions.size() - 1)]; return MoveToPositionIfNeeded(tombPosition, TOMB_POSITION_TOLERANCE); } -bool IccSindragosaFrostBeaconAction::HandleNonBeaconedPlayer(const Unit* boss) +bool IccSindragosaFrostBeaconAction::HandleNonBeaconedPlayer(Unit const* boss) { // Collect beaconed players std::vector beaconedPlayers; @@ -6280,7 +6216,7 @@ bool IccSindragosaFrostBeaconAction::HandleNonBeaconedPlayer(const Unit* boss) if (diff && (diff == RAID_DIFFICULTY_25MAN_NORMAL || diff == RAID_DIFFICULTY_25MAN_HEROIC)) is25Man = true; - const Position& safePosition = is25Man ? ICC_SINDRAGOSA_FBOMB_POSITION : ICC_SINDRAGOSA_FBOMB10_POSITION; + Position const& safePosition = is25Man ? ICC_SINDRAGOSA_FBOMB_POSITION : ICC_SINDRAGOSA_FBOMB10_POSITION; const float dist = bot->GetExactDist2d(safePosition.GetPositionX(), safePosition.GetPositionY()); if (dist > MOVE_TOLERANCE) @@ -6288,14 +6224,14 @@ bool IccSindragosaFrostBeaconAction::HandleNonBeaconedPlayer(const Unit* boss) return MoveToPosition(safePosition); } } - return botAI->IsHeal(bot); // Continue for healers, wait for others + return BotRoleService::IsHealStatic(bot); // Continue for healers, wait for others } // Ground phase - position based on role and avoid beaconed players - const bool isRanged = botAI->IsRanged(bot) || (bot->GetExactDist2d(ICC_SINDRAGOSA_RANGED_POSITION.GetPositionX(),ICC_SINDRAGOSA_RANGED_POSITION.GetPositionY()) < + const bool isRanged = BotRoleService::IsRangedStatic(bot) || (bot->GetExactDist2d(ICC_SINDRAGOSA_RANGED_POSITION.GetPositionX(),ICC_SINDRAGOSA_RANGED_POSITION.GetPositionY()) < bot->GetExactDist2d(ICC_SINDRAGOSA_MELEE_POSITION.GetPositionX(),ICC_SINDRAGOSA_MELEE_POSITION.GetPositionY())); - const Position& targetPosition = isRanged ? ICC_SINDRAGOSA_RANGED_POSITION : ICC_SINDRAGOSA_MELEE_POSITION; + Position const& targetPosition = isRanged ? ICC_SINDRAGOSA_RANGED_POSITION : ICC_SINDRAGOSA_MELEE_POSITION; const float deltaX = std::abs(targetPosition.GetPositionX() - bot->GetPositionX()); const float deltaY = std::abs(targetPosition.GetPositionY() - bot->GetPositionY()); @@ -6313,7 +6249,7 @@ bool IccSindragosaFrostBeaconAction::HandleNonBeaconedPlayer(const Unit* boss) return false; } -bool IccSindragosaFrostBeaconAction::MoveToPositionIfNeeded(const Position& position, float tolerance) +bool IccSindragosaFrostBeaconAction::MoveToPositionIfNeeded(Position const& position, float tolerance) { const float distance = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); if (distance > tolerance) @@ -6323,7 +6259,7 @@ bool IccSindragosaFrostBeaconAction::MoveToPositionIfNeeded(const Position& posi return distance <= tolerance; } -bool IccSindragosaFrostBeaconAction::MoveToPosition(const Position& position) +bool IccSindragosaFrostBeaconAction::MoveToPosition(Position const& position) { float posX = position.GetPositionX(); float posY = position.GetPositionY(); @@ -6335,20 +6271,20 @@ bool IccSindragosaFrostBeaconAction::MoveToPosition(const Position& position) true, false); } -bool IccSindragosaFrostBeaconAction::IsBossFlying(const Unit* boss) +bool IccSindragosaFrostBeaconAction::IsBossFlying(Unit const* boss) { return boss->GetExactDist2d(ICC_SINDRAGOSA_FLYING_POSITION.GetPositionX(), ICC_SINDRAGOSA_FLYING_POSITION.GetPositionY()) < 30.0f; } -bool IccSindragosaBlisteringColdAction::Execute(Event event) +bool IccSindragosaBlisteringColdAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); if (!boss) return false; // Only non-tanks should move out - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) return false; float dist = bot->GetExactDist2d(boss->GetPositionX(), boss->GetPositionY()); @@ -6389,17 +6325,17 @@ bool IccSindragosaBlisteringColdAction::Execute(Event event) return false; } -bool IccSindragosaUnchainedMagicAction::Execute(Event event) +bool IccSindragosaUnchainedMagicAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); if (!boss) return false; - Aura* aura = botAI->GetAura("Unchained Magic", bot, false, true); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("Unchained Magic", bot, false, true); if (!aura) return false; - Aura* aura1 = botAI->GetAura("Instability", bot, false, true); + Aura* aura1 = botAI->GetServices().GetSpellService().GetAura("Instability", bot, false, true); Difficulty diff = bot->GetRaidDifficulty(); if (aura && (diff == RAID_DIFFICULTY_10MAN_NORMAL || diff == RAID_DIFFICULTY_25MAN_NORMAL)) @@ -6411,13 +6347,13 @@ bool IccSindragosaUnchainedMagicAction::Execute(Event event) return false; } -bool IccSindragosaChilledToTheBoneAction::Execute(Event event) +bool IccSindragosaChilledToTheBoneAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); if (!boss) return false; - Aura* aura = botAI->GetAura("Chilled to the Bone", bot, false, true); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("Chilled to the Bone", bot, false, true); if (!aura) return false; @@ -6434,14 +6370,14 @@ bool IccSindragosaChilledToTheBoneAction::Execute(Event event) return false; } -bool IccSindragosaMysticBuffetAction::Execute(Event event) +bool IccSindragosaMysticBuffetAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); if (!boss || !bot || !bot->IsAlive()) return false; // Check if we have Mystic Buffet - Aura* aura = botAI->GetAura("mystic buffet", bot, false, true); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("mystic buffet", bot, false, true); if (!aura) return false; @@ -6456,7 +6392,7 @@ bool IccSindragosaMysticBuffetAction::Execute(Event event) if (!group) return false; - static const std::array tombEntries = {NPC_TOMB1, NPC_TOMB2, NPC_TOMB3, NPC_TOMB4}; + static std::array const tombEntries = {NPC_TOMB1, NPC_TOMB2, NPC_TOMB3, NPC_TOMB4}; const GuidVector tombGuids = AI_VALUE(GuidVector, "possible targets no los"); Unit* nearestTomb = nullptr; @@ -6503,7 +6439,7 @@ bool IccSindragosaMysticBuffetAction::Execute(Event event) if (shouldMoveLOS2) { // If already at LOS2 and have 3+ stacks, stay still - if (atLOS2 && aura && !botAI->IsHeal(bot)) + if (atLOS2 && aura && !BotRoleService::IsHealStatic(bot)) { return true; } @@ -6517,7 +6453,7 @@ bool IccSindragosaMysticBuffetAction::Execute(Event event) return false; } -bool IccSindragosaFrostBombAction::Execute(Event event) +bool IccSindragosaFrostBombAction::Execute(Event /*event*/) { if (!bot || !bot->IsAlive() || bot->HasAura(SPELL_ICE_TOMB)) // Skip if dead or in Ice Tomb return false; @@ -6580,7 +6516,7 @@ bool IccSindragosaFrostBombAction::Execute(Event event) } // Add any new GUIDs to our persistent list - for (const ObjectGuid& guid : currentGuids) + for (ObjectGuid const& guid : currentGuids) { if (std::find(allGroupGuids.begin(), allGroupGuids.end(), guid) == allGroupGuids.end()) { @@ -6598,7 +6534,7 @@ bool IccSindragosaFrostBombAction::Execute(Event event) // Assign group indices to GUIDs that don't have assignments yet for (size_t i = 0; i < allGroupGuids.size(); ++i) { - const ObjectGuid& guid = allGroupGuids[i]; + ObjectGuid const& guid = allGroupGuids[i]; if (persistentGroupAssignments.find(guid) == persistentGroupAssignments.end()) { // Assign to group based on their position in the sorted list @@ -6709,7 +6645,6 @@ bool IccSindragosaFrostBombAction::Execute(Event event) } } Unit* losTomb = myTombs[bestIdx]; - ObjectGuid losTombGuid = myTombGuids[bestIdx]; // Calculate position for LOS (stand at least 6.5f behind the tomb from the bomb) float angle = marker->GetAngle(losTomb); @@ -6792,7 +6727,6 @@ bool IccSindragosaFrostBombAction::Execute(Event event) // Clear the marker for our group's icon group->SetTargetIcon(iconIndex, bot->GetGUID(), ObjectGuid::Empty); } - Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); bot->AttackStop(); return true; } @@ -6802,10 +6736,10 @@ bool IccSindragosaFrostBombAction::Execute(Event event) } // The Lich King -bool IccLichKingShadowTrapAction::Execute(Event event) +bool IccLichKingShadowTrapAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); - if (!boss || !botAI->IsTank(bot)) + if (!boss || !BotRoleService::IsTankStatic(bot)) return false; Difficulty diff = bot->GetRaidDifficulty(); @@ -6819,7 +6753,7 @@ bool IccLichKingShadowTrapAction::Execute(Event event) if (!bot->HasAura(SPELL_AGEIS_OF_DALARAN)) bot->AddAura(SPELL_AGEIS_OF_DALARAN, bot); - if (!bot->HasAura(SPELL_NO_THREAT) && !botAI->IsTank(bot)) + if (!bot->HasAura(SPELL_NO_THREAT) && !BotRoleService::IsTankStatic(bot)) bot->AddAura(SPELL_NO_THREAT, bot); if (!bot->HasAura(SPELL_PAIN_SUPPRESION)) @@ -6929,9 +6863,9 @@ bool IccLichKingShadowTrapAction::Execute(Event event) return false; } -bool IccLichKingNecroticPlagueAction::Execute(Event event) +bool IccLichKingNecroticPlagueAction::Execute(Event /*event*/) { - bool hasPlague = botAI->HasAura("Necrotic Plague", bot); + bool hasPlague = botAI->GetServices().GetSpellService().HasAura("Necrotic Plague", bot); // Only execute if we have the plague if (!hasPlague) return false; @@ -6981,7 +6915,7 @@ bool IccLichKingNecroticPlagueAction::Execute(Event event) return false; } -bool IccLichKingWinterAction::Execute(Event event) +bool IccLichKingWinterAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); if (!boss) @@ -6990,14 +6924,14 @@ bool IccLichKingWinterAction::Execute(Event event) Unit* iceSphere = AI_VALUE2(Unit*, "find target", "ice sphere"); bool isVictim = false; - if (iceSphere && iceSphere->GetVictim() == bot && !botAI->IsTank(bot)) + if (iceSphere && iceSphere->GetVictim() == bot && !BotRoleService::IsTankStatic(bot)) isVictim = true; // First priority: Get out of Defile if we're in one if (!IsPositionSafeFromDefile(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), 3.0f)) { // Find nearest safe position (use tank position as fallback) - const Position* safePos = botAI->IsTank(bot) ? GetMainTankPosition() : GetMainTankRangedPosition(); + Position const* safePos = BotRoleService::IsTankStatic(bot) ? GetMainTankPosition() : GetMainTankRangedPosition(); TryMoveToPosition(safePos->GetPositionX(), safePos->GetPositionY(), 840.857f, true); return true; } @@ -7015,7 +6949,7 @@ bool IccLichKingWinterAction::Execute(Event event) if (!bot->HasAura(SPELL_AGEIS_OF_DALARAN)) bot->AddAura(SPELL_AGEIS_OF_DALARAN, bot); - if (!bot->HasAura(SPELL_NO_THREAT) && !botAI->IsTank(bot)) + if (!bot->HasAura(SPELL_NO_THREAT) && !BotRoleService::IsTankStatic(bot)) bot->AddAura(SPELL_NO_THREAT, bot); if (!bot->HasAura(SPELL_PAIN_SUPPRESION)) @@ -7032,7 +6966,6 @@ bool IccLichKingWinterAction::Execute(Event event) const ObjectGuid currentSkullTarget = group->GetTargetIcon(7); if (!currentSkullTarget.IsEmpty()) { - Unit* skullTarget = ObjectAccessor::GetUnit(*bot, currentSkullTarget); group->SetTargetIcon(7, bot->GetGUID(), ObjectGuid::Empty); } } @@ -7068,13 +7001,13 @@ void IccLichKingWinterAction::HandlePositionCorrection() if (currentTarget && boss && currentTarget == boss) botAI->Reset(); - if (botAI->IsTank(bot) && currentTarget && + if (BotRoleService::IsTankStatic(bot) && currentTarget && ((currentTarget->GetEntry() == NPC_ICE_SPHERE1 || currentTarget->GetEntry() == NPC_ICE_SPHERE2 || currentTarget->GetEntry() == NPC_ICE_SPHERE3 || currentTarget->GetEntry() == NPC_ICE_SPHERE4))) botAI->Reset(); } -const Position* IccLichKingWinterAction::GetMainTankPosition() +Position const* IccLichKingWinterAction::GetMainTankPosition() { Unit* mainTank = AI_VALUE(Unit*, "main tank"); if (!mainTank) @@ -7136,7 +7069,7 @@ const Position* IccLichKingWinterAction::GetMainTankPosition() return &ICC_LK_FROST1_POSITION; } -const Position* IccLichKingWinterAction::GetMainTankRangedPosition() +Position const* IccLichKingWinterAction::GetMainTankRangedPosition() { Unit* mainTank = AI_VALUE(Unit*, "main tank"); if (!mainTank) @@ -7186,7 +7119,7 @@ const Position* IccLichKingWinterAction::GetMainTankRangedPosition() } // Map main tank's melee position to corresponding ranged position - const Position* tankMeleePos = GetMainTankPosition(); + Position const* tankMeleePos = GetMainTankPosition(); if (tankMeleePos == &ICC_LK_FROST1_POSITION) return &ICC_LK_FROSTR1_POSITION; @@ -7196,7 +7129,7 @@ const Position* IccLichKingWinterAction::GetMainTankRangedPosition() return &ICC_LK_FROSTR3_POSITION; } -bool IccLichKingWinterAction::IsPositionSafeFromDefile(float x, float y, float z, float minSafeDistance) +bool IccLichKingWinterAction::IsPositionSafeFromDefile(float x, float y, float /*z*/, float minSafeDistance) { Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); if (!boss) @@ -7332,7 +7265,7 @@ bool IccLichKingWinterAction::IsValidCollectibleAdd(Unit* unit) // FIXED HandleTankPositioning method void IccLichKingWinterAction::HandleTankPositioning() { - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) return; Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); @@ -7340,7 +7273,7 @@ void IccLichKingWinterAction::HandleTankPositioning() return; // Get the target position based on main tank's choice - const Position* targetPos = GetMainTankPosition(); + Position const* targetPos = GetMainTankPosition(); // First check if current position is safe if (!IsPositionSafeFromDefile(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), 3.0f)) @@ -7353,7 +7286,7 @@ void IccLichKingWinterAction::HandleTankPositioning() float distToTarget = bot->GetDistance2d(targetPos->GetPositionX(), targetPos->GetPositionY()); // MAIN TANK: Always stay at tank position - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) { // Main tank should always maintain position at tank spot if (distToTarget > 2.0f) @@ -7370,7 +7303,7 @@ void IccLichKingWinterAction::HandleTankPositioning() HandleMainTankAddManagement(boss, targetPos); } // ASSIST TANK: More flexible positioning based on add collection - else if (botAI->IsAssistTank(bot)) + else if (BotRoleService::IsAssistTankStatic(bot)) { // First ensure we're reasonably close to tank area if (distToTarget > 15.0f) @@ -7392,11 +7325,11 @@ void IccLichKingWinterAction::HandleTankPositioning() void IccLichKingWinterAction::HandleMeleePositioning() { // Skip if this is a tank - they have their own positioning logic - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) return; Unit* currentTarget = AI_VALUE(Unit*, "current target"); // Handle melee positioning behind target (for DPS only) - if (currentTarget && !botAI->IsRanged(bot) && currentTarget->isInFront(bot) && currentTarget->IsAlive() && + if (currentTarget && !BotRoleService::IsRangedStatic(bot) && currentTarget->isInFront(bot) && currentTarget->IsAlive() && currentTarget->GetEntry() != NPC_THE_LICH_KING && currentTarget->GetEntry() != NPC_ICE_SPHERE1 && currentTarget->GetEntry() != NPC_ICE_SPHERE2 && currentTarget->GetEntry() != NPC_ICE_SPHERE3 && currentTarget->GetEntry() != NPC_ICE_SPHERE4) { @@ -7426,10 +7359,10 @@ void IccLichKingWinterAction::HandleMeleePositioning() TryMoveToPosition(newX, newY, z, false); } // Handle non-ranged DPS positioning - USE MAIN TANK'S POSITION - if (!botAI->IsRanged(bot)) + if (!BotRoleService::IsRangedStatic(bot)) { Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); - const Position* targetPos = GetMainTankPosition(); + Position const* targetPos = GetMainTankPosition(); float distToTarget = bot->GetDistance2d(targetPos->GetPositionX(), targetPos->GetPositionY()); if (distToTarget > 8.0f) { @@ -7470,11 +7403,11 @@ void IccLichKingWinterAction::HandleMeleePositioning() // Updated HandleRangedPositioning method void IccLichKingWinterAction::HandleRangedPositioning() { - if (!botAI->IsRanged(bot)) + if (!BotRoleService::IsRangedStatic(bot)) return; // Get the ranged position based on main tank's choice - const Position* targetPos = GetMainTankRangedPosition(); + Position const* targetPos = GetMainTankRangedPosition(); // First check if current position is safe if (!IsPositionSafeFromDefile(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), 3.0f)) @@ -7496,7 +7429,7 @@ void IccLichKingWinterAction::HandleRangedPositioning() } // Handle sphere targeting for ranged DPS - if (botAI->IsRangedDps(bot)) + if (BotRoleService::IsRangedDpsStatic(bot)) { bool hasHunter = false; Group* group = bot->GetGroup(); @@ -7556,9 +7489,9 @@ void IccLichKingWinterAction::HandleRangedPositioning() } } -void IccLichKingWinterAction::HandleMainTankAddManagement(Unit* boss, const Position* tankPos) +void IccLichKingWinterAction::HandleMainTankAddManagement(Unit* /*boss*/, Position const* tankPos) { - if (!botAI->IsMainTank(bot)) + if (!BotRoleService::IsMainTankStatic(bot)) return; // First, ensure we're at the correct tank position @@ -7607,7 +7540,7 @@ void IccLichKingWinterAction::HandleMainTankAddManagement(Unit* boss, const Posi Unit* addVictim = add->GetVictim(); // Highest priority: Adds attacking non-tanks - if (addVictim && addVictim->IsPlayer() && !botAI->IsTank(addVictim->ToPlayer())) + if (addVictim && addVictim->IsPlayer() && !BotRoleService::IsTankStatic(addVictim->ToPlayer())) { if (!priorityAdd || bot->GetDistance(add) < bot->GetDistance(priorityAdd)) { @@ -7679,9 +7612,9 @@ void IccLichKingWinterAction::HandleMainTankAddManagement(Unit* boss, const Posi } } -void IccLichKingWinterAction::HandleAssistTankAddManagement(Unit* boss, const Position* tankPos) +void IccLichKingWinterAction::HandleAssistTankAddManagement(Unit* /*boss*/, Position const* tankPos) { - if (!botAI->IsAssistTank(bot)) + if (!BotRoleService::IsAssistTankStatic(bot)) return; Unit* mainTank = AI_VALUE(Unit*, "main tank"); @@ -7703,7 +7636,7 @@ void IccLichKingWinterAction::HandleAssistTankAddManagement(Unit* boss, const Po continue; Unit* addVictim = unit->GetVictim(); - if (addVictim && addVictim->IsPlayer() && !botAI->IsTank(addVictim->ToPlayer())) + if (addVictim && addVictim->IsPlayer() && !BotRoleService::IsTankStatic(addVictim->ToPlayer())) { float addDist = bot->GetDistance(unit); if (addDist < closestDist) @@ -7783,7 +7716,7 @@ void IccLichKingWinterAction::HandleAssistTankAddManagement(Unit* boss, const Po } } -bool IccLichKingAddsAction::Execute(Event event) +bool IccLichKingAddsAction::Execute(Event /*event*/) { if (bot->HasAura(SPELL_HARVEST_SOUL_VALKYR)) // Don't process actions if bot is picked up by Val'kyr return false; @@ -7804,7 +7737,7 @@ bool IccLichKingAddsAction::Execute(Event event) if (boss && boss->HealthBelowPct(60) && boss->HealthAbovePct(40) && !bot->HasAura(SPELL_EMPOWERED_BLOOD)) bot->AddAura(SPELL_EMPOWERED_BLOOD, bot); - if (!bot->HasAura(SPELL_NO_THREAT) && !botAI->IsTank(bot)) + if (!bot->HasAura(SPELL_NO_THREAT) && !BotRoleService::IsTankStatic(bot)) bot->AddAura(SPELL_NO_THREAT, bot); if (!bot->HasAura(SPELL_PAIN_SUPPRESION)) @@ -7812,9 +7745,7 @@ bool IccLichKingAddsAction::Execute(Event event) //------CHEAT------- } - Unit* spiritWarden = AI_VALUE2(Unit*, "find target", "spirit warden"); - bool hasPlague = botAI->HasAura("Necrotic Plague", bot); - Unit* terenasMenethilHC = bot->FindNearestCreature(NPC_TERENAS_MENETHIL_HC, 55.0f); + bool hasPlague = botAI->GetServices().GetSpellService().HasAura("Necrotic Plague", bot); Group* group = bot->GetGroup(); if (group && boss && boss->HealthAbovePct(71)) @@ -7864,6 +7795,9 @@ bool IccLichKingAddsAction::Execute(Event event) } //-----------Valkyr bot suicide if group fails to kill Valkyr in time------------- comment out if you dont want it + // For heroic mode, get Terenas Menethil for spirit world positioning + Unit* terenasMenethilHC = AI_VALUE2(Unit*, "find target", "terenas menethil"); + // Handle teleportation fixes HandleTeleportationFixes(diff, terenasMenethilHC); @@ -7928,11 +7862,11 @@ void IccLichKingAddsAction::HandleTeleportationFixes(Difficulty diff, Unit* tere } // temp solution for bots going underground due to buggy ice platfroms and adds that go underground - if (abs(bot->GetPositionZ() - 840.857f) > 1.0f && !botAI->GetAura("Harvest Soul", bot, false, false) && - !botAI->GetAura("Harvest Souls", bot, false, false)) + if (abs(bot->GetPositionZ() - 840.857f) > 1.0f && !botAI->GetServices().GetSpellService().GetAura("Harvest Soul", bot, false, false) && + !botAI->GetServices().GetSpellService().GetAura("Harvest Souls", bot, false, false)) bot->TeleportTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), 840.857f, bot->GetOrientation()); - if (abs(bot->GetPositionZ() - 1049.865f) > 5.0f && botAI->GetAura("Harvest Soul", bot, false, false) && + if (abs(bot->GetPositionZ() - 1049.865f) > 5.0f && botAI->GetServices().GetSpellService().GetAura("Harvest Soul", bot, false, false) && terenasMenethilHC) bot->TeleportTo(bot->GetMapId(), terenasMenethilHC->GetPositionX(), terenasMenethilHC->GetPositionY(), 1049.865f, bot->GetOrientation()); @@ -7940,7 +7874,7 @@ void IccLichKingAddsAction::HandleTeleportationFixes(Difficulty diff, Unit* tere bool IccLichKingAddsAction::HandleSpiritBombAvoidance(Difficulty diff, Unit* terenasMenethilHC) { - if (!botAI->IsMainTank(bot) || !terenasMenethilHC || !diff || + if (!BotRoleService::IsMainTankStatic(bot) || !terenasMenethilHC || !diff || !(diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) return false; @@ -8120,7 +8054,7 @@ bool IccLichKingAddsAction::HandleSpiritBombAvoidance(Difficulty diff, Unit* ter void IccLichKingAddsAction::HandleHeroicNonTankPositioning(Difficulty diff, Unit* terenasMenethilHC) { - if (!terenasMenethilHC || botAI->IsMainTank(bot) || !diff || + if (!terenasMenethilHC || BotRoleService::IsMainTankStatic(bot) || !diff || !(diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) return; @@ -8135,7 +8069,7 @@ void IccLichKingAddsAction::HandleHeroicNonTankPositioning(Difficulty diff, Unit void IccLichKingAddsAction::HandleSpiritMarkingAndTargeting(Difficulty diff, Unit* terenasMenethilHC) { - if (!terenasMenethilHC || botAI->IsMainTank(bot) || !diff || + if (!terenasMenethilHC || BotRoleService::IsMainTankStatic(bot) || !diff || !(diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) return; @@ -8237,7 +8171,7 @@ void IccLichKingAddsAction::HandleSpiritMarkingAndTargeting(Difficulty diff, Uni } // Only ranged DPS use star for RTI - if (botAI->IsRangedDps(bot)) + if (BotRoleService::IsRangedDpsStatic(bot)) { context->GetValue("rti")->Set("star"); Unit* starTarget = botAI->GetUnit(group->GetTargetIcon(STAR_ICON_INDEX)); @@ -8269,7 +8203,7 @@ bool IccLichKingAddsAction::HandleQuakeMechanics(Unit* boss) float botY = bot->GetPositionY(); float targetX, targetY; - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) { // Non-tanks: use offset position as direction guide float offsetX = boss->GetPositionX() + 15.0f; @@ -8324,7 +8258,7 @@ bool IccLichKingAddsAction::HandleQuakeMechanics(Unit* boss) return false; } -void IccLichKingAddsAction::HandleShamblingHorrors(Unit* boss, bool hasPlague) +void IccLichKingAddsAction::HandleShamblingHorrors(Unit* /*boss*/, bool /*hasPlague*/) { // Find closest shambling horror GuidVector npcs2 = AI_VALUE(GuidVector, "nearest hostile npcs"); @@ -8351,19 +8285,19 @@ void IccLichKingAddsAction::HandleShamblingHorrors(Unit* boss, bool hasPlague) if (!closestHorror || hasPlague) { } - else if (!hasPlague && closestHorror->isInFront(bot) && closestHorror->IsAlive() && !botAI->IsTank(bot) && + else if (!hasPlague && closestHorror->isInFront(bot) && closestHorror->IsAlive() && !BotRoleService::IsTankStatic(bot) && bot->GetDistance2d(closestHorror) < 3.0f) return FleePosition(closestHorror->GetPosition(), 2.0f, 250U); */ // If bot is hunter and shambling is enraged, use Tranquilizing Shot - if (bot->getClass() == CLASS_HUNTER && closestHorror && botAI->HasAura("Enrage", closestHorror)) - botAI->CastSpell("Tranquilizing Shot", closestHorror); + if (bot->getClass() == CLASS_HUNTER && closestHorror && botAI->GetServices().GetSpellService().HasAura("Enrage", closestHorror)) + botAI->GetServices().GetSpellService().CastSpell("Tranquilizing Shot", closestHorror); } bool IccLichKingAddsAction::HandleAssistTankAddManagement(Unit* boss, Difficulty diff) { - if (!botAI->IsAssistTank(bot) || !boss || boss->HealthBelowPct(71)) + if (!BotRoleService::IsAssistTankStatic(bot) || !boss || boss->HealthBelowPct(71)) return false; // Find all adds and categorize them by targeting status @@ -8402,7 +8336,7 @@ bool IccLichKingAddsAction::HandleAssistTankAddManagement(Unit* boss, Difficulty Unit* closestAdd = nullptr; float closestDist = 999.0f; - for (const ObjectGuid& addGuid : addsNotTargetingUs) + for (ObjectGuid const& addGuid : addsNotTargetingUs) { Unit* add = botAI->GetUnit(addGuid); if (add && add->IsAlive()) @@ -8431,7 +8365,7 @@ bool IccLichKingAddsAction::HandleAssistTankAddManagement(Unit* boss, Difficulty if (targetToAttack) { // Generate threat on ALL adds not targeting us using ranged abilities - for (const ObjectGuid& addGuid : addsNotTargetingUs) + for (ObjectGuid const& addGuid : addsNotTargetingUs) { Unit* add = botAI->GetUnit(addGuid); if (add && add->IsAlive()) @@ -8442,12 +8376,12 @@ bool IccLichKingAddsAction::HandleAssistTankAddManagement(Unit* boss, Difficulty if (dist <= 30.0f) { // Try taunt first if available - if (botAI->CastSpell("taunt", add)) + if (botAI->GetServices().GetSpellService().CastSpell("taunt", add)) { continue; } // Fall back to ranged attack - else if (botAI->CastSpell("shoot", add) || botAI->CastSpell("throw", add)) + else if (botAI->GetServices().GetSpellService().CastSpell("shoot", add) || botAI->GetServices().GetSpellService().CastSpell("throw", add)) { continue; } @@ -8485,8 +8419,6 @@ bool IccLichKingAddsAction::HandleAssistTankAddManagement(Unit* boss, Difficulty // In heroic mode, stay at melee position if (diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) { - Unit* mainTank = AI_VALUE(Unit*, "main tank"); - if (bot->GetExactDist2d(ICC_LICH_KING_ASSISTHC_POSITION.GetPositionX(), ICC_LICH_KING_ASSISTHC_POSITION.GetPositionY()) > 2.0f) { @@ -8515,7 +8447,7 @@ bool IccLichKingAddsAction::HandleAssistTankAddManagement(Unit* boss, Difficulty // Check if current target is still valid (alive and attacking us) if (currentTarget && currentTarget->IsAlive()) { - for (const ObjectGuid& addGuid : addsTargetingUs) + for (ObjectGuid const& addGuid : addsTargetingUs) { if (addGuid == currentTarget->GetGUID()) { @@ -8531,7 +8463,7 @@ bool IccLichKingAddsAction::HandleAssistTankAddManagement(Unit* boss, Difficulty currentTarget = nullptr; // Priority 1: Shambling Horror - for (const ObjectGuid& addGuid : addsTargetingUs) + for (ObjectGuid const& addGuid : addsTargetingUs) { Unit* add = botAI->GetUnit(addGuid); if (add && add->IsAlive()) @@ -8548,7 +8480,7 @@ bool IccLichKingAddsAction::HandleAssistTankAddManagement(Unit* boss, Difficulty // Priority 2: Any other add if no Shambling Horror if (!currentTarget) { - for (const ObjectGuid& addGuid : addsTargetingUs) + for (ObjectGuid const& addGuid : addsTargetingUs) { Unit* add = botAI->GetUnit(addGuid); if (add && add->IsAlive()) @@ -8573,7 +8505,7 @@ bool IccLichKingAddsAction::HandleAssistTankAddManagement(Unit* boss, Difficulty void IccLichKingAddsAction::HandleMeleePositioning(Unit* boss, bool hasPlague, Difficulty diff) { - if (!boss || !botAI->IsMelee(bot) || botAI->IsAssistTank(bot) || boss->HealthBelowPct(71) || hasPlague) + if (!boss || !BotRoleService::IsMeleeStatic(bot) || BotRoleService::IsAssistTankStatic(bot) || boss->HealthBelowPct(71) || hasPlague) return; if (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC) @@ -8581,14 +8513,14 @@ void IccLichKingAddsAction::HandleMeleePositioning(Unit* boss, bool hasPlague, D float currentDist = bot->GetDistance(ICC_LICH_KING_MELEE_POSITION); - if (currentDist > 6.0f && !botAI->IsMainTank(bot)) + if (currentDist > 6.0f && !BotRoleService::IsMainTankStatic(bot)) { MoveTo(bot->GetMapId(), ICC_LICH_KING_MELEE_POSITION.GetPositionX(), ICC_LICH_KING_MELEE_POSITION.GetPositionY(), ICC_LICH_KING_MELEE_POSITION.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_FORCED, true, false); } - if (currentDist > 6.0f && botAI->IsMainTank(bot) && boss && boss->GetVictim() == bot) + if (currentDist > 6.0f && BotRoleService::IsMainTankStatic(bot) && boss && boss->GetVictim() == bot) { Position currentPos = bot->GetPosition(); Position targetPos = ICC_LICH_KING_MELEE_POSITION; @@ -8599,7 +8531,7 @@ void IccLichKingAddsAction::HandleMeleePositioning(Unit* boss, bool hasPlague, D // Calculate distance and normalize direction float distance = sqrt(dx * dx + dy * dy); - if (distance > 0.1) + if (distance > 0.1f) { dx /= distance; dy /= distance; @@ -8626,7 +8558,7 @@ void IccLichKingAddsAction::HandleMeleePositioning(Unit* boss, bool hasPlague, D void IccLichKingAddsAction::HandleMainTankTargeting(Unit* boss, Difficulty diff) { - if (!botAI->IsMainTank(bot) || !boss) + if (!BotRoleService::IsMainTankStatic(bot) || !boss) return; if (!(diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) @@ -8642,7 +8574,7 @@ void IccLichKingAddsAction::HandleMainTankTargeting(Unit* boss, Difficulty diff) void IccLichKingAddsAction::HandleNonTankHeroicPositioning(Unit* boss, Difficulty diff, bool hasPlague) { - if (botAI->IsTank(bot) || !boss) + if (BotRoleService::IsTankStatic(bot) || !boss) return; if (!(diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) @@ -8689,7 +8621,7 @@ void IccLichKingAddsAction::HandleNonTankHeroicPositioning(Unit* boss, Difficult void IccLichKingAddsAction::HandleRangedPositioning(Unit* boss, bool hasPlague, Difficulty diff) { - if (!boss || !botAI->IsRanged(bot) || boss->HealthBelowPct(71) || hasPlague) + if (!boss || !BotRoleService::IsRangedStatic(bot) || boss->HealthBelowPct(71) || hasPlague) return; if (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC) @@ -8723,8 +8655,6 @@ void IccLichKingAddsAction::HandleDefileMechanics(Unit* boss, Difficulty diff) // Gather all defile units std::vector defiles; - Unit* closestDefile = nullptr; - float closestDistance = std::numeric_limits::max(); GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); for (auto& npc : npcs) @@ -8733,12 +8663,6 @@ void IccLichKingAddsAction::HandleDefileMechanics(Unit* boss, Difficulty diff) if (unit && unit->IsAlive() && unit->GetEntry() == DEFILE_NPC_ID) { defiles.push_back(unit); - float dist = bot->GetDistance(unit); - if (dist < closestDistance) - { - closestDistance = dist; - closestDefile = unit; - } } } @@ -9049,7 +8973,7 @@ void IccLichKingAddsAction::HandleValkyrMechanics(Difficulty diff) return; } - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) return; // Filter out dead Val'kyrs to ensure accurate group calculation @@ -9067,7 +8991,7 @@ void IccLichKingAddsAction::HandleValkyrMechanics(Difficulty diff) HandleValkyrAssignment(aliveGrabbingValkyrs); } -void IccLichKingAddsAction::HandleValkyrMarking(const std::vector& grabbingValkyrs, Difficulty diff) +void IccLichKingAddsAction::HandleValkyrMarking(std::vector const& grabbingValkyrs, Difficulty diff) { Group* group = bot->GetGroup(); if (!group) @@ -9078,7 +9002,6 @@ void IccLichKingAddsAction::HandleValkyrMarking(const std::vector& grabbi std::sort(sortedValkyrs.begin(), sortedValkyrs.end(), [](Unit* a, Unit* b) { return a->GetGUID() < b->GetGUID(); }); static constexpr uint8_t ICON_INDICES[] = {7, 6, 0}; // Skull, Cross, Star - static constexpr const char* ICON_NAMES[] = {"skull", "cross", "star"}; // In heroic mode, clean up invalid markers for all possible icons if (diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) @@ -9122,7 +9045,7 @@ void IccLichKingAddsAction::HandleValkyrMarking(const std::vector& grabbi } } -void IccLichKingAddsAction::HandleValkyrAssignment(const std::vector& grabbingValkyrs) +void IccLichKingAddsAction::HandleValkyrAssignment(std::vector const& grabbingValkyrs) { Group* group = bot->GetGroup(); if (!group) @@ -9153,7 +9076,7 @@ void IccLichKingAddsAction::HandleValkyrAssignment(const std::vector& gra for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) { Player* member = itr->GetSource(); - if (member && !botAI->IsMainTank(member)) + if (member && !BotRoleService::IsMainTankStatic(member)) assistMembers.push_back(member); } @@ -9225,7 +9148,7 @@ std::vector IccLichKingAddsAction::CalculateBalancedGroupSizes(size_t to return groupSizes; } -size_t IccLichKingAddsAction::GetAssignedValkyrIndex(size_t assistIndex, const std::vector& groupSizes) +size_t IccLichKingAddsAction::GetAssignedValkyrIndex(size_t assistIndex, std::vector const& groupSizes) { size_t currentIndex = 0; @@ -9261,44 +9184,44 @@ void IccLichKingAddsAction::ApplyCCToValkyr(Unit* valkyr) switch (bot->getClass()) { case CLASS_MAGE: - if (!botAI->HasAura("Frost Nova", valkyr)) - botAI->CastSpell("Frost Nova", valkyr); + if (!botAI->GetServices().GetSpellService().HasAura("Frost Nova", valkyr)) + botAI->GetServices().GetSpellService().CastSpell("Frost Nova", valkyr); break; case CLASS_DRUID: - if (!botAI->HasAura("Entangling Roots", valkyr)) - botAI->CastSpell("Entangling Roots", valkyr); + if (!botAI->GetServices().GetSpellService().HasAura("Entangling Roots", valkyr)) + botAI->GetServices().GetSpellService().CastSpell("Entangling Roots", valkyr); break; case CLASS_PALADIN: - if (!botAI->HasAura("Hammer of Justice", valkyr)) - botAI->CastSpell("Hammer of Justice", valkyr); + if (!botAI->GetServices().GetSpellService().HasAura("Hammer of Justice", valkyr)) + botAI->GetServices().GetSpellService().CastSpell("Hammer of Justice", valkyr); break; case CLASS_WARRIOR: - if (!botAI->HasAura("Hamstring", valkyr)) - botAI->CastSpell("Hamstring", valkyr); + if (!botAI->GetServices().GetSpellService().HasAura("Hamstring", valkyr)) + botAI->GetServices().GetSpellService().CastSpell("Hamstring", valkyr); break; case CLASS_HUNTER: - if (!botAI->HasAura("Concussive Shot", valkyr)) - botAI->CastSpell("Concussive Shot", valkyr); + if (!botAI->GetServices().GetSpellService().HasAura("Concussive Shot", valkyr)) + botAI->GetServices().GetSpellService().CastSpell("Concussive Shot", valkyr); break; case CLASS_ROGUE: - if (!botAI->HasAura("Kidney Shot", valkyr)) - botAI->CastSpell("Kidney Shot", valkyr); + if (!botAI->GetServices().GetSpellService().HasAura("Kidney Shot", valkyr)) + botAI->GetServices().GetSpellService().CastSpell("Kidney Shot", valkyr); break; case CLASS_SHAMAN: - if (!botAI->HasAura("Frost Shock", valkyr)) - botAI->CastSpell("Frost Shock", valkyr); + if (!botAI->GetServices().GetSpellService().HasAura("Frost Shock", valkyr)) + botAI->GetServices().GetSpellService().CastSpell("Frost Shock", valkyr); break; case CLASS_DEATH_KNIGHT: - if (!botAI->HasAura("Chains of Ice", valkyr)) - botAI->CastSpell("Chains of Ice", valkyr); + if (!botAI->GetServices().GetSpellService().HasAura("Chains of Ice", valkyr)) + botAI->GetServices().GetSpellService().CastSpell("Chains of Ice", valkyr); break; case CLASS_PRIEST: - if (!botAI->HasAura("Psychic Scream", valkyr)) - botAI->CastSpell("Psychic Scream", valkyr); + if (!botAI->GetServices().GetSpellService().HasAura("Psychic Scream", valkyr)) + botAI->GetServices().GetSpellService().CastSpell("Psychic Scream", valkyr); break; case CLASS_WARLOCK: - if (!botAI->HasAura("Fear", valkyr)) - botAI->CastSpell("Fear", valkyr); + if (!botAI->GetServices().GetSpellService().HasAura("Fear", valkyr)) + botAI->GetServices().GetSpellService().CastSpell("Fear", valkyr); break; default: break; diff --git a/src/Ai/Raid/Icecrown/Action/RaidIccActions.h b/src/Ai/Raid/Icecrown/Action/RaidIccActions.h index 9ac2a48a04..73162a89ef 100644 --- a/src/Ai/Raid/Icecrown/Action/RaidIccActions.h +++ b/src/Ai/Raid/Icecrown/Action/RaidIccActions.h @@ -34,7 +34,7 @@ const Position ICC_FESTERGUT_MELEE_SPORE = Position(4269.1772f, 3144.7673f, 360. const Position ICC_ROTFACE_TANK_POSITION = Position(4447.061f, 3150.9758f, 360.38568f); const Position ICC_ROTFACE_BIG_OOZE_POSITION = Position(4432.687f, 3142.3035f, 360.38623f); const Position ICC_ROTFACE_SAFE_POSITION = Position(4446.557f, 3065.6594f, 360.51843f); -const Position ICC_ROTFACE_CENTER_POSITION = Position(4446.0547f, 3144.8677f, 360.38593f); //actual center 4.74089 4445.6616f, 3137.1526f, 360.38608 +const Position ICC_ROTFACE_CENTER_POSITION = Position(4446.0547f, 3144.8677f, 360.38593f); //actual center 4.74089f 4445.6616f, 3137.1526f, 360.38608 const Position ICC_PUTRICIDE_TANK_POSITION = Position(4373.227f, 3222.058f, 389.4029f); const Position ICC_PUTRICIDE_GREEN_POSITION = Position(4423.4126f, 3194.2715f, 389.37683f); const Position ICC_PUTRICIDE_BAD_POSITION = Position(4356.1724f, 3261.5232f, 389.3985f); @@ -92,7 +92,7 @@ class IccLmTankPositionAction : public AttackAction : AttackAction(botAI, name) {} bool Execute(Event event) override; - bool MoveTowardPosition(const Position& position, float incrementSize); + bool MoveTowardPosition(Position const& position, float incrementSize); }; class IccSpikeAction : public AttackAction @@ -102,7 +102,7 @@ class IccSpikeAction : public AttackAction bool Execute(Event event) override; bool HandleSpikeTargeting(Unit* boss); - bool MoveTowardPosition(const Position& position, float incrementSize); + bool MoveTowardPosition(Position const& position, float incrementSize); void UpdateRaidTargetIcon(Unit* target); }; @@ -133,7 +133,7 @@ class IccAddsLadyDeathwhisperAction : public AttackAction bool Execute(Event event) override; bool IsTargetedByShade(uint32 shadeEntry); - bool MoveTowardPosition(const Position& position, float incrementSize); + bool MoveTowardPosition(Position const& position, float incrementSize); bool HandleAddTargeting(Unit* boss); void UpdateRaidTargetIcon(Unit* target); @@ -186,7 +186,7 @@ class IccGunshipTeleportAllyAction : public AttackAction : AttackAction(botAI, name) {} bool Execute(Event event) override; - bool TeleportTo(const Position& position); + bool TeleportTo(Position const& position); void CleanupSkullIcon(uint8_t SKULL_ICON_INDEX); void UpdateBossSkullIcon(Unit* boss, uint8_t SKULL_ICON_INDEX); }; @@ -198,7 +198,7 @@ class IccGunshipTeleportHordeAction : public AttackAction : AttackAction(botAI, name) {} bool Execute(Event event) override; - bool TeleportTo(const Position& position); + bool TeleportTo(Position const& position); void CleanupSkullIcon(uint8_t SKULL_ICON_INDEX); void UpdateBossSkullIcon(Unit* boss, uint8_t SKULL_ICON_INDEX); }; @@ -255,7 +255,7 @@ class IccFestergutSporeAction : public AttackAction bool hasLowestGuid = false; }; SporeInfo FindSporedPlayers(); - Position DetermineTargetPosition(bool hasSpore, const SporeInfo& sporeInfo, const Position& spreadRangedPos); + Position DetermineTargetPosition(bool hasSpore, SporeInfo const& sporeInfo, Position const& spreadRangedPos); bool CheckMainTankSpore(); }; @@ -332,8 +332,8 @@ class IccPutricideGasCloudAction : public AttackAction bool Execute(Event event) override; bool HandleGaseousBloatMovement(Unit* gasCloud); - Position CalculateEmergencyPosition(const Position& botPos, float dx, float dy); - bool FindSafeMovementPosition(const Position& botPos, const Position& cloudPos, float dx, float dy, int numAngles, + Position CalculateEmergencyPosition(Position const& botPos, float dx, float dy); + bool FindSafeMovementPosition(Position const& botPos, Position const& cloudPos, float dx, float dy, int numAngles, Position& resultPos); bool HandleGroupAuraSituation(Unit* gasCloud); bool GroupHasGaseousBloat(Group* group); @@ -350,10 +350,10 @@ class IccPutricideAvoidMalleableGooAction : public MovementAction bool HandleUnboundPlague(Unit* boss); bool HandleBossPositioning(Unit* boss); Position CalculateBossPosition(Unit* boss, float distance); - bool HasObstacleBetween(const Position& from, const Position& to); - bool IsOnPath(const Position& from, const Position& to, const Position& point, float threshold); - Position CalculateArcPoint(const Position& current, const Position& target, const Position& center); - Position CalculateIncrementalMove(const Position& current, const Position& target, float maxDistance); + bool HasObstacleBetween(Position const& from, Position const& to); + bool IsOnPath(Position const& from, Position const& to, Position const& point, float threshold); + Position CalculateArcPoint(Position const& current, Position const& target, Position const& center); + Position CalculateIncrementalMove(Position const& current, Position const& target, float maxDistance); }; //BPC @@ -415,7 +415,7 @@ class IccBqlGroupPositionAction : public AttackAction bool HandleTankPosition(Unit* boss, Aura* frenzyAura, Aura* shadowAura); bool HandleShadowsMovement(); - Position AdjustControlPoint(const Position& wall, const Position& center, float factor); + Position AdjustControlPoint(Position const& wall, Position const& center, float factor); Position CalculateBezierPoint(float t, const Position path[4]); bool HandleGroupPosition(Unit* boss, Aura* frenzyAura, Aura* shadowAura); @@ -440,8 +440,8 @@ class IccBqlPactOfDarkfallenAction : public MovementAction : MovementAction(botAI, "icc bql pact of darkfallen") {} bool Execute(Event event) override; - void CalculateCenterPosition(Position& targetPos, const std::vector& playersWithAura); - bool MoveToTargetPosition(const Position& targetPos, int auraCount); + void CalculateCenterPosition(Position& targetPos, std::vector const& playersWithAura); + bool MoveToTargetPosition(Position const& targetPos, int auraCount); }; class IccBqlVampiricBiteAction : public AttackAction @@ -483,9 +483,9 @@ class IccValithriaGroupAction : public AttackAction : AttackAction(botAI, "icc valithria group") {} bool Execute(Event event) override; - bool MoveTowardsPosition(const Position& pos, float increment); + bool MoveTowardsPosition(Position const& pos, float increment); bool Handle25ManGroupLogic(); - bool HandleMarkingLogic(bool inGroup1, bool inGroup2, const Position& group1Pos, const Position& group2Pos); + bool HandleMarkingLogic(bool inGroup1, bool inGroup2, Position const& group1Pos, Position const& group2Pos); bool Handle10ManGroupLogic(); }; @@ -523,7 +523,7 @@ class IccSindragosaGroupPositionAction : public AttackAction bool HandleTankPositioning(Unit* boss); bool HandleNonTankPositioning(); - bool MoveIncrementallyToPosition(const Position& targetPos, float maxStep); + bool MoveIncrementallyToPosition(Position const& targetPos, float maxStep); }; class IccSindragosaFrostBeaconAction : public MovementAction @@ -534,11 +534,11 @@ class IccSindragosaFrostBeaconAction : public MovementAction bool Execute(Event event) override; void HandleSupportActions(); - bool HandleBeaconedPlayer(const Unit* boss); - bool HandleNonBeaconedPlayer(const Unit* boss); - bool MoveToPositionIfNeeded(const Position& position, float tolerance); - bool MoveToPosition(const Position& position); - bool IsBossFlying(const Unit* boss); + bool HandleBeaconedPlayer(Unit const* boss); + bool HandleNonBeaconedPlayer(Unit const* boss); + bool MoveToPositionIfNeeded(Position const& position, float tolerance); + bool MoveToPosition(Position const& position); + bool IsBossFlying(Unit const* boss); private: static constexpr uint32 FROST_BEACON_AURA_ID = SPELL_FROST_BEACON; @@ -627,12 +627,12 @@ class IccLichKingWinterAction : public AttackAction void HandleTankPositioning(); void HandleMeleePositioning(); void HandleRangedPositioning(); - void HandleMainTankAddManagement(Unit* boss, const Position* tankPos); - void HandleAssistTankAddManagement(Unit* boss, const Position* tankPos); + void HandleMainTankAddManagement(Unit* boss, Position const* tankPos); + void HandleAssistTankAddManagement(Unit* boss, Position const* tankPos); private: - const Position* GetMainTankPosition(); - const Position* GetMainTankRangedPosition(); + Position const* GetMainTankPosition(); + Position const* GetMainTankRangedPosition(); bool TryMoveToPosition(float targetX, float targetY, float targetZ, bool isForced = true); }; @@ -657,10 +657,10 @@ class IccLichKingAddsAction : public AttackAction void HandleDefileMechanics(Unit* boss, Difficulty diff); void HandleValkyrMechanics(Difficulty diff); std::vector CalculateBalancedGroupSizes(size_t totalAssist, size_t numValkyrs); - size_t GetAssignedValkyrIndex(size_t assistIndex, const std::vector& groupSizes); + size_t GetAssignedValkyrIndex(size_t assistIndex, std::vector const& groupSizes); std::string GetRTIValueForValkyr(size_t valkyrIndex); - void HandleValkyrMarking(const std::vector& grabbingValkyrs, Difficulty diff); - void HandleValkyrAssignment(const std::vector& grabbingValkyrs); + void HandleValkyrMarking(std::vector const& grabbingValkyrs, Difficulty diff); + void HandleValkyrAssignment(std::vector const& grabbingValkyrs); void ApplyCCToValkyr(Unit* valkyr); bool IsValkyr(Unit* unit); void HandleVileSpiritMechanics(); diff --git a/src/Ai/Raid/Icecrown/Multiplier/RaidIccMultipliers.cpp b/src/Ai/Raid/Icecrown/Multiplier/RaidIccMultipliers.cpp index fc02d56bea..c5bf6eee5a 100644 --- a/src/Ai/Raid/Icecrown/Multiplier/RaidIccMultipliers.cpp +++ b/src/Ai/Raid/Icecrown/Multiplier/RaidIccMultipliers.cpp @@ -1,4 +1,6 @@ #include "RaidIccMultipliers.h" +#include "BotSpellService.h" +#include "BotRoleService.h" #include "ChooseTargetActions.h" #include "DKActions.h" @@ -25,8 +27,6 @@ // LK global variables namespace { -uint32 g_lastPlagueTime = 0; -bool g_plagueAllowedToCure = false; std::map g_plagueTimes; std::map g_allowCure; std::mutex g_plagueMutex; // Lock before accessing shared variables @@ -82,13 +82,13 @@ float IccAddsDbsMultiplier::GetValue(Action* action) dynamic_cast(action)) return 0.0f; - if (botAI->IsRanged(bot)) + if (BotRoleService::IsRangedStatic(bot)) if (dynamic_cast(action)) return 0.0f; - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) { - Aura* aura = botAI->GetAura("rune of blood", bot); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("rune of blood", bot); if (aura) { if (dynamic_cast(action)) @@ -111,9 +111,9 @@ float IccDogsMultiplier::GetValue(Action* action) if (!bossPresent) return 1.0f; - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) { - Aura* aura = botAI->GetAura("mortal wound", bot, false, true); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("mortal wound", bot, false, true); if (aura && aura->GetStackAmount() >= 8) { if (dynamic_cast(action)) @@ -138,9 +138,9 @@ float IccFestergutMultiplier::GetValue(Action* action) if (dynamic_cast(action)) return 0.0f; - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) { - Aura* aura = botAI->GetAura("gastric bloat", bot, false, true); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("gastric bloat", bot, false, true); if (aura && aura->GetStackAmount() >= 6) { if (dynamic_cast(action)) @@ -175,7 +175,7 @@ float IccRotfaceMultiplier::GetValue(Action* action) if (dynamic_cast(action)) return 0.0f; - if (botAI->IsAssistTank(bot) && (dynamic_cast(action) || dynamic_cast(action))) + if (BotRoleService::IsAssistTankStatic(bot) && (dynamic_cast(action) || dynamic_cast(action))) return 0.0f; Unit* boss = AI_VALUE2(Unit*, "find target", "big ooze"); @@ -235,8 +235,8 @@ float IccAddsPutricideMultiplier::GetValue(Action* action) if (!boss) return 1.0f; - bool hasGaseousBloat = botAI->HasAura("Gaseous Bloat", bot); - bool hasUnboundPlague = botAI->HasAura("Unbound Plague", bot); + bool hasGaseousBloat = botAI->GetServices().GetSpellService().HasAura("Gaseous Bloat", bot); + bool hasUnboundPlague = botAI->GetServices().GetSpellService().HasAura("Unbound Plague", bot); if (!(bot->getClass() == CLASS_HUNTER) && dynamic_cast(action)) return 0.0f; @@ -250,9 +250,9 @@ float IccAddsPutricideMultiplier::GetValue(Action* action) if (dynamic_cast(action)) return 0.0f; - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) { - Aura* aura = botAI->GetAura("mutated plague", bot, false, true); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("mutated plague", bot, false, true); if (aura && aura->GetStackAmount() >= 4) { if (dynamic_cast(action)) @@ -270,7 +270,7 @@ float IccAddsPutricideMultiplier::GetValue(Action* action) if (dynamic_cast(action)) return 1.0f; - if (botAI->IsHeal(bot)) + if (BotRoleService::IsHealStatic(bot)) return 1.0f; else return 0.0f; // Cancel all other actions when we need to handle Gaseous Bloat @@ -288,7 +288,7 @@ float IccAddsPutricideMultiplier::GetValue(Action* action) { if (dynamic_cast(action)) return 0.0f; - if (dynamic_cast(action) && !botAI->IsMainTank(bot)) + if (dynamic_cast(action) && !BotRoleService::IsMainTankStatic(bot)) return 0.0f; //if (dynamic_cast(action) && !hasGaseousBloat) //return 0.0f; @@ -311,16 +311,16 @@ float IccBpcAssistMultiplier::GetValue(Action* action) dynamic_cast(action) || dynamic_cast(action)) return 0.0f; - Aura* aura = botAI->GetAura("Shadow Prison", bot, false, true); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("Shadow Prison", bot, false, true); if (aura) { - if (aura->GetStackAmount() > 18 && botAI->IsTank(bot)) + if (aura->GetStackAmount() > 18 && BotRoleService::IsTankStatic(bot)) { if (dynamic_cast(action)) return 0.0f; } - if (aura->GetStackAmount() > 12 && !botAI->IsTank(bot)) + if (aura->GetStackAmount() > 12 && !BotRoleService::IsTankStatic(bot)) { if (dynamic_cast(action)) return 0.0f; @@ -366,7 +366,7 @@ float IccBpcAssistMultiplier::GetValue(Action* action) return 0.0f; // Cancel all other actions when we need to handle Ball of Flame } - static const std::array bombEntries = {NPC_KINETIC_BOMB1, NPC_KINETIC_BOMB2, NPC_KINETIC_BOMB3, + static std::array const bombEntries = {NPC_KINETIC_BOMB1, NPC_KINETIC_BOMB2, NPC_KINETIC_BOMB3, NPC_KINETIC_BOMB4}; const GuidVector bombs = AI_VALUE(GuidVector, "possible targets no los"); @@ -393,7 +393,7 @@ float IccBpcAssistMultiplier::GetValue(Action* action) break; } - if (bombFound && !(aura && aura->GetStackAmount() > 12) && !botAI->IsTank(bot)) + if (bombFound && !(aura && aura->GetStackAmount() > 12) && !BotRoleService::IsTankStatic(bot)) { // If kinetic bomb action is active, disable these actions if (dynamic_cast(action)) @@ -405,7 +405,7 @@ float IccBpcAssistMultiplier::GetValue(Action* action) } // For assist tank during BPC fight - if (botAI->IsAssistTank(bot) && !(aura && aura->GetStackAmount() > 18)) + if (BotRoleService::IsAssistTankStatic(bot) && !(aura && aura->GetStackAmount() > 18)) { // Allow BPC-specific actions if (dynamic_cast(action)) @@ -430,10 +430,10 @@ float IccBqlMultiplier::GetValue(Action* action) if (!boss) return 1.0f; - Aura* aura2 = botAI->GetAura("Swarming Shadows", bot); - Aura* aura = botAI->GetAura("Frenzied Bloodthirst", bot); + Aura* aura2 = botAI->GetServices().GetSpellService().GetAura("Swarming Shadows", bot); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("Frenzied Bloodthirst", bot); - if (botAI->IsRanged(bot)) + if (BotRoleService::IsRangedStatic(bot)) if (dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action)) return 0.0f; @@ -447,7 +447,7 @@ float IccBqlMultiplier::GetValue(Action* action) return 0.0f; // Cancel all other actions when we need to handle Pact of Darkfallen } - if (botAI->IsMelee(bot) && ((boss->GetPositionZ() - ICC_BQL_CENTER_POSITION.GetPositionZ()) > 5.0f) && !aura) + if (BotRoleService::IsMeleeStatic(bot) && ((boss->GetPositionZ() - ICC_BQL_CENTER_POSITION.GetPositionZ()) > 5.0f) && !aura) { if (dynamic_cast(action)) return 1.0f; @@ -473,7 +473,7 @@ float IccBqlMultiplier::GetValue(Action* action) } if ((boss->GetExactDist2d(ICC_BQL_TANK_POSITION.GetPositionX(), ICC_BQL_TANK_POSITION.GetPositionY()) > 10.0f) && - botAI->IsRanged(bot) && !((boss->GetPositionZ() - bot->GetPositionZ()) > 5.0f)) + BotRoleService::IsRangedStatic(bot) && !((boss->GetPositionZ() - bot->GetPositionZ()) > 5.0f)) { if (dynamic_cast(action) || dynamic_cast(action)) return 0.0f; @@ -487,8 +487,8 @@ float IccValithriaDreamCloudMultiplier::GetValue(Action* action) { Unit* boss = bot->FindNearestCreature(NPC_VALITHRIA_DREAMWALKER, 100.0f); - Aura* twistedNightmares = botAI->GetAura("Twisted Nightmares", bot); - Aura* emeraldVigor = botAI->GetAura("Emerald Vigor", bot); + Aura* twistedNightmares = botAI->GetServices().GetSpellService().GetAura("Twisted Nightmares", bot); + Aura* emeraldVigor = botAI->GetServices().GetSpellService().GetAura("Emerald Vigor", bot); if (!boss && !bot->HasAura(SPELL_DREAM_STATE)) return 1.0f; @@ -496,13 +496,13 @@ float IccValithriaDreamCloudMultiplier::GetValue(Action* action) if (dynamic_cast(action) || dynamic_cast(action)) return 0.0f; - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) { if (dynamic_cast(action)) return 0.0f; } - if (botAI->IsHeal(bot) && (twistedNightmares || emeraldVigor)) + if (BotRoleService::IsHealStatic(bot) && (twistedNightmares || emeraldVigor)) if (dynamic_cast(action) || dynamic_cast(action)) return 0.0f; @@ -525,7 +525,7 @@ float IccSindragosaMultiplier::GetValue(Action* action) Unit* boss = bot->FindNearestCreature(NPC_SINDRAGOSA, 200.0f); if (!boss) return 1.0f; - Aura* aura = botAI->GetAura("Unchained Magic", bot, false, true); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("Unchained Magic", bot, false, true); Difficulty diff = bot->GetRaidDifficulty(); @@ -598,15 +598,15 @@ float IccSindragosaMultiplier::GetValue(Action* action) return 0.0f; } - if (anyoneHasFrostBeacon && !botAI->IsMainTank(bot)) + if (anyoneHasFrostBeacon && !BotRoleService::IsMainTankStatic(bot)) { if (dynamic_cast(action)) return 0.0f; } - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) { - Aura* aura = botAI->GetAura("mystic buffet", bot, false, true); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("mystic buffet", bot, false, true); if (aura && aura->GetStackAmount() >= 6) { if (dynamic_cast(action)) @@ -616,13 +616,13 @@ float IccSindragosaMultiplier::GetValue(Action* action) } } - if (!botAI->IsTank(bot) && boss && boss->HealthBelowPct(35)) + if (!BotRoleService::IsTankStatic(bot) && boss && boss->HealthBelowPct(35)) { if (dynamic_cast(action)) return 0.0f; } - if (boss && botAI->IsTank(bot)) + if (boss && BotRoleService::IsTankStatic(bot)) { if (boss->HealthBelowPct(35)) { @@ -669,13 +669,13 @@ float IccLichKingAddsMultiplier::GetValue(Action* action) { Unit* mainTank = AI_VALUE(Unit*, "main tank"); - if (!botAI->IsMainTank(bot) && mainTank && bot->GetExactDist2d(mainTank->GetPositionX(), mainTank->GetPositionY()) < 2.0f) + if (!BotRoleService::IsMainTankStatic(bot) && mainTank && bot->GetExactDist2d(mainTank->GetPositionX(), mainTank->GetPositionY()) < 2.0f) { if (dynamic_cast(action)) return 0.0f; } - if (botAI->IsMelee(bot) || (bot->getClass() == CLASS_WARLOCK)) + if (BotRoleService::IsMeleeStatic(bot) || (bot->getClass() == CLASS_WARLOCK)) { if (dynamic_cast(action) || dynamic_cast(action)) return 1.0f; @@ -711,7 +711,7 @@ float IccLichKingAddsMultiplier::GetValue(Action* action) { if (Player* member = ref->GetSource()) { - if (botAI->HasAura("Necrotic Plague", member)) + if (botAI->GetServices().GetSpellService().HasAura("Necrotic Plague", member)) { anyBotHasPlague = true; plaguedPlayerGuid = member->GetGUID(); // Changed from GetObjectGuid() @@ -768,7 +768,7 @@ float IccLichKingAddsMultiplier::GetValue(Action* action) if (boss && !boss->HealthBelowPct(71)) { - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) if (dynamic_cast(action)) return 0.0f; @@ -813,7 +813,7 @@ float IccLichKingAddsMultiplier::GetValue(Action* action) if (dynamic_cast(action) || dynamic_cast(action)) return 1.0f; - if (botAI->IsAssistTank(bot) && dynamic_cast(action)) + if (BotRoleService::IsAssistTankStatic(bot) && dynamic_cast(action)) return 0.0f; if (dynamic_cast(action)) @@ -838,7 +838,7 @@ float IccLichKingAddsMultiplier::GetValue(Action* action) } - if (botAI->IsRanged(bot) && !botAI->GetAura("Harvest Soul", bot, false, false)) + if (BotRoleService::IsRangedStatic(bot) && !botAI->GetServices().GetSpellService().GetAura("Harvest Soul", bot, false, false)) { // Check for defile presence GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); @@ -865,7 +865,7 @@ float IccLichKingAddsMultiplier::GetValue(Action* action) } } - if (botAI->IsAssistTank(bot) && boss && !boss->HealthBelowPct(71) && currentTarget == boss) + if (BotRoleService::IsAssistTankStatic(bot) && boss && !boss->HealthBelowPct(71) && currentTarget == boss) { if (dynamic_cast(action)) return 0.0f; diff --git a/src/Ai/Raid/Icecrown/Strategy/RaidIccStrategy.h b/src/Ai/Raid/Icecrown/Strategy/RaidIccStrategy.h index 53967c3344..3c1a89a7ac 100644 --- a/src/Ai/Raid/Icecrown/Strategy/RaidIccStrategy.h +++ b/src/Ai/Raid/Icecrown/Strategy/RaidIccStrategy.h @@ -12,7 +12,7 @@ class RaidIccStrategy : public Strategy RaidIccStrategy(PlayerbotAI* ai) : Strategy(ai) {} virtual std::string const getName() override { return "icc"; } virtual void InitTriggers(std::vector& triggers) override; - virtual void InitMultipliers(std::vector &multipliers) override; + virtual void InitMultipliers(std::vector& multipliers) override; }; #endif diff --git a/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp b/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp index b9ebb8ca8d..252cbfbc0a 100644 --- a/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp +++ b/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp @@ -1,4 +1,6 @@ #include "RaidIccTriggers.h" +#include "BotSpellService.h" +#include "BotRoleService.h" #include "RaidIccActions.h" #include "NearestNpcsValue.h" #include "PlayerbotAIConfig.h" @@ -84,7 +86,7 @@ bool IccGunshipCannonNearTrigger::IsActive() if (!mount1 && !mount2) return false; - if (!botAI->IsDps(bot)) + if (!BotRoleService::IsDpsStatic(bot)) return false; // Player* master = botAI->GetMaster(); // if (!master) @@ -146,14 +148,14 @@ bool IccDbsMainTankRuneOfBloodTrigger::IsActive() if (!boss) return false; - if (!botAI->IsAssistTankOfIndex(bot, 0)) + if (!BotRoleService::IsAssistTankOfIndexStatic(bot, 0)) return false; Unit* mt = AI_VALUE(Unit*, "main tank"); if (!mt) return false; - Aura* aura = botAI->GetAura("rune of blood", mt); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("rune of blood", mt); if (!aura) return false; @@ -170,14 +172,14 @@ bool IccStinkyPreciousMainTankMortalWoundTrigger::IsActive() if (!bossPresent) return false; - if (!botAI->IsAssistTankOfIndex(bot, 0)) + if (!BotRoleService::IsAssistTankOfIndexStatic(bot, 0)) return false; Unit* mt = AI_VALUE(Unit*, "main tank"); if (!mt) return false; - Aura* aura = botAI->GetAura("mortal wound", mt, false, true); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("mortal wound", mt, false, true); if (!aura || aura->GetStackAmount() < 8) return false; @@ -204,7 +206,7 @@ bool IccFestergutMainTankGastricBloatTrigger::IsActive() { return false; } - if (!botAI->IsAssistTankOfIndex(bot, 0)) + if (!BotRoleService::IsAssistTankOfIndexStatic(bot, 0)) { return false; } @@ -213,7 +215,7 @@ bool IccFestergutMainTankGastricBloatTrigger::IsActive() { return false; } - Aura* aura = botAI->GetAura("Gastric Bloat", mt, false, true); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("Gastric Bloat", mt, false, true); if (!aura || aura->GetStackAmount() < 6) { return false; @@ -224,7 +226,7 @@ bool IccFestergutMainTankGastricBloatTrigger::IsActive() bool IccFestergutSporeTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "festergut"); - if (!boss || botAI->IsTank(bot)) + if (!boss || BotRoleService::IsTankStatic(bot)) return false; // Check for spore aura (ID: 69279) on any bot in the group @@ -249,7 +251,7 @@ bool IccFestergutSporeTrigger::IsActive() bool IccRotfaceTankPositionTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "rotface"); - if (!boss || !(botAI->IsTank(bot) || botAI->IsMelee(bot))) + if (!boss || !(BotRoleService::IsTankStatic(bot) || BotRoleService::IsMeleeStatic(bot))) return false; if (bot->HasAura(SPELL_EXPERIENCED)) @@ -296,15 +298,15 @@ bool IccPutricideGrowingOozePuddleTrigger::IsActive() if (!bot->HasAura(SPELL_AGEIS_OF_DALARAN)) bot->AddAura(SPELL_AGEIS_OF_DALARAN, bot); - if (!bot->HasAura(SPELL_NO_THREAT) && botAI->HasAggro(boss) && !botAI->IsTank(bot)) + if (!bot->HasAura(SPELL_NO_THREAT) && botAI->GetServices().GetRoleService().HasAggro(boss) && !BotRoleService::IsTankStatic(bot)) bot->AddAura(SPELL_NO_THREAT, bot); - if (botAI->IsMainTank(bot) && !bot->HasAura(SPELL_SPITEFULL_FURY) && boss->GetVictim() != bot) + if (BotRoleService::IsMainTankStatic(bot) && !bot->HasAura(SPELL_SPITEFULL_FURY) && boss->GetVictim() != bot) bot->AddAura(SPELL_SPITEFULL_FURY, bot); //-------CHEAT------- } - const GuidVector& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); for (auto const& npc : npcs) { if (Unit* unit = botAI->GetUnit(npc)) @@ -324,7 +326,7 @@ bool IccPutricideVolatileOozeTrigger::IsActive() if (!boss) return false; - if (botAI->HasAura("Gaseous Bloat", bot)) + if (botAI->GetServices().GetSpellService().HasAura("Gaseous Bloat", bot)) return false; return true; @@ -338,7 +340,7 @@ bool IccPutricideGasCloudTrigger::IsActive() Unit* boss1 = AI_VALUE2(Unit*, "find target", "volatile ooze"); - bool hasGaseousBloat = botAI->HasAura("Gaseous Bloat", bot); + bool hasGaseousBloat = botAI->GetServices().GetSpellService().HasAura("Gaseous Bloat", bot); if (hasGaseousBloat && boss1) return true; @@ -358,7 +360,7 @@ bool IccPutricideMainTankMutatedPlagueTrigger::IsActive() if (!bossPresent) return false; - if (!botAI->IsAssistTankOfIndex(bot, 0)) + if (!BotRoleService::IsAssistTankOfIndexStatic(bot, 0)) { return false; } @@ -367,7 +369,7 @@ bool IccPutricideMainTankMutatedPlagueTrigger::IsActive() { return false; } - Aura* aura = botAI->GetAura("Mutated Plague", mt, false, true); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("Mutated Plague", mt, false, true); if (!aura || aura->GetStackAmount() < 4) { return false; @@ -381,7 +383,7 @@ bool IccPutricideMalleableGooTrigger::IsActive() if (!boss) return false; - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) return true; Unit* boss1 = AI_VALUE2(Unit*, "find target", "volatile ooze"); @@ -405,10 +407,10 @@ bool IccBpcKelesethTankTrigger::IsActive() if (bot->HasAura(SPELL_EXPERIENCED)) bot->RemoveAura(SPELL_EXPERIENCED); - if (!botAI->IsAssistTank(bot)) + if (!BotRoleService::IsAssistTankStatic(bot)) return false; - Aura* aura = botAI->GetAura("Shadow Prison", bot, false, true); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("Shadow Prison", bot, false, true); if (aura) if (aura->GetStackAmount() > 18) return false; @@ -418,7 +420,7 @@ bool IccBpcKelesethTankTrigger::IsActive() bool IccBpcMainTankTrigger::IsActive() { - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) return false; Unit* valanar = AI_VALUE2(Unit*, "find target", "prince valanar"); @@ -437,19 +439,19 @@ bool IccBpcMainTankTrigger::IsActive() bool IccBpcEmpoweredVortexTrigger::IsActive() { // Tanks should ignore this mechanic - if (botAI->IsMainTank(bot) || botAI->IsAssistTank(bot)) + if (BotRoleService::IsMainTankStatic(bot) || BotRoleService::IsAssistTankStatic(bot)) return false; Unit* valanar = AI_VALUE2(Unit*, "find target", "prince valanar"); if (!valanar) return false; - Aura* aura = botAI->GetAura("Shadow Prison", bot, false, true); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("Shadow Prison", bot, false, true); if (aura) if (aura->GetStackAmount() > 12) return false; - Aura* auraValanar = botAI->GetAura("Invocation of Blood", valanar); + Aura* auraValanar = botAI->GetServices().GetSpellService().GetAura("Invocation of Blood", valanar); if (!auraValanar) return false; @@ -465,17 +467,17 @@ bool IccBpcKineticBombTrigger::IsActive() if (!(valanar || taldaram || keleseth)) return false; - if (!botAI->IsRanged(bot) || botAI->IsHeal(bot)) + if (!BotRoleService::IsRangedStatic(bot) || BotRoleService::IsHealStatic(bot)) return false; // Early exit condition - if Shadow Prison has too many stacks - if (Aura* aura = botAI->GetAura("Shadow Prison", bot, false, true)) + if (Aura* aura = botAI->GetServices().GetSpellService().GetAura("Shadow Prison", bot, false, true)) { if (aura->GetStackAmount() > 12) return false; } - static const std::array bombEntries = {NPC_KINETIC_BOMB1, NPC_KINETIC_BOMB2, NPC_KINETIC_BOMB3, + static std::array const bombEntries = {NPC_KINETIC_BOMB1, NPC_KINETIC_BOMB2, NPC_KINETIC_BOMB3, NPC_KINETIC_BOMB4}; const GuidVector bombs = AI_VALUE(GuidVector, "possible targets no los"); @@ -502,7 +504,7 @@ bool IccBpcKineticBombTrigger::IsActive() break; } - return botAI->IsRangedDps(bot) && bombFound; + return BotRoleService::IsRangedDpsStatic(bot) && bombFound; } bool IccBpcBallOfFlameTrigger::IsActive() @@ -514,7 +516,7 @@ bool IccBpcBallOfFlameTrigger::IsActive() if (!(valanar || taldaram || keleseth)) return false; - Aura* auraTaldaram = botAI->GetAura("Invocation of Blood", taldaram); + Aura* auraTaldaram = botAI->GetServices().GetSpellService().GetAura("Invocation of Blood", taldaram); if (!auraTaldaram) return false; @@ -540,7 +542,7 @@ bool IccBqlPactOfDarkfallenTrigger::IsActive() if (!boss) return false; - Aura* aura = botAI->GetAura("Pact of the Darkfallen", bot); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("Pact of the Darkfallen", bot); if (!aura) return false; @@ -553,7 +555,7 @@ bool IccBqlVampiricBiteTrigger::IsActive() if (!boss) return false; - Aura* aura = botAI->GetAura("Frenzied Bloodthirst", bot); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("Frenzied Bloodthirst", bot); if (!aura) return false; @@ -601,12 +603,12 @@ bool IccValithriaPortalTrigger::IsActive() if (!boss) return false; - Aura* aura = botAI->GetAura("Twisted Nightmares", bot, false, true); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("Twisted Nightmares", bot, false, true); if (aura && aura->GetStackAmount() >= 25) return false; // Only healers should use portals - if (!botAI->IsHeal(bot) || bot->HasAura(SPELL_DREAM_STATE)) + if (!BotRoleService::IsHealStatic(bot) || bot->HasAura(SPELL_DREAM_STATE)) return false; Creature* worm = bot->FindNearestCreature(NPC_ROT_WORM, 100.0f); @@ -630,7 +632,7 @@ bool IccValithriaPortalTrigger::IsActive() if (!member || !member->IsAlive() || botAI->IsRealPlayer()) continue; - if (botAI->IsHeal(member) && !botAI->IsRealPlayer()) + if (BotRoleService::IsHealStatic(member) && !botAI->IsRealPlayer()) { healerCount++; healerGuids.push_back(member->GetGUID()); @@ -711,7 +713,7 @@ bool IccValithriaHealTrigger::IsActive() return false; // Only healers should use healing - if (!botAI->IsHeal(bot) || bot->HasAura(SPELL_DREAM_STATE) || bot->HealthBelowPct(50)) + if (!BotRoleService::IsHealStatic(bot) || bot->HasAura(SPELL_DREAM_STATE) || bot->HealthBelowPct(50)) return false; Creature* worm = bot->FindNearestCreature(NPC_ROT_WORM, 100.0f); @@ -735,7 +737,7 @@ bool IccValithriaHealTrigger::IsActive() if (!member || !member->IsAlive() || botAI->IsRealPlayer()) continue; - if (botAI->IsHeal(member) && !botAI->IsRealPlayer()) + if (BotRoleService::IsHealStatic(member) && !botAI->IsRealPlayer()) { healerCount++; healerGuids.push_back(member->GetGUID()); @@ -843,10 +845,10 @@ bool IccSindragosaGroupPositionTrigger::IsActive() if (!bot->HasAura(SPELL_AGEIS_OF_DALARAN)) bot->AddAura(SPELL_AGEIS_OF_DALARAN, bot); - if (!bot->HasAura(SPELL_NO_THREAT) && botAI->HasAggro(boss) && !botAI->IsTank(bot)) + if (!bot->HasAura(SPELL_NO_THREAT) && botAI->GetServices().GetRoleService().HasAggro(boss) && !BotRoleService::IsTankStatic(bot)) bot->AddAura(SPELL_NO_THREAT, bot); - if (botAI->IsMainTank(bot) && !bot->HasAura(SPELL_SPITEFULL_FURY) && boss->GetVictim() != bot) + if (BotRoleService::IsMainTankStatic(bot) && !bot->HasAura(SPELL_SPITEFULL_FURY) && boss->GetVictim() != bot) bot->AddAura(SPELL_SPITEFULL_FURY, bot); //-------CHEAT------- } @@ -890,7 +892,7 @@ bool IccSindragosaBlisteringColdTrigger::IsActive() if (!boss) return false; - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) return false; // Don't move if any bot in group has ice tomb @@ -923,7 +925,7 @@ bool IccSindragosaUnchainedMagicTrigger::IsActive() if (!boss) return false; - Aura* aura = botAI->GetAura("unchained magic", bot, false, false); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("unchained magic", bot, false, false); if (!aura) return false; @@ -946,7 +948,7 @@ bool IccSindragosaChilledToTheBoneTrigger::IsActive() if (!boss) return false; - Aura* aura = botAI->GetAura("Chilled to the Bone", bot, false, false); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("Chilled to the Bone", bot, false, false); if (!aura) return false; @@ -972,7 +974,7 @@ bool IccSindragosaMysticBuffetTrigger::IsActive() if (boss->GetVictim() == bot) return false; - Aura* aura = botAI->GetAura("mystic buffet", bot, false, true); + Aura* aura = botAI->GetServices().GetSpellService().GetAura("mystic buffet", bot, false, true); if (!aura) return false; @@ -991,15 +993,15 @@ bool IccSindragosaMainTankMysticBuffetTrigger::IsActive() if (!boss) return false; - Aura* aura = botAI->GetAura("mystic buffet", bot, false, false); - if (botAI->IsTank(bot) && aura) //main tank will delete mystic buffet until I find a better way to swap tanks, atm it is not great since while swapping they will wipe group 7/10 times. + Aura* aura = botAI->GetServices().GetSpellService().GetAura("mystic buffet", bot, false, false); + if (BotRoleService::IsTankStatic(bot) && aura) //main tank will delete mystic buffet until I find a better way to swap tanks, atm it is not great since while swapping they will wipe group 7/10 times. bot->RemoveAura(aura->GetId()); - if (botAI->IsTank(bot) && boss->GetVictim() == bot) + if (BotRoleService::IsTankStatic(bot) && boss->GetVictim() == bot) return false; // Only for assist tank - if (!botAI->IsAssistTankOfIndex(bot, 0)) + if (!BotRoleService::IsAssistTankOfIndexStatic(bot, 0)) return false; // Don't swap if we have frost beacon @@ -1011,12 +1013,12 @@ bool IccSindragosaMainTankMysticBuffetTrigger::IsActive() return false; // Check main tank stacks - Aura* mtAura = botAI->GetAura("mystic buffet", mt, false, true); + Aura* mtAura = botAI->GetServices().GetSpellService().GetAura("mystic buffet", mt, false, true); if (!mtAura || mtAura->GetStackAmount() < 6) return false; // Check our own stacks - don't taunt if we have too many - Aura* selfAura = botAI->GetAura("mystic buffet", bot, false, true); + Aura* selfAura = botAI->GetServices().GetSpellService().GetAura("mystic buffet", bot, false, true); if (selfAura && selfAura->GetStackAmount() > 6) return false; @@ -1038,7 +1040,7 @@ bool IccSindragosaTankSwapPositionTrigger::IsActive() return false; // Only for assist tank - if (!botAI->IsAssistTankOfIndex(bot, 0)) + if (!BotRoleService::IsAssistTankOfIndexStatic(bot, 0)) return false; // Don't move to position if we have frost beacon @@ -1046,7 +1048,7 @@ bool IccSindragosaTankSwapPositionTrigger::IsActive() return false; // Check our own stacks - don't try to tank if we have too many - Aura* selfAura = botAI->GetAura("mystic buffet", bot, false, true); + Aura* selfAura = botAI->GetServices().GetSpellService().GetAura("mystic buffet", bot, false, true); if (selfAura && selfAura->GetStackAmount() > 6) return false; @@ -1055,7 +1057,7 @@ bool IccSindragosaTankSwapPositionTrigger::IsActive() if (!mt) return false; - Aura* mtAura = botAI->GetAura("mystic buffet", mt, false, true); + Aura* mtAura = botAI->GetServices().GetSpellService().GetAura("mystic buffet", mt, false, true); if (!mtAura) return false; @@ -1094,11 +1096,11 @@ bool IccLichKingShadowTrapTrigger::IsActive() if (!boss) return false; - bool hasPlague = botAI->HasAura("Necrotic Plague", bot); + bool hasPlague = botAI->GetServices().GetSpellService().HasAura("Necrotic Plague", bot); if (hasPlague) return false; - if (!botAI->IsMainTank(bot)) + if (!BotRoleService::IsMainTankStatic(bot)) return false; if (boss->HealthBelowPct(65)) @@ -1106,8 +1108,6 @@ bool IccLichKingShadowTrapTrigger::IsActive() // search for all nearby traps GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - std::vector nearbyTraps; - bool needToMove = false; for (auto& npc : npcs) { @@ -1124,7 +1124,7 @@ bool IccLichKingShadowTrapTrigger::IsActive() bool IccLichKingNecroticPlagueTrigger::IsActive() { - bool hasPlague = botAI->HasAura("Necrotic Plague", bot); + bool hasPlague = botAI->GetServices().GetSpellService().HasAura("Necrotic Plague", bot); return hasPlague; } @@ -1151,14 +1151,14 @@ bool IccLichKingWinterTrigger::IsActive() isCasting = true; bool isWinter = false; - if (boss && boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER1) || + if (boss && (boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER1) || boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER2) || boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER5) || boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER6) || boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER3) || boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER4) || boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER7) || - boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER8)) + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER8))) isWinter = true; if (hasWinterAura || hasWinter2Aura) @@ -1174,7 +1174,7 @@ bool IccLichKingAddsTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); - bool hasPlague = botAI->HasAura("Necrotic Plague", bot); + bool hasPlague = botAI->GetServices().GetSpellService().HasAura("Necrotic Plague", bot); if (hasPlague) return false; diff --git a/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.h b/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.h index cd332e0046..5890922627 100644 --- a/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.h +++ b/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.h @@ -202,16 +202,16 @@ const uint32 DEFILE_NPC_ID = 38757; const size_t DEFILE_AURA_COUNT = 4; // All fanatics and adherents entry ids Lady Deathwhisper -static const std::array addEntriesLady = { +static std::array const addEntriesLady = { 37949, 38394, 38625, 38626, 38010, 38397, 39000, 39001, 38136, 38396, 38632, 38633, 37890, 38393, 38628, 38629, 38135, 38395, 38634, 38009, 38398, 38630, 38631}; -const std::vector spellEntriesFlood = { +std::vector const spellEntriesFlood = { 69782, 69783, 69796, 69797, 69798, 69799, 69801, 69802, 69795}; -const std::vector availableTargetsGS = { +std::vector const availableTargetsGS = { NPC_KOR_KRON_AXETHROWER, NPC_KOR_KRON_ROCKETEER, NPC_KOR_KRON_BATTLE_MAGE, NPC_IGB_HIGH_OVERLORD_SAURFANG, NPC_SKYBREAKER_RIFLEMAN, NPC_SKYBREAKER_MORTAR_SOLDIER, NPC_SKYBREAKER_SORCERER, NPC_IGB_MURADIN_BRONZEBEARD}; diff --git a/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp b/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp index 2e5d7abcfd..da7740b2ee 100644 --- a/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp +++ b/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.cpp @@ -1,4 +1,6 @@ +#include "BotSpellService.h" #include "RaidKarazhanActions.h" +#include "BotRoleService.h" #include "RaidKarazhanHelpers.h" #include "Playerbots.h" #include "PlayerbotTextMgr.h" @@ -9,13 +11,13 @@ using namespace KarazhanHelpers; // Mana Warps blow up when they die for massive raid damage // But they cannot cast the ability if they are stunned -bool ManaWarpStunCreatureBeforeWarpBreachAction::Execute(Event event) +bool ManaWarpStunCreatureBeforeWarpBreachAction::Execute(Event /*event*/) { Unit* manaWarp = GetFirstAliveUnitByEntry(botAI, NPC_MANA_WARP); if (!manaWarp) return false; - static const std::array spells = + static std::array const spells = { "bash", "concussion blow", @@ -29,8 +31,8 @@ bool ManaWarpStunCreatureBeforeWarpBreachAction::Execute(Event event) for (const char* spell : spells) { - if (botAI->CanCastSpell(spell, manaWarp)) - return botAI->CastSpell(spell, manaWarp); + if (botAI->GetServices().GetSpellService().CanCastSpell(spell, manaWarp)) + return botAI->GetServices().GetSpellService().CastSpell(spell, manaWarp); } return false; @@ -39,7 +41,7 @@ bool ManaWarpStunCreatureBeforeWarpBreachAction::Execute(Event event) // Attumen the Huntsman // Prioritize Midnight until Attumen is mounted -bool AttumenTheHuntsmanMarkTargetAction::Execute(Event event) +bool AttumenTheHuntsmanMarkTargetAction::Execute(Event /*event*/) { Unit* attumenMounted = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED); if (attumenMounted) @@ -60,7 +62,7 @@ bool AttumenTheHuntsmanMarkTargetAction::Execute(Event event) if (IsInstanceTimerManager(botAI, bot)) MarkTargetWithStar(bot, midnight); - if (!botAI->IsAssistTankOfIndex(bot, 0)) + if (!BotRoleService::IsAssistTankOfIndexStatic(bot, 0)) { SetRtiTarget(botAI, "star", midnight); @@ -76,7 +78,7 @@ bool AttumenTheHuntsmanMarkTargetAction::Execute(Event event) } // Off tank should move Attumen out of the way so he doesn't cleave bots -bool AttumenTheHuntsmanSplitBossesAction::Execute(Event event) +bool AttumenTheHuntsmanSplitBossesAction::Execute(Event /*event*/) { Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight"); if (!midnight) @@ -104,13 +106,13 @@ bool AttumenTheHuntsmanSplitBossesAction::Execute(Event event) } // Stack behind mounted Attumen (inside minimum range of Berserker Charge) -bool AttumenTheHuntsmanStackBehindAction::Execute(Event event) +bool AttumenTheHuntsmanStackBehindAction::Execute(Event /*event*/) { Unit* attumenMounted = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED); if (!attumenMounted) return false; - const float distanceBehind = botAI->IsRanged(bot) ? 6.0f : 2.0f; + const float distanceBehind = BotRoleService::IsRangedStatic(bot) ? 6.0f : 2.0f; float orientation = attumenMounted->GetOrientation() + M_PI; float rearX = attumenMounted->GetPositionX() + std::cos(orientation) * distanceBehind; float rearY = attumenMounted->GetPositionY() + std::sin(orientation) * distanceBehind; @@ -125,7 +127,7 @@ bool AttumenTheHuntsmanStackBehindAction::Execute(Event event) } // Reset timer for bots to pause DPS when Attumen mounts Midnight -bool AttumenTheHuntsmanManageDpsTimerAction::Execute(Event event) +bool AttumenTheHuntsmanManageDpsTimerAction::Execute(Event /*event*/) { Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight"); if (!midnight) @@ -152,7 +154,7 @@ bool AttumenTheHuntsmanManageDpsTimerAction::Execute(Event event) // Moroes -bool MoroesMainTankAttackBossAction::Execute(Event event) +bool MoroesMainTankAttackBossAction::Execute(Event /*event*/) { Unit* moroes = AI_VALUE2(Unit*, "find target", "moroes"); if (!moroes) @@ -168,7 +170,7 @@ bool MoroesMainTankAttackBossAction::Execute(Event event) } // Mark targets with skull in the recommended kill order -bool MoroesMarkTargetAction::Execute(Event event) +bool MoroesMarkTargetAction::Execute(Event /*event*/) { Unit* dorothea = AI_VALUE2(Unit*, "find target", "baroness dorothea millstipe"); Unit* catriona = AI_VALUE2(Unit*, "find target", "lady catriona von'indi"); @@ -193,7 +195,7 @@ bool MoroesMarkTargetAction::Execute(Event event) // Tank the boss in the center of the room // Move to healers after Repentenace to break the stun -bool MaidenOfVirtueMoveBossToHealerAction::Execute(Event event) +bool MaidenOfVirtueMoveBossToHealerAction::Execute(Event /*event*/) { Unit* maiden = AI_VALUE2(Unit*, "find target", "maiden of virtue"); if (!maiden) @@ -208,7 +210,7 @@ bool MaidenOfVirtueMoveBossToHealerAction::Execute(Event event) for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (!member || !member->IsAlive() || !botAI->IsHeal(member) || + if (!member || !member->IsAlive() || !BotRoleService::IsHealStatic(member) || !member->HasAura(SPELL_REPENTANCE)) continue; @@ -228,7 +230,7 @@ bool MaidenOfVirtueMoveBossToHealerAction::Execute(Event event) } } - const Position& position = MAIDEN_OF_VIRTUE_BOSS_POSITION; + Position const& position = MAIDEN_OF_VIRTUE_BOSS_POSITION; const float maxDistance = 2.0f; float distanceToPosition = maiden->GetExactDist2d(position); if (distanceToPosition > maxDistance) @@ -247,7 +249,7 @@ bool MaidenOfVirtueMoveBossToHealerAction::Execute(Event event) } // Spread out ranged DPS between the pillars -bool MaidenOfVirtuePositionRangedAction::Execute(Event event) +bool MaidenOfVirtuePositionRangedAction::Execute(Event /*event*/) { const uint8 maxIndex = 7; uint8 index = 0; @@ -257,7 +259,7 @@ bool MaidenOfVirtuePositionRangedAction::Execute(Event event) for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (!member || !botAI->IsRanged(member)) + if (!member || !BotRoleService::IsRangedStatic(member)) continue; if (member == bot) @@ -272,7 +274,7 @@ bool MaidenOfVirtuePositionRangedAction::Execute(Event event) } } - const Position& position = MAIDEN_OF_VIRTUE_RANGED_POSITION[index]; + Position const& position = MAIDEN_OF_VIRTUE_RANGED_POSITION[index]; if (bot->GetExactDist2d(position) > 2.0f) { bot->AttackStop(); @@ -287,7 +289,7 @@ bool MaidenOfVirtuePositionRangedAction::Execute(Event event) // The Big Bad Wolf // Tank the boss at the front left corner of the stage -bool BigBadWolfPositionBossAction::Execute(Event event) +bool BigBadWolfPositionBossAction::Execute(Event /*event*/) { Unit* wolf = AI_VALUE2(Unit*, "find target", "the big bad wolf"); if (!wolf) @@ -298,7 +300,7 @@ bool BigBadWolfPositionBossAction::Execute(Event event) if (wolf->GetVictim() == bot) { - const Position& position = BIG_BAD_WOLF_BOSS_POSITION; + Position const& position = BIG_BAD_WOLF_BOSS_POSITION; float distanceToPosition = wolf->GetExactDist2d(position); if (distanceToPosition > 2.0f) @@ -318,7 +320,7 @@ bool BigBadWolfPositionBossAction::Execute(Event event) } // Run away, little girl, run away -bool BigBadWolfRunAwayFromBossAction::Execute(Event event) +bool BigBadWolfRunAwayFromBossAction::Execute(Event /*event*/) { const ObjectGuid botGuid = bot->GetGUID(); uint8 index = bigBadWolfRunIndex.count(botGuid) ? bigBadWolfRunIndex[botGuid] : 0; @@ -333,7 +335,7 @@ bool BigBadWolfRunAwayFromBossAction::Execute(Event event) bot->AttackStop(); bot->InterruptNonMeleeSpells(true); - const Position& position = BIG_BAD_WOLF_RUN_POSITION[index]; + Position const& position = BIG_BAD_WOLF_RUN_POSITION[index]; return MoveTo(KARAZHAN_MAP_ID, position.GetPositionX(), position.GetPositionY(), position.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_FORCED, true, false); } @@ -341,7 +343,7 @@ bool BigBadWolfRunAwayFromBossAction::Execute(Event event) // Romulo and Julianne // Keep the couple within 10% HP of each other -bool RomuloAndJulianneMarkTargetAction::Execute(Event event) +bool RomuloAndJulianneMarkTargetAction::Execute(Event /*event*/) { Unit* romulo = AI_VALUE2(Unit*, "find target", "romulo"); if (!romulo) @@ -370,7 +372,7 @@ bool RomuloAndJulianneMarkTargetAction::Execute(Event event) // The Wizard of Oz // Mark targets with skull in the recommended kill order -bool WizardOfOzMarkTargetAction::Execute(Event event) +bool WizardOfOzMarkTargetAction::Execute(Event /*event*/) { Unit* dorothee = AI_VALUE2(Unit*, "find target", "dorothee"); Unit* tito = AI_VALUE2(Unit*, "find target", "tito"); @@ -387,11 +389,11 @@ bool WizardOfOzMarkTargetAction::Execute(Event event) } // Mages spam Scorch on Strawman to disorient him -bool WizardOfOzScorchStrawmanAction::Execute(Event event) +bool WizardOfOzScorchStrawmanAction::Execute(Event /*event*/) { Unit* strawman = AI_VALUE2(Unit*, "find target", "strawman"); - if (strawman && botAI->CanCastSpell("scorch", strawman)) - return botAI->CastSpell("scorch", strawman); + if (strawman && botAI->GetServices().GetSpellService().CanCastSpell("scorch", strawman)) + return botAI->GetServices().GetSpellService().CastSpell("scorch", strawman); return false; } @@ -399,7 +401,7 @@ bool WizardOfOzScorchStrawmanAction::Execute(Event event) // The Curator // Prioritize destroying Astral Flares -bool TheCuratorMarkAstralFlareAction::Execute(Event event) +bool TheCuratorMarkAstralFlareAction::Execute(Event /*event*/) { Unit* flare = AI_VALUE2(Unit*, "find target", "astral flare"); if (!flare) @@ -415,7 +417,7 @@ bool TheCuratorMarkAstralFlareAction::Execute(Event event) // Tank the boss in the center of the hallway near the Guardian's Library // Main tank and off tank will attack the boss; others will focus on Astral Flares -bool TheCuratorPositionBossAction::Execute(Event event) +bool TheCuratorPositionBossAction::Execute(Event /*event*/) { Unit* curator = AI_VALUE2(Unit*, "find target", "the curator"); if (!curator) @@ -429,7 +431,7 @@ bool TheCuratorPositionBossAction::Execute(Event event) if (curator->GetVictim() == bot) { - const Position& position = THE_CURATOR_BOSS_POSITION; + Position const& position = THE_CURATOR_BOSS_POSITION; float distanceToPosition = curator->GetExactDist2d(position); if (distanceToPosition > 2.0f) @@ -449,7 +451,7 @@ bool TheCuratorPositionBossAction::Execute(Event event) } // Spread out ranged DPS to avoid Arcing Sear damage -bool TheCuratorSpreadRangedAction::Execute(Event event) +bool TheCuratorSpreadRangedAction::Execute(Event /*event*/) { const float minDistance = 5.0f; Unit* nearestPlayer = GetNearestPlayerInRadius(bot, minDistance); @@ -467,7 +469,7 @@ bool TheCuratorSpreadRangedAction::Execute(Event event) // Terestian Illhoof // Prioritize (1) Demon Chains, (2) Kil'rek, (3) Illhoof -bool TerestianIllhoofMarkTargetAction::Execute(Event event) +bool TerestianIllhoofMarkTargetAction::Execute(Event /*event*/) { Unit* demonChains = AI_VALUE2(Unit*, "find target", "demon chains"); Unit* kilrek = AI_VALUE2(Unit*, "find target", "kil'rek"); @@ -483,7 +485,7 @@ bool TerestianIllhoofMarkTargetAction::Execute(Event event) // Shade of Aran // Run to the edge of the room to avoid Arcane Explosion -bool ShadeOfAranRunAwayFromArcaneExplosionAction::Execute(Event event) +bool ShadeOfAranRunAwayFromArcaneExplosionAction::Execute(Event /*event*/) { Unit* aran = AI_VALUE2(Unit*, "find target", "shade of aran"); if (!aran) @@ -502,7 +504,7 @@ bool ShadeOfAranRunAwayFromArcaneExplosionAction::Execute(Event event) } // I will not move when Flame Wreath is cast or the raid blows up -bool ShadeOfAranStopMovingDuringFlameWreathAction::Execute(Event event) +bool ShadeOfAranStopMovingDuringFlameWreathAction::Execute(Event /*event*/) { AI_VALUE(LastMovement&, "last movement").Set(nullptr); @@ -517,7 +519,7 @@ bool ShadeOfAranStopMovingDuringFlameWreathAction::Execute(Event event) } // Mark Conjured Elementals with skull so DPS can burn them down -bool ShadeOfAranMarkConjuredElementalAction::Execute(Event event) +bool ShadeOfAranMarkConjuredElementalAction::Execute(Event /*event*/) { Unit* elemental = GetFirstAliveUnitByEntry(botAI, NPC_CONJURED_ELEMENTAL); @@ -529,7 +531,7 @@ bool ShadeOfAranMarkConjuredElementalAction::Execute(Event event) // Don't get closer than 11 yards to Aran to avoid counterspell // Don't get farther than 15 yards from Aran to avoid getting stuck in alcoves -bool ShadeOfAranRangedMaintainDistanceAction::Execute(Event event) +bool ShadeOfAranRangedMaintainDistanceAction::Execute(Event /*event*/) { Unit* aran = AI_VALUE2(Unit*, "find target", "shade of aran"); if (!aran) @@ -593,7 +595,7 @@ bool ShadeOfAranRangedMaintainDistanceAction::Execute(Event event) // One tank bot per phase will dance in and out of the red beam (5 seconds in, 5 seconds out) // Tank bots will ignore void zones--their positioning is too important to risk losing beam control -bool NetherspiteBlockRedBeamAction::Execute(Event event) +bool NetherspiteBlockRedBeamAction::Execute(Event /*event*/) { Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite"); if (!netherspite) @@ -680,7 +682,7 @@ Position NetherspiteBlockRedBeamAction::GetPositionOnBeam(Unit* netherspite, Uni // Two non-Rogue/Warrior DPS bots will block the blue beam for each phase (swap at 25 debuff stacks) // When avoiding void zones, blocking bots will move along the beam to continue blocking -bool NetherspiteBlockBlueBeamAction::Execute(Event event) +bool NetherspiteBlockBlueBeamAction::Execute(Event /*event*/) { Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite"); if (!netherspite) @@ -717,7 +719,7 @@ bool NetherspiteBlockBlueBeamAction::Execute(Event event) } _wasBlockingBlueBeam[botGuid] = true; - float idealDistance = botAI->IsRanged(bot) ? 25.0f : 18.0f; + float idealDistance = BotRoleService::IsRangedStatic(bot) ? 25.0f : 18.0f; std::vector voidZones = GetAllVoidZones(botAI, bot); float bx = netherspite->GetPositionX(); @@ -773,7 +775,7 @@ bool NetherspiteBlockBlueBeamAction::Execute(Event event) // Two healer bots will block the green beam for each phase (swap at 25 debuff stacks) // OR one rogue or DPS warrior bot will block the green beam for an entire phase (if they begin the phase as the blocker) // When avoiding void zones, blocking bots will move along the beam to continue blocking -bool NetherspiteBlockGreenBeamAction::Execute(Event event) +bool NetherspiteBlockGreenBeamAction::Execute(Event /*event*/) { Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite"); if (!netherspite) @@ -863,13 +865,12 @@ bool NetherspiteBlockGreenBeamAction::Execute(Event event) } // All bots not currently blocking a beam will avoid beams and void zones -bool NetherspiteAvoidBeamAndVoidZoneAction::Execute(Event event) +bool NetherspiteAvoidBeamAndVoidZoneAction::Execute(Event /*event*/) { Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite"); if (!netherspite) return false; - auto [redBlocker, greenBlocker, blueBlocker] = GetCurrentBeamBlockers(botAI, bot); std::vector voidZones = GetAllVoidZones(botAI, bot); bool nearVoidZone = !IsSafePosition(bot->GetPositionX(), bot->GetPositionY(), @@ -954,7 +955,7 @@ bool NetherspiteAvoidBeamAndVoidZoneAction::Execute(Event event) } bool NetherspiteAvoidBeamAndVoidZoneAction::IsAwayFromBeams( - float x, float y, const std::vector& beams, Unit* netherspite) + float x, float y, std::vector const& beams, Unit* netherspite) { for (auto const& beam : beams) { @@ -979,7 +980,7 @@ bool NetherspiteAvoidBeamAndVoidZoneAction::IsAwayFromBeams( return true; } -bool NetherspiteBanishPhaseAvoidVoidZoneAction::Execute(Event event) +bool NetherspiteBanishPhaseAvoidVoidZoneAction::Execute(Event /*event*/) { std::vector voidZones = GetAllVoidZones(botAI, bot); @@ -992,7 +993,7 @@ bool NetherspiteBanishPhaseAvoidVoidZoneAction::Execute(Event event) return false; } -bool NetherspiteManageTimersAndTrackersAction::Execute(Event event) +bool NetherspiteManageTimersAndTrackersAction::Execute(Event /*event*/) { Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite"); if (!netherspite) @@ -1010,7 +1011,7 @@ bool NetherspiteManageTimersAndTrackersAction::Execute(Event event) if (IsInstanceTimerManager(botAI, bot)) netherspiteDpsWaitTimer.insert_or_assign(instanceId, now); - if (botAI->IsTank(bot) && !bot->HasAura(SPELL_RED_BEAM_DEBUFF)) + if (BotRoleService::IsTankStatic(bot) && !bot->HasAura(SPELL_RED_BEAM_DEBUFF)) { redBeamMoveTimer.erase(botGuid); lastBeamMoveSideways.erase(botGuid); @@ -1021,7 +1022,7 @@ bool NetherspiteManageTimersAndTrackersAction::Execute(Event event) if (IsInstanceTimerManager(botAI, bot)) netherspiteDpsWaitTimer.erase(instanceId); - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) { redBeamMoveTimer.erase(botGuid); lastBeamMoveSideways.erase(botGuid); @@ -1032,7 +1033,7 @@ bool NetherspiteManageTimersAndTrackersAction::Execute(Event event) if (IsInstanceTimerManager(botAI, bot)) netherspiteDpsWaitTimer.try_emplace(instanceId, now); - if (botAI->IsTank(bot) && bot->HasAura(SPELL_RED_BEAM_DEBUFF)) + if (BotRoleService::IsTankStatic(bot) && bot->HasAura(SPELL_RED_BEAM_DEBUFF)) { redBeamMoveTimer.try_emplace(botGuid, now); lastBeamMoveSideways.try_emplace(botGuid, false); @@ -1044,7 +1045,7 @@ bool NetherspiteManageTimersAndTrackersAction::Execute(Event event) // Move away from the boss to avoid Shadow Nova when Enfeebled // Do not cross within Infernal Hellfire radius while doing so -bool PrinceMalchezaarEnfeebledAvoidHazardAction::Execute(Event event) +bool PrinceMalchezaarEnfeebledAvoidHazardAction::Execute(Event /*event*/) { Unit* malchezaar = AI_VALUE2(Unit*, "find target", "prince malchezaar"); if (!malchezaar) @@ -1121,7 +1122,7 @@ bool PrinceMalchezaarEnfeebledAvoidHazardAction::Execute(Event event) // Move away from infernals while staying within range of the boss // Prioritize finding a safe path to the new location, but will fallback to just finding a safe location if needed -bool PrinceMalchezaarNonTankAvoidInfernalAction::Execute(Event event) +bool PrinceMalchezaarNonTankAvoidInfernalAction::Execute(Event /*event*/) { Unit* malchezaar = AI_VALUE2(Unit*, "find target", "prince malchezaar"); if (!malchezaar) @@ -1188,7 +1189,7 @@ bool PrinceMalchezaarNonTankAvoidInfernalAction::Execute(Event event) // This is similar to the non-tank avoid infernal action, but the movement is based on the bot's location // And the safe distance from infernals is larger to give melee more room to maneuver -bool PrinceMalchezaarMainTankMovementAction::Execute(Event event) +bool PrinceMalchezaarMainTankMovementAction::Execute(Event /*event*/) { Unit* malchezaar = AI_VALUE2(Unit*, "find target", "prince malchezaar"); if (!malchezaar) @@ -1254,7 +1255,7 @@ bool PrinceMalchezaarMainTankMovementAction::Execute(Event event) // The tank position is near the Southeastern area of the Master's Terrace // The tank moves Nightbane into position in two steps to try to get Nightbane to face sideways to the raid -bool NightbaneGroundPhasePositionBossAction::Execute(Event event) +bool NightbaneGroundPhasePositionBossAction::Execute(Event /*event*/) { Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane"); if (!nightbane) @@ -1275,7 +1276,7 @@ bool NightbaneGroundPhasePositionBossAction::Execute(Event event) NIGHTBANE_TRANSITION_BOSS_POSITION, NIGHTBANE_FINAL_BOSS_POSITION }; - const Position& position = tankPositions[step]; + Position const& position = tankPositions[step]; const float maxDistance = 0.5f; float distanceToTarget = bot->GetExactDist2d(position); @@ -1300,7 +1301,7 @@ bool NightbaneGroundPhasePositionBossAction::Execute(Event event) // Ranged bots rotate between 3 positions to avoid standing in Charred Earth, which lasts for // 30s and has a minimum cooldown of 18s (so there can be 2 active at once) // Ranged positions are near the Northeastern door to the tower -bool NightbaneGroundPhaseRotateRangedPositionsAction::Execute(Event event) +bool NightbaneGroundPhaseRotateRangedPositionsAction::Execute(Event /*event*/) { const ObjectGuid botGuid = bot->GetGUID(); uint8 index = nightbaneRangedStep.count(botGuid) ? nightbaneRangedStep[botGuid] : 0; @@ -1311,7 +1312,7 @@ bool NightbaneGroundPhaseRotateRangedPositionsAction::Execute(Event event) NIGHTBANE_RANGED_POSITION2, NIGHTBANE_RANGED_POSITION3 }; - const Position& position = rangedPositions[index]; + Position const& position = rangedPositions[index]; const float maxDistance = 2.0f; float distanceToTarget = bot->GetExactDist2d(position); @@ -1320,7 +1321,7 @@ bool NightbaneGroundPhaseRotateRangedPositionsAction::Execute(Event event) { index = (index + 1) % 3; nightbaneRangedStep[botGuid] = index; - const Position& newPosition = rangedPositions[index]; + Position const& newPosition = rangedPositions[index]; float newDistanceToTarget = bot->GetExactDist2d(newPosition); if (newDistanceToTarget > maxDistance) { @@ -1344,7 +1345,7 @@ bool NightbaneGroundPhaseRotateRangedPositionsAction::Execute(Event event) } // For countering Bellowing Roars during the ground phase -bool NightbaneCastFearWardOnMainTankAction::Execute(Event event) +bool NightbaneCastFearWardOnMainTankAction::Execute(Event /*event*/) { Player* mainTank = nullptr; if (Group* group = bot->GetGroup()) @@ -1352,7 +1353,7 @@ bool NightbaneCastFearWardOnMainTankAction::Execute(Event event) for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (member && botAI->IsMainTank(member)) + if (member && BotRoleService::IsMainTankStatic(member)) { mainTank = member; break; @@ -1360,14 +1361,14 @@ bool NightbaneCastFearWardOnMainTankAction::Execute(Event event) } } - if (mainTank && botAI->CanCastSpell("fear ward", mainTank)) - return botAI->CastSpell("fear ward", mainTank); + if (mainTank && botAI->GetServices().GetSpellService().CanCastSpell("fear ward", mainTank)) + return botAI->GetServices().GetSpellService().CastSpell("fear ward", mainTank); return false; } // Put pets on passive during the flight phase so they don't try to chase Nightbane off the map -bool NightbaneControlPetAggressionAction::Execute(Event event) +bool NightbaneControlPetAggressionAction::Execute(Event /*event*/) { Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane"); if (!nightbane) @@ -1393,7 +1394,7 @@ bool NightbaneControlPetAggressionAction::Execute(Event event) // 2. Once Rain of Bones hits, the whole party moves to a new stack position // This action lasts for the first 35 seconds of the flight phase, after which Nightbane gets // ready to land, and the player will need to lead the bots over near the ground phase position -bool NightbaneFlightPhaseMovementAction::Execute(Event event) +bool NightbaneFlightPhaseMovementAction::Execute(Event /*event*/) { Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane"); if (!nightbane || nightbane->GetPositionZ() <= NIGHTBANE_FLIGHT_Z) @@ -1439,7 +1440,7 @@ bool NightbaneFlightPhaseMovementAction::Execute(Event event) return false; } -bool NightbaneManageTimersAndTrackersAction::Execute(Event event) +bool NightbaneManageTimersAndTrackersAction::Execute(Event /*event*/) { Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane"); if (!nightbane) @@ -1452,10 +1453,10 @@ bool NightbaneManageTimersAndTrackersAction::Execute(Event event) // Erase DPS wait timer and tank and ranged position tracking on encounter reset if (nightbane->GetHealth() == nightbane->GetMaxHealth()) { - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) nightbaneTankStep.erase(botGuid); - if (botAI->IsRanged(bot)) + if (BotRoleService::IsRangedStatic(bot)) nightbaneRangedStep.erase(botGuid); if (IsInstanceTimerManager(botAI, bot)) @@ -1476,10 +1477,10 @@ bool NightbaneManageTimersAndTrackersAction::Execute(Event event) // at beginning of flight phase else if (nightbane->GetPositionZ() > NIGHTBANE_FLIGHT_Z) { - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) nightbaneTankStep.erase(botGuid); - if (botAI->IsRanged(bot)) + if (BotRoleService::IsRangedStatic(bot)) nightbaneRangedStep.erase(botGuid); if (IsInstanceTimerManager(botAI, bot)) diff --git a/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.h b/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.h index 6fca1bcbb3..59cfeb3167 100644 --- a/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.h +++ b/src/Ai/Raid/Karazhan/Action/RaidKarazhanActions.h @@ -228,7 +228,7 @@ class NetherspiteAvoidBeamAndVoidZoneAction : public MovementAction Unit* portal; float minDist, maxDist; }; - bool IsAwayFromBeams(float x, float y, const std::vector& beams, Unit* netherspite); + bool IsAwayFromBeams(float x, float y, std::vector const& beams, Unit* netherspite); }; class NetherspiteBanishPhaseAvoidVoidZoneAction : public MovementAction diff --git a/src/Ai/Raid/Karazhan/Multiplier/RaidKarazhanMultipliers.cpp b/src/Ai/Raid/Karazhan/Multiplier/RaidKarazhanMultipliers.cpp index 117a17f38a..3eceb11da6 100644 --- a/src/Ai/Raid/Karazhan/Multiplier/RaidKarazhanMultipliers.cpp +++ b/src/Ai/Raid/Karazhan/Multiplier/RaidKarazhanMultipliers.cpp @@ -1,4 +1,5 @@ #include "RaidKarazhanMultipliers.h" +#include "BotRoleService.h" #include "RaidKarazhanActions.h" #include "RaidKarazhanHelpers.h" #include "AttackAction.h" @@ -40,7 +41,7 @@ float AttumenTheHuntsmanStayStackedMultiplier::GetValue(Action* action) if (!attumenMounted) return 1.0f; - if (!botAI->IsMainTank(bot) && attumenMounted->GetVictim() != bot) + if (!BotRoleService::IsMainTankStatic(bot) && attumenMounted->GetVictim() != bot) { if (dynamic_cast(action) || dynamic_cast(action) || @@ -68,7 +69,7 @@ float AttumenTheHuntsmanWaitForDpsMultiplier::GetValue(Action* action) auto it = attumenDpsWaitTimer.find(instanceId); if (it == attumenDpsWaitTimer.end() || (now - it->second) < dpsWaitSeconds) { - if (!botAI->IsMainTank(bot)) + if (!BotRoleService::IsMainTankStatic(bot)) { if (dynamic_cast(action) || (dynamic_cast(action) && !dynamic_cast(action))) @@ -209,7 +210,7 @@ float NetherspiteWaitForDpsMultiplier::GetValue(Action* action) auto it = netherspiteDpsWaitTimer.find(instanceId); if (it == netherspiteDpsWaitTimer.end() || (now - it->second) < dpsWaitSeconds) { - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) { if (dynamic_cast(action) || (dynamic_cast(action) && !dynamic_cast(action))) @@ -308,7 +309,7 @@ float NightbaneWaitForDpsMultiplier::GetValue(Action* action) auto it = nightbaneDpsWaitTimer.find(instanceId); if (it == nightbaneDpsWaitTimer.end() || (now - it->second) < dpsWaitSeconds) { - if (!botAI->IsMainTank(bot)) + if (!BotRoleService::IsMainTankStatic(bot)) { if (dynamic_cast(action) || (dynamic_cast(action) && !dynamic_cast(action))) @@ -328,7 +329,7 @@ float NightbaneDisableAvoidAoeMultiplier::GetValue(Action* action) if (!nightbane) return 1.0f; - if (nightbane->GetPositionZ() > NIGHTBANE_FLIGHT_Z || botAI->IsMainTank(bot)) + if (nightbane->GetPositionZ() > NIGHTBANE_FLIGHT_Z || BotRoleService::IsMainTankStatic(bot)) { if (dynamic_cast(action)) return 0.0f; @@ -351,8 +352,8 @@ float NightbaneDisableMovementMultiplier::GetValue(Action* action) // Disable CombatFormationMoveAction for all bots except: // (1) main tank and (2) only during the ground phase, other melee - if (botAI->IsRanged(bot) || - (botAI->IsMelee(bot) && !botAI->IsMainTank(bot) && + if (BotRoleService::IsRangedStatic(bot) || + (BotRoleService::IsMeleeStatic(bot) && !BotRoleService::IsMainTankStatic(bot) && nightbane->GetPositionZ() > NIGHTBANE_FLIGHT_Z)) { if (dynamic_cast(action)) diff --git a/src/Ai/Raid/Karazhan/Trigger/RaidKarazhanTriggers.cpp b/src/Ai/Raid/Karazhan/Trigger/RaidKarazhanTriggers.cpp index 2fb7d5af0f..549e429565 100644 --- a/src/Ai/Raid/Karazhan/Trigger/RaidKarazhanTriggers.cpp +++ b/src/Ai/Raid/Karazhan/Trigger/RaidKarazhanTriggers.cpp @@ -1,4 +1,6 @@ +#include "BotSpellService.h" #include "RaidKarazhanTriggers.h" +#include "BotRoleService.h" #include "RaidKarazhanHelpers.h" #include "RaidKarazhanActions.h" #include "Playerbots.h" @@ -13,7 +15,7 @@ bool ManaWarpIsAboutToExplodeTrigger::IsActive() bool AttumenTheHuntsmanNeedTargetPriorityTrigger::IsActive() { - if (botAI->IsHeal(bot)) + if (BotRoleService::IsHealStatic(bot)) return false; Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight"); @@ -22,7 +24,7 @@ bool AttumenTheHuntsmanNeedTargetPriorityTrigger::IsActive() bool AttumenTheHuntsmanAttumenSpawnedTrigger::IsActive() { - if (!botAI->IsAssistTankOfIndex(bot, 0)) + if (!BotRoleService::IsAssistTankOfIndexStatic(bot, 0)) return false; Unit* attumen = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN); @@ -31,7 +33,7 @@ bool AttumenTheHuntsmanAttumenSpawnedTrigger::IsActive() bool AttumenTheHuntsmanAttumenIsMountedTrigger::IsActive() { - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) return false; Unit* attumenMounted = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED); @@ -49,7 +51,7 @@ bool AttumenTheHuntsmanBossWipesAggroWhenMountingTrigger::IsActive() bool MoroesBossEngagedByMainTankTrigger::IsActive() { - if (!botAI->IsMainTank(bot)) + if (!BotRoleService::IsMainTankStatic(bot)) return false; Unit* moroes = AI_VALUE2(Unit*, "find target", "moroes"); @@ -58,7 +60,7 @@ bool MoroesBossEngagedByMainTankTrigger::IsActive() bool MoroesNeedTargetPriorityTrigger::IsActive() { - if (!botAI->IsDps(bot)) + if (!BotRoleService::IsDpsStatic(bot)) return false; Unit* dorothea = AI_VALUE2(Unit*, "find target", "baroness dorothea millstipe"); @@ -74,7 +76,7 @@ bool MoroesNeedTargetPriorityTrigger::IsActive() bool MaidenOfVirtueHealersAreStunnedByRepentanceTrigger::IsActive() { - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) return false; Unit* maiden = AI_VALUE2(Unit*, "find target", "maiden of virtue"); @@ -83,7 +85,7 @@ bool MaidenOfVirtueHealersAreStunnedByRepentanceTrigger::IsActive() bool MaidenOfVirtueHolyWrathDealsChainDamageTrigger::IsActive() { - if (!botAI->IsRanged(bot)) + if (!BotRoleService::IsRangedStatic(bot)) return false; Unit* maiden = AI_VALUE2(Unit*, "find target", "maiden of virtue"); @@ -92,7 +94,7 @@ bool MaidenOfVirtueHolyWrathDealsChainDamageTrigger::IsActive() bool BigBadWolfBossEngagedByTankTrigger::IsActive() { - if (!botAI->IsTank(bot) || bot->HasAura(SPELL_LITTLE_RED_RIDING_HOOD)) + if (!BotRoleService::IsTankStatic(bot) || bot->HasAura(SPELL_LITTLE_RED_RIDING_HOOD)) return false; Unit* wolf = AI_VALUE2(Unit*, "find target", "the big bad wolf"); @@ -151,7 +153,7 @@ bool WizardOfOzStrawmanIsVulnerableToFireTrigger::IsActive() bool TheCuratorAstralFlareSpawnedTrigger::IsActive() { - if (!botAI->IsDps(bot)) + if (!BotRoleService::IsDpsStatic(bot)) return false; Unit* flare = AI_VALUE2(Unit*, "find target", "astral flare"); @@ -160,7 +162,7 @@ bool TheCuratorAstralFlareSpawnedTrigger::IsActive() bool TheCuratorBossEngagedByTanksTrigger::IsActive() { - if (!botAI->IsMainTank(bot) && !botAI->IsAssistTankOfIndex(bot, 0)) + if (!BotRoleService::IsMainTankStatic(bot) && !BotRoleService::IsAssistTankOfIndexStatic(bot, 0)) return false; Unit* curator = AI_VALUE2(Unit*, "find target", "the curator"); @@ -169,7 +171,7 @@ bool TheCuratorBossEngagedByTanksTrigger::IsActive() bool TheCuratorBossAstralFlaresCastArcingSearTrigger::IsActive() { - if (!botAI->IsRanged(bot)) + if (!BotRoleService::IsRangedStatic(bot)) return false; Unit* curator = AI_VALUE2(Unit*, "find target", "the curator"); @@ -212,7 +214,7 @@ bool ShadeOfAranConjuredElementalsSummonedTrigger::IsActive() bool ShadeOfAranBossUsesCounterspellAndBlizzardTrigger::IsActive() { - if (!botAI->IsRanged(bot)) + if (!BotRoleService::IsRangedStatic(bot)) return false; Unit* aran = AI_VALUE2(Unit*, "find target", "shade of aran"); @@ -279,7 +281,7 @@ bool NetherspiteBossIsBanishedTrigger::IsActive() bool NetherspiteNeedToManageTimersAndTrackersTrigger::IsActive() { - if (!botAI->IsTank(bot) && !IsInstanceTimerManager(botAI, bot)) + if (!BotRoleService::IsTankStatic(bot) && !IsInstanceTimerManager(botAI, bot)) return false; Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite"); @@ -293,7 +295,7 @@ bool PrinceMalchezaarBotIsEnfeebledTrigger::IsActive() bool PrinceMalchezaarInfernalsAreSpawnedTrigger::IsActive() { - if (botAI->IsMainTank(bot) || bot->HasAura(SPELL_ENFEEBLE)) + if (BotRoleService::IsMainTankStatic(bot) || bot->HasAura(SPELL_ENFEEBLE)) return false; Unit* malchezaar = AI_VALUE2(Unit*, "find target", "prince malchezaar"); @@ -302,7 +304,7 @@ bool PrinceMalchezaarInfernalsAreSpawnedTrigger::IsActive() bool PrinceMalchezaarBossEngagedByMainTankTrigger::IsActive() { - if (!botAI->IsMainTank(bot)) + if (!BotRoleService::IsMainTankStatic(bot)) return false; Unit* malchezaar = AI_VALUE2(Unit*, "find target", "prince malchezaar"); @@ -311,7 +313,7 @@ bool PrinceMalchezaarBossEngagedByMainTankTrigger::IsActive() bool NightbaneBossEngagedByMainTankTrigger::IsActive() { - if (!botAI->IsMainTank(bot)) + if (!BotRoleService::IsMainTankStatic(bot)) return false; Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane"); @@ -320,7 +322,7 @@ bool NightbaneBossEngagedByMainTankTrigger::IsActive() bool NightbaneRangedBotsAreInCharredEarthTrigger::IsActive() { - if (!botAI->IsRanged(bot)) + if (!BotRoleService::IsRangedStatic(bot)) return false; Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane"); @@ -342,7 +344,7 @@ bool NightbaneMainTankIsSusceptibleToFearTrigger::IsActive() for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (member && botAI->IsMainTank(member)) + if (member && BotRoleService::IsMainTankStatic(member)) { mainTank = member; break; @@ -351,7 +353,7 @@ bool NightbaneMainTankIsSusceptibleToFearTrigger::IsActive() } return mainTank && !mainTank->HasAura(SPELL_FEAR_WARD) && - botAI->CanCastSpell("fear ward", mainTank); + botAI->GetServices().GetSpellService().CanCastSpell("fear ward", mainTank); } bool NightbanePetsIgnoreCollisionToChaseFlyingBossTrigger::IsActive() diff --git a/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.cpp b/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.cpp index 821cc67019..ecc115b3c1 100644 --- a/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.cpp +++ b/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.cpp @@ -1,4 +1,5 @@ #include "RaidKarazhanHelpers.h" +#include "BotRoleService.h" #include "RaidKarazhanActions.h" #include "Playerbots.h" #include "RtiTargetValue.h" @@ -90,7 +91,7 @@ namespace KarazhanHelpers MarkTargetWithIcon(bot, target, RtiTargetValue::moonIndex); } - void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target) + void SetRtiTarget(PlayerbotAI* botAI, std::string const& rtiName, Unit* target) { if (!target) return; @@ -106,14 +107,14 @@ namespace KarazhanHelpers } // Only one bot is needed to set/reset instance-wide timers - bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot) + bool IsInstanceTimerManager(PlayerbotAI* /*botAI*/, Player* bot) { if (Group* group = bot->GetGroup()) { for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (member && member->IsAlive() && botAI->IsDps(member) && GET_PLAYERBOT_AI(member)) + if (member && member->IsAlive() && BotRoleService::IsDpsStatic(member) && GET_PLAYERBOT_AI(member)) return member == bot; } } @@ -121,7 +122,7 @@ namespace KarazhanHelpers return false; } - Unit* GetFirstAliveUnit(const std::vector& units) + Unit* GetFirstAliveUnit(std::vector const& units) { for (Unit* unit : units) { @@ -196,7 +197,7 @@ namespace KarazhanHelpers } // Red beam blockers: tank bots, no Nether Exhaustion Red - std::vector GetRedBlockers(PlayerbotAI* botAI, Player* bot) + std::vector GetRedBlockers(PlayerbotAI* /*botAI*/, Player* bot) { std::vector redBlockers; if (Group* group = bot->GetGroup()) @@ -204,7 +205,7 @@ namespace KarazhanHelpers for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (!member || !member->IsAlive() || !botAI->IsTank(member) || !GET_PLAYERBOT_AI(member) || + if (!member || !member->IsAlive() || !BotRoleService::IsTankStatic(member) || !GET_PLAYERBOT_AI(member) || member->HasAura(SPELL_NETHER_EXHAUSTION_RED)) continue; @@ -216,7 +217,7 @@ namespace KarazhanHelpers } // Blue beam blockers: non-Rogue/Warrior DPS bots, no Nether Exhaustion Blue and <24 stacks of Blue Beam debuff - std::vector GetBlueBlockers(PlayerbotAI* botAI, Player* bot) + std::vector GetBlueBlockers(PlayerbotAI* /*botAI*/, Player* bot) { std::vector blueBlockers; if (Group* group = bot->GetGroup()) @@ -231,7 +232,7 @@ namespace KarazhanHelpers Aura* blueBuff = member->GetAura(SPELL_BLUE_BEAM_DEBUFF); bool overStack = blueBuff && blueBuff->GetStackAmount() >= 24; - bool isDps = botAI->IsDps(member); + bool isDps = BotRoleService::IsDpsStatic(member); bool isWarrior = member->getClass() == CLASS_WARRIOR; bool isRogue = member->getClass() == CLASS_ROGUE; @@ -246,7 +247,7 @@ namespace KarazhanHelpers // Green beam blockers: // (1) Prioritize Rogues and non-tank Warrior bots, no Nether Exhaustion Green // (2) Then assign Healer bots, no Nether Exhaustion Green and <24 stacks of Green Beam debuff - std::vector GetGreenBlockers(PlayerbotAI* botAI, Player* bot) + std::vector GetGreenBlockers(PlayerbotAI* /*botAI*/, Player* bot) { std::vector greenBlockers; if (Group* group = bot->GetGroup()) @@ -259,7 +260,7 @@ namespace KarazhanHelpers bool hasExhaustion = member->HasAura(SPELL_NETHER_EXHAUSTION_GREEN); bool isRogue = member->getClass() == CLASS_ROGUE; - bool isDpsWarrior = member->getClass() == CLASS_WARRIOR && botAI->IsDps(member); + bool isDpsWarrior = member->getClass() == CLASS_WARRIOR && BotRoleService::IsDpsStatic(member); bool eligibleRogueWarrior = (isRogue || isDpsWarrior) && !hasExhaustion; if (eligibleRogueWarrior) @@ -275,7 +276,7 @@ namespace KarazhanHelpers bool hasExhaustion = member->HasAura(SPELL_NETHER_EXHAUSTION_GREEN); Aura* greenBuff = member->GetAura(SPELL_GREEN_BEAM_DEBUFF); bool overStack = greenBuff && greenBuff->GetStackAmount() >= 24; - bool isHealer = botAI->IsHeal(member); + bool isHealer = BotRoleService::IsHealStatic(member); bool eligibleHealer = isHealer && !hasExhaustion && !overStack; if (eligibleHealer) @@ -381,7 +382,7 @@ namespace KarazhanHelpers return voidZones; } - bool IsSafePosition(float x, float y, float z, const std::vector& hazards, float hazardRadius) + bool IsSafePosition(float x, float y, float /*z*/, std::vector const& hazards, float hazardRadius) { for (Unit* hazard : hazards) { @@ -407,7 +408,7 @@ namespace KarazhanHelpers return infernals; } - bool IsStraightPathSafe(const Position& start, const Position& target, const std::vector& hazards, + bool IsStraightPathSafe(Position const& start, Position const& target, std::vector const& hazards, float hazardRadius, float stepSize) { float sx = start.GetPositionX(); @@ -426,7 +427,6 @@ namespace KarazhanHelpers float t = checkDist / totalDist; float checkX = sx + (tx - sx) * t; float checkY = sy + (ty - sy) * t; - float checkZ = sz + (tz - sz) * t; for (Unit* hazard : hazards) { const float hx = checkX - hazard->GetPositionX(); @@ -441,7 +441,7 @@ namespace KarazhanHelpers bool TryFindSafePositionWithSafePath( Player* bot, float originX, float originY, float originZ, float centerX, float centerY, float centerZ, - const std::vector& hazards, float safeDistance, float stepSize, uint8 numAngles, + std::vector const& hazards, float safeDistance, float stepSize, uint8 numAngles, float maxSampleDist, bool requireSafePath, float& bestDestX, float& bestDestY, float& bestDestZ) { float bestMoveDist = std::numeric_limits::max(); diff --git a/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.h b/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.h index 394693b2e4..446bc113b1 100644 --- a/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.h +++ b/src/Ai/Raid/Karazhan/Util/RaidKarazhanHelpers.h @@ -111,9 +111,9 @@ namespace KarazhanHelpers void MarkTargetWithStar(Player* bot, Unit* target); void MarkTargetWithCircle(Player* bot, Unit* target); void MarkTargetWithMoon(Player* bot, Unit* target); - void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target); + void SetRtiTarget(PlayerbotAI* botAI, std::string const& rtiName, Unit* target); bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot); - Unit* GetFirstAliveUnit(const std::vector& units); + Unit* GetFirstAliveUnit(std::vector const& units); Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry); Unit* GetNearestPlayerInRadius(Player* bot, float radius); bool IsFlameWreathActive(PlayerbotAI* botAI, Player* bot); @@ -122,14 +122,14 @@ namespace KarazhanHelpers std::vector GetGreenBlockers(PlayerbotAI* botAI, Player* bot); std::tuple GetCurrentBeamBlockers(PlayerbotAI* botAI, Player* bot); std::vector GetAllVoidZones(PlayerbotAI *botAI, Player* bot); - bool IsSafePosition (float x, float y, float z, const std::vector& hazards, float hazardRadius); + bool IsSafePosition (float x, float y, float z, std::vector const& hazards, float hazardRadius); std::vector GetSpawnedInfernals(PlayerbotAI* botAI); bool IsStraightPathSafe( - const Position& start, const Position& target, - const std::vector& hazards, float hazardRadius, float stepSize); + Position const& start, Position const& target, + std::vector const& hazards, float hazardRadius, float stepSize); bool TryFindSafePositionWithSafePath( Player* bot, float originX, float originY, float originZ, float centerX, float centerY, float centerZ, - const std::vector& hazards, float safeDistance, float stepSize, uint8 numAngles, + std::vector const& hazards, float safeDistance, float stepSize, uint8 numAngles, float maxSampleDist, bool requireSafePath, float& bestDestX, float& bestDestY, float& bestDestZ); } diff --git a/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp b/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp index 69fc862442..7b3251c3a5 100644 --- a/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp +++ b/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.cpp @@ -1,4 +1,6 @@ #include "RaidMagtheridonActions.h" +#include "BotRoleService.h" +#include "BotSpellService.h" #include "RaidMagtheridonHelpers.h" #include "Creature.h" #include "ObjectAccessor.h" @@ -7,7 +9,7 @@ using namespace MagtheridonHelpers; -bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event event) +bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event /*event*/) { Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); if (!magtheridon) @@ -30,7 +32,7 @@ bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event event) (!channelerStar || !channelerStar->IsAlive()) && (!channelerCircle || !channelerCircle->IsAlive())) { - const Location& position = MagtheridonsLairLocations::WaitingForMagtheridonPosition; + Location const& position = MagtheridonsLairLocations::WaitingForMagtheridonPosition; if (!bot->IsWithinDist2d(position.x, position.y, 2.0f)) { return MoveTo(bot->GetMapId(), position.x, position.y, position.z, false, false, false, false, @@ -67,7 +69,7 @@ bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event event) return false; } -bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event) +bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event /*event*/) { Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER); if (!channelerDiamond || !channelerDiamond->IsAlive()) @@ -81,7 +83,7 @@ bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event) if (channelerDiamond->GetVictim() == bot) { - const Location& position = MagtheridonsLairLocations::NWChannelerTankPosition; + Location const& position = MagtheridonsLairLocations::NWChannelerTankPosition; const float maxDistance = 3.0f; if (bot->GetExactDist2d(position.x, position.y) > maxDistance) @@ -100,7 +102,7 @@ bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event) return false; } -bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event event) +bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event /*event*/) { Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER); if (!channelerTriangle || !channelerTriangle->IsAlive()) @@ -114,7 +116,7 @@ bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event event) if (channelerTriangle->GetVictim() == bot) { - const Location& position = MagtheridonsLairLocations::NEChannelerTankPosition; + Location const& position = MagtheridonsLairLocations::NEChannelerTankPosition; const float maxDistance = 3.0f; if (bot->GetExactDist2d(position.x, position.y) > maxDistance) @@ -134,7 +136,7 @@ bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event event) } // Misdirect West & East Channelers to Main Tank -bool MagtheridonMisdirectHellfireChannelers::Execute(Event event) +bool MagtheridonMisdirectHellfireChannelers::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) @@ -162,7 +164,7 @@ bool MagtheridonMisdirectHellfireChannelers::Execute(Event event) for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (member && member->IsAlive() && botAI->IsMainTank(member)) + if (member && member->IsAlive() && BotRoleService::IsMainTankStatic(member)) { mainTank = member; break; @@ -178,14 +180,14 @@ bool MagtheridonMisdirectHellfireChannelers::Execute(Event event) if (mainTank && channelerStar && channelerStar->IsAlive() && channelerStar->GetVictim() != mainTank) { - if (botAI->CanCastSpell("misdirection", mainTank)) - return botAI->CastSpell("misdirection", mainTank); + if (botAI->GetServices().GetSpellService().CanCastSpell("misdirection", mainTank)) + return botAI->GetServices().GetSpellService().CastSpell("misdirection", mainTank); if (!bot->HasAura(SPELL_MISDIRECTION)) return false; - if (botAI->CanCastSpell("steady shot", channelerStar)) - return botAI->CastSpell("steady shot", channelerStar); + if (botAI->GetServices().GetSpellService().CanCastSpell("steady shot", channelerStar)) + return botAI->GetServices().GetSpellService().CastSpell("steady shot", channelerStar); } break; @@ -193,14 +195,14 @@ bool MagtheridonMisdirectHellfireChannelers::Execute(Event event) if (mainTank && channelerCircle && channelerCircle->IsAlive() && channelerCircle->GetVictim() != mainTank) { - if (botAI->CanCastSpell("misdirection", mainTank)) - return botAI->CastSpell("misdirection", mainTank); + if (botAI->GetServices().GetSpellService().CanCastSpell("misdirection", mainTank)) + return botAI->GetServices().GetSpellService().CastSpell("misdirection", mainTank); if (!bot->HasAura(SPELL_MISDIRECTION)) return false; - if (botAI->CanCastSpell("steady shot", channelerCircle)) - return botAI->CastSpell("steady shot", channelerCircle); + if (botAI->GetServices().GetSpellService().CanCastSpell("steady shot", channelerCircle)) + return botAI->GetServices().GetSpellService().CastSpell("steady shot", channelerCircle); } break; @@ -211,7 +213,7 @@ bool MagtheridonMisdirectHellfireChannelers::Execute(Event event) return false; } -bool MagtheridonAssignDPSPriorityAction::Execute(Event event) +bool MagtheridonAssignDPSPriorityAction::Execute(Event /*event*/) { // Listed in order of priority Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER); @@ -306,13 +308,13 @@ bool MagtheridonAssignDPSPriorityAction::Execute(Event event) // Assign Burning Abyssals to Warlocks to Banish // Burning Abyssals in excess of Warlocks in party will be Feared -bool MagtheridonWarlockCCBurningAbyssalAction::Execute(Event event) +bool MagtheridonWarlockCCBurningAbyssalAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) return false; - const GuidVector& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); std::vector abyssals; for (auto const& npc : npcs) @@ -340,26 +342,26 @@ bool MagtheridonWarlockCCBurningAbyssalAction::Execute(Event event) } } - if (warlockIndex >= 0 && warlockIndex < abyssals.size()) + if (warlockIndex >= 0 && static_cast(warlockIndex) < abyssals.size()) { Unit* assignedAbyssal = abyssals[warlockIndex]; - if (!assignedAbyssal->HasAura(SPELL_BANISH) && botAI->CanCastSpell(SPELL_BANISH, assignedAbyssal, true)) - return botAI->CastSpell("banish", assignedAbyssal); + if (!assignedAbyssal->HasAura(SPELL_BANISH) && botAI->GetServices().GetSpellService().CanCastSpell(SPELL_BANISH, assignedAbyssal, true)) + return botAI->GetServices().GetSpellService().CastSpell("banish", assignedAbyssal); } for (size_t i = warlocks.size(); i < abyssals.size(); ++i) { Unit* excessAbyssal = abyssals[i]; if (!excessAbyssal->HasAura(SPELL_BANISH) && !excessAbyssal->HasAura(SPELL_FEAR) && - botAI->CanCastSpell(SPELL_FEAR, excessAbyssal, true)) - return botAI->CastSpell("fear", excessAbyssal); + botAI->GetServices().GetSpellService().CanCastSpell(SPELL_FEAR, excessAbyssal, true)) + return botAI->GetServices().GetSpellService().CastSpell("fear", excessAbyssal); } return false; } // Main tank will back up to the Northern point of the room -bool MagtheridonMainTankPositionBossAction::Execute(Event event) +bool MagtheridonMainTankPositionBossAction::Execute(Event /*event*/) { Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); if (!magtheridon) @@ -373,7 +375,7 @@ bool MagtheridonMainTankPositionBossAction::Execute(Event event) if (magtheridon->GetVictim() == bot) { - const Location& position = MagtheridonsLairLocations::MagtheridonTankPosition; + Location const& position = MagtheridonsLairLocations::MagtheridonTankPosition; const float maxDistance = 2.0f; if (bot->GetExactDist2d(position.x, position.y) > maxDistance) @@ -399,7 +401,7 @@ bool MagtheridonMainTankPositionBossAction::Execute(Event event) std::unordered_map MagtheridonSpreadRangedAction::initialPositions; std::unordered_map MagtheridonSpreadRangedAction::hasReachedInitialPosition; -bool MagtheridonSpreadRangedAction::Execute(Event event) +bool MagtheridonSpreadRangedAction::Execute(Event /*event*/) { Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); if (!magtheridon) @@ -439,8 +441,8 @@ bool MagtheridonSpreadRangedAction::Execute(Event event) members.push_back(member); } - bool isHealer = botAI->IsHeal(bot); - const Location& center = isHealer + bool isHealer = BotRoleService::IsHealStatic(bot); + Location const& center = isHealer ? MagtheridonsLairLocations::HealerSpreadPosition : MagtheridonsLairLocations::RangedSpreadPosition; float maxSpreadRadius = isHealer ? 15.0f : 20.0f; @@ -456,7 +458,7 @@ bool MagtheridonSpreadRangedAction::Execute(Event event) uint8 count = members.size(); float angle = 2 * M_PI * botIndex / count; - float radius = static_cast(rand()) / RAND_MAX * maxSpreadRadius; + float radius = static_cast(rand()) / static_cast(RAND_MAX) * maxSpreadRadius; float targetX = centerX + radius * cos(angle); float targetY = centerY + radius * sin(angle); @@ -489,8 +491,8 @@ bool MagtheridonSpreadRangedAction::Execute(Event event) if (distToCenter > maxSpreadRadius + radiusBuffer) { - float angle = static_cast(rand()) / RAND_MAX * 2.0f * M_PI; - float radius = static_cast(rand()) / RAND_MAX * maxSpreadRadius; + float angle = static_cast(rand()) / static_cast(RAND_MAX) * 2.0f * M_PI; + float radius = static_cast(rand()) / static_cast(RAND_MAX) * maxSpreadRadius; float targetX = centerX + radius * cos(angle); float targetY = centerY + radius * sin(angle); @@ -508,15 +510,15 @@ bool MagtheridonSpreadRangedAction::Execute(Event event) } // For bots that are assigned to click cubes -// Magtheridon casts Blast Nova every 54.35 to 55.40s, with a 2s cast time -bool MagtheridonUseManticronCubeAction::Execute(Event event) +// Magtheridon casts Blast Nova every 54.35f to 55.40s, with a 2s cast time +bool MagtheridonUseManticronCubeAction::Execute(Event /*event*/) { Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); if (!magtheridon) return false; auto it = botToCubeAssignment.find(bot->GetGUID()); - const CubeInfo& cubeInfo = it->second; + CubeInfo const& cubeInfo = it->second; GameObject* cube = botAI->GetGameObject(cubeInfo.guid); if (!cube) return false; @@ -543,7 +545,7 @@ bool MagtheridonUseManticronCubeAction::Execute(Event event) return false; } -bool MagtheridonUseManticronCubeAction::HandleCubeRelease(Unit* magtheridon, GameObject* cube) +bool MagtheridonUseManticronCubeAction::HandleCubeRelease(Unit* magtheridon, GameObject* /*cube*/) { if (bot->HasAura(SPELL_SHADOW_GRASP) && !(magtheridon->HasUnitState(UNIT_STATE_CASTING) && @@ -575,7 +577,7 @@ bool MagtheridonUseManticronCubeAction::ShouldActivateCubeLogic(Unit* magtherido return (now - lastBlastNova >= 49); } -bool MagtheridonUseManticronCubeAction::HandleWaitingPhase(const CubeInfo& cubeInfo) +bool MagtheridonUseManticronCubeAction::HandleWaitingPhase(CubeInfo const& cubeInfo) { const float safeWaitDistance = 8.0f; float cubeDist = bot->GetExactDist2d(cubeInfo.x, cubeInfo.y); @@ -598,7 +600,7 @@ bool MagtheridonUseManticronCubeAction::HandleWaitingPhase(const CubeInfo& cubeI } } - float angle = static_cast(rand()) / RAND_MAX * 2.0f * M_PI; + float angle = static_cast(rand()) / static_cast(RAND_MAX) * 2.0f * M_PI; float fallbackX = cubeInfo.x + cos(angle) * safeWaitDistance; float fallbackY = cubeInfo.y + sin(angle) * safeWaitDistance; float fallbackZ = bot->GetPositionZ(); @@ -610,7 +612,7 @@ bool MagtheridonUseManticronCubeAction::HandleWaitingPhase(const CubeInfo& cubeI return true; } -bool MagtheridonUseManticronCubeAction::HandleCubeInteraction(const CubeInfo& cubeInfo, GameObject* cube) +bool MagtheridonUseManticronCubeAction::HandleCubeInteraction(CubeInfo const& cubeInfo, GameObject* cube) { const float interactDistance = 1.0f; float cubeDist = bot->GetExactDist2d(cubeInfo.x, cubeInfo.y); @@ -650,7 +652,7 @@ bool MagtheridonUseManticronCubeAction::HandleCubeInteraction(const CubeInfo& cu // is not interrupted or takes too long to interrupt, the timer will be thrown off for the rest of the encounter. // Correcting this issue is complicated and probably would need some rewriting--I have not done so and // and view the current solution as sufficient since in TBC a missed Blast Nova would be a guaranteed wipe anyway. -bool MagtheridonManageTimersAndAssignmentsAction::Execute(Event event) +bool MagtheridonManageTimersAndAssignmentsAction::Execute(Event /*event*/) { Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); if (!magtheridon) diff --git a/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.h b/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.h index 6c4ed84c22..a394d3e419 100644 --- a/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.h +++ b/src/Ai/Raid/Magtheridon/Action/RaidMagtheridonActions.h @@ -85,8 +85,8 @@ class MagtheridonUseManticronCubeAction : public MovementAction private: bool HandleCubeRelease(Unit* magtheridon, GameObject* cube); bool ShouldActivateCubeLogic(Unit* magtheridon); - bool HandleWaitingPhase(const CubeInfo& cubeInfo); - bool HandleCubeInteraction(const CubeInfo& cubeInfo, GameObject* cube); + bool HandleWaitingPhase(CubeInfo const& cubeInfo); + bool HandleCubeInteraction(CubeInfo const& cubeInfo, GameObject* cube); }; class MagtheridonManageTimersAndAssignmentsAction : public Action diff --git a/src/Ai/Raid/Magtheridon/Multiplier/RaidMagtheridonMultipliers.cpp b/src/Ai/Raid/Magtheridon/Multiplier/RaidMagtheridonMultipliers.cpp index 9580fd923a..7359f931aa 100644 --- a/src/Ai/Raid/Magtheridon/Multiplier/RaidMagtheridonMultipliers.cpp +++ b/src/Ai/Raid/Magtheridon/Multiplier/RaidMagtheridonMultipliers.cpp @@ -1,4 +1,5 @@ #include +#include "BotRoleService.h" #include #include "RaidMagtheridonMultipliers.h" @@ -46,8 +47,8 @@ float MagtheridonWaitToAttackMultiplier::GetValue(Action* action) if (it == dpsWaitTimer.end() || (time(nullptr) - it->second) < dpsWaitSeconds) { - if (!botAI->IsMainTank(bot) && (dynamic_cast(action) || - (!botAI->IsHeal(bot) && dynamic_cast(action)))) + if (!BotRoleService::IsMainTankStatic(bot) && (dynamic_cast(action) || + (!BotRoleService::IsHealStatic(bot) && dynamic_cast(action)))) return 0.0f; } @@ -59,11 +60,10 @@ float MagtheridonWaitToAttackMultiplier::GetValue(Action* action) float MagtheridonDisableOffTankAssistMultiplier::GetValue(Action* action) { Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); - Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler"); if (!magtheridon) return 1.0f; - if ((botAI->IsAssistTankOfIndex(bot, 0) || botAI->IsAssistTankOfIndex(bot, 1)) && + if ((BotRoleService::IsAssistTankOfIndexStatic(bot, 0) || BotRoleService::IsAssistTankOfIndexStatic(bot, 1)) && dynamic_cast(action)) return 0.0f; diff --git a/src/Ai/Raid/Magtheridon/Trigger/RaidMagtheridonTriggers.cpp b/src/Ai/Raid/Magtheridon/Trigger/RaidMagtheridonTriggers.cpp index 35442df6e0..778f6c19e8 100644 --- a/src/Ai/Raid/Magtheridon/Trigger/RaidMagtheridonTriggers.cpp +++ b/src/Ai/Raid/Magtheridon/Trigger/RaidMagtheridonTriggers.cpp @@ -1,4 +1,5 @@ #include "RaidMagtheridonTriggers.h" +#include "BotRoleService.h" #include "RaidMagtheridonHelpers.h" #include "Playerbots.h" @@ -8,7 +9,7 @@ bool MagtheridonFirstThreeChannelersEngagedByMainTankTrigger::IsActive() { Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); - return magtheridon && botAI->IsMainTank(bot) && + return magtheridon && BotRoleService::IsMainTankStatic(bot) && magtheridon->HasAura(SPELL_SHADOW_CAGE); } @@ -17,7 +18,7 @@ bool MagtheridonNWChannelerEngagedByFirstAssistTankTrigger::IsActive() Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER); - return magtheridon && botAI->IsAssistTankOfIndex(bot, 0) && + return magtheridon && BotRoleService::IsAssistTankOfIndexStatic(bot, 0) && channelerDiamond && channelerDiamond->IsAlive(); } @@ -26,7 +27,7 @@ bool MagtheridonNEChannelerEngagedBySecondAssistTankTrigger::IsActive() Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER); - return magtheridon && botAI->IsAssistTankOfIndex(bot, 1) && + return magtheridon && BotRoleService::IsAssistTankOfIndexStatic(bot, 1) && channelerTriangle && channelerTriangle->IsAlive(); } @@ -50,9 +51,9 @@ bool MagtheridonDeterminingKillOrderTrigger::IsActive() Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER); Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER); - if (!magtheridon || botAI->IsHeal(bot) || botAI->IsMainTank(bot) || - (botAI->IsAssistTankOfIndex(bot, 0) && channelerDiamond && channelerDiamond->IsAlive()) || - (botAI->IsAssistTankOfIndex(bot, 1) && channelerTriangle && channelerTriangle->IsAlive())) + if (!magtheridon || BotRoleService::IsHealStatic(bot) || BotRoleService::IsMainTankStatic(bot) || + (BotRoleService::IsAssistTankOfIndexStatic(bot, 0) && channelerDiamond && channelerDiamond->IsAlive()) || + (BotRoleService::IsAssistTankOfIndexStatic(bot, 1) && channelerTriangle && channelerTriangle->IsAlive())) return false; return (channeler && channeler->IsAlive()) || (magtheridon && @@ -65,8 +66,8 @@ bool MagtheridonBurningAbyssalSpawnedTrigger::IsActive() if (!magtheridon || bot->getClass() != CLASS_WARLOCK) return false; - const GuidVector& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - return std::any_of(npcs.begin(), npcs.end(), [this](const ObjectGuid& npc) + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + return std::any_of(npcs.begin(), npcs.end(), [this](ObjectGuid const& npc) { Unit* unit = botAI->GetUnit(npc); return unit && unit->GetEntry() == NPC_BURNING_ABYSSAL; @@ -77,7 +78,7 @@ bool MagtheridonBossEngagedByMainTankTrigger::IsActive() { Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); - return magtheridon && botAI->IsMainTank(bot) && + return magtheridon && BotRoleService::IsMainTankStatic(bot) && !magtheridon->HasAura(SPELL_SHADOW_CAGE); } @@ -86,7 +87,7 @@ bool MagtheridonBossEngagedByRangedTrigger::IsActive() Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon"); Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler"); - return magtheridon && botAI->IsRanged(bot) && + return magtheridon && BotRoleService::IsRangedStatic(bot) && !(channeler && channeler->IsAlive()); } diff --git a/src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.cpp b/src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.cpp index dc88d2a192..4291fdf452 100644 --- a/src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.cpp +++ b/src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.cpp @@ -1,4 +1,5 @@ #include "RaidMagtheridonHelpers.h" +#include "BotRoleService.h" #include "Creature.h" #include "GameObject.h" #include "GroupReference.h" @@ -43,7 +44,7 @@ namespace MagtheridonHelpers group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID()); } - void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target) + void SetRtiTarget(PlayerbotAI* botAI, std::string const& rtiName, Unit* target) { if (!target) return; @@ -88,10 +89,10 @@ namespace MagtheridonHelpers MarkTargetWithIcon(bot, target, RtiTargetValue::crossIndex); } - const std::vector MANTICRON_CUBE_DB_GUIDS = { 43157, 43158, 43159, 43160, 43161 }; + std::vector const MANTICRON_CUBE_DB_GUIDS = { 43157, 43158, 43159, 43160, 43161 }; // Get the positions of all Manticron Cubes by their database GUIDs - std::vector GetAllCubeInfosByDbGuids(Map* map, const std::vector& cubeDbGuids) + std::vector GetAllCubeInfosByDbGuids(Map* map, std::vector const& cubeDbGuids) { std::vector cubes; if (!map) @@ -120,7 +121,7 @@ namespace MagtheridonHelpers std::unordered_map botToCubeAssignment; - void AssignBotsToCubesByGuidAndCoords(Group* group, const std::vector& cubes, PlayerbotAI* botAI) + void AssignBotsToCubesByGuidAndCoords(Group* group, std::vector const& cubes, PlayerbotAI* /*botAI*/) { botToCubeAssignment.clear(); if (!group) @@ -133,7 +134,7 @@ namespace MagtheridonHelpers for (GroupReference* ref = group->GetFirstMember(); ref && cubeIndex < cubes.size(); ref = ref->next()) { Player* member = ref->GetSource(); - if (!member || !member->IsAlive() || !botAI->IsRangedDps(member, true) || + if (!member || !member->IsAlive() || !BotRoleService::IsRangedDpsStatic(member, true) || member->getClass() == CLASS_WARLOCK || !GET_PLAYERBOT_AI(member)) continue; @@ -149,7 +150,7 @@ namespace MagtheridonHelpers ref && candidates.size() < cubes.size(); ref = ref->next()) { Player* member = ref->GetSource(); - if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) || botAI->IsTank(member)) + if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) || BotRoleService::IsTankStatic(member)) continue; if (std::find(candidates.begin(), candidates.end(), member) == candidates.end()) @@ -174,7 +175,7 @@ namespace MagtheridonHelpers std::unordered_map spreadWaitTimer; std::unordered_map dpsWaitTimer; - bool IsSafeFromMagtheridonHazards(PlayerbotAI* botAI, Player* bot, float x, float y, float z) + bool IsSafeFromMagtheridonHazards(PlayerbotAI* botAI, Player* /*bot*/, float x, float y, float /*z*/) { // Debris std::vector debrisHazards; @@ -209,14 +210,14 @@ namespace MagtheridonHelpers return true; } - bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot) + bool IsInstanceTimerManager(PlayerbotAI* /*botAI*/, Player* bot) { if (Group* group = bot->GetGroup()) { for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (member && member->IsAlive() && botAI->IsDps(member) && GET_PLAYERBOT_AI(member)) + if (member && member->IsAlive() && BotRoleService::IsDpsStatic(member) && GET_PLAYERBOT_AI(member)) return member == bot; } } diff --git a/src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.h b/src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.h index f9f514de3e..3543b4d341 100644 --- a/src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.h +++ b/src/Ai/Raid/Magtheridon/Util/RaidMagtheridonHelpers.h @@ -52,7 +52,7 @@ namespace MagtheridonHelpers void MarkTargetWithDiamond(Player* bot, Unit* target); void MarkTargetWithTriangle(Player* bot, Unit* target); void MarkTargetWithCross(Player* bot, Unit* target); - void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target); + void SetRtiTarget(PlayerbotAI* botAI, std::string const& rtiName, Unit* target); bool IsSafeFromMagtheridonHazards(PlayerbotAI* botAI, Player* bot, float x, float y, float z); bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot); @@ -77,10 +77,10 @@ namespace MagtheridonHelpers float x, y, z; }; - extern const std::vector MANTICRON_CUBE_DB_GUIDS; + extern std::vector const MANTICRON_CUBE_DB_GUIDS; extern std::unordered_map botToCubeAssignment; - std::vector GetAllCubeInfosByDbGuids(Map* map, const std::vector& cubeDbGuids); - void AssignBotsToCubesByGuidAndCoords(Group* group, const std::vector& cubes, PlayerbotAI* botAI); + std::vector GetAllCubeInfosByDbGuids(Map* map, std::vector const& cubeDbGuids); + void AssignBotsToCubesByGuidAndCoords(Group* group, std::vector const& cubes, PlayerbotAI* botAI); extern std::unordered_map lastBlastNovaState; extern std::unordered_map blastNovaTimer; extern std::unordered_map spreadWaitTimer; diff --git a/src/Ai/Raid/MoltenCore/Action/RaidMcActions.cpp b/src/Ai/Raid/MoltenCore/Action/RaidMcActions.cpp index b18c8b8534..9374d37423 100644 --- a/src/Ai/Raid/MoltenCore/Action/RaidMcActions.cpp +++ b/src/Ai/Raid/MoltenCore/Action/RaidMcActions.cpp @@ -4,6 +4,7 @@ #include "RtiTargetValue.h" #include "RaidMcTriggers.h" #include "RaidMcHelpers.h" +#include "BotRoleService.h" static constexpr float LIVING_BOMB_DISTANCE = 20.0f; static constexpr float INFERNO_DISTANCE = 20.0f; @@ -12,20 +13,20 @@ static constexpr float INFERNO_DISTANCE = 20.0f; static constexpr float ARCANE_EXPLOSION_DISTANCE = 26.0f; // dedicated tank positions; prevents assist tanks from positioning Core Ragers on steep walls on pull -static const Position GOLEMAGG_TANK_POSITION{795.7308, -994.8848, -207.18661}; -static const Position CORE_RAGER_TANK_POSITION{846.6453, -1019.0639, -198.9819}; +static const Position GOLEMAGG_TANK_POSITION{795.7308f, -994.8848f, -207.18661f}; +static const Position CORE_RAGER_TANK_POSITION{846.6453f, -1019.0639f, -198.9819f}; static constexpr float GOLEMAGGS_TRUST_DISTANCE = 30.0f; static constexpr float CORE_RAGER_STEP_DISTANCE = 5.0f; using namespace MoltenCoreHelpers; -bool McMoveFromGroupAction::Execute(Event event) +bool McMoveFromGroupAction::Execute(Event /*event*/) { return MoveFromGroup(LIVING_BOMB_DISTANCE); } -bool McMoveFromBaronGeddonAction::Execute(Event event) +bool McMoveFromBaronGeddonAction::Execute(Event /*event*/) { if (Unit* boss = AI_VALUE2(Unit*, "find target", "baron geddon")) { @@ -42,7 +43,7 @@ bool McMoveFromBaronGeddonAction::Execute(Event event) return false; } -bool McShazzrahMoveAwayAction::Execute(Event event) +bool McShazzrahMoveAwayAction::Execute(Event /*event*/) { if (Unit* boss = AI_VALUE2(Unit*, "find target", "shazzrah")) { @@ -53,7 +54,7 @@ bool McShazzrahMoveAwayAction::Execute(Event event) return false; } -bool McGolemaggMarkBossAction::Execute(Event event) +bool McGolemaggMarkBossAction::Execute(Event /*event*/) { if (Unit* boss = AI_VALUE2(Unit*, "find target", "golemagg the incinerator")) { @@ -70,7 +71,7 @@ bool McGolemaggMarkBossAction::Execute(Event event) return false; } -bool McGolemaggTankAction::MoveUnitToPosition(Unit* target, const Position& tankPosition, float maxDistance, +bool McGolemaggTankAction::MoveUnitToPosition(Unit* target, Position const& tankPosition, float maxDistance, float stepDistance) { if (bot->GetVictim() != target) @@ -115,7 +116,7 @@ bool McGolemaggTankAction::FindCoreRagers(Unit*& coreRager1, Unit*& coreRager2) return coreRager1 != nullptr && coreRager2 != nullptr; } -bool McGolemaggMainTankAttackGolemaggAction::Execute(Event event) +bool McGolemaggMainTankAttackGolemaggAction::Execute(Event /*event*/) { // At this point, we know we are not the last living tank in the group. if (Unit* boss = AI_VALUE2(Unit*, "find target", "golemagg the incinerator")) @@ -139,8 +140,8 @@ bool McGolemaggAssistTankAttackCoreRagerAction::Execute(Event event) return false; // Step 0: Filter additional assist tanks. We only need 2. - bool isFirstAssistTank = PlayerbotAI::IsAssistTankOfIndex(bot, 0, true); - bool isSecondAssistTank = PlayerbotAI::IsAssistTankOfIndex(bot, 1, true); + bool isFirstAssistTank = BotRoleService::IsAssistTankOfIndexStatic(bot, 0, true); + bool isSecondAssistTank = BotRoleService::IsAssistTankOfIndexStatic(bot, 1, true); if (!isFirstAssistTank && !isSecondAssistTank) return Attack(boss); @@ -167,7 +168,7 @@ bool McGolemaggAssistTankAttackCoreRagerAction::Execute(Event event) // Step 3: Select the right target if (myCoreRager->GetVictim() != bot) { - // Step 3.1: My Core Rager isn't attacking me. Attack until it does. + // Step 3.1f: My Core Rager isn't attacking me. Attack until it does. if (bot->GetVictim() != myCoreRager) return Attack(myCoreRager); return botAI->DoSpecificAction("taunt spell", event, true); @@ -176,11 +177,11 @@ bool McGolemaggAssistTankAttackCoreRagerAction::Execute(Event event) Unit* otherCoreRagerVictim = otherCoreRager->GetVictim(); if (otherCoreRagerVictim) // Core Rager victim can be NULL { - // Step 3.2: Check if the other Core Rager isn't attacking its assist tank. + // Step 3.2f: Check if the other Core Rager isn't attacking its assist tank. Player* otherCoreRagerPlayerVictim = otherCoreRagerVictim->ToPlayer(); if (otherCoreRagerPlayerVictim && - !PlayerbotAI::IsAssistTankOfIndex(otherCoreRagerPlayerVictim, 0, true) && - !PlayerbotAI::IsAssistTankOfIndex(otherCoreRagerPlayerVictim, 1, true)) + !BotRoleService::IsAssistTankOfIndexStatic(otherCoreRagerPlayerVictim, 0, true) && + !BotRoleService::IsAssistTankOfIndexStatic(otherCoreRagerPlayerVictim, 1, true)) { // Assume we are the only assist tank or the other assist tank is dead => pick up other Core Rager! if (bot->GetVictim() != otherCoreRager) @@ -190,13 +191,13 @@ bool McGolemaggAssistTankAttackCoreRagerAction::Execute(Event event) } if (bot->GetVictim() != myCoreRager) - return Attack(myCoreRager); // Step 3.3: Attack our Core Rager in case we previously switched in 3.2. + return Attack(myCoreRager); // Step 3.3f: Attack our Core Rager in case we previously switched in 3.2f. // Step 4: Prevent Golemagg's Trust on Core Ragers if (myCoreRager->HasAura(SPELL_GOLEMAGGS_TRUST) || (otherCoreRagerVictim == bot && otherCoreRager->HasAura(SPELL_GOLEMAGGS_TRUST))) { - // Step 4.1: Move Core Ragers to dedicated tank position (only if Golemagg is far enough away from said position) + // Step 4.1f: Move Core Ragers to dedicated tank position (only if Golemagg is far enough away from said position) float bossDistanceToCoreRagerTankPosition = boss->GetExactDist2d( CORE_RAGER_TANK_POSITION.GetPositionX(), CORE_RAGER_TANK_POSITION.GetPositionY()); if (bossDistanceToCoreRagerTankPosition > GOLEMAGGS_TRUST_DISTANCE) @@ -207,7 +208,7 @@ bool McGolemaggAssistTankAttackCoreRagerAction::Execute(Event event) return MoveUnitToPosition(myCoreRager, CORE_RAGER_TANK_POSITION, CORE_RAGER_STEP_DISTANCE); } - // Step 4.2: if boss is too close to tank position, or we are already there, move away from Golemagg to try to out-range Golemagg's Trust + // Step 4.2f: if boss is too close to tank position, or we are already there, move away from Golemagg to try to out-range Golemagg's Trust return MoveAway(boss, CORE_RAGER_STEP_DISTANCE, true); } diff --git a/src/Ai/Raid/MoltenCore/Action/RaidMcActions.h b/src/Ai/Raid/MoltenCore/Action/RaidMcActions.h index 680b311d3c..ecf0a12a66 100644 --- a/src/Ai/Raid/MoltenCore/Action/RaidMcActions.h +++ b/src/Ai/Raid/MoltenCore/Action/RaidMcActions.h @@ -44,7 +44,7 @@ class McGolemaggTankAction : public AttackAction McGolemaggTankAction(PlayerbotAI* botAI, std::string const name) : AttackAction(botAI, name) {} protected: - bool MoveUnitToPosition(Unit* target, const Position& tankPosition, float maxDistance, float stepDistance = 3.0f); + bool MoveUnitToPosition(Unit* target, Position const& tankPosition, float maxDistance, float stepDistance = 3.0f); bool FindCoreRagers(Unit*& coreRager1, Unit*& coreRager2) const; }; diff --git a/src/Ai/Raid/MoltenCore/Multiplier/RaidMcMultipliers.cpp b/src/Ai/Raid/MoltenCore/Multiplier/RaidMcMultipliers.cpp index d1ee936b0d..d41018ef4a 100644 --- a/src/Ai/Raid/MoltenCore/Multiplier/RaidMcMultipliers.cpp +++ b/src/Ai/Raid/MoltenCore/Multiplier/RaidMcMultipliers.cpp @@ -1,4 +1,5 @@ #include "RaidMcMultipliers.h" +#include "BotRoleService.h" #include "Playerbots.h" #include "ChooseTargetActions.h" @@ -16,7 +17,7 @@ using namespace MoltenCoreHelpers; static bool IsDpsBotWithAoeAction(Player* bot, Action* action) { - if (PlayerbotAI::IsDps(bot)) + if (BotRoleService::IsDpsStatic(bot)) { if (dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action) || @@ -86,7 +87,7 @@ static bool IsSingleLivingTankInGroup(Player* bot) Player* member = itr->GetSource(); if (!member || !member->IsAlive() || member == bot) continue; - if (PlayerbotAI::IsTank(member)) + if (BotRoleService::IsTankStatic(member)) return false; } } @@ -97,14 +98,14 @@ float GolemaggMultiplier::GetValue(Action* action) { if (AI_VALUE2(Unit*, "find target", "golemagg the incinerator")) { - if (PlayerbotAI::IsTank(bot) && IsSingleLivingTankInGroup(bot)) + if (BotRoleService::IsTankStatic(bot) && IsSingleLivingTankInGroup(bot)) { // Only one tank => Pick up Golemagg and the two Core Ragers if (dynamic_cast(action) || dynamic_cast(action)) return 0.0f; } - if (PlayerbotAI::IsAssistTank(bot)) + if (BotRoleService::IsAssistTankStatic(bot)) { // The first two assist tanks manage the Core Ragers. The remaining assist tanks attack the boss. if (dynamic_cast(action)) diff --git a/src/Ai/Raid/MoltenCore/Strategy/RaidMcStrategy.h b/src/Ai/Raid/MoltenCore/Strategy/RaidMcStrategy.h index 45b503e933..81937c0622 100644 --- a/src/Ai/Raid/MoltenCore/Strategy/RaidMcStrategy.h +++ b/src/Ai/Raid/MoltenCore/Strategy/RaidMcStrategy.h @@ -11,7 +11,7 @@ class RaidMcStrategy : public Strategy RaidMcStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} std::string const getName() override { return "moltencore"; } void InitTriggers(std::vector& triggers) override; - void InitMultipliers(std::vector &multipliers) override; + void InitMultipliers(std::vector& multipliers) override; }; #endif diff --git a/src/Ai/Raid/MoltenCore/Trigger/RaidMcTriggers.cpp b/src/Ai/Raid/MoltenCore/Trigger/RaidMcTriggers.cpp index 834d703d33..ff8f58a610 100644 --- a/src/Ai/Raid/MoltenCore/Trigger/RaidMcTriggers.cpp +++ b/src/Ai/Raid/MoltenCore/Trigger/RaidMcTriggers.cpp @@ -1,4 +1,5 @@ #include "RaidMcTriggers.h" +#include "BotRoleService.h" #include "SharedDefines.h" #include "RaidMcHelpers.h" @@ -20,21 +21,21 @@ bool McBaronGeddonInfernoTrigger::IsActive() bool McShazzrahRangedTrigger::IsActive() { - return AI_VALUE2(Unit*, "find target", "shazzrah") && PlayerbotAI::IsRanged(bot); + return AI_VALUE2(Unit*, "find target", "shazzrah") && BotRoleService::IsRangedStatic(bot); } bool McGolemaggMarkBossTrigger::IsActive() { // any tank may mark the boss - return AI_VALUE2(Unit*, "find target", "golemagg the incinerator") && PlayerbotAI::IsTank(bot); + return AI_VALUE2(Unit*, "find target", "golemagg the incinerator") && BotRoleService::IsTankStatic(bot); } bool McGolemaggIsMainTankTrigger::IsActive() { - return AI_VALUE2(Unit*, "find target", "golemagg the incinerator") && PlayerbotAI::IsMainTank(bot); + return AI_VALUE2(Unit*, "find target", "golemagg the incinerator") && BotRoleService::IsMainTankStatic(bot); } bool McGolemaggIsAssistTankTrigger::IsActive() { - return AI_VALUE2(Unit*, "find target", "golemagg the incinerator") && PlayerbotAI::IsAssistTank(bot); + return AI_VALUE2(Unit*, "find target", "golemagg the incinerator") && BotRoleService::IsAssistTankStatic(bot); } diff --git a/src/Ai/Raid/ObsidianSanctum/Action/RaidOsActions.cpp b/src/Ai/Raid/ObsidianSanctum/Action/RaidOsActions.cpp index 724b91902f..dbc0f3a76d 100644 --- a/src/Ai/Raid/ObsidianSanctum/Action/RaidOsActions.cpp +++ b/src/Ai/Raid/ObsidianSanctum/Action/RaidOsActions.cpp @@ -1,9 +1,10 @@ #include "RaidOsActions.h" +#include "BotRoleService.h" #include "RaidOsTriggers.h" #include "Playerbots.h" -bool SartharionTankPositionAction::Execute(Event event) +bool SartharionTankPositionAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); if (!boss) { return false; } @@ -42,7 +43,7 @@ bool SartharionTankPositionAction::Execute(Event event) // Adjustable, this is the acceptable distance to stack point that will be accepted as "safe" float looseDistance = 12.0f; - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) { if (bot->GetExactDist2d(SARTHARION_MAINTANK_POSITION.first, SARTHARION_MAINTANK_POSITION.second) > looseDistance) { @@ -84,7 +85,7 @@ bool SartharionTankPositionAction::Execute(Event event) return false; } -bool AvoidTwilightFissureAction::Execute(Event event) +bool AvoidTwilightFissureAction::Execute(Event /*event*/) { const float radius = 5.0f; @@ -104,7 +105,7 @@ bool AvoidTwilightFissureAction::Execute(Event event) return false; } -bool AvoidFlameTsunamiAction::Execute(Event event) +bool AvoidFlameTsunamiAction::Execute(Event /*event*/) { // Adjustable, this is the acceptable distance to stack point that will be accepted as "safe" float looseDistance = 4.0f; @@ -145,7 +146,7 @@ bool AvoidFlameTsunamiAction::Execute(Event event) return false; } - if (botAI->IsMelee(bot)) + if (BotRoleService::IsMeleeStatic(bot)) { if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_MELEE) > looseDistance) { @@ -167,7 +168,7 @@ bool AvoidFlameTsunamiAction::Execute(Event event) return false; } -bool SartharionAttackPriorityAction::Execute(Event event) +bool SartharionAttackPriorityAction::Execute(Event /*event*/) { Unit* sartharion = AI_VALUE2(Unit*, "find target", "sartharion"); Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron"); @@ -206,7 +207,7 @@ bool SartharionAttackPriorityAction::Execute(Event event) return false; } -bool EnterTwilightPortalAction::Execute(Event event) +bool EnterTwilightPortalAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); if (!boss || !boss->HasAura(SPELL_GIFT_OF_TWILIGHT_FIRE)) { return false; } @@ -227,7 +228,7 @@ bool EnterTwilightPortalAction::Execute(Event event) return true; } -bool ExitTwilightPortalAction::Execute(Event event) +bool ExitTwilightPortalAction::Execute(Event /*event*/) { GameObject* portal = bot->FindNearestGameObject(GO_NORMAL_PORTAL, 100.0f); if (!portal) { return false; } diff --git a/src/Ai/Raid/ObsidianSanctum/Action/RaidOsActions.h b/src/Ai/Raid/ObsidianSanctum/Action/RaidOsActions.h index eba7c11d3d..7ad9a24f76 100644 --- a/src/Ai/Raid/ObsidianSanctum/Action/RaidOsActions.h +++ b/src/Ai/Raid/ObsidianSanctum/Action/RaidOsActions.h @@ -9,9 +9,9 @@ const float TSUNAMI_LEFT_SAFE_MELEE = 552.0f; const float TSUNAMI_LEFT_SAFE_RANGED = 504.0f; const float TSUNAMI_RIGHT_SAFE_ALL = 529.0f; -const std::pair SARTHARION_MAINTANK_POSITION = {3258.5f, 532.5f}; -const std::pair SARTHARION_OFFTANK_POSITION = {3230.0f, 526.0f}; -const std::pair SARTHARION_RANGED_POSITION = {3248.0f, 507.0f}; +std::pair const SARTHARION_MAINTANK_POSITION = {3258.5f, 532.5f}; +std::pair const SARTHARION_OFFTANK_POSITION = {3230.0f, 526.0f}; +std::pair const SARTHARION_RANGED_POSITION = {3248.0f, 507.0f}; class SartharionTankPositionAction : public AttackAction { diff --git a/src/Ai/Raid/ObsidianSanctum/Multiplier/RaidOsMultipliers.cpp b/src/Ai/Raid/ObsidianSanctum/Multiplier/RaidOsMultipliers.cpp index bc63f967d0..fe4898d332 100644 --- a/src/Ai/Raid/ObsidianSanctum/Multiplier/RaidOsMultipliers.cpp +++ b/src/Ai/Raid/ObsidianSanctum/Multiplier/RaidOsMultipliers.cpp @@ -1,3 +1,4 @@ +#include "BotRoleService.h" #include "RaidOsMultipliers.h" #include "ChooseTargetActions.h" @@ -22,24 +23,24 @@ float SartharionMultiplier::GetValue(Action* action) Unit* target = action->GetTarget(); - if (botAI->IsMainTank(bot) && dynamic_cast(action)) + if (BotRoleService::IsMainTankStatic(bot) && dynamic_cast(action)) { // return 0.0f; } - if (botAI->IsDps(bot) && dynamic_cast(action)) + if (BotRoleService::IsDpsStatic(bot) && dynamic_cast(action)) { return 0.0f; } - if (botAI->IsMainTank(bot) && target && target != boss && + if (BotRoleService::IsMainTankStatic(bot) && target && target != boss && (dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action))) { return 0.0f; } - if (botAI->IsAssistTank(bot) && target && target == boss && + if (BotRoleService::IsAssistTankStatic(bot) && target && target == boss && (dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action))) { diff --git a/src/Ai/Raid/ObsidianSanctum/Strategy/RaidOsStrategy.cpp b/src/Ai/Raid/ObsidianSanctum/Strategy/RaidOsStrategy.cpp index 4468de991f..4209841e03 100644 --- a/src/Ai/Raid/ObsidianSanctum/Strategy/RaidOsStrategy.cpp +++ b/src/Ai/Raid/ObsidianSanctum/Strategy/RaidOsStrategy.cpp @@ -26,7 +26,7 @@ void RaidOsStrategy::InitTriggers(std::vector& triggers) { NextAction("exit twilight portal", ACTION_RAID + 1) })); } -void RaidOsStrategy::InitMultipliers(std::vector &multipliers) +void RaidOsStrategy::InitMultipliers(std::vector& multipliers) { multipliers.push_back(new SartharionMultiplier(botAI)); } diff --git a/src/Ai/Raid/ObsidianSanctum/Strategy/RaidOsStrategy.h b/src/Ai/Raid/ObsidianSanctum/Strategy/RaidOsStrategy.h index 44983f1fa8..082c3985ea 100644 --- a/src/Ai/Raid/ObsidianSanctum/Strategy/RaidOsStrategy.h +++ b/src/Ai/Raid/ObsidianSanctum/Strategy/RaidOsStrategy.h @@ -10,8 +10,8 @@ class RaidOsStrategy : public Strategy public: RaidOsStrategy(PlayerbotAI* ai) : Strategy(ai) {} virtual std::string const getName() override { return "wotlk-os"; } - virtual void InitTriggers(std::vector &triggers) override; - virtual void InitMultipliers(std::vector &multipliers) override; + virtual void InitTriggers(std::vector& triggers) override; + virtual void InitMultipliers(std::vector& multipliers) override; }; #endif diff --git a/src/Ai/Raid/ObsidianSanctum/Trigger/RaidOsTriggers.cpp b/src/Ai/Raid/ObsidianSanctum/Trigger/RaidOsTriggers.cpp index 710e3cac10..ef279907e7 100644 --- a/src/Ai/Raid/ObsidianSanctum/Trigger/RaidOsTriggers.cpp +++ b/src/Ai/Raid/ObsidianSanctum/Trigger/RaidOsTriggers.cpp @@ -1,21 +1,25 @@ #include "RaidOsTriggers.h" +#include "BotRoleService.h" #include "SharedDefines.h" bool SartharionTankTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); - if (!boss) { return false; } + if (!boss) + return false; - return botAI->IsTank(bot); + return BotRoleService::IsTankStatic(bot); } bool FlameTsunamiTrigger::IsActive() { - if (botAI->IsTank(bot)) { return false; } + if (BotRoleService::IsTankStatic(bot)) + return false; Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); - if (!boss) { return false; } + if (!boss) + return false; GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); for (auto& npc : npcs) @@ -36,7 +40,8 @@ bool FlameTsunamiTrigger::IsActive() bool TwilightFissureTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); - if (!boss) { return false; } + if (!boss) + return false; GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); for (auto& npc : npcs) @@ -57,17 +62,20 @@ bool TwilightFissureTrigger::IsActive() bool SartharionDpsTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); - if (!boss) { return false; } + if (!boss) + return false; - return botAI->IsDps(bot); + return BotRoleService::IsDpsStatic(bot); } bool SartharionMeleePositioningTrigger::IsActive() { - if (!botAI->IsMelee(bot) || !botAI->IsDps(bot)) { return false; } + if (!BotRoleService::IsMeleeStatic(bot) || !BotRoleService::IsDpsStatic(bot)) + return false; Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); - if (!boss) { return false; } + if (!boss) + return false; Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron"); Unit* tenebron = AI_VALUE2(Unit*, "find target", "tenebron"); @@ -78,19 +86,20 @@ bool SartharionMeleePositioningTrigger::IsActive() bool TwilightPortalEnterTrigger::IsActive() { - if (botAI->IsMainTank(bot) || botAI->IsAssistHealOfIndex(bot, 0)) { return false; } + if (BotRoleService::IsMainTankStatic(bot) || BotRoleService::IsAssistHealOfIndexStatic(bot, 0)) + return false; // In 25-man, take two healers in. Otherwise just take one // if (bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) // { - // if (botAI->IsAssistHealOfIndex(bot, 0) || botAI->IsAssistHealOfIndex(bot, 1)) + // if (BotRoleService::IsAssistHealOfIndexStatic(bot, 0) || BotRoleService::IsAssistHealOfIndexStatic(bot, 1)) // { // return false; // } // } // else // { - // if (botAI->IsAssistHealOfIndex(bot, 0)) + // if (BotRoleService::IsAssistHealOfIndexStatic(bot, 0)) // { // return false; // } @@ -105,7 +114,8 @@ bool TwilightPortalEnterTrigger::IsActive() } Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); - if (!boss) { return false; } + if (!boss) + return false; // GuidVector objects = AI_VALUE(GuidVector, "nearest game objects no los"); // for (auto& object : objects) diff --git a/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp b/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp index c0cd4bebab..4bbce91bfb 100644 --- a/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp +++ b/src/Ai/Raid/Onyxia/Action/RaidOnyxiaActions.cpp @@ -7,7 +7,7 @@ #include "Playerbots.h" #include "PositionAction.h" -bool RaidOnyxiaMoveToSideAction::Execute(Event event) +bool RaidOnyxiaMoveToSideAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "onyxia"); if (!boss) @@ -38,29 +38,40 @@ bool RaidOnyxiaMoveToSideAction::Execute(Event event) return false; } -bool RaidOnyxiaSpreadOutAction::Execute(Event event) +bool RaidOnyxiaSpreadOutAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "onyxia"); if (!boss) return false; - Player* target = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL)->m_targets.GetUnitTarget()->ToPlayer(); - if (target != bot) + // Trigger may fire on one tick, but the action can execute on a later tick. + // By that time the cast may have finished, so current spell can be null. + Spell* currentSpell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); + if (!currentSpell || !currentSpell->m_spellInfo) + return false; + + // Fireball + if (currentSpell->m_spellInfo->Id != 18392) + return false; + + Unit* unitTarget = currentSpell->m_targets.GetUnitTarget(); + Player* target = unitTarget ? unitTarget->ToPlayer() : nullptr; + if (!target || target != bot) return false; // bot->Yell("Spreading out — I'm the Fireball target!", LANG_UNIVERSAL); return MoveFromGroup(9.0f); // move 9 yards } -bool RaidOnyxiaMoveToSafeZoneAction::Execute(Event event) +bool RaidOnyxiaMoveToSafeZoneAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "onyxia"); if (!boss) return false; Spell* currentSpell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); - if (!currentSpell) + if (!currentSpell || !currentSpell->m_spellInfo) return false; uint32 spellId = currentSpell->m_spellInfo->Id; @@ -94,7 +105,7 @@ bool RaidOnyxiaMoveToSafeZoneAction::Execute(Event event) false, false, false, false, MovementPriority::MOVEMENT_COMBAT); } -bool RaidOnyxiaKillWhelpsAction::Execute(Event event) +bool RaidOnyxiaKillWhelpsAction::Execute(Event /*event*/) { Unit* currentTarget = AI_VALUE(Unit*, "current target"); // If already attacking a whelp, don't swap targets @@ -118,7 +129,7 @@ bool RaidOnyxiaKillWhelpsAction::Execute(Event event) return false; } -bool OnyxiaAvoidEggsAction::Execute(Event event) +bool OnyxiaAvoidEggsAction::Execute(Event /*event*/) { Position botPos = Position(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()); diff --git a/src/Ai/Raid/Onyxia/Strategy/RaidOnyxiaStrategy.cpp b/src/Ai/Raid/Onyxia/Strategy/RaidOnyxiaStrategy.cpp index b1217f59eb..bb61bc0044 100644 --- a/src/Ai/Raid/Onyxia/Strategy/RaidOnyxiaStrategy.cpp +++ b/src/Ai/Raid/Onyxia/Strategy/RaidOnyxiaStrategy.cpp @@ -24,7 +24,7 @@ void RaidOnyxiaStrategy::InitTriggers(std::vector& triggers) "ony whelps spawn", { NextAction("ony kill whelps", ACTION_RAID + 1) })); } -void RaidOnyxiaStrategy::InitMultipliers(std::vector& multipliers) +void RaidOnyxiaStrategy::InitMultipliers(std::vector& /*multipliers*/) { // Empty for now } diff --git a/src/Ai/Raid/Onyxia/Trigger/RaidOnyxiaTriggers.cpp b/src/Ai/Raid/Onyxia/Trigger/RaidOnyxiaTriggers.cpp index aed3a82619..5cfbd44946 100644 --- a/src/Ai/Raid/Onyxia/Trigger/RaidOnyxiaTriggers.cpp +++ b/src/Ai/Raid/Onyxia/Trigger/RaidOnyxiaTriggers.cpp @@ -1,4 +1,5 @@ #include "RaidOnyxiaTriggers.h" +#include "BotRoleService.h" #include "GenericTriggers.h" #include "ObjectAccessor.h" @@ -6,6 +7,20 @@ #include "Playerbots.h" #include "NearestNpcsValue.h" +// Onyxia Deep Breath spell IDs +enum OnyxiaDeepBreathSpells +{ + SPELL_DEEP_BREATH_N_TO_S = 17086, // North to South + SPELL_DEEP_BREATH_S_TO_N = 18351, // South to North + SPELL_DEEP_BREATH_E_TO_W = 18576, // East to West + SPELL_DEEP_BREATH_W_TO_E = 18609, // West to East + SPELL_DEEP_BREATH_SE_TO_NW = 18564, // Southeast to Northwest + SPELL_DEEP_BREATH_NW_TO_SE = 18584, // Northwest to Southeast + SPELL_DEEP_BREATH_SW_TO_NE = 18596, // Southwest to Northeast + SPELL_DEEP_BREATH_NE_TO_SW = 18617, // Northeast to Southwest + SPELL_ONYXIA_FIREBALL = 18392 // Onyxia Fireball +}; + OnyxiaDeepBreathTrigger::OnyxiaDeepBreathTrigger(PlayerbotAI* botAI) : Trigger(botAI, "ony deep breath warning") {} bool OnyxiaDeepBreathTrigger::IsActive() @@ -17,19 +32,19 @@ bool OnyxiaDeepBreathTrigger::IsActive() // Check if Onyxia is casting Spell* currentSpell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); - if (!currentSpell) + if (!currentSpell || !currentSpell->m_spellInfo) return false; uint32 spellId = currentSpell->m_spellInfo->Id; - if (spellId == 17086 || // North to South - spellId == 18351 || // South to North - spellId == 18576 || // East to West - spellId == 18609 || // West to East - spellId == 18564 || // Southeast to Northwest - spellId == 18584 || // Northwest to Southeast - spellId == 18596 || // Southwest to Northeast - spellId == 18617 // Northeast to Southwest + if (spellId == SPELL_DEEP_BREATH_N_TO_S || + spellId == SPELL_DEEP_BREATH_S_TO_N || + spellId == SPELL_DEEP_BREATH_E_TO_W || + spellId == SPELL_DEEP_BREATH_W_TO_E || + spellId == SPELL_DEEP_BREATH_SE_TO_NW || + spellId == SPELL_DEEP_BREATH_NW_TO_SE || + spellId == SPELL_DEEP_BREATH_SW_TO_NE || + spellId == SPELL_DEEP_BREATH_NE_TO_SW ) { return true; @@ -43,7 +58,7 @@ OnyxiaNearTailTrigger::OnyxiaNearTailTrigger(PlayerbotAI* botAI) : Trigger(botAI bool OnyxiaNearTailTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "onyxia"); - if (!boss || botAI->IsTank(bot)) + if (!boss || BotRoleService::IsTankStatic(bot)) return false; // Skip if Onyxia is in air or transitioning @@ -65,7 +80,7 @@ bool RaidOnyxiaFireballSplashTrigger::IsActive() // Check if Onyxia is casting Fireball Spell* currentSpell = boss->GetCurrentSpell(CURRENT_GENERIC_SPELL); - if (!currentSpell || currentSpell->m_spellInfo->Id != 18392) // 18392 is the classic Fireball ID + if (!currentSpell || currentSpell->m_spellInfo->Id != SPELL_ONYXIA_FIREBALL) return false; GuidVector nearbyUnits = AI_VALUE(GuidVector, "nearest friendly players"); @@ -91,7 +106,7 @@ bool RaidOnyxiaWhelpsSpawnTrigger::IsActive() if (!boss) return false; - return !botAI->IsHeal(bot) && boss->IsFlying(); // DPS + Tanks only + return !BotRoleService::IsHealStatic(bot) && boss->IsFlying(); // DPS + Tanks only } OnyxiaAvoidEggsTrigger::OnyxiaAvoidEggsTrigger(PlayerbotAI* botAI) : Trigger(botAI, "ony avoid eggs") {} diff --git a/src/Ai/Raid/Ulduar/Action/RaidUlduarActions.cpp b/src/Ai/Raid/Ulduar/Action/RaidUlduarActions.cpp index b7c3723bb3..cbba4897b1 100644 --- a/src/Ai/Raid/Ulduar/Action/RaidUlduarActions.cpp +++ b/src/Ai/Raid/Ulduar/Action/RaidUlduarActions.cpp @@ -1,5 +1,6 @@ #include "RaidUlduarActions.h" +#include "BotRoleService.h" #include #include @@ -7,6 +8,14 @@ #include #include "AiObjectContext.h" + +// Flame Leviathan NPC IDs +enum FlameLeviathanNpcs +{ + NPC_FLAME_LEVIATHAN_TURRET = 33139, // Flame Leviathan Turret + NPC_LEVIATHAN_DEFENSE_TURRET = 33142, // Leviathan Defense Turret + NPC_FLAME_LEVIATHAN = 33113 // Flame Leviathan boss +}; #include "DBCEnums.h" #include "GameObject.h" #include "Group.h" @@ -28,15 +37,16 @@ #include "Vehicle.h" #include #include +#include "BotSpellService.h" -const std::string ADD_STRATEGY_CHAR = "+"; -const std::string REMOVE_STRATEGY_CHAR = "-"; +std::string const ADD_STRATEGY_CHAR = "+"; +std::string const REMOVE_STRATEGY_CHAR = "-"; -const std::vector availableVehicles = {NPC_VEHICLE_CHOPPER, NPC_SALVAGED_DEMOLISHER, +std::vector const availableVehicles = {NPC_VEHICLE_CHOPPER, NPC_SALVAGED_DEMOLISHER, NPC_SALVAGED_DEMOLISHER_TURRET, NPC_SALVAGED_SIEGE_ENGINE, NPC_SALVAGED_SIEGE_ENGINE_TURRET}; -const std::vector corners = { +std::vector const corners = { {183.53f, 66.53f, 409.80f}, {383.03f, 75.10f, 411.71f}, {379.74f, -133.05f, 410.88f}, {158.67f, -137.54f, 409.80f}}; const Position ULDUAR_KOLOGARN_RESTORE_POSITION = Position(1764.3749f, -24.02903f, 448.0f, 0.00087690353f); @@ -50,7 +60,7 @@ const Position yoggPortalLoc[] = { {1960.62f, -32.00f, 325.5f}, {1981.98f, -5.69f, 325.5f}, {1982.78f, -45.73f, 325.5f}, {2000.66f, -29.68f, 325.5f}, {1999.88f, -19.61f, 325.5f}, {1961.37f, -19.54f, 325.5f}}; -bool FlameLeviathanVehicleAction::Execute(Event event) +bool FlameLeviathanVehicleAction::Execute(Event /*event*/) { vehicleBase_ = bot->GetVehicleBase(); vehicle_ = bot->GetVehicle(); @@ -65,11 +75,11 @@ bool FlameLeviathanVehicleAction::Execute(Event event) Unit* unit = botAI->GetUnit(*i); if (!unit) continue; - if (unit->GetEntry() == 33139) // Flame Leviathan Turret + if (unit->GetEntry() == NPC_FLAME_LEVIATHAN_TURRET) continue; - if (unit->GetEntry() == 33142) // Leviathan Defense Turret + if (unit->GetEntry() == NPC_LEVIATHAN_DEFENSE_TURRET) continue; - if (unit->GetEntry() == 33113) // Flame Leviathan + if (unit->GetEntry() == NPC_FLAME_LEVIATHAN) { flame = unit; continue; @@ -109,7 +119,7 @@ bool FlameLeviathanVehicleAction::MoveAvoidChasing(Unit* target) return false; if (avoidChaseIdx == -1) { - for (int i = 0; i < corners.size(); i++) + for (size_t i = 0; i < corners.size(); i++) { if (bot->GetExactDist(corners[i]) > target->GetExactDist(corners[i])) continue; @@ -124,7 +134,7 @@ bool FlameLeviathanVehicleAction::MoveAvoidChasing(Unit* target) if (bot->GetExactDist(corners[avoidChaseIdx]) < 5.0f && target->GetExactDist(bot) < 50.0f) avoidChaseIdx = (avoidChaseIdx + 1) % corners.size(); } - const Position& to = corners[avoidChaseIdx]; + Position const& to = corners[avoidChaseIdx]; return MoveTo(bot->GetMap()->GetId(), to.GetPositionX(), to.GetPositionY(), to.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); } @@ -137,16 +147,16 @@ bool FlameLeviathanVehicleAction::DemolisherAction(Unit* target) if (!bluePyrite || (vehicleBase_->GetPower(POWER_ENERGY) >= 20) || bluePyrite->GetDuration() <= 5000) { uint32 spellId = 62490; - if (botAI->CanCastVehicleSpell(spellId, target)) - if (botAI->CastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CanCastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CastVehicleSpell(spellId, target)) { vehicleBase_->AddSpellCooldown(spellId, 0, 1000); return true; } } uint32 spellId = 62306; - if (botAI->CanCastVehicleSpell(spellId, target)) - if (botAI->CastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CanCastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CastVehicleSpell(spellId, target)) { vehicleBase_->AddSpellCooldown(spellId, 0, 1000); return true; @@ -172,8 +182,8 @@ bool FlameLeviathanVehicleAction::DemolisherTurretAction(Unit* target) if (vehicleBase_->GetPower(POWER_ENERGY) <= 25) // Liquid Pyrite { uint32 spellId = 62479; - if (botAI->CanCastVehicleSpell(spellId, unit)) - if (botAI->CastVehicleSpell(spellId, unit)) + if (botAI->GetServices().GetSpellService().CanCastVehicleSpell(spellId, unit)) + if (botAI->GetServices().GetSpellService().CastVehicleSpell(spellId, unit)) { vehicleBase_->AddSpellCooldown(spellId, 0, 1000); return true; @@ -192,8 +202,8 @@ bool FlameLeviathanVehicleAction::DemolisherTurretAction(Unit* target) if (unit->GetEntry() == 33214) // Mechanolift 304-A { uint32 spellId = 64979; - if (botAI->CanCastVehicleSpell(spellId, unit)) - if (botAI->CastVehicleSpell(spellId, unit)) + if (botAI->GetServices().GetSpellService().CanCastVehicleSpell(spellId, unit)) + if (botAI->GetServices().GetSpellService().CastVehicleSpell(spellId, unit)) { vehicleBase_->AddSpellCooldown(spellId, 0, 1000); return true; @@ -204,8 +214,8 @@ bool FlameLeviathanVehicleAction::DemolisherTurretAction(Unit* target) if (!target) return false; uint32 spellId = 62634; - if (botAI->CanCastVehicleSpell(spellId, target)) - if (botAI->CastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CanCastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CastVehicleSpell(spellId, target)) { vehicleBase_->AddSpellCooldown(spellId, 0, 1000); return true; @@ -220,16 +230,16 @@ bool FlameLeviathanVehicleAction::SiegeEngineAction(Unit* target) if (target->GetCurrentSpell(CURRENT_CHANNELED_SPELL) || target->HasAura(62396)) { uint32 spellId = 62522; - if (botAI->CanCastVehicleSpell(spellId, target)) - if (botAI->CastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CanCastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CastVehicleSpell(spellId, target)) { vehicleBase_->AddSpellCooldown(spellId, 0, 10000); return true; } } uint32 spellId = 62345; - if (vehicleBase_->GetPower(POWER_ENERGY) >= 80 && botAI->CanCastVehicleSpell(spellId, target)) - if (botAI->CastVehicleSpell(spellId, target)) + if (vehicleBase_->GetPower(POWER_ENERGY) >= 80 && botAI->GetServices().GetSpellService().CanCastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CastVehicleSpell(spellId, target)) { vehicleBase_->AddSpellCooldown(spellId, 0, 1000); return true; @@ -242,8 +252,8 @@ bool FlameLeviathanVehicleAction::SiegeEngineTurretAction(Unit* target) if (!target) return false; uint32 spellId = 62358; - if (botAI->CanCastVehicleSpell(spellId, target)) - if (botAI->CastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CanCastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CastVehicleSpell(spellId, target)) { vehicleBase_->AddSpellCooldown(spellId, 0, 1000); return true; @@ -256,15 +266,15 @@ bool FlameLeviathanVehicleAction::ChopperAction(Unit* target) if (!target) return false; uint32 spellId = 62286; - if (botAI->CanCastVehicleSpell(spellId, target)) - if (botAI->CastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CanCastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CastVehicleSpell(spellId, target)) { vehicleBase_->AddSpellCooldown(spellId, 0, 15000); return true; } spellId = 62974; - if (botAI->CanCastVehicleSpell(spellId, target)) - if (botAI->CastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CanCastVehicleSpell(spellId, target)) + if (botAI->GetServices().GetSpellService().CastVehicleSpell(spellId, target)) { vehicleBase_->AddSpellCooldown(spellId, 0, 1000); return true; @@ -272,7 +282,7 @@ bool FlameLeviathanVehicleAction::ChopperAction(Unit* target) return false; } -bool FlameLeviathanEnterVehicleAction::Execute(Event event) +bool FlameLeviathanEnterVehicleAction::Execute(Event /*event*/) { // do not switch vehicles yet if (bot->GetVehicle()) @@ -314,7 +324,7 @@ bool FlameLeviathanEnterVehicleAction::EnterVehicle(Unit* vehicleBase, bool move if (dist > INTERACTION_DISTANCE) return MoveTo(vehicleBase); - botAI->RemoveShapeshift(); + botAI->GetServices().GetSpellService().RemoveShapeshift(); // Use HandleSpellClick instead of Unit::EnterVehicle to handle special vehicle script (ulduar) vehicleBase->HandleSpellClick(bot); @@ -333,7 +343,7 @@ bool FlameLeviathanEnterVehicleAction::ShouldEnter(Unit* target) if (!vehicleKit) return false; - bool isMelee = botAI->IsMelee(bot); + bool isMelee = BotRoleService::IsMeleeStatic(bot); bool allMain = AllMainVehiclesOnUse(); bool inUse = vehicleKit->IsVehicleInUse(); int32 entry = target->GetEntry(); @@ -417,7 +427,7 @@ bool FlameLeviathanEnterVehicleAction::AllMainVehiclesOnUse() return demolisher >= maxC && siege >= maxC; } -bool RazorscaleAvoidDevouringFlameAction::Execute(Event event) +bool RazorscaleAvoidDevouringFlameAction::Execute(Event /*event*/) { RazorscaleBossHelper razorscaleHelper(botAI); @@ -426,7 +436,7 @@ bool RazorscaleAvoidDevouringFlameAction::Execute(Event event) return false; } - bool isMainTank = botAI->IsMainTank(bot); + bool isMainTank = BotRoleService::IsMainTankStatic(bot); const float flameRadius = 3.5f; // Main tank moves further so they can hold adds away from flames, but only during the air phases @@ -460,7 +470,7 @@ bool RazorscaleAvoidDevouringFlameAction::Execute(Event event) } // Off tanks are following the main tank during grounded and should prioritise stacking - if (razorscaleHelper.IsGroundPhase() && (botAI->IsTank(bot) && !botAI->IsMainTank(bot))) + if (razorscaleHelper.IsGroundPhase() && (BotRoleService::IsTankStatic(bot) && !BotRoleService::IsMainTankStatic(bot))) { return false; } @@ -475,7 +485,7 @@ bool RazorscaleAvoidDevouringFlameAction::Execute(Event event) bool RazorscaleAvoidDevouringFlameAction::isUseful() { - bool isMainTank = botAI->IsMainTank(bot); + bool isMainTank = BotRoleService::IsMainTankStatic(bot); const float flameRadius = 3.5f; const float safeDistanceMultiplier = isMainTank ? 2.3f : 1.0f; @@ -498,10 +508,10 @@ bool RazorscaleAvoidDevouringFlameAction::isUseful() return false; // No nearby flames or bot is at a safe distance } -bool RazorscaleAvoidSentinelAction::Execute(Event event) +bool RazorscaleAvoidSentinelAction::Execute(Event /*event*/) { - bool isMainTank = botAI->IsMainTank(bot); - bool isRanged = botAI->IsRanged(bot); + bool isMainTank = BotRoleService::IsMainTankStatic(bot); + bool isRanged = BotRoleService::IsRangedStatic(bot); const float radius = 8.0f; GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); @@ -540,7 +550,7 @@ bool RazorscaleAvoidSentinelAction::Execute(Event event) // Iterate through the first 3 bot tanks to assign the Skull marker for (int i = 0; i < 3; ++i) { - if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank + if (BotRoleService::IsAssistTankOfIndexStatic(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank { Group* group = bot->GetGroup(); if (group && lowestHealthSentinel) @@ -579,7 +589,7 @@ bool RazorscaleAvoidSentinelAction::Execute(Event event) bool RazorscaleAvoidSentinelAction::isUseful() { - bool isMainTank = botAI->IsMainTank(bot); + bool isMainTank = BotRoleService::IsMainTankStatic(bot); Unit* mainTankUnit = AI_VALUE(Unit*, "main tank"); Player* mainTank = mainTankUnit ? mainTankUnit->ToPlayer() : nullptr; @@ -594,14 +604,14 @@ bool RazorscaleAvoidSentinelAction::isUseful() { for (int i = 0; i < 3; ++i) { - if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank + if (BotRoleService::IsAssistTankOfIndexStatic(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank { return true; // This bot should assist with marking } } } - bool isRanged = botAI->IsRanged(bot); + bool isRanged = BotRoleService::IsRangedStatic(bot); const float radius = 8.0f; GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); @@ -620,9 +630,9 @@ bool RazorscaleAvoidSentinelAction::isUseful() return false; } -bool RazorscaleAvoidWhirlwindAction::Execute(Event event) +bool RazorscaleAvoidWhirlwindAction::Execute(Event /*event*/) { - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) { return false; } @@ -647,7 +657,7 @@ bool RazorscaleAvoidWhirlwindAction::Execute(Event event) bool RazorscaleAvoidWhirlwindAction::isUseful() { // Tanks do not avoid Whirlwind - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) { return false; } @@ -692,7 +702,7 @@ bool RazorscaleIgnoreBossAction::isUseful() return true; // Movement to the center is the top priority for all bots } - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) { return false; } @@ -726,7 +736,7 @@ bool RazorscaleIgnoreBossAction::isUseful() { for (int i = 0; i < 3; ++i) // Only iterate through the first 3 indexes { - if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Valid bot tank + if (BotRoleService::IsAssistTankOfIndexStatic(bot, i) && GET_PLAYERBOT_AI(bot)) // Valid bot tank { return true; // This bot should assign the marker } @@ -737,7 +747,7 @@ bool RazorscaleIgnoreBossAction::isUseful() return false; } -bool RazorscaleIgnoreBossAction::Execute(Event event) +bool RazorscaleIgnoreBossAction::Execute(Event /*event*/) { if (!bot) { @@ -766,7 +776,7 @@ bool RazorscaleIgnoreBossAction::Execute(Event event) RazorscaleBossHelper::RAZORSCALE_ARENA_RADIUS - 10.0f, MovementPriority::MOVEMENT_NORMAL); } - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) { return false; } @@ -788,7 +798,7 @@ bool RazorscaleIgnoreBossAction::Execute(Event event) { for (int i = 0; i < 3; ++i) // Only iterate through the first 3 indexes { - if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank + if (BotRoleService::IsAssistTankOfIndexStatic(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank { group->SetTargetIcon(moonIndex, bot->GetGUID(), boss->GetGUID()); SetNextMovementDelay(1000); @@ -816,7 +826,7 @@ bool RazorscaleGroundedAction::isUseful() return false; } - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) { Group* group = bot->GetGroup(); if (!group) @@ -830,7 +840,7 @@ bool RazorscaleGroundedAction::isUseful() return currentMoonTarget == boss->GetGUID(); } - if (botAI->IsTank(bot) && !botAI->IsMainTank(bot)) + if (BotRoleService::IsTankStatic(bot) && !BotRoleService::IsMainTankStatic(bot)) { Group* group = bot->GetGroup(); if (!group) @@ -841,7 +851,7 @@ bool RazorscaleGroundedAction::isUseful() for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (member && botAI->IsMainTank(member)) + if (member && BotRoleService::IsMainTankStatic(member)) { mainTank = member; break; @@ -856,12 +866,12 @@ bool RazorscaleGroundedAction::isUseful() } } - if (botAI->IsMelee(bot)) + if (BotRoleService::IsMeleeStatic(bot)) { return false; } - if (botAI->IsRanged(bot)) + if (BotRoleService::IsRangedStatic(bot)) { constexpr float landingX = 588.0f; constexpr float landingY = -166.0f; @@ -894,7 +904,7 @@ bool RazorscaleGroundedAction::isUseful() return false; } -bool RazorscaleGroundedAction::Execute(Event event) +bool RazorscaleGroundedAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "razorscale"); if (!boss || !boss->IsAlive() || boss->GetPositionZ() > RazorscaleBossHelper::RAZORSCALE_FLYING_Z_THRESHOLD) @@ -912,7 +922,7 @@ bool RazorscaleGroundedAction::Execute(Event event) // Iterate through the first 3 bot tanks to handle the moon marker for (int i = 0; i < 3; ++i) { - if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank + if (BotRoleService::IsAssistTankOfIndexStatic(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank { int8 moonIndex = 4; ObjectGuid currentMoonTarget = group->GetTargetIcon(moonIndex); @@ -927,7 +937,7 @@ bool RazorscaleGroundedAction::Execute(Event event) } } } - else if (botAI->IsMainTank(bot)) // Bot is the main tank + else if (BotRoleService::IsMainTankStatic(bot)) // Bot is the main tank { int8 moonIndex = 4; ObjectGuid currentMoonTarget = group->GetTargetIcon(moonIndex); @@ -941,13 +951,13 @@ bool RazorscaleGroundedAction::Execute(Event event) } } - if (mainTank && (botAI->IsTank(bot) && !botAI->IsMainTank(bot))) + if (mainTank && (BotRoleService::IsTankStatic(bot) && !BotRoleService::IsMainTankStatic(bot))) { constexpr float followDistance = 2.0f; return MoveNear(mainTank, followDistance, MovementPriority::MOVEMENT_COMBAT); } - if (botAI->IsRanged(bot)) + if (BotRoleService::IsRangedStatic(bot)) { constexpr float landingX = 588.0f; constexpr float landingY = -166.0f; @@ -977,7 +987,7 @@ bool RazorscaleGroundedAction::Execute(Event event) return false; } -bool RazorscaleHarpoonAction::Execute(Event event) +bool RazorscaleHarpoonAction::Execute(Event /*event*/) { if (!bot) { @@ -995,7 +1005,7 @@ bool RazorscaleHarpoonAction::Execute(Event event) return false; // Retrieve harpoon data from the helper - const std::vector& harpoonData = razorscaleHelper.GetHarpoonData(); + std::vector const& harpoonData = razorscaleHelper.GetHarpoonData(); GameObject* closestHarpoon = nullptr; float minDistance = std::numeric_limits::max(); @@ -1031,7 +1041,7 @@ bool RazorscaleHarpoonAction::Execute(Event event) for (auto& guid : groupBots) { Player* member = ObjectAccessor::FindPlayer(guid); - if (member && member->IsAlive() && botAI->IsRanged(member) && botAI->IsDps(member) && !botAI->IsHeal(member)) + if (member && member->IsAlive() && BotRoleService::IsRangedStatic(member) && BotRoleService::IsDpsStatic(member) && !BotRoleService::IsHealStatic(member)) { float distance = member->GetDistance2d(closestHarpoon); if (distance < minDistance) @@ -1085,7 +1095,7 @@ bool RazorscaleHarpoonAction::isUseful() if (!boss || !boss->IsAlive()) return false; - const std::vector& harpoonData = razorscaleHelper.GetHarpoonData(); + std::vector const& harpoonData = razorscaleHelper.GetHarpoonData(); for (auto const& harpoon : harpoonData) { @@ -1097,7 +1107,7 @@ bool RazorscaleHarpoonAction::isUseful() if (RazorscaleBossHelper::IsHarpoonReady(harpoonGO)) { // Check if this bot is a ranged DPS (not a healer) - if (botAI->IsRanged(bot) && botAI->IsDps(bot) && !botAI->IsHeal(bot)) + if (BotRoleService::IsRangedStatic(bot) && BotRoleService::IsDpsStatic(bot) && !BotRoleService::IsHealStatic(bot)) return true; } } @@ -1109,11 +1119,11 @@ bool RazorscaleHarpoonAction::isUseful() bool RazorscaleFuseArmorAction::isUseful() { // If this bot cannot tank at all, no need to do anything - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) return false; // If this bot is the main tank AND has Fuse Armor at the threshold, return true immediately - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) { Aura* fuseArmor = bot->GetAura(RazorscaleBossHelper::SPELL_FUSEARMOR); if (fuseArmor && fuseArmor->GetStackAmount() >= RazorscaleBossHelper::FUSEARMOR_THRESHOLD) @@ -1131,7 +1141,7 @@ bool RazorscaleFuseArmorAction::isUseful() if (!member) continue; - if (botAI->IsMainTank(member) && member != bot) + if (BotRoleService::IsMainTankStatic(member) && member != bot) { Aura* fuseArmor = member->GetAura(RazorscaleBossHelper::SPELL_FUSEARMOR); if (fuseArmor && fuseArmor->GetStackAmount() >= RazorscaleBossHelper::FUSEARMOR_THRESHOLD) @@ -1145,7 +1155,7 @@ bool RazorscaleFuseArmorAction::isUseful() return false; } -bool RazorscaleFuseArmorAction::Execute(Event event) +bool RazorscaleFuseArmorAction::Execute(Event /*event*/) { // We already know from isUseful() that: // 1) This bot can tank, AND @@ -1164,7 +1174,7 @@ bool IronAssemblyLightningTendrilsAction::isUseful() return ironAssemblyLightningTendrilsTrigger.IsActive(); } -bool IronAssemblyLightningTendrilsAction::Execute(Event event) +bool IronAssemblyLightningTendrilsAction::Execute(Event /*event*/) { const float radius = 18.0f + 10.0f; // 18 yards + 10 yards for safety @@ -1188,7 +1198,7 @@ bool IronAssemblyOverloadAction::isUseful() return ironAssemblyOverloadTrigger.IsActive(); } -bool IronAssemblyOverloadAction::Execute(Event event) +bool IronAssemblyOverloadAction::Execute(Event /*event*/) { const float radius = 20.0f + 5.0f; // 20 yards + 5 yards for safety @@ -1212,7 +1222,7 @@ bool IronAssemblyRuneOfPowerAction::isUseful() return ironAssemblyRuneOfPowerTrigger.IsActive(); } -bool IronAssemblyRuneOfPowerAction::Execute(Event event) +bool IronAssemblyRuneOfPowerAction::Execute(Event /*event*/) { Unit* target = botAI->GetUnit(bot->GetTarget()); if (!target || !target->IsAlive()) @@ -1227,7 +1237,7 @@ bool KologarnMarkDpsTargetAction::isUseful() return kologarnMarkDpsTargetTrigger.IsActive(); } -bool KologarnMarkDpsTargetAction::Execute(Event event) +bool KologarnMarkDpsTargetAction::Execute(Event /*event*/) { Unit* targetToMark = nullptr; Unit* additionalTargetToMark = nullptr; @@ -1249,7 +1259,6 @@ bool KologarnMarkDpsTargetAction::Execute(Event event) if (!target) continue; - uint32 creatureId = target->GetEntry(); if (target->GetEntry() == NPC_RUBBLE && target->IsAlive()) { targetToMark = target; @@ -1287,7 +1296,7 @@ bool KologarnMarkDpsTargetAction::Execute(Event event) targetToCcMark = leftArm; } - bool isMainTank = botAI->IsMainTank(bot); + bool isMainTank = BotRoleService::IsMainTankStatic(bot); Unit* mainTankUnit = AI_VALUE(Unit*, "main tank"); Player* mainTank = mainTankUnit ? mainTankUnit->ToPlayer() : nullptr; @@ -1296,7 +1305,7 @@ bool KologarnMarkDpsTargetAction::Execute(Event event) // Iterate through the first 3 bot tanks to assign the Skull marker for (int i = 0; i < 3; ++i) { - if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank + if (BotRoleService::IsAssistTankOfIndexStatic(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank { Group* group = bot->GetGroup(); if (group) @@ -1338,7 +1347,7 @@ bool KologarnMarkDpsTargetAction::Execute(Event event) { for (int i = 0; i < 3; ++i) { - if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot) && bot->IsAlive()) // Bot is a valid tank + if (BotRoleService::IsAssistTankOfIndexStatic(bot, i) && GET_PLAYERBOT_AI(bot) && bot->IsAlive()) // Bot is a valid tank { Group* group = bot->GetGroup(); if (group) @@ -1362,7 +1371,7 @@ bool KologarnMarkDpsTargetAction::Execute(Event event) return false; } -bool KologarnFallFromFloorAction::Execute(Event event) +bool KologarnFallFromFloorAction::Execute(Event /*event*/) { return bot->TeleportTo(bot->GetMapId(), ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionX(), ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionY(), @@ -1376,7 +1385,7 @@ bool KologarnFallFromFloorAction::isUseful() return kologarnFallFromFloorTrigger.IsActive(); } -bool KologarnRubbleSlowdownAction::Execute(Event event) +bool KologarnRubbleSlowdownAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) @@ -1388,10 +1397,10 @@ bool KologarnRubbleSlowdownAction::Execute(Event event) if (!currentSkullUnit || !currentSkullUnit->IsAlive() || currentSkullUnit->GetEntry() != NPC_RUBBLE) return false; - return botAI->CastSpell("frost trap", currentSkullUnit); + return botAI->GetServices().GetSpellService().CastSpell("frost trap", currentSkullUnit); } -bool KologarnEyebeamAction::Execute(Event event) +bool KologarnEyebeamAction::Execute(Event /*event*/) { float distanceToLeftPoint = bot->GetExactDist(ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION); float distanceToRightPoint = bot->GetExactDist(ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION); @@ -1450,9 +1459,9 @@ bool KologarnRtiTargetAction::isUseful() return kologarnRtiTargetTrigger.IsActive(); } -bool KologarnRtiTargetAction::Execute(Event event) +bool KologarnRtiTargetAction::Execute(Event /*event*/) { - if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0)) + if (BotRoleService::IsMainTankStatic(bot) || BotRoleService::IsAssistTankOfIndexStatic(bot, 0)) { context->GetValue("rti")->Set("cross"); return true; @@ -1473,13 +1482,13 @@ bool KologarnCrunchArmorAction::isUseful() return botAI->HasCheat(BotCheatMask::raid); } -bool KologarnCrunchArmorAction::Execute(Event event) +bool KologarnCrunchArmorAction::Execute(Event /*event*/) { bot->RemoveAura(SPELL_CRUNCH_ARMOR); return true; } -bool AuriayaFallFromFloorAction::Execute(Event event) +bool AuriayaFallFromFloorAction::Execute(Event /*event*/) { Player* master = botAI->GetMaster(); @@ -1525,7 +1534,7 @@ bool HodirMoveSnowpackedIcicleAction::isUseful() return true; } -bool HodirMoveSnowpackedIcicleAction::Execute(Event event) +bool HodirMoveSnowpackedIcicleAction::Execute(Event /*event*/) { Creature* target = bot->FindNearestCreature(NPC_SNOWPACKED_ICICLE, 100.0f); if (!target) @@ -1535,7 +1544,7 @@ bool HodirMoveSnowpackedIcicleAction::Execute(Event event) false, false, true, MovementPriority::MOVEMENT_NORMAL, true); } -bool HodirBitingColdJumpAction::Execute(Event event) +bool HodirBitingColdJumpAction::Execute(Event /*event*/) { bot->RemoveAurasDueToSpell(SPELL_BITING_COLD_PLAYER_AURA); @@ -1591,7 +1600,7 @@ bool FreyaMoveAwayNatureBombAction::isUseful() return true; } -bool FreyaMoveAwayNatureBombAction::Execute(Event event) +bool FreyaMoveAwayNatureBombAction::Execute(Event /*event*/) { GameObject* target = bot->FindNearestGameObject(GOBJECT_NATURE_BOMB, 12.0f); if (!target) @@ -1606,7 +1615,7 @@ bool FreyaMarkDpsTargetAction::isUseful() return freyaMarkDpsTargetTrigger.IsActive(); } -bool FreyaMarkDpsTargetAction::Execute(Event event) +bool FreyaMarkDpsTargetAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "freya"); if (!boss || !boss->IsAlive()) @@ -1707,7 +1716,7 @@ bool FreyaMarkDpsTargetAction::Execute(Event event) return false; // No target to mark } - bool isMainTank = botAI->IsMainTank(bot); + bool isMainTank = BotRoleService::IsMainTankStatic(bot); Unit* mainTankUnit = AI_VALUE(Unit*, "main tank"); Player* mainTank = mainTankUnit ? mainTankUnit->ToPlayer() : nullptr; int8 squareIndex = 5; // Square @@ -1718,7 +1727,7 @@ bool FreyaMarkDpsTargetAction::Execute(Event event) // Iterate through the first 3 bot tanks to assign the Skull marker for (int i = 0; i < 3; ++i) { - if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank + if (BotRoleService::IsAssistTankOfIndexStatic(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank { Group* group = bot->GetGroup(); if (group) @@ -1762,7 +1771,7 @@ bool FreyaMoveToHealingSporeAction::isUseful() return freyaMoveToHealingSporeTrigger.IsActive(); } -bool FreyaMoveToHealingSporeAction::Execute(Event event) +bool FreyaMoveToHealingSporeAction::Execute(Event /*event*/) { GuidVector targets = AI_VALUE(GuidVector, "nearest npcs"); Creature* nearestSpore = nullptr; @@ -1804,7 +1813,7 @@ bool ThorimUnbalancingStrikeAction::isUseful() return botAI->HasCheat(BotCheatMask::raid); } -bool ThorimUnbalancingStrikeAction::Execute(Event event) +bool ThorimUnbalancingStrikeAction::Execute(Event /*event*/) { bot->RemoveAura(SPELL_UNBALANCING_STRIKE); return true; @@ -1816,7 +1825,7 @@ bool ThorimMarkDpsTargetAction::isUseful() return thorimMarkDpsTargetTrigger.IsActive(); } -bool ThorimMarkDpsTargetAction::Execute(Event event) +bool ThorimMarkDpsTargetAction::Execute(Event /*event*/) { Unit* targetToMark = nullptr; @@ -1839,7 +1848,7 @@ bool ThorimMarkDpsTargetAction::Execute(Event event) return true; } - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) { ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex); Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget); @@ -1860,7 +1869,7 @@ bool ThorimMarkDpsTargetAction::Execute(Event event) else return false; } - else if (botAI->IsAssistTankOfIndex(bot, 0)) + else if (BotRoleService::IsAssistTankOfIndexStatic(bot, 0)) { ObjectGuid currentCrossTarget = group->GetTargetIcon(RtiTargetValue::crossIndex); Unit* currentCrossUnit = botAI->GetUnit(currentCrossTarget); @@ -1896,13 +1905,13 @@ bool ThorimMarkDpsTargetAction::Execute(Event event) if (!targetToMark) return false; // No target to mark - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) { group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), targetToMark->GetGUID()); return true; } - if (botAI->IsAssistTankOfIndex(bot, 0)) + if (BotRoleService::IsAssistTankOfIndexStatic(bot, 0)) { group->SetTargetIcon(RtiTargetValue::crossIndex, bot->GetGUID(), targetToMark->GetGUID()); return true; @@ -1917,7 +1926,7 @@ bool ThorimArenaPositioningAction::isUseful() return thorimArenaPositioningTrigger.IsActive(); } -bool ThorimArenaPositioningAction::Execute(Event event) +bool ThorimArenaPositioningAction::Execute(Event /*event*/) { FollowMasterStrategy followMasterStrategy(botAI); @@ -1939,7 +1948,7 @@ bool ThorimGauntletPositioningAction::isUseful() return thorimGauntletPositioningTrigger.IsActive(); } -bool ThorimGauntletPositioningAction::Execute(Event event) +bool ThorimGauntletPositioningAction::Execute(Event /*event*/) { FollowMasterStrategy followMasterStrategy(botAI); @@ -2113,7 +2122,7 @@ bool ThorimGauntletPositioningAction::Execute(Event event) return false; } -bool ThorimFallFromFloorAction::Execute(Event event) +bool ThorimFallFromFloorAction::Execute(Event /*event*/) { Player* master = botAI->GetMaster(); @@ -2130,12 +2139,12 @@ bool ThorimFallFromFloorAction::isUseful() return thorimFallFromFloorTrigger.IsActive(); } -bool ThorimPhase2PositioningAction::Execute(Event event) +bool ThorimPhase2PositioningAction::Execute(Event /*event*/) { Position targetPosition; bool backward = false; - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) { targetPosition = ULDUAR_THORIM_PHASE2_TANK_SPOT; backward = true; @@ -2153,7 +2162,7 @@ bool ThorimPhase2PositioningAction::Execute(Event event) if (!member) continue; - if (botAI->IsRanged(member) || botAI->IsHeal(member)) + if (BotRoleService::IsRangedStatic(member) || BotRoleService::IsHealStatic(member)) { if (bot->GetGUID() == member->GetGUID()) break; @@ -2190,7 +2199,7 @@ bool ThorimPhase2PositioningAction::isUseful() return thorimPhase2PositioningTrigger.IsActive(); } -bool MimironShockBlastAction::Execute(Event event) +bool MimironShockBlastAction::Execute(Event /*event*/) { Unit* leviathanMkII = nullptr; Unit* vx001 = nullptr; @@ -2231,7 +2240,7 @@ bool MimironShockBlastAction::Execute(Event event) MoveAway(leviathanMkII, radius - currentDistance); - if (botAI->IsMelee(bot)) + if (BotRoleService::IsMeleeStatic(bot)) { botAI->SetNextCheckDelay(100); } @@ -2264,7 +2273,7 @@ bool MimironShockBlastAction::isUseful() return mimironShockBlastTrigger.IsActive(); } -bool MimironPhase1PositioningAction::Execute(Event event) +bool MimironPhase1PositioningAction::Execute(Event /*event*/) { SET_AI_VALUE(float, "disperse distance", 6.0f); return true; @@ -2276,7 +2285,7 @@ bool MimironPhase1PositioningAction::isUseful() return mimironPhase1PositioningTrigger.IsActive(); } -bool MimironP3Wx2LaserBarrageAction::Execute(Event event) +bool MimironP3Wx2LaserBarrageAction::Execute(Event /*event*/) { auto master = botAI->GetMaster(); if (!master || !master->IsAlive()) @@ -2300,7 +2309,7 @@ bool MimironRapidBurstAction::isUseful() return mimironRapidBurstTrigger.IsActive(); } -bool MimironRapidBurstAction::Execute(Event event) +bool MimironRapidBurstAction::Execute(Event /*event*/) { Unit* leviathanMkII = nullptr; @@ -2335,7 +2344,7 @@ bool MimironRapidBurstAction::Execute(Event event) if (bot->GetGUID() == member->GetGUID()) { - if (botAI->IsRanged(bot)) + if (BotRoleService::IsRangedStatic(bot)) { switch (memberSpotNumber) { @@ -2352,7 +2361,7 @@ bool MimironRapidBurstAction::Execute(Event event) break; } } - else if (botAI->IsMainTank(bot) && leviathanMkII) + else if (BotRoleService::IsMainTankStatic(bot) && leviathanMkII) { targetPosition = ULDUAR_MIMIRON_PHASE4_TANK_SPOT; } @@ -2407,7 +2416,7 @@ bool MimironRapidBurstAction::Execute(Event event) return true; } -bool MimironAerialCommandUnitAction::Execute(Event event) +bool MimironAerialCommandUnitAction::Execute(Event /*event*/) { Unit* boss = nullptr; Unit* bombBot = nullptr; @@ -2435,7 +2444,7 @@ bool MimironAerialCommandUnitAction::Execute(Event event) } } - if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0)) + if (BotRoleService::IsMainTankStatic(bot) || BotRoleService::IsAssistTankOfIndexStatic(bot, 0)) { Group* group = bot->GetGroup(); if (!group) @@ -2479,9 +2488,8 @@ bool MimironRocketStrikeAction::isUseful() return mimironRocketStrikeTrigger.IsActive(); } -bool MimironRocketStrikeAction::Execute(Event event) +bool MimironRocketStrikeAction::Execute(Event /*event*/) { - Unit* leviathanMkII = nullptr; Unit* vx001 = nullptr; Unit* aerialCommandUnit = nullptr; @@ -2493,11 +2501,7 @@ bool MimironRocketStrikeAction::Execute(Event event) if (!target || !target->IsAlive()) continue; - if (target->GetEntry() == NPC_LEVIATHAN_MKII) - { - leviathanMkII = target; - } - else if (target->GetEntry() == NPC_VX001) + if (target->GetEntry() == NPC_VX001) { vx001 = target; } @@ -2540,7 +2544,7 @@ bool MimironRocketStrikeAction::Execute(Event event) } } -bool MimironPhase4MarkDpsAction::Execute(Event event) +bool MimironPhase4MarkDpsAction::Execute(Event /*event*/) { Unit* leviathanMkII = nullptr; Unit* vx001 = nullptr; @@ -2579,7 +2583,7 @@ bool MimironPhase4MarkDpsAction::Execute(Event event) return false; } - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) { Unit* highestHealthUnit = nullptr; uint32 highestHealth = 0; @@ -2629,10 +2633,10 @@ bool MimironPhase4MarkDpsAction::Execute(Event event) } } -bool MimironCheatAction::Execute(Event event) +bool MimironCheatAction::Execute(Event /*event*/) { GuidVector targets = AI_VALUE(GuidVector, "nearest npcs"); - for (const ObjectGuid& guid : targets) + for (ObjectGuid const& guid : targets) { Unit* unit = botAI->GetUnit(guid); if (!unit || !unit->IsAlive()) @@ -2651,7 +2655,7 @@ bool MimironCheatAction::Execute(Event event) return true; } -bool VezaxCheatAction::Execute(Event event) +bool VezaxCheatAction::Execute(Event /*event*/) { // Restore bot's mana to full uint32 maxMana = bot->GetMaxPower(POWER_MANA); @@ -2663,7 +2667,7 @@ bool VezaxCheatAction::Execute(Event event) return true; } -bool VezaxShadowCrashAction::Execute(Event event) +bool VezaxShadowCrashAction::Execute(Event /*event*/) { // Find General Vezax boss Unit* boss = AI_VALUE2(Unit*, "find target", "general vezax"); @@ -2707,7 +2711,7 @@ bool VezaxShadowCrashAction::Execute(Event event) true); } -bool VezaxMarkOfTheFacelessAction::Execute(Event event) +bool VezaxMarkOfTheFacelessAction::Execute(Event /*event*/) { return MoveTo(bot->GetMapId(), ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT.GetPositionX(), ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT.GetPositionY(), @@ -2715,7 +2719,7 @@ bool VezaxMarkOfTheFacelessAction::Execute(Event event) MovementPriority::MOVEMENT_FORCED, true, false); } -bool YoggSaronOminousCloudCheatAction::Execute(Event event) +bool YoggSaronOminousCloudCheatAction::Execute(Event /*event*/) { YoggSaronTrigger yoggSaronTrigger(botAI); @@ -2735,14 +2739,14 @@ bool YoggSaronOminousCloudCheatAction::Execute(Event event) return true; } -bool YoggSaronGuardianPositioningAction::Execute(Event event) +bool YoggSaronGuardianPositioningAction::Execute(Event /*event*/) { return MoveTo(bot->GetMapId(), ULDUAR_YOGG_SARON_MIDDLE.GetPositionX(), ULDUAR_YOGG_SARON_MIDDLE.GetPositionY(), ULDUAR_YOGG_SARON_MIDDLE.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_FORCED, true, false); } -bool YoggSaronSanityAction::Execute(Event event) +bool YoggSaronSanityAction::Execute(Event /*event*/) { Creature* sanityWell = bot->FindNearestCreature(NPC_SANITY_WELL, 200.0f); @@ -2751,7 +2755,7 @@ bool YoggSaronSanityAction::Execute(Event event) true, false); } -bool YoggSaronMarkTargetAction::Execute(Event event) +bool YoggSaronMarkTargetAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) @@ -2820,9 +2824,9 @@ bool YoggSaronMarkTargetAction::Execute(Event event) GuidVector targets = AI_VALUE(GuidVector, "nearest npcs"); - int lowestHealth = std::numeric_limits::max(); + uint32 lowestHealth = std::numeric_limits::max(); Unit* lowestHealthUnit = nullptr; - for (const ObjectGuid& guid : targets) + for (ObjectGuid const& guid : targets) { Unit* unit = botAI->GetUnit(guid); if (!unit || !unit->IsAlive()) @@ -2880,7 +2884,7 @@ bool YoggSaronMarkTargetAction::Execute(Event event) return false; } -bool YoggSaronBrainLinkAction::Execute(Event event) +bool YoggSaronBrainLinkAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) @@ -2900,7 +2904,7 @@ bool YoggSaronBrainLinkAction::Execute(Event event) return false; } -bool YoggSaronMoveToEnterPortalAction::Execute(Event event) +bool YoggSaronMoveToEnterPortalAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) @@ -2917,7 +2921,7 @@ bool YoggSaronMoveToEnterPortalAction::Execute(Event event) } Player* master = botAI->GetMaster(); - if (master && !botAI->IsTank(master)) + if (master && !BotRoleService::IsTankStatic(master)) { portalNumber++; brainRoomTeamCount--; @@ -2926,7 +2930,7 @@ bool YoggSaronMoveToEnterPortalAction::Execute(Event event) for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) { Player* member = gref->GetSource(); - if (!member || !member->IsAlive() || botAI->IsTank(member) || botAI->GetMaster()->GetGUID() == member->GetGUID()) + if (!member || !member->IsAlive() || BotRoleService::IsTankStatic(member) || botAI->GetMaster()->GetGUID() == member->GetGUID()) { continue; } @@ -2969,7 +2973,7 @@ bool YoggSaronMoveToEnterPortalAction::Execute(Event event) } } -bool YoggSaronFallFromFloorAction::Execute(Event event) +bool YoggSaronFallFromFloorAction::Execute(Event /*event*/) { std::string rtiMark = AI_VALUE(std::string, "rti"); if (rtiMark == "skull") @@ -3001,7 +3005,7 @@ bool YoggSaronFallFromFloorAction::Execute(Event event) return false; } -bool YoggSaronBossRoomMovementCheatAction::Execute(Event event) +bool YoggSaronBossRoomMovementCheatAction::Execute(Event /*event*/) { FollowMasterStrategy followMasterStrategy(botAI); if (botAI->HasStrategy(followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT)) @@ -3037,7 +3041,7 @@ bool YoggSaronBossRoomMovementCheatAction::Execute(Event event) currentSkullUnit->GetPositionZ(), bot->GetOrientation()); } -bool YoggSaronUsePortalAction::Execute(Event event) +bool YoggSaronUsePortalAction::Execute(Event /*event*/) { Creature* assignedPortal = bot->FindNearestCreature(NPC_DESCEND_INTO_MADNESS, 2.0f, true); if (!assignedPortal) @@ -3054,7 +3058,7 @@ bool YoggSaronUsePortalAction::Execute(Event event) return assignedPortal->HandleSpellClick(bot); } -bool YoggSaronIllusionRoomAction::Execute(Event event) +bool YoggSaronIllusionRoomAction::Execute(Event /*event*/) { YoggSaronTrigger yoggSaronTrigger(botAI); @@ -3176,7 +3180,7 @@ bool YoggSaronIllusionRoomAction::SetBrainRtiTarget(YoggSaronTrigger yoggSaronTr return true; } -bool YoggSaronMoveToExitPortalAction::Execute(Event event) +bool YoggSaronMoveToExitPortalAction::Execute(Event /*event*/) { GameObject* portal = bot->FindNearestGameObject(GO_FLEE_TO_THE_SURFACE_PORTAL, 100.0f); if (!portal) @@ -3207,7 +3211,7 @@ bool YoggSaronMoveToExitPortalAction::Execute(Event event) return true; } -bool YoggSaronLunaticGazeAction::Execute(Event event) +bool YoggSaronLunaticGazeAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "yogg-saron"); if (!boss || !boss->IsAlive()) @@ -3218,7 +3222,7 @@ bool YoggSaronLunaticGazeAction::Execute(Event event) float newAngle = Position::NormalizeOrientation(angle + M_PI); // Add 180 degrees (PI radians) bot->SetFacingTo(newAngle); - if (botAI->IsRangedDps(bot)) + if (BotRoleService::IsRangedDpsStatic(bot)) { if (AI_VALUE(std::string, "rti") != "cross") { @@ -3229,9 +3233,9 @@ bool YoggSaronLunaticGazeAction::Execute(Event event) return true; } -bool YoggSaronPhase3PositioningAction::Execute(Event event) +bool YoggSaronPhase3PositioningAction::Execute(Event /*event*/) { - if (botAI->IsRanged(bot)) + if (BotRoleService::IsRangedStatic(bot)) { if (botAI->HasCheat(BotCheatMask::raid)) { @@ -3249,7 +3253,7 @@ bool YoggSaronPhase3PositioningAction::Execute(Event event) } } - if (botAI->IsMelee(bot) && !botAI->IsTank(bot)) + if (BotRoleService::IsMeleeStatic(bot) && !BotRoleService::IsTankStatic(bot)) { if (botAI->HasCheat(BotCheatMask::raid)) { @@ -3266,7 +3270,7 @@ bool YoggSaronPhase3PositioningAction::Execute(Event event) } } - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) { if (bot->GetDistance(ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT) > 30.0f) { diff --git a/src/Ai/Raid/Ulduar/Multiplier/RaidUlduarMultipliers.cpp b/src/Ai/Raid/Ulduar/Multiplier/RaidUlduarMultipliers.cpp index 16df9c9af2..e58dfd3c84 100644 --- a/src/Ai/Raid/Ulduar/Multiplier/RaidUlduarMultipliers.cpp +++ b/src/Ai/Raid/Ulduar/Multiplier/RaidUlduarMultipliers.cpp @@ -20,7 +20,7 @@ #include "UseMeetingStoneAction.h" #include "WarriorActions.h" -float FlameLeviathanMultiplier::GetValue(Action* action) +float FlameLeviathanMultiplier::GetValue(Action* /*action*/) { // if (dynamic_cast(action)) // return 0.0f; diff --git a/src/Ai/Raid/Ulduar/RaidUlduarBossHelper.cpp b/src/Ai/Raid/Ulduar/RaidUlduarBossHelper.cpp index 72333a079b..68458dbbfb 100644 --- a/src/Ai/Raid/Ulduar/RaidUlduarBossHelper.cpp +++ b/src/Ai/Raid/Ulduar/RaidUlduarBossHelper.cpp @@ -1,4 +1,5 @@ #include "ChatHelper.h" +#include "BotRoleService.h" #include "RaidUlduarBossHelper.h" #include "ObjectAccessor.h" #include "GameObject.h" @@ -13,7 +14,7 @@ std::unordered_map RazorscaleBossHelper::_harpoonCooldowns; // Prevent role assignment spam std::unordered_map RazorscaleBossHelper::_lastRoleSwapTime; -const std::time_t RazorscaleBossHelper::_roleSwapCooldown; +std::time_t const RazorscaleBossHelper::_roleSwapCooldown; bool RazorscaleBossHelper::UpdateBossAI() { @@ -104,9 +105,9 @@ GameObject* RazorscaleBossHelper::FindNearestHarpoon(float x, float y, float z) return nearestHarpoon; } -const std::vector& RazorscaleBossHelper::GetHarpoonData() +std::vector const& RazorscaleBossHelper::GetHarpoonData() { - static const std::vector harpoonData = + static std::vector const harpoonData = { { GO_RAZORSCALE_HARPOON_1, SPELL_CHAIN_1 }, { GO_RAZORSCALE_HARPOON_2, SPELL_CHAIN_2 }, @@ -173,7 +174,7 @@ void RazorscaleBossHelper::AssignRolesBasedOnHealth() for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (!member || !botAI->IsTank(member, true) || !member->IsAlive()) + if (!member || !BotRoleService::IsTankStatic(member, true) || !member->IsAlive()) continue; Aura* fuseArmor = member->GetAura(SPELL_FUSEARMOR); @@ -204,7 +205,7 @@ void RazorscaleBossHelper::AssignRolesBasedOnHealth() for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (member && botAI->IsMainTank(member)) + if (member && BotRoleService::IsMainTankStatic(member)) group->SetGroupMemberFlag(member->GetGUID(), false, MEMBER_FLAG_MAINTANK); } @@ -212,8 +213,8 @@ void RazorscaleBossHelper::AssignRolesBasedOnHealth() group->SetGroupMemberFlag(newMainTank->GetGUID(), true, MEMBER_FLAG_MAINTANK); // Yell a message regardless of whether the new main tank is a bot or a real player - const std::string playerName = newMainTank->GetName(); - const std::string text = playerName + " set as main tank!"; + std::string const playerName = newMainTank->GetName(); + std::string const text = playerName + " set as main tank!"; bot->Yell(text, LANG_UNIVERSAL); ObjectGuid botGuid = bot->GetGUID(); diff --git a/src/Ai/Raid/Ulduar/RaidUlduarBossHelper.h b/src/Ai/Raid/Ulduar/RaidUlduarBossHelper.h index 592fbc80ea..41eb1fdd9c 100644 --- a/src/Ai/Raid/Ulduar/RaidUlduarBossHelper.h +++ b/src/Ai/Raid/Ulduar/RaidUlduarBossHelper.h @@ -85,7 +85,7 @@ class RazorscaleBossHelper : public AiObject static void SetHarpoonOnCooldown(GameObject* harpoonGO); GameObject* FindNearestHarpoon(float x, float y, float z) const; - static const std::vector& GetHarpoonData(); + static std::vector const& GetHarpoonData(); void AssignRolesBasedOnHealth(); bool AreRolesAssigned() const; @@ -98,7 +98,7 @@ class RazorscaleBossHelper : public AiObject static std::unordered_map _lastRoleSwapTime; // The cooldown that applies to every bot - static const std::time_t _roleSwapCooldown = 10; + static std::time_t const _roleSwapCooldown = 10; static std::unordered_map _harpoonCooldowns; }; diff --git a/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.cpp b/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.cpp index f883917ed0..f02a6e256d 100644 --- a/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.cpp +++ b/src/Ai/Raid/Ulduar/Trigger/RaidUlduarTriggers.cpp @@ -1,4 +1,6 @@ #include "RaidUlduarTriggers.h" +#include "BotSpellService.h" +#include "BotRoleService.h" #include "EventMap.h" #include "GameObject.h" @@ -15,11 +17,11 @@ #include #include -const std::vector availableVehicles = {NPC_VEHICLE_CHOPPER, NPC_SALVAGED_DEMOLISHER, +std::vector const availableVehicles = {NPC_VEHICLE_CHOPPER, NPC_SALVAGED_DEMOLISHER, NPC_SALVAGED_DEMOLISHER_TURRET, NPC_SALVAGED_SIEGE_ENGINE, NPC_SALVAGED_SIEGE_ENGINE_TURRET}; -const std::vector illusionMobs = +std::vector const illusionMobs = { NPC_INFLUENCE_TENTACLE, NPC_RUBY_CONSORT, @@ -193,7 +195,7 @@ bool RazorscaleGroundedTrigger::IsActive() bool RazorscaleHarpoonAvailableTrigger::IsActive() { // Get harpoon data from the helper - const std::vector& harpoonData = RazorscaleBossHelper::GetHarpoonData(); + std::vector const& harpoonData = RazorscaleBossHelper::GetHarpoonData(); // Get the boss entity Unit* boss = AI_VALUE2(Unit*, "find target", "razorscale"); @@ -243,7 +245,7 @@ bool RazorscaleFuseArmorTrigger::IsActive() } // Only proceed if this bot can actually tank - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) return false; Group* group = bot->GetGroup(); @@ -254,7 +256,7 @@ bool RazorscaleFuseArmorTrigger::IsActive() for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) { Player* member = gref->GetSource(); - if (!member || !botAI->IsMainTank(member)) + if (!member || !BotRoleService::IsMainTankStatic(member)) continue; Aura* fuseArmor = member->GetAura(RazorscaleBossHelper::SPELL_FUSEARMOR); @@ -283,7 +285,7 @@ bool IronAssemblyLightningTendrilsTrigger::IsActive() bool IronAssemblyOverloadTrigger::IsActive() { // Check if bot is tank - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) return false; // Check boss and it is alive @@ -312,7 +314,7 @@ bool IronAssemblyRuneOfPowerTrigger::IsActive() if (target->GetVictim() != bot) return false; - return botAI->IsTank(bot); + return BotRoleService::IsTankStatic(bot); } bool KologarnMarkDpsTargetTrigger::IsActive() @@ -323,7 +325,7 @@ bool KologarnMarkDpsTargetTrigger::IsActive() return false; // Only tank bot can mark target - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) return false; // Get current raid dps target @@ -350,7 +352,6 @@ bool KologarnMarkDpsTargetTrigger::IsActive() if (!target) continue; - uint32 creatureId = target->GetEntry(); if (target->GetEntry() == NPC_RUBBLE && target->IsAlive()) { return true; // Found a rubble to mark @@ -473,7 +474,7 @@ bool KologarnAttackDpsTargetTrigger::IsActive() ObjectGuid skullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex); ObjectGuid crossTarget = group->GetTargetIcon(RtiTargetValue::crossIndex); - if (crossTarget && (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0))) + if (crossTarget && (BotRoleService::IsMainTankStatic(bot) || BotRoleService::IsAssistTankOfIndexStatic(bot, 0))) { return currentTarget->GetGUID() != crossTarget; } @@ -493,7 +494,7 @@ bool KologarnRtiTargetTrigger::IsActive() std::string rtiMark = AI_VALUE(std::string, "rti"); - if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0)) + if (BotRoleService::IsMainTankStatic(bot) || BotRoleService::IsAssistTankOfIndexStatic(bot, 0)) return rtiMark != "cross"; return rtiMark != "skull"; @@ -535,8 +536,8 @@ bool HodirBitingColdTrigger::IsActive() if (!master || !master->IsAlive()) return false; - return botAI->GetAura("biting cold", bot, false, false, 2) && - !botAI->GetAura("biting cold", master, false, false, 2); + return botAI->GetServices().GetSpellService().GetAura("biting cold", bot, false, false, 2) && + !botAI->GetServices().GetSpellService().GetAura("biting cold", master, false, false, 2); } // Snowpacked Icicle Target @@ -591,7 +592,7 @@ bool FreyaMarkDpsTargetTrigger::IsActive() return false; // Only tank bot can mark target - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) return false; // Get current raid dps target @@ -724,7 +725,7 @@ bool FreyaMoveToHealingSporeTrigger::IsActive() if (!boss || !boss->IsAlive()) return false; - if (!botAI->IsRanged(bot)) + if (!BotRoleService::IsRangedStatic(bot)) return false; Unit* conservatory = AI_VALUE2(Unit*, "find target", "ancient conservator"); @@ -736,7 +737,7 @@ bool FreyaMoveToHealingSporeTrigger::IsActive() bool foundSpore = false; // Iterate through all targets to find healthy spores - for (const ObjectGuid& guid : targets) + for (ObjectGuid const& guid : targets) { Unit* unit = botAI->GetUnit(guid); if (!unit || !unit->IsAlive()) @@ -786,7 +787,7 @@ bool ThorimMarkDpsTargetTrigger::IsActive() if (!group) return false; - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) { ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex); Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget); @@ -825,13 +826,13 @@ bool ThorimMarkDpsTargetTrigger::IsActive() return false; } - else if (botAI->IsAssistTankOfIndex(bot, 0)) + else if (BotRoleService::IsAssistTankOfIndexStatic(bot, 0)) { Player* mainTank = nullptr; for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) { Player* member = gref->GetSource(); - if (member && botAI->IsMainTank(member)) + if (member && BotRoleService::IsMainTankStatic(member)) { mainTank = member; break; @@ -854,8 +855,6 @@ bool ThorimMarkDpsTargetTrigger::IsActive() Unit* runicColossus = AI_VALUE2(Unit*, "find target", "runic colossus"); Unit* ancientRuneGiant = AI_VALUE2(Unit*, "find target", "ancient rune giant"); - Unit* ironHonorGuard = AI_VALUE2(Unit*, "find target", "iron ring guard"); - Unit* ironRingGuard = AI_VALUE2(Unit*, "find target", "iron honor guard"); if (acolyte && acolyte->IsAlive() && (!currentCrossUnit || currentCrossUnit->GetEntry() != acolyte->GetEntry())) return true; @@ -909,21 +908,21 @@ bool ThorimGauntletPositioningTrigger::IsActive() if (!member) continue; - if (requiredDpsQuantity > 0 && botAI->IsDps(member)) + if (requiredDpsQuantity > 0 && BotRoleService::IsDpsStatic(member)) { requiredDpsQuantity--; if (bot->GetGUID() == member->GetGUID()) break; } - if (requiredAssistTankQuantity > 0 && botAI->IsAssistTankOfIndex(member, 0)) + if (requiredAssistTankQuantity > 0 && BotRoleService::IsAssistTankOfIndexStatic(member, 0)) { requiredAssistTankQuantity--; if (bot->GetGUID() == member->GetGUID()) break; } - if (requiredHealerQuantity > 0 && botAI->IsHeal(member)) + if (requiredHealerQuantity > 0 && BotRoleService::IsHealStatic(member)) { requiredHealerQuantity--; if (bot->GetGUID() == member->GetGUID()) @@ -1029,21 +1028,21 @@ bool ThorimArenaPositioningTrigger::IsActive() if (!member) continue; - if (requiredDpsQuantity > 0 && botAI->IsDps(member)) + if (requiredDpsQuantity > 0 && BotRoleService::IsDpsStatic(member)) { requiredDpsQuantity--; if (bot->GetGUID() == member->GetGUID()) return false; } - if (requiredAssistTankQuantity > 0 && botAI->IsAssistTankOfIndex(member, 0)) + if (requiredAssistTankQuantity > 0 && BotRoleService::IsAssistTankOfIndexStatic(member, 0)) { requiredAssistTankQuantity--; if (bot->GetGUID() == member->GetGUID()) return false; } - if (requiredHealerQuantity > 0 && botAI->IsHeal(member)) + if (requiredHealerQuantity > 0 && BotRoleService::IsHealStatic(member)) { requiredHealerQuantity--; if (bot->GetGUID() == member->GetGUID()) @@ -1089,12 +1088,12 @@ bool ThorimFallFromFloorTrigger::IsActive() bool ThorimPhase2PositioningTrigger::IsActive() { - if (!botAI->IsRanged(bot) && !botAI->IsMainTank(bot)) + if (!BotRoleService::IsRangedStatic(bot) && !BotRoleService::IsMainTankStatic(bot)) return false; Unit* boss = AI_VALUE2(Unit*, "find target", "thorim"); if (!boss || !boss->IsInWorld() || boss->IsDuringRemoveFromWorld()) - return false; + return false; if (!boss->IsAlive()) return false; @@ -1105,7 +1104,7 @@ bool ThorimPhase2PositioningTrigger::IsActive() if (boss->GetPositionZ() > ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD) return false; - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) { if (bot->GetDistance(ULDUAR_THORIM_PHASE2_TANK_SPOT) > 1.0f && boss->GetVictim() == bot) return true; @@ -1124,7 +1123,7 @@ bool ThorimPhase2PositioningTrigger::IsActive() if (!member) continue; - if (botAI->IsRanged(member)) + if (BotRoleService::IsRangedStatic(member)) { if (bot->GetGUID() == member->GetGUID()) break; @@ -1163,7 +1162,7 @@ bool MimironShockBlastTrigger::IsActive() return false; } - if (botAI->IsMelee(bot)) + if (BotRoleService::IsMeleeStatic(bot)) { return true; } @@ -1175,14 +1174,12 @@ bool MimironShockBlastTrigger::IsActive() bool MimironPhase1PositioningTrigger::IsActive() { - if (!botAI->IsRanged(bot)) + if (!BotRoleService::IsRangedStatic(bot)) { return false; } Unit* leviathanMkII = nullptr; - Unit* vx001 = nullptr; - Unit* aerialCommandUnit = nullptr; GuidVector targets = AI_VALUE(GuidVector, "possible targets"); Unit* target = nullptr; @@ -1281,12 +1278,12 @@ bool MimironRapidBurstTrigger::IsActive() return false; } - if (botAI->IsMainTank(bot) && leviathanMkII && leviathanMkII->IsAlive() && leviathanMkII->GetVictim() != bot) + if (BotRoleService::IsMainTankStatic(bot) && leviathanMkII && leviathanMkII->IsAlive() && leviathanMkII->GetVictim() != bot) { return false; } - if (botAI->IsMelee(bot) && !botAI->IsMainTank(bot) && leviathanMkII && aerialCommandUnit) + if (BotRoleService::IsMeleeStatic(bot) && !BotRoleService::IsMainTankStatic(bot) && leviathanMkII && aerialCommandUnit) { return false; } @@ -1313,7 +1310,7 @@ bool MimironRapidBurstTrigger::IsActive() if (bot->GetGUID() == member->GetGUID()) { - if (botAI->IsRanged(bot)) + if (BotRoleService::IsRangedStatic(bot)) { switch (memberSpotNumber) { @@ -1332,7 +1329,7 @@ bool MimironRapidBurstTrigger::IsActive() } else { - if (botAI->IsMainTank(bot) && leviathanMkII) + if (BotRoleService::IsMainTankStatic(bot) && leviathanMkII) { memberPosition = ULDUAR_MIMIRON_PHASE4_TANK_SPOT; } @@ -1369,7 +1366,7 @@ bool MimironRapidBurstTrigger::IsActive() float nearestRocketStrikeDistance = std::numeric_limits::max(); bool rocketStrikeDetected = false; - for (const ObjectGuid& guid : npcs) + for (ObjectGuid const& guid : npcs) { Unit* unit = botAI->GetUnit(guid); if (!unit) @@ -1433,12 +1430,12 @@ bool MimironAerialCommandUnitTrigger::IsActive() return false; } - if (!botAI->IsRanged(bot) && !botAI->IsMainTank(bot) && !botAI->IsAssistTankOfIndex(bot, 0)) + if (!BotRoleService::IsRangedStatic(bot) && !BotRoleService::IsMainTankStatic(bot) && !BotRoleService::IsAssistTankOfIndexStatic(bot, 0)) { return false; } - if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0)) + if (BotRoleService::IsMainTankStatic(bot) || BotRoleService::IsAssistTankOfIndexStatic(bot, 0)) { Group* group = bot->GetGroup(); if (!group) @@ -1533,7 +1530,7 @@ bool MimironPhase4MarkDpsTrigger::IsActive() return false; } - if (botAI->IsMainTank(bot)) + if (BotRoleService::IsMainTankStatic(bot)) { Unit* highestHealthUnit = nullptr; uint32 highestHealth = 0; @@ -1576,13 +1573,13 @@ bool MimironCheatTrigger::IsActive() return false; } - if (!botAI->IsMainTank(bot)) + if (!BotRoleService::IsMainTankStatic(bot)) { return false; } GuidVector targets = AI_VALUE(GuidVector, "nearest npcs"); - for (const ObjectGuid& guid : targets) + for (ObjectGuid const& guid : targets) { Unit* unit = botAI->GetUnit(guid); if (!unit || !unit->IsAlive()) @@ -1634,7 +1631,7 @@ bool VezaxShadowCrashTrigger::IsActive() return false; } - return botAI->HasAura(SPELL_SHADOW_CRASH, bot); + return botAI->GetServices().GetSpellService().HasAura(SPELL_SHADOW_CRASH, bot); } bool VezaxMarkOfTheFacelessTrigger::IsActive() @@ -1647,7 +1644,7 @@ bool VezaxMarkOfTheFacelessTrigger::IsActive() return false; } - if (!botAI->HasAura(SPELL_MARK_OF_THE_FACELESS, bot)) + if (!botAI->GetServices().GetSpellService().HasAura(SPELL_MARK_OF_THE_FACELESS, bot)) { return false; } @@ -1750,7 +1747,7 @@ bool YoggSaronTrigger::IsInChamberOfTheAspectsIllusion() bool YoggSaronTrigger::IsMasterIsInIllusionGroup() { Player* master = botAI->GetMaster(); - return master && !botAI->IsTank(master); + return master && !BotRoleService::IsTankStatic(master); } bool YoggSaronTrigger::IsMasterIsInBrainRoom() @@ -1796,7 +1793,7 @@ Unit* YoggSaronTrigger::GetIllusionRoomRtiTarget() return nullptr; } - uint8 rtiIndex = RtiTargetValue::GetRtiIndex(AI_VALUE(std::string, "rti")); + int32 rtiIndex = RtiTargetValue::GetRtiIndex(AI_VALUE(std::string, "rti")); if (rtiIndex == -1) { return nullptr; // Invalid RTI mark @@ -1836,7 +1833,7 @@ Unit* YoggSaronTrigger::GetNextIllusionRoomRtiTarget() if (botAI->HasCheat(BotCheatMask::raid)) { - for (const ObjectGuid& guid : targets) + for (ObjectGuid const& guid : targets) { Unit* unit = botAI->GetUnit(guid); if (unit && unit->IsAlive() && unit->GetEntry() == NPC_LAUGHING_SKULL) @@ -1851,7 +1848,7 @@ Unit* YoggSaronTrigger::GetNextIllusionRoomRtiTarget() for (const uint32& creatureId : illusionMobs) { - for (const ObjectGuid& guid : targets) + for (ObjectGuid const& guid : targets) { Unit* unit = botAI->GetUnit(guid); if (unit && unit->IsAlive() && unit->GetEntry() == creatureId) @@ -1896,7 +1893,7 @@ bool YoggSaronOminousCloudCheatTrigger::IsActive() return false; } - if (!botAI->IsBotMainTank(bot)) + if (!BotRoleService::IsBotMainTankStatic(bot)) { return false; } @@ -1918,7 +1915,7 @@ bool YoggSaronGuardianPositioningTrigger::IsActive() return false; } - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) { return false; } @@ -1926,7 +1923,7 @@ bool YoggSaronGuardianPositioningTrigger::IsActive() GuidVector targets = AI_VALUE(GuidVector, "nearest npcs"); bool thereIsAnyGuardian = false; - for (const ObjectGuid& guid : targets) + for (ObjectGuid const& guid : targets) { Unit* unit = botAI->GetUnit(guid); if (!unit || !unit->IsAlive()) @@ -1939,7 +1936,7 @@ bool YoggSaronGuardianPositioningTrigger::IsActive() thereIsAnyGuardian = true; ObjectGuid unitTargetGuid = unit->GetTarget(); Player* targetedPlayer = botAI->GetPlayer(unitTargetGuid); - if (!targetedPlayer || !botAI->IsTank(targetedPlayer)) + if (!targetedPlayer || !BotRoleService::IsTankStatic(targetedPlayer)) { return false; } @@ -1993,7 +1990,7 @@ bool YoggSaronMarkTargetTrigger::IsActive() return false; } - if (!botAI->IsBotMainTank(bot)) + if (!BotRoleService::IsBotMainTankStatic(bot)) { return false; } @@ -2060,7 +2057,7 @@ bool YoggSaronMarkTargetTrigger::IsActive() } GuidVector targets = AI_VALUE(GuidVector, "nearest npcs"); - for (const ObjectGuid& guid : targets) + for (ObjectGuid const& guid : targets) { Unit* unit = botAI->GetUnit(guid); if (!unit || !unit->IsAlive()) @@ -2136,7 +2133,7 @@ bool YoggSaronMoveToEnterPortalTrigger::IsActive() for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) { Player* member = gref->GetSource(); - if (!member || !member->IsAlive() || botAI->IsTank(member)) + if (!member || !member->IsAlive() || BotRoleService::IsTankStatic(member)) { continue; } @@ -2352,20 +2349,20 @@ bool YoggSaronPhase3PositioningTrigger::IsActive() return false; } - if (botAI->IsRanged(bot) && bot->GetDistance2d(ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionX(), + if (BotRoleService::IsRangedStatic(bot) && bot->GetDistance2d(ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionX(), ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionY()) > 15.0f) { return true; } - if (botAI->IsMelee(bot) && !botAI->IsTank(bot) && + if (BotRoleService::IsMeleeStatic(bot) && !BotRoleService::IsTankStatic(bot) && bot->GetDistance2d(ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionX(), ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionY()) > 15.0f) { return true; } - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) { if (bot->GetDistance(ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT) > 30.0f) { @@ -2375,7 +2372,7 @@ bool YoggSaronPhase3PositioningTrigger::IsActive() GuidVector targets = AI_VALUE(GuidVector, "nearest npcs"); bool thereIsAnyGuardian = false; - for (const ObjectGuid& guid : targets) + for (ObjectGuid const& guid : targets) { Unit* unit = botAI->GetUnit(guid); if (!unit || !unit->IsAlive()) @@ -2388,7 +2385,7 @@ bool YoggSaronPhase3PositioningTrigger::IsActive() thereIsAnyGuardian = true; ObjectGuid unitTargetGuid = unit->GetTarget(); Player* targetedPlayer = botAI->GetPlayer(unitTargetGuid); - if (!targetedPlayer || !botAI->IsTank(targetedPlayer)) + if (!targetedPlayer || !BotRoleService::IsTankStatic(targetedPlayer)) { return false; } diff --git a/src/Ai/Raid/VaultOfArchavon/Action/RaidVoAActions.cpp b/src/Ai/Raid/VaultOfArchavon/Action/RaidVoAActions.cpp index 05d6328e5c..eeb5e82578 100644 --- a/src/Ai/Raid/VaultOfArchavon/Action/RaidVoAActions.cpp +++ b/src/Ai/Raid/VaultOfArchavon/Action/RaidVoAActions.cpp @@ -1,4 +1,5 @@ #include "RaidVoAActions.h" +#include "BotRoleService.h" #include "RaidVoATriggers.h" #include "Define.h" #include "Event.h" @@ -10,7 +11,7 @@ const Position VOA_EMALON_RESTORE_POSITION = Position(-221.8f, -243.8f, 96.8f, 4.7f); -bool EmalonMarkBossAction::Execute(Event event) +bool EmalonMarkBossAction::Execute(Event /*event*/) { Unit* boss = AI_VALUE2(Unit*, "find target", "emalon the storm watcher"); if (!boss || !boss->IsAlive()) @@ -18,7 +19,7 @@ bool EmalonMarkBossAction::Execute(Event event) return false; } - bool isMainTank = botAI->IsMainTank(bot); + bool isMainTank = BotRoleService::IsMainTankStatic(bot); Unit* mainTankUnit = AI_VALUE(Unit*, "main tank"); Player* mainTank = mainTankUnit ? mainTankUnit->ToPlayer() : nullptr; @@ -27,7 +28,7 @@ bool EmalonMarkBossAction::Execute(Event event) // Iterate through the first 3 bot tanks to assign the Skull marker for (int i = 0; i < 3; ++i) { - if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank + if (BotRoleService::IsAssistTankOfIndexStatic(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank { Group* group = bot->GetGroup(); if (group && boss) @@ -72,7 +73,7 @@ bool EmalonMarkBossAction::isUseful() return emalonMarkBossTrigger.IsActive(); } -bool EmalonLightingNovaAction::Execute(Event event) +bool EmalonLightingNovaAction::Execute(Event /*event*/) { const float radius = 25.0f; // 20 yards + 5 yard for safety for 10 man. For 25man there is no maximum range but 25 yards should be ok @@ -96,7 +97,7 @@ bool EmalonLightingNovaAction::isUseful() return emalonLightingNovaTrigger.IsActive(); } -bool EmalonOverchargeAction::Execute(Event event) +bool EmalonOverchargeAction::Execute(Event /*event*/) { // Check if there is any overcharged minion Unit* minion = nullptr; @@ -119,7 +120,7 @@ bool EmalonOverchargeAction::Execute(Event event) return false; } - bool isMainTank = botAI->IsMainTank(bot); + bool isMainTank = BotRoleService::IsMainTankStatic(bot); Unit* mainTankUnit = AI_VALUE(Unit*, "main tank"); Player* mainTank = mainTankUnit ? mainTankUnit->ToPlayer() : nullptr; @@ -128,7 +129,7 @@ bool EmalonOverchargeAction::Execute(Event event) // Iterate through the first 3 bot tanks to assign the Skull marker for (int i = 0; i < 3; ++i) { - if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank + if (BotRoleService::IsAssistTankOfIndexStatic(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank { Group* group = bot->GetGroup(); if (group && minion) @@ -173,7 +174,7 @@ bool EmalonOverchargeAction::isUseful() return emalonOverchargeTrigger.IsActive(); } -bool EmalonFallFromFloorAction::Execute(Event event) +bool EmalonFallFromFloorAction::Execute(Event /*event*/) { return bot->TeleportTo(bot->GetMapId(), VOA_EMALON_RESTORE_POSITION.GetPositionX(), VOA_EMALON_RESTORE_POSITION.GetPositionY(), VOA_EMALON_RESTORE_POSITION.GetPositionZ(), diff --git a/src/Ai/Raid/VaultOfArchavon/Trigger/RaidVoATriggers.cpp b/src/Ai/Raid/VaultOfArchavon/Trigger/RaidVoATriggers.cpp index 03a8b9b35d..810d1c9a8d 100644 --- a/src/Ai/Raid/VaultOfArchavon/Trigger/RaidVoATriggers.cpp +++ b/src/Ai/Raid/VaultOfArchavon/Trigger/RaidVoATriggers.cpp @@ -1,4 +1,5 @@ #include "RaidVoATriggers.h" +#include "BotRoleService.h" #include "EventMap.h" #include "Object.h" @@ -8,7 +9,7 @@ bool EmalonMarkBossTrigger::IsActive() { // Only tank bot can mark target - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) { return false; } @@ -63,7 +64,7 @@ bool EmalonLightingNovaTrigger::IsActive() } // Tank dont need to move - if (botAI->IsTank(bot)) + if (BotRoleService::IsTankStatic(bot)) { return false; } @@ -78,7 +79,7 @@ bool EmalonLightingNovaTrigger::IsActive() bool EmalonOverchargeTrigger::IsActive() { // Only tank bot can mark target - if (!botAI->IsTank(bot)) + if (!BotRoleService::IsTankStatic(bot)) { return false; } diff --git a/src/Ai/World/Rpg/Action/NewRpgAction.cpp b/src/Ai/World/Rpg/Action/NewRpgAction.cpp index 01595edf79..696ee4ce57 100644 --- a/src/Ai/World/Rpg/Action/NewRpgAction.cpp +++ b/src/Ai/World/Rpg/Action/NewRpgAction.cpp @@ -1,4 +1,5 @@ #include "NewRpgAction.h" +#include "BotSpellService.h" #include #include @@ -50,7 +51,7 @@ bool StartRpgDoQuestAction::Execute(Event event) std::string const text = event.getParam(); PlayerbotChatHandler ch(owner); uint32 questId = ch.extractQuestId(text); - const Quest* quest = sObjectMgr->GetQuestTemplate(questId); + Quest const* quest = sObjectMgr->GetQuestTemplate(questId); if (quest) { botAI->rpgInfo.ChangeToDoQuest(questId, quest); @@ -61,7 +62,7 @@ bool StartRpgDoQuestAction::Execute(Event event) return false; } -bool NewRpgStatusUpdateAction::Execute(Event event) +bool NewRpgStatusUpdateAction::Execute(Event /*event*/) { NewRpgInfo& info = botAI->rpgInfo; switch (info.status) @@ -150,7 +151,7 @@ bool NewRpgStatusUpdateAction::Execute(Event event) return false; } -bool NewRpgGoGrindAction::Execute(Event event) +bool NewRpgGoGrindAction::Execute(Event /*event*/) { if (SearchQuestGiverAndAcceptOrReward()) return true; @@ -158,7 +159,7 @@ bool NewRpgGoGrindAction::Execute(Event event) return MoveFarTo(botAI->rpgInfo.go_grind.pos); } -bool NewRpgGoCampAction::Execute(Event event) +bool NewRpgGoCampAction::Execute(Event /*event*/) { if (SearchQuestGiverAndAcceptOrReward()) return true; @@ -166,7 +167,7 @@ bool NewRpgGoCampAction::Execute(Event event) return MoveFarTo(botAI->rpgInfo.go_camp.pos); } -bool NewRpgWanderRandomAction::Execute(Event event) +bool NewRpgWanderRandomAction::Execute(Event /*event*/) { if (SearchQuestGiverAndAcceptOrReward()) return true; @@ -174,7 +175,7 @@ bool NewRpgWanderRandomAction::Execute(Event event) return MoveRandomNear(); } -bool NewRpgWanderNpcAction::Execute(Event event) +bool NewRpgWanderNpcAction::Execute(Event /*event*/) { NewRpgInfo& info = botAI->rpgInfo; if (!info.wander_npc.npcOrGo) @@ -216,14 +217,12 @@ bool NewRpgWanderNpcAction::Execute(Event event) return true; } -bool NewRpgDoQuestAction::Execute(Event event) +bool NewRpgDoQuestAction::Execute(Event /*event*/) { if (SearchQuestGiverAndAcceptOrReward()) return true; - NewRpgInfo& info = botAI->rpgInfo; uint32 questId = RPG_INFO(quest, questId); - const Quest* quest = RPG_INFO(quest, quest); uint8 questStatus = bot->GetQuestStatus(questId); switch (questStatus) { @@ -247,7 +246,7 @@ bool NewRpgDoQuestAction::DoIncompleteQuest() int32 currentObjective = botAI->rpgInfo.do_quest.objectiveIdx; // check if the objective has completed Quest const* quest = sObjectMgr->GetQuestTemplate(questId); - const QuestStatusData& q_status = bot->getQuestStatusMap().at(questId); + QuestStatusData const& q_status = bot->getQuestStatusMap().at(questId); bool completed = true; if (currentObjective < QUEST_OBJECTIVES_COUNT) { @@ -315,7 +314,7 @@ bool NewRpgDoQuestAction::DoIncompleteQuest() int32 currentObjective = botAI->rpgInfo.do_quest.objectiveIdx; // check if the objective has progression Quest const* quest = sObjectMgr->GetQuestTemplate(questId); - const QuestStatusData& q_status = bot->getQuestStatusMap().at(questId); + QuestStatusData const& q_status = bot->getQuestStatusMap().at(questId); if (currentObjective < QUEST_OBJECTIVES_COUNT) { if (q_status.CreatureOrGOCount[currentObjective] != 0 && quest->RequiredNpcOrGoCount[currentObjective]) @@ -351,7 +350,7 @@ bool NewRpgDoQuestAction::DoIncompleteQuest() bool NewRpgDoQuestAction::DoCompletedQuest() { uint32 questId = RPG_INFO(quest, questId); - const Quest* quest = RPG_INFO(quest, quest); + Quest const* quest = RPG_INFO(quest, quest); if (RPG_INFO(quest, objectiveIdx) != -1) { @@ -408,7 +407,7 @@ bool NewRpgDoQuestAction::DoCompletedQuest() return false; } -bool NewRpgTravelFlightAction::Execute(Event event) +bool NewRpgTravelFlightAction::Execute(Event /*event*/) { if (bot->IsInFlight()) { @@ -421,14 +420,13 @@ bool NewRpgTravelFlightAction::Execute(Event event) botAI->rpgInfo.ChangeToIdle(); return true; } - const TaxiNodesEntry* entry = sTaxiNodesStore.LookupEntry(botAI->rpgInfo.flight.toNode); if (bot->GetDistance(flightMaster) > INTERACTION_DISTANCE) { return MoveFarTo(flightMaster); } std::vector nodes = {botAI->rpgInfo.flight.fromNode, botAI->rpgInfo.flight.toNode}; - botAI->RemoveShapeshift(); + botAI->GetServices().GetSpellService().RemoveShapeshift(); if (bot->IsMounted()) { bot->Dismount(); diff --git a/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp b/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp index 4898d7e36c..d2d046c427 100644 --- a/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp +++ b/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp @@ -1,6 +1,7 @@ #include "NewRpgBaseAction.h" #include "BroadcastHelper.h" +#include "FlightMasterCache.h" #include "ChatHelper.h" #include "Creature.h" #include "FlightMasterCache.h" @@ -29,6 +30,7 @@ #include "StatsWeightCalculator.h" #include "Timer.h" #include "TravelMgr.h" +#include "BotChatService.h" bool NewRpgBaseAction::MoveFarTo(WorldPosition dest) { @@ -60,7 +62,7 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest) // Unfortunately we've been stuck here for over 5 mins, fallback to teleporting directly to the destination botAI->rpgInfo.stuckTs = getMSTime(); botAI->rpgInfo.stuckAttempts = 0; - const AreaTableEntry* entry = sAreaTableStore.LookupEntry(bot->GetZoneId()); + AreaTableEntry const* entry = sAreaTableStore.LookupEntry(bot->GetZoneId()); std::string zone_name = PlayerbotAI::GetLocalizedAreaName(entry); LOG_DEBUG( "playerbots", @@ -89,7 +91,7 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest) while (attempt--) { float angle = bot->GetAngle(&dest); - float delta = urand(1, 100) <= 75 ? (rand_norm() - 0.5) * M_PI * 0.5 : (rand_norm() - 0.5) * M_PI * 2; + float delta = urand(1, 100) <= 75 ? (rand_norm() - 0.5f) * M_PI * 0.5f : (rand_norm() - 0.5f) * M_PI * 2; angle += delta; float dis = rand_norm() * pathFinderDis; float dx = x + cos(angle) * dis; @@ -135,10 +137,10 @@ bool NewRpgBaseAction::MoveWorldObjectTo(ObjectGuid guid, float distance) float angle = 0.f; if (!object->ToUnit() || !object->ToUnit()->isMoving()) - angle = object->GetAngle(bot) + (M_PI * irand(-25, 25) / 100.0); // Closest 45 degrees towards the target + angle = object->GetAngle(bot) + (M_PI * irand(-25, 25) / 100.0f); // Closest 45 degrees towards the target else angle = object->GetOrientation() + - (M_PI * irand(-25, 25) / 100.0); // 45 degrees infront of target (leading it's movement) + (M_PI * irand(-25, 25) / 100.0f); // 45 degrees infront of target (leading it's movement) float rnd = rand_norm(); x += cos(angle) * distance * rnd; @@ -221,24 +223,24 @@ bool NewRpgBaseAction::InteractWithNpcOrGameObjectForQuest(ObjectGuid guid) // } bot->PrepareQuestMenu(guid); - const QuestMenu& menu = bot->PlayerTalkClass->GetQuestMenu(); + QuestMenu const& menu = bot->PlayerTalkClass->GetQuestMenu(); if (menu.Empty()) return true; for (uint8 idx = 0; idx < menu.GetMenuItemCount(); idx++) { - const QuestMenuItem& item = menu.GetItem(idx); - const Quest* quest = sObjectMgr->GetQuestTemplate(item.QuestId); + QuestMenuItem const& item = menu.GetItem(idx); + Quest const* quest = sObjectMgr->GetQuestTemplate(item.QuestId); if (!quest) continue; - const QuestStatus& status = bot->GetQuestStatus(item.QuestId); + QuestStatus const& status = bot->GetQuestStatus(item.QuestId); if (status == QUEST_STATUS_NONE && bot->CanTakeQuest(quest, false) && bot->CanAddQuest(quest, false) && IsQuestWorthDoing(quest) && IsQuestCapableDoing(quest)) { AcceptQuest(quest, guid); if (botAI->GetMaster()) - botAI->TellMasterNoFacing("Quest accepted " + ChatHelper::FormatQuest(quest)); + botAI->GetServices().GetChatService().TellMasterNoFacing("Quest accepted " + ChatHelper::FormatQuest(quest)); BroadcastHelper::BroadcastQuestAccepted(botAI, bot, quest); botAI->rpgStatistic.questAccepted++; LOG_DEBUG("playerbots", "[New RPG] {} accept quest {}", bot->GetName(), quest->GetQuestId()); @@ -247,7 +249,7 @@ bool NewRpgBaseAction::InteractWithNpcOrGameObjectForQuest(ObjectGuid guid) { TurnInQuest(quest, guid); if (botAI->GetMaster()) - botAI->TellMasterNoFacing("Quest rewarded " + ChatHelper::FormatQuest(quest)); + botAI->GetServices().GetChatService().TellMasterNoFacing("Quest rewarded " + ChatHelper::FormatQuest(quest)); BroadcastHelper::BroadcastQuestTurnedIn(botAI, bot, quest); botAI->rpgStatistic.questRewarded++; LOG_DEBUG("playerbots", "[New RPG] {} turned in quest {}", bot->GetName(), quest->GetQuestId()); @@ -371,7 +373,6 @@ bool NewRpgBaseAction::IsWithinInteractionDist(Object* questGiver) case TYPEID_GAMEOBJECT: { ObjectGuid guid = questGiver->GetGUID(); - GameobjectTypes type = GAMEOBJECT_TYPE_QUESTGIVER; if (GameObject* go = bot->GetMap()->GetGameObject(guid)) { if (go->IsWithinDistInMap(bot)) @@ -530,7 +531,7 @@ bool NewRpgBaseAction::OrganizeQuestLog() if (!questId) continue; - const Quest* quest = sObjectMgr->GetQuestTemplate(questId); + Quest const* quest = sObjectMgr->GetQuestTemplate(questId); if (!IsQuestWorthDoing(quest) || !IsQuestCapableDoing(quest) || bot->GetQuestStatus(questId) == QUEST_STATUS_FAILED) { @@ -539,7 +540,7 @@ bool NewRpgBaseAction::OrganizeQuestLog() packet << (uint8)i; bot->GetSession()->HandleQuestLogRemoveQuest(packet); if (botAI->GetMaster()) - botAI->TellMasterNoFacing("Quest dropped " + ChatHelper::FormatQuest(quest)); + botAI->GetServices().GetChatService().TellMasterNoFacing("Quest dropped " + ChatHelper::FormatQuest(quest)); botAI->rpgStatistic.questDropped++; dropped++; } @@ -556,15 +557,15 @@ bool NewRpgBaseAction::OrganizeQuestLog() if (!questId) continue; - const Quest* quest = sObjectMgr->GetQuestTemplate(questId); - if (quest->GetZoneOrSort() < 0 || (quest->GetZoneOrSort() > 0 && quest->GetZoneOrSort() != bot->GetZoneId())) + Quest const* quest = sObjectMgr->GetQuestTemplate(questId); + if (quest->GetZoneOrSort() < 0 || (quest->GetZoneOrSort() > 0 && static_cast(quest->GetZoneOrSort()) != bot->GetZoneId())) { LOG_DEBUG("playerbots", "[New RPG] {} drop quest {}", bot->GetName(), questId); WorldPacket packet(CMSG_QUESTLOG_REMOVE_QUEST); packet << (uint8)i; bot->GetSession()->HandleQuestLogRemoveQuest(packet); if (botAI->GetMaster()) - botAI->TellMasterNoFacing("Quest dropped " + ChatHelper::FormatQuest(quest)); + botAI->GetServices().GetChatService().TellMasterNoFacing("Quest dropped " + ChatHelper::FormatQuest(quest)); botAI->rpgStatistic.questDropped++; dropped++; } @@ -580,13 +581,13 @@ bool NewRpgBaseAction::OrganizeQuestLog() if (!questId) continue; - const Quest* quest = sObjectMgr->GetQuestTemplate(questId); + Quest const* quest = sObjectMgr->GetQuestTemplate(questId); LOG_DEBUG("playerbots", "[New RPG] {} drop quest {}", bot->GetName(), questId); WorldPacket packet(CMSG_QUESTLOG_REMOVE_QUEST); packet << (uint8)i; bot->GetSession()->HandleQuestLogRemoveQuest(packet); if (botAI->GetMaster()) - botAI->TellMasterNoFacing("Quest dropped " + ChatHelper::FormatQuest(quest)); + botAI->GetServices().GetChatService().TellMasterNoFacing("Quest dropped " + ChatHelper::FormatQuest(quest)); botAI->rpgStatistic.questDropped++; } @@ -682,17 +683,17 @@ bool NewRpgBaseAction::HasQuestToAcceptOrReward(WorldObject* object) { ObjectGuid guid = object->GetGUID(); bot->PrepareQuestMenu(guid); - const QuestMenu& menu = bot->PlayerTalkClass->GetQuestMenu(); + QuestMenu const& menu = bot->PlayerTalkClass->GetQuestMenu(); if (menu.Empty()) return false; for (uint8 idx = 0; idx < menu.GetMenuItemCount(); idx++) { - const QuestMenuItem& item = menu.GetItem(idx); - const Quest* quest = sObjectMgr->GetQuestTemplate(item.QuestId); + QuestMenuItem const& item = menu.GetItem(idx); + Quest const* quest = sObjectMgr->GetQuestTemplate(item.QuestId); if (!quest) continue; - const QuestStatus& status = bot->GetQuestStatus(item.QuestId); + QuestStatus const& status = bot->GetQuestStatus(item.QuestId); if (status == QUEST_STATUS_COMPLETE && bot->CanRewardQuest(quest, 0, false)) { return true; @@ -700,12 +701,12 @@ bool NewRpgBaseAction::HasQuestToAcceptOrReward(WorldObject* object) } for (uint8 idx = 0; idx < menu.GetMenuItemCount(); idx++) { - const QuestMenuItem& item = menu.GetItem(idx); - const Quest* quest = sObjectMgr->GetQuestTemplate(item.QuestId); + QuestMenuItem const& item = menu.GetItem(idx); + Quest const* quest = sObjectMgr->GetQuestTemplate(item.QuestId); if (!quest) continue; - const QuestStatus& status = bot->GetQuestStatus(item.QuestId); + QuestStatus const& status = bot->GetQuestStatus(item.QuestId); if (status == QUEST_STATUS_NONE && bot->CanTakeQuest(quest, false) && bot->CanAddQuest(quest, false) && IsQuestWorthDoing(quest) && IsQuestCapableDoing(quest)) { @@ -718,7 +719,7 @@ bool NewRpgBaseAction::HasQuestToAcceptOrReward(WorldObject* object) static std::vector GenerateRandomWeights(int n) { std::vector weights(n); - float sum = 0.0; + float sum = 0.0f; for (int i = 0; i < n; ++i) { @@ -738,17 +739,17 @@ bool NewRpgBaseAction::GetQuestPOIPosAndObjectiveIdx(uint32 questId, std::vector if (!quest) return false; - const QuestPOIVector* poiVector = sObjectMgr->GetQuestPOIVector(questId); + QuestPOIVector const* poiVector = sObjectMgr->GetQuestPOIVector(questId); if (!poiVector) { return false; } - const QuestStatusData& q_status = bot->getQuestStatusMap().at(questId); + QuestStatusData const& q_status = bot->getQuestStatusMap().at(questId); if (toComplete && q_status.Status == QUEST_STATUS_COMPLETE) { - for (const QuestPOI& qPoi : *poiVector) + for (QuestPOI const& qPoi : *poiVector) { if (qPoi.MapId != bot->GetMapId()) continue; @@ -764,7 +765,7 @@ bool NewRpgBaseAction::GetQuestPOIPosAndObjectiveIdx(uint32 questId, std::vector std::vector weights = GenerateRandomWeights(qPoi.points.size()); for (size_t i = 0; i < qPoi.points.size(); i++) { - const QuestPOIPoint& point = qPoi.points[i]; + QuestPOIPoint const& point = qPoi.points[i]; dx += point.x * weights[i]; dy += point.y * weights[i]; } @@ -814,7 +815,7 @@ bool NewRpgBaseAction::GetQuestPOIPosAndObjectiveIdx(uint32 questId, std::vector } // Get POIs to go - for (const QuestPOI& qPoi : *poiVector) + for (QuestPOI const& qPoi : *poiVector) { if (qPoi.MapId != bot->GetMapId()) continue; @@ -822,7 +823,7 @@ bool NewRpgBaseAction::GetQuestPOIPosAndObjectiveIdx(uint32 questId, std::vector bool inComplete = false; for (uint32 objective : incompleteObjectiveIdx) { - if (qPoi.ObjectiveIndex == objective) + if (static_cast(qPoi.ObjectiveIndex) == objective) { inComplete = true; break; @@ -836,7 +837,7 @@ bool NewRpgBaseAction::GetQuestPOIPosAndObjectiveIdx(uint32 questId, std::vector std::vector weights = GenerateRandomWeights(qPoi.points.size()); for (size_t i = 0; i < qPoi.points.size(); i++) { - const QuestPOIPoint& point = qPoi.points[i]; + QuestPOIPoint const& point = qPoi.points[i]; dx += point.x * weights[i]; dy += point.y * weights[i]; } @@ -866,7 +867,7 @@ bool NewRpgBaseAction::GetQuestPOIPosAndObjectiveIdx(uint32 questId, std::vector WorldPosition NewRpgBaseAction::SelectRandomGrindPos(Player* bot) { - const std::vector& locs = sRandomPlayerbotMgr->locsPerLevelCache[bot->GetLevel()]; + std::vector const& locs = sRandomPlayerbotMgr->locsPerLevelCache[bot->GetLevel()]; float hiRange = 500.0f; float loRange = 2500.0f; if (bot->GetLevel() < 5) @@ -924,7 +925,7 @@ WorldPosition NewRpgBaseAction::SelectRandomGrindPos(Player* bot) WorldPosition NewRpgBaseAction::SelectRandomCampPos(Player* bot) { - const std::vector& locs = IsAlliance(bot->getRace()) + std::vector const& locs = IsAlliance(bot->getRace()) ? sRandomPlayerbotMgr->allianceStarterPerLevelCache[bot->GetLevel()] : sRandomPlayerbotMgr->hordeStarterPerLevelCache[bot->GetLevel()]; @@ -969,7 +970,22 @@ WorldPosition NewRpgBaseAction::SelectRandomCampPos(Player* bot) bool NewRpgBaseAction::SelectRandomFlightTaxiNode(ObjectGuid& flightMaster, uint32& fromNode, uint32& toNode) { - Creature* nearestFlightMaster = sFlightMasterCache->GetNearestFlightMaster(bot); + std::vector const& flightMasters = IsAlliance(bot->getRace()) + ? sFlightMasterCache->GetAllianceFlightMasters() + : sFlightMasterCache->GetHordeFlightMasters(); + Creature* nearestFlightMaster = nullptr; + for (const uint32& guid : flightMasters) + { + Creature* flightMaster = ObjectAccessor::GetSpawnedCreatureByDBGUID(bot->GetMapId(), guid); + if (!flightMaster) + continue; + + if (bot->GetMapId() != flightMaster->GetMapId()) + continue; + + if (!nearestFlightMaster || bot->GetDistance(nearestFlightMaster) > bot->GetDistance(flightMaster)) + nearestFlightMaster = flightMaster; + } if (!nearestFlightMaster || bot->GetDistance(nearestFlightMaster) > 500.0f) return false; @@ -1119,7 +1135,7 @@ bool NewRpgBaseAction::RandomChangeStatus(std::vector candidateSta if (availableQuests.size()) { uint32 questId = availableQuests[urand(0, availableQuests.size() - 1)]; - const Quest* quest = sObjectMgr->GetQuestTemplate(questId); + Quest const* quest = sObjectMgr->GetQuestTemplate(questId); if (quest) { botAI->rpgInfo.ChangeToDoQuest(questId, quest); diff --git a/src/Ai/World/Rpg/NewRpgInfo.cpp b/src/Ai/World/Rpg/NewRpgInfo.cpp index ff830464a5..b1721279c1 100644 --- a/src/Ai/World/Rpg/NewRpgInfo.cpp +++ b/src/Ai/World/Rpg/NewRpgInfo.cpp @@ -34,7 +34,7 @@ void NewRpgInfo::ChangeToWanderRandom() WANDER_RANDOM = WanderRandom(); } -void NewRpgInfo::ChangeToDoQuest(uint32 questId, const Quest* quest) +void NewRpgInfo::ChangeToDoQuest(uint32 questId, Quest const* quest) { Reset(); status = RPG_DO_QUEST; @@ -66,7 +66,7 @@ void NewRpgInfo::ChangeToIdle() status = RPG_IDLE; } -bool NewRpgInfo::CanChangeTo(NewRpgStatus status) { return true; } +bool NewRpgInfo::CanChangeTo(NewRpgStatus /*status*/) { return true; } void NewRpgInfo::Reset() { diff --git a/src/Ai/World/Rpg/NewRpgInfo.h b/src/Ai/World/Rpg/NewRpgInfo.h index 5c14f70d17..7f90d04c8c 100644 --- a/src/Ai/World/Rpg/NewRpgInfo.h +++ b/src/Ai/World/Rpg/NewRpgInfo.h @@ -39,7 +39,7 @@ struct NewRpgInfo // RPG_DO_QUEST struct DoQuest { - const Quest* quest{nullptr}; + Quest const* quest{nullptr}; uint32 questId{0}; int32 objectiveIdx{0}; WorldPosition pos{}; @@ -89,7 +89,7 @@ struct NewRpgInfo void ChangeToGoCamp(WorldPosition pos); void ChangeToWanderNpc(); void ChangeToWanderRandom(); - void ChangeToDoQuest(uint32 questId, const Quest* quest); + void ChangeToDoQuest(uint32 questId, Quest const* quest); void ChangeToTravelFlight(ObjectGuid fromFlightMaster, uint32 fromNode, uint32 toNode); void ChangeToRest(); void ChangeToIdle(); @@ -106,7 +106,7 @@ struct NewRpgStatistic uint32 questAbandoned{0}; uint32 questRewarded{0}; uint32 questDropped{0}; - NewRpgStatistic operator+(const NewRpgStatistic& other) const + NewRpgStatistic operator+(NewRpgStatistic const& other) const { NewRpgStatistic result; result.questAccepted = this->questAccepted + other.questAccepted; @@ -116,7 +116,7 @@ struct NewRpgStatistic result.questDropped = this->questDropped + other.questDropped; return result; } - NewRpgStatistic& operator+=(const NewRpgStatistic& other) + NewRpgStatistic& operator+=(NewRpgStatistic const& other) { this->questAccepted += other.questAccepted; this->questCompleted += other.questCompleted; diff --git a/src/Ai/World/Rpg/Strategy/NewRpgStrategy.cpp b/src/Ai/World/Rpg/Strategy/NewRpgStrategy.cpp index 030ad6b4fc..57b3ab2938 100644 --- a/src/Ai/World/Rpg/Strategy/NewRpgStrategy.cpp +++ b/src/Ai/World/Rpg/Strategy/NewRpgStrategy.cpp @@ -69,7 +69,7 @@ void NewRpgStrategy::InitTriggers(std::vector& triggers) ); } -void NewRpgStrategy::InitMultipliers(std::vector& multipliers) +void NewRpgStrategy::InitMultipliers(std::vector& /*multipliers*/) { } diff --git a/src/Bot/Cmd/ChatFilter.cpp b/src/Bot/Cmd/ChatFilter.cpp index 6025a56274..485fbae445 100644 --- a/src/Bot/Cmd/ChatFilter.cpp +++ b/src/Bot/Cmd/ChatFilter.cpp @@ -4,6 +4,7 @@ */ #include "ChatFilter.h" +#include "BotRoleService.h" #include "Group.h" #include "Playerbots.h" @@ -14,7 +15,7 @@ #include #include -static std::string ToLower(const std::string& str) +static std::string ToLower(std::string const& str) { std::string out = str; std::transform(out.begin(), out.end(), out.begin(), [](unsigned char c){ return std::tolower(c); }); @@ -40,31 +41,31 @@ class StrategyChatFilter : public ChatFilter std::string msgLower = ToLower(message); bool tank = msgLower.find("@tank") == 0; - if (tank && !botAI->IsTank(bot)) + if (tank && !BotRoleService::IsTankStatic(bot)) return ""; bool dps = msgLower.find("@dps") == 0; - if (dps && (botAI->IsTank(bot) || botAI->IsHeal(bot))) + if (dps && (BotRoleService::IsTankStatic(bot) || BotRoleService::IsHealStatic(bot))) return ""; bool heal = msgLower.find("@heal") == 0; - if (heal && !botAI->IsHeal(bot)) + if (heal && !BotRoleService::IsHealStatic(bot)) return ""; bool ranged = msgLower.find("@ranged") == 0; - if (ranged && !botAI->IsRanged(bot)) + if (ranged && !BotRoleService::IsRangedStatic(bot)) return ""; bool melee = msgLower.find("@melee") == 0; - if (melee && botAI->IsRanged(bot)) + if (melee && BotRoleService::IsRangedStatic(bot)) return ""; bool rangeddps = msgLower.find("@rangeddps") == 0; - if (rangeddps && (!botAI->IsRanged(bot) || botAI->IsTank(bot) || botAI->IsHeal(bot))) + if (rangeddps && (!BotRoleService::IsRangedStatic(bot) || BotRoleService::IsTankStatic(bot) || BotRoleService::IsHealStatic(bot))) return ""; bool meleedps = msgLower.find("@meleedps") == 0; - if (meleedps && (!botAI->IsMelee(bot) || botAI->IsTank(bot) || botAI->IsHeal(bot))) + if (meleedps && (!BotRoleService::IsMeleeStatic(bot) || BotRoleService::IsTankStatic(bot) || BotRoleService::IsHealStatic(bot))) return ""; if (tank || dps || heal || ranged || melee) @@ -138,17 +139,19 @@ class CombatTypeChatFilter : public ChatFilter return ""; break; case CLASS_DRUID: - if (ranged && botAI->IsTank(bot)) + if (ranged && BotRoleService::IsTankStatic(bot)) return ""; - if (melee && !botAI->IsTank(bot)) + if (melee && !BotRoleService::IsTankStatic(bot)) return ""; break; case CLASS_SHAMAN: - if (melee && botAI->IsHeal(bot)) + if (melee && BotRoleService::IsHealStatic(bot)) return ""; - if (ranged && !botAI->IsHeal(bot)) + if (ranged && !BotRoleService::IsHealStatic(bot)) return ""; break; + default: + break; } return ChatFilter::Filter(message); @@ -370,7 +373,7 @@ class SpecChatFilter : public ChatFilter private: std::map, std::string> specTabNames; - bool ParseSpecPrefix(const std::string& message, std::string& specPrefix, std::string& rest) + bool ParseSpecPrefix(std::string const& message, std::string& specPrefix, std::string& rest) { std::string msgLower = ToLower(message); for (auto const& entry : specTabNames) @@ -387,7 +390,7 @@ class SpecChatFilter : public ChatFilter return false; } - bool MatchesSpec(Player* bot, const std::string& specPrefix) + bool MatchesSpec(Player* bot, std::string const& specPrefix) { uint8 cls = bot->getClass(); int specTab = AiFactory::GetPlayerSpecTab(bot); @@ -395,12 +398,12 @@ class SpecChatFilter : public ChatFilter // For druids, specTab==1 is always feral; distinguish bear/cat at runtime by role if (cls == CLASS_DRUID && specTab == 1) { - botSpecClass = botAI->IsTank(bot) ? "bear" : "cat"; + botSpecClass = BotRoleService::IsTankStatic(bot) ? "bear" : "cat"; } // For death knights, specTab==0 is always blood; distinguish tank/dps at runtime by role else if (cls == CLASS_DEATH_KNIGHT && specTab == 0) { - botSpecClass = botAI->IsTank(bot) ? "bdkt" : "bdkd"; + botSpecClass = BotRoleService::IsTankStatic(bot) ? "bdkt" : "bdkd"; } else { @@ -421,8 +424,8 @@ class AuraChatFilter : public ChatFilter { Player* bot = botAI->GetBot(); std::string msgLower = ToLower(message); - const std::string auraPrefix = "@aura"; - const std::string noAuraPrefix = "@noaura"; + std::string const auraPrefix = "@aura"; + std::string const noAuraPrefix = "@noaura"; size_t prefixLen = 0; bool isNoAura = false; if (msgLower.find(auraPrefix) == 0) @@ -480,7 +483,7 @@ class AggroByChatFilter : public ChatFilter { Player* bot = botAI->GetBot(); std::string msgLower = ToLower(message); - const std::string prefix = "@aggroby"; + std::string const prefix = "@aggroby"; size_t prefixLen = prefix.length(); if (msgLower.find(prefix) != 0) { diff --git a/src/Bot/Cmd/ChatHelper.cpp b/src/Bot/Cmd/ChatHelper.cpp index bf28874700..288684060f 100644 --- a/src/Bot/Cmd/ChatHelper.cpp +++ b/src/Bot/Cmd/ChatHelper.cpp @@ -224,7 +224,7 @@ std::string const ChatHelper::formatMoney(uint32 copper) return out.str(); } -std::string ChatHelper::parseValue(const std::string& type, const std::string& text) +std::string ChatHelper::parseValue(std::string const& type, std::string const& text) { std::string retString; @@ -431,7 +431,7 @@ std::string const ChatHelper::FormatItem(ItemTemplate const* proto, uint32 count snprintf(color, sizeof(color), "%x", ItemQualityColors[proto->Quality]); std::string itemName; - const ItemLocale* locale = sObjectMgr->GetItemLocale(proto->ItemId); + ItemLocale const* locale = sObjectMgr->GetItemLocale(proto->ItemId); if (locale && locale->Name.size() > sWorld->GetDefaultDbcLocale()) itemName = locale->Name[sWorld->GetDefaultDbcLocale()]; @@ -693,12 +693,12 @@ std::set extractGeneric(std::string_view text, std::string_view prefix) return ids; } -std::set ChatHelper::ExtractAllQuestIds(const std::string& text) +std::set ChatHelper::ExtractAllQuestIds(std::string const& text) { return extractGeneric(text, "Hquest:"); } -std::set ChatHelper::ExtractAllItemIds(const std::string& text) +std::set ChatHelper::ExtractAllItemIds(std::string const& text) { return extractGeneric(text, "Hitem:"); } diff --git a/src/Bot/Cmd/ChatHelper.h b/src/Bot/Cmd/ChatHelper.h index 562f230782..bc1f105e96 100644 --- a/src/Bot/Cmd/ChatHelper.h +++ b/src/Bot/Cmd/ChatHelper.h @@ -40,7 +40,7 @@ class ChatHelper : public PlayerbotAIAware static ItemIds parseItems(std::string const text); static ItemWithRandomProperty parseItemWithRandomProperty(std::string const text); uint32 parseSpell(std::string const text); - static std::string parseValue(const std::string& type, const std::string& text); + static std::string parseValue(std::string const& type, std::string const& text); static std::string const FormatQuest(Quest const* quest); static std::string const FormatItem(ItemTemplate const* proto, uint32 count = 0, uint32 total = 0); @@ -70,8 +70,8 @@ class ChatHelper : public PlayerbotAIAware void eraseAllSubStr(std::string& mainStr, std::string const toErase); - static std::set ExtractAllQuestIds(const std::string& text); - static std::set ExtractAllItemIds(const std::string& text); + static std::set ExtractAllQuestIds(std::string const& text); + static std::set ExtractAllItemIds(std::string const& text); private: static std::map consumableSubClasses; diff --git a/src/Bot/Cmd/PlayerbotCommandServer.cpp b/src/Bot/Cmd/PlayerbotCommandServer.cpp index 83931229a0..f33e87d164 100644 --- a/src/Bot/Cmd/PlayerbotCommandServer.cpp +++ b/src/Bot/Cmd/PlayerbotCommandServer.cpp @@ -27,7 +27,7 @@ bool ReadLine(socket_ptr sock, std::string* buffer, std::string* line) char buf[1025]; boost::system::error_code error; size_t n = sock->read_some(boost::asio::buffer(buf), error); - if (n == -1 || error == boost::asio::error::eof) + if (n == 0 || error == boost::asio::error::eof) return false; else if (error) throw boost::system::system_error(error); // Some other error. diff --git a/src/Bot/Core/BotServiceContainer.h b/src/Bot/Core/BotServiceContainer.h new file mode 100644 index 0000000000..55d57201c1 --- /dev/null +++ b/src/Bot/Core/BotServiceContainer.h @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_BOT_SERVICE_CONTAINER_H +#define _PLAYERBOT_BOT_SERVICE_CONTAINER_H + +#include + +#include "IBotContext.h" +#include "IChatService.h" +#include "IConfigProvider.h" +#include "IItemService.h" +#include "IRoleService.h" +#include "ISpellService.h" + +/** + * @brief Container for all bot services + * + * This class aggregates all service interfaces needed by bot components, + * providing a single point of access for dependency injection. + * + * Usage in production: + * @code + * BotServiceContainer services; + * services.Initialize(playerbotAI); // Wraps existing PlayerbotAI + * @endcode + * + * Usage in tests: + * @code + * BotServiceContainer services; + * services.SetContext(std::make_unique()); + * services.SetSpellService(std::make_unique()); + * // etc. + * @endcode + */ +class BotServiceContainer +{ +public: + BotServiceContainer() = default; + ~BotServiceContainer() = default; + + // Prevent copying + BotServiceContainer(BotServiceContainer const&) = delete; + BotServiceContainer& operator=(BotServiceContainer const&) = delete; + + // Allow moving + BotServiceContainer(BotServiceContainer&&) = default; + BotServiceContainer& operator=(BotServiceContainer&&) = default; + + // Service accessors + IBotContext& GetContext() + { + return *_context; + } + + ISpellService& GetSpellService() + { + return *_spellService; + } + + IChatService& GetChatService() + { + return *_chatService; + } + + IRoleService& GetRoleService() + { + return *_roleService; + } + + IItemService& GetItemService() + { + return *_itemService; + } + + IConfigProvider& GetConfig() + { + return *_config; + } + + // Const accessors + IBotContext const& GetContext() const + { + return *_context; + } + + ISpellService const& GetSpellService() const + { + return *_spellService; + } + + IChatService const& GetChatService() const + { + return *_chatService; + } + + IRoleService const& GetRoleService() const + { + return *_roleService; + } + + IItemService const& GetItemService() const + { + return *_itemService; + } + + IConfigProvider const& GetConfig() const + { + return *_config; + } + + // Service setters for dependency injection + void SetContext(std::unique_ptr context) + { + _context = std::move(context); + } + + void SetSpellService(std::unique_ptr service) + { + _spellService = std::move(service); + } + + void SetChatService(std::unique_ptr service) + { + _chatService = std::move(service); + } + + void SetRoleService(std::unique_ptr service) + { + _roleService = std::move(service); + } + + void SetItemService(std::unique_ptr service) + { + _itemService = std::move(service); + } + + void SetConfig(std::unique_ptr config) + { + _config = std::move(config); + } + + // Check if all services are initialized + bool IsInitialized() const + { + return _context && _spellService && _chatService && _roleService && _itemService && _config; + } + +private: + std::unique_ptr _context; + std::unique_ptr _spellService; + std::unique_ptr _chatService; + std::unique_ptr _roleService; + std::unique_ptr _itemService; + std::unique_ptr _config; +}; + +#endif diff --git a/src/Bot/Core/ManagerRegistry.h b/src/Bot/Core/ManagerRegistry.h new file mode 100644 index 0000000000..2d0b7cb891 --- /dev/null +++ b/src/Bot/Core/ManagerRegistry.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_MANAGER_REGISTRY_H +#define _PLAYERBOT_MANAGER_REGISTRY_H + +#include +#include +#include + +#include "Bot/Interface/ITravelManager.h" +#include "Bot/Interface/IRandomBotManager.h" +#include "Bot/Interface/IBotRepository.h" + +/** + * @brief Central registry for all manager interfaces + * + * This class provides a single point of access for all manager interfaces, + * replacing direct singleton access with dependency injection support. + * + * Usage in production: + * @code + * // Get the global registry + * auto& registry = ManagerRegistry::Instance(); + * + * // Access managers through interfaces + * auto& travelMgr = registry.GetTravelManager(); + * auto& randomBotMgr = registry.GetRandomBotManager(); + * @endcode + * + * Usage in tests: + * @code + * ManagerRegistry registry; + * registry.SetTravelManager(std::make_shared()); + * // Use registry in tests... + * @endcode + */ +class ManagerRegistry +{ +public: + ManagerRegistry() = default; + ~ManagerRegistry() = default; + + // Singleton access for global registry + static ManagerRegistry& Instance() + { + static ManagerRegistry instance; + return instance; + } + + // Prevent copying + ManagerRegistry(ManagerRegistry const&) = delete; + ManagerRegistry& operator=(ManagerRegistry const&) = delete; + + // Manager accessors + ITravelManager& GetTravelManager() + { + return *_travelManager; + } + + IRandomBotManager& GetRandomBotManager() + { + return *_randomBotManager; + } + + IBotRepository& GetBotRepository() + { + return *_botRepository; + } + + // Const accessors + ITravelManager const& GetTravelManager() const + { + return *_travelManager; + } + + IRandomBotManager const& GetRandomBotManager() const + { + return *_randomBotManager; + } + + IBotRepository const& GetBotRepository() const + { + return *_botRepository; + } + + // Manager setters for dependency injection + void SetTravelManager(std::shared_ptr manager) + { + _travelManager = std::move(manager); + } + + void SetRandomBotManager(std::shared_ptr manager) + { + _randomBotManager = std::move(manager); + } + + void SetBotRepository(std::shared_ptr repository) + { + _botRepository = std::move(repository); + } + + // Check if managers are initialized + bool HasTravelManager() const { return _travelManager != nullptr; } + bool HasRandomBotManager() const { return _randomBotManager != nullptr; } + bool HasBotRepository() const { return _botRepository != nullptr; } + + bool IsInitialized() const + { + return _travelManager && _randomBotManager && _botRepository; + } + + // Generic template-based access (for extensibility) + template + T& Get(); + + template + void Register(std::shared_ptr manager); + +private: + std::shared_ptr _travelManager; + std::shared_ptr _randomBotManager; + std::shared_ptr _botRepository; + + // Generic storage for additional managers + std::unordered_map> _managers; +}; + +// Template specializations +template<> +inline ITravelManager& ManagerRegistry::Get() +{ + return GetTravelManager(); +} + +template<> +inline IRandomBotManager& ManagerRegistry::Get() +{ + return GetRandomBotManager(); +} + +template<> +inline IBotRepository& ManagerRegistry::Get() +{ + return GetBotRepository(); +} + +template<> +inline void ManagerRegistry::Register(std::shared_ptr manager) +{ + SetTravelManager(std::move(manager)); +} + +template<> +inline void ManagerRegistry::Register(std::shared_ptr manager) +{ + SetRandomBotManager(std::move(manager)); +} + +template<> +inline void ManagerRegistry::Register(std::shared_ptr repository) +{ + SetBotRepository(std::move(repository)); +} + +// Convenience macro for accessing the global registry +#define sManagerRegistry ManagerRegistry::Instance() + +#endif diff --git a/src/Bot/Debug/PerfMonitor.cpp b/src/Bot/Debug/PerfMonitor.cpp index c6d134edb9..60d49234da 100644 --- a/src/Bot/Debug/PerfMonitor.cpp +++ b/src/Bot/Debug/PerfMonitor.cpp @@ -56,7 +56,7 @@ void PerfMonitor::PrintStats(bool perTick, bool fullStack) if (!perTick) { float updateAITotalTime = 0; - for (auto& map : data[PERF_MON_TOTAL]) + for (auto& map : data[PerformanceMetric::Total]) if (map.first.find("PlayerbotAI::UpdateAIInternal") != std::string::npos) updateAITotalTime += map.second->totalTime; @@ -77,19 +77,19 @@ void PerfMonitor::PrintStats(bool perTick, bool fullStack) std::string key; switch (i->first) { - case PERF_MON_TRIGGER: + case PerformanceMetric::Trigger: key = "Trigger"; break; - case PERF_MON_VALUE: + case PerformanceMetric::Value: key = "Value"; break; - case PERF_MON_ACTION: + case PerformanceMetric::Action: key = "Action"; break; - case PERF_MON_RNDBOT: + case PerformanceMetric::RndBot: key = "RndBot"; break; - case PERF_MON_TOTAL: + case PerformanceMetric::Total: key = "Total"; break; default: @@ -152,8 +152,8 @@ void PerfMonitor::PrintStats(bool perTick, bool fullStack) } else { - float fullTickCount = data[PERF_MON_TOTAL]["PlayerbotAIBase::FullTick"]->count; - float fullTickTotalTime = data[PERF_MON_TOTAL]["PlayerbotAIBase::FullTick"]->totalTime; + float fullTickCount = data[PerformanceMetric::Total]["PlayerbotAIBase::FullTick"]->count; + float fullTickTotalTime = data[PerformanceMetric::Total]["PlayerbotAIBase::FullTick"]->totalTime; LOG_INFO( "playerbots", @@ -172,19 +172,19 @@ void PerfMonitor::PrintStats(bool perTick, bool fullStack) std::string key; switch (i->first) { - case PERF_MON_TRIGGER: + case PerformanceMetric::Trigger: key = "Trigger"; break; - case PERF_MON_VALUE: + case PerformanceMetric::Value: key = "Value"; break; - case PERF_MON_ACTION: + case PerformanceMetric::Action: key = "Action"; break; - case PERF_MON_RNDBOT: + case PerformanceMetric::RndBot: key = "RndBot"; break; - case PERF_MON_TOTAL: + case PerformanceMetric::Total: key = "Total"; break; default: @@ -231,7 +231,7 @@ void PerfMonitor::PrintStats(bool perTick, bool fullStack) time, minTime, maxTime, avg, amount, key.c_str(), disName.c_str()); } } - if (i->first != PERF_MON_TOTAL) + if (i->first != PerformanceMetric::Total) { float tPerc = (float)typeTotalTime / (float)fullTickTotalTime * 100.0f; float tTime = (float)typeTotalTime / fullTickCount / 1000.0f; diff --git a/src/Bot/Debug/PerfMonitor.h b/src/Bot/Debug/PerfMonitor.h index 0d3eb8aad1..9db9a29003 100644 --- a/src/Bot/Debug/PerfMonitor.h +++ b/src/Bot/Debug/PerfMonitor.h @@ -25,13 +25,13 @@ struct PerformanceData std::mutex lock; }; -enum PerformanceMetric +enum class PerformanceMetric { - PERF_MON_TRIGGER, - PERF_MON_VALUE, - PERF_MON_ACTION, - PERF_MON_RNDBOT, - PERF_MON_TOTAL + Trigger, + Value, + Action, + RndBot, + Total }; class PerfMonitorOperation diff --git a/src/Bot/Engine/Action/Action.h b/src/Bot/Engine/Action/Action.h index 2395c5ea87..d540b5970a 100644 --- a/src/Bot/Engine/Action/Action.h +++ b/src/Bot/Engine/Action/Action.h @@ -3,7 +3,8 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ -#pragma once +#ifndef MOD_PLAYERBOTS_BOT_ENGINE_ACTION_ACTION_H +#define MOD_PLAYERBOTS_BOT_ENGINE_ACTION_ACTION_H #include "AiObject.h" #include "Common.h" @@ -19,6 +20,15 @@ class NextAction NextAction(std::string const name, float relevance = 0.0f) : relevance(relevance), name(name) {} // name after relevance - whipowill NextAction(NextAction const& o) : relevance(o.relevance), name(o.name) {} // name after relevance - whipowill + NextAction& operator=(NextAction const& o) + { + if (this != &o) + { + relevance = o.relevance; + name = o.name; + } + return *this; + } std::string const getName() { return name; } float getRelevance() { return relevance; } @@ -100,7 +110,7 @@ class ActionNode Action* getAction() { return action; } void setAction(Action* action) { this->action = action; } - const std::string getName() { return name; } + std::string const getName() { return name; } std::vector getContinuers() { @@ -116,7 +126,7 @@ class ActionNode } private: - const std::string name; + std::string const name; Action* action; std::vector continuers; std::vector alternatives; @@ -145,3 +155,5 @@ class ActionBasket Event event; uint32_t created; }; + +#endif // MOD_PLAYERBOTS_BOT_ENGINE_ACTION_ACTION_H diff --git a/src/Bot/Engine/Engine.cpp b/src/Bot/Engine/Engine.cpp index 6ed61a1a4b..8f19ed4da8 100644 --- a/src/Bot/Engine/Engine.cpp +++ b/src/Bot/Engine/Engine.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "Engine.h" #include "Action.h" @@ -124,7 +125,7 @@ void Engine::Init() strategyTypeMask |= strategy->GetType(); strategy->InitMultipliers(multipliers); strategy->InitTriggers(triggers); - for (auto &iter : strategy->actionNodeFactories.creators) + for (auto& iter : strategy->actionNodeFactories.creators) { actionNodeFactories.creators[iter.first] = iter.second; } @@ -138,7 +139,7 @@ void Engine::Init() } } -bool Engine::DoNextAction(Unit* unit, uint32 depth, bool minimal) +bool Engine::DoNextAction(Unit* /*unit*/, uint32 /*depth*/, bool minimal) { LogAction("--- AI Tick ---"); @@ -204,7 +205,7 @@ bool Engine::DoNextAction(Unit* unit, uint32 depth, bool minimal) } } - PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_ACTION, action->getName(), &aiObjectContext->performanceStack); + PerfMonitorOperation* pmo = sPerfMonitor->start(PerformanceMetric::Action, action->getName(), &aiObjectContext->performanceStack); actionExecuted = ListenAndExecute(action, event); if (pmo) pmo->finish(); @@ -457,7 +458,7 @@ void Engine::ProcessTriggers(bool minimal) continue; PerfMonitorOperation* pmo = - sPerfMonitor->start(PERF_MON_TRIGGER, trigger->getName(), &aiObjectContext->performanceStack); + sPerfMonitor->start(PerformanceMetric::Trigger, trigger->getName(), &aiObjectContext->performanceStack); Event event = trigger->Check(); if (pmo) pmo->finish(); @@ -588,7 +589,7 @@ bool Engine::ListenAndExecute(Action* action, Event event) if (!event.GetSource().empty()) out << " [" << event.GetSource() << "]"; - botAI->TellMasterNoFacing(out); + botAI->GetServices().GetChatService().TellMasterNoFacing(out); } actionExecuted = actionExecutionListeners.OverrideResult(action, actionExecuted, event); @@ -649,7 +650,9 @@ void Engine::ChangeStrategy(std::string const names) toggleStrategy(name + 1); break; case '?': - botAI->TellMaster(ListStrategies()); + botAI->GetServices().GetChatService().TellMaster(ListStrategies()); + break; + default: break; } } diff --git a/src/Bot/Engine/NamedObjectContext.cpp b/src/Bot/Engine/NamedObjectContext.cpp index fcb85261d4..0cec7a82f2 100644 --- a/src/Bot/Engine/NamedObjectContext.cpp +++ b/src/Bot/Engine/NamedObjectContext.cpp @@ -14,12 +14,12 @@ void Qualified::Qualify(int qual) qualifier = out.str(); } -std::string const Qualified::MultiQualify(const std::vector& qualifiers, const std::string& separator, const std::string_view brackets) +std::string const Qualified::MultiQualify(std::vector const& qualifiers, std::string const& separator, std::string_view const brackets) { std::stringstream out; for (uint8 i = 0; i < qualifiers.size(); ++i) { - const std::string& qualifier = qualifiers[i]; + std::string const& qualifier = qualifiers[i]; if (i == qualifiers.size() - 1) { out << qualifier; @@ -40,13 +40,13 @@ std::string const Qualified::MultiQualify(const std::vector& qualif } } -std::vector Qualified::getMultiQualifiers(const std::string& qualifier1) +std::vector Qualified::getMultiQualifiers(std::string const& qualifier1) { std::istringstream iss(qualifier1); return {std::istream_iterator{iss}, std::istream_iterator{}}; } -int32 Qualified::getMultiQualifier(const std::string& qualifier1, uint32 pos) +int32 Qualified::getMultiQualifier(std::string const& qualifier1, uint32 pos) { return std::stoi(getMultiQualifiers(qualifier1)[pos]); } diff --git a/src/Bot/Engine/NamedObjectContext.h b/src/Bot/Engine/NamedObjectContext.h index b60232a5c3..f780d66cc6 100644 --- a/src/Bot/Engine/NamedObjectContext.h +++ b/src/Bot/Engine/NamedObjectContext.h @@ -30,10 +30,10 @@ class Qualified std::string const getQualifier() { return qualifier; } - static std::string const MultiQualify(const std::vector& qualifiers, const std::string& separator, - const std::string_view brackets = "{}"); - static std::vector getMultiQualifiers(const std::string& qualifier1); - static int32 getMultiQualifier(const std::string& qualifier1, uint32 pos); + static std::string const MultiQualify(std::vector const& qualifiers, std::string const& separator, + std::string_view const brackets = "{}"); + static std::vector getMultiQualifiers(std::string const& qualifier1); + static int32 getMultiQualifier(std::string const& qualifier1, uint32 pos); protected: std::string qualifier; @@ -158,8 +158,8 @@ class NamedObjectContextList { public: using ObjectCreator = std::function; - const std::unordered_map& creators; - const std::vector*>& contexts; + std::unordered_map const& creators; + std::vector*> const& contexts; std::unordered_map created; NamedObjectContextList(const SharedNamedObjectContextList& shared) @@ -191,7 +191,7 @@ class NamedObjectContextList if (creators.find(name) == creators.end()) return nullptr; - const ObjectCreator& creator = creators.at(name); + ObjectCreator const& creator = creators.at(name); T* object = creator(botAI); Qualified* q = dynamic_cast(object); @@ -201,7 +201,7 @@ class NamedObjectContextList return object; } - T* GetContextObject(const std::string& name, PlayerbotAI* botAI) + T* GetContextObject(std::string const& name, PlayerbotAI* botAI) { if (created.find(name) == created.end()) { @@ -212,7 +212,7 @@ class NamedObjectContextList return created[name]; } - std::set GetSiblings(const std::string& name) + std::set GetSiblings(std::string const& name) { for (auto i = contexts.begin(); i != contexts.end(); i++) { @@ -281,7 +281,7 @@ class NamedObjectFactoryList if (creators.find(name) == creators.end()) return nullptr; - const ObjectCreator& creator = creators[name]; + ObjectCreator const& creator = creators[name]; T* object = creator(botAI); Qualified* q = dynamic_cast(object); @@ -298,7 +298,7 @@ class NamedObjectFactoryList creators[iter.first] = iter.second; } - T* GetContextObject(const std::string& name, PlayerbotAI* botAI) + T* GetContextObject(std::string const& name, PlayerbotAI* botAI) { if (T* object = create(name, botAI)) return object; diff --git a/src/Bot/Engine/PlayerbotAIAware.h b/src/Bot/Engine/PlayerbotAIAware.h index 8e1fda1b42..6178ba0474 100644 --- a/src/Bot/Engine/PlayerbotAIAware.h +++ b/src/Bot/Engine/PlayerbotAIAware.h @@ -7,14 +7,55 @@ #define _PLAYERBOT_PLAYERbotAIAWARE_H class PlayerbotAI; +class BotServiceContainer; +/** + * @brief Base class for components that need access to bot AI context + * + * This class provides two construction modes: + * 1. Legacy mode: Direct PlayerbotAI pointer (for existing code) + * 2. DI mode: BotServiceContainer for testable, decoupled code + * + * During the refactoring transition, both modes are supported. + * New code should prefer using the BotServiceContainer constructor. + */ class PlayerbotAIAware { public: - PlayerbotAIAware(PlayerbotAI* botAI) : botAI(botAI) {} + /** + * @brief Legacy constructor for direct PlayerbotAI access + * @param botAI Pointer to the bot's AI instance + * @deprecated Prefer using the BotServiceContainer constructor for new code + */ + PlayerbotAIAware(PlayerbotAI* botAI) : botAI(botAI), services_(nullptr) {} + + /** + * @brief DI constructor for testable, decoupled access + * @param services Reference to the bot's service container + * + * When using this constructor, botAI will be nullptr. + * Use GetServices() to access bot functionality. + */ + explicit PlayerbotAIAware(BotServiceContainer& services) : botAI(nullptr), services_(&services) {} + + virtual ~PlayerbotAIAware() = default; + + /** + * @brief Check if services are available (DI mode) + * @return true if using service container + */ + bool HasServices() const { return services_ != nullptr; } + + /** + * @brief Get the service container + * @return Pointer to service container, or nullptr if in legacy mode + */ + BotServiceContainer* GetServices() { return services_; } + BotServiceContainer const* GetServices() const { return services_; } protected: - PlayerbotAI* botAI; + PlayerbotAI* botAI; // Legacy: direct AI pointer + BotServiceContainer* services_; // DI: service container }; #endif diff --git a/src/Bot/Engine/PlayerbotAIBase.cpp b/src/Bot/Engine/PlayerbotAIBase.cpp index c034979340..f74a4927fd 100644 --- a/src/Bot/Engine/PlayerbotAIBase.cpp +++ b/src/Bot/Engine/PlayerbotAIBase.cpp @@ -14,7 +14,7 @@ void PlayerbotAIBase::UpdateAI(uint32 elapsed, bool minimal) if (totalPmo) totalPmo->finish(); - totalPmo = sPerfMonitor->start(PERF_MON_TOTAL, "PlayerbotAIBase::FullTick"); + totalPmo = sPerfMonitor->start(PerformanceMetric::Total, "PlayerbotAIBase::FullTick"); if (nextAICheckDelay > elapsed) nextAICheckDelay -= elapsed; diff --git a/src/Bot/Engine/Strategy/CustomStrategy.cpp b/src/Bot/Engine/Strategy/CustomStrategy.cpp index d4dd3e514e..3910666c8e 100644 --- a/src/Bot/Engine/Strategy/CustomStrategy.cpp +++ b/src/Bot/Engine/Strategy/CustomStrategy.cpp @@ -30,12 +30,12 @@ NextAction toNextAction(std::string const action) throw std::invalid_argument("Invalid action"); } -std::vector toNextActionArray(const std::string actions) +std::vector toNextActionArray(std::string const actions) { - const std::vector tokens = split(actions, ','); + std::vector const tokens = split(actions, ','); std::vector res = {}; - for (const std::string token : tokens) + for (std::string const& token : tokens) { res.push_back(toNextAction(token)); } diff --git a/src/Bot/Engine/Trigger/Trigger.cpp b/src/Bot/Engine/Trigger/Trigger.cpp index 07105be299..6d06b5f581 100644 --- a/src/Bot/Engine/Trigger/Trigger.cpp +++ b/src/Bot/Engine/Trigger/Trigger.cpp @@ -37,7 +37,7 @@ bool Trigger::needCheck(uint32 now) if (checkInterval < 2) return true; - if (!lastCheckTime || now - lastCheckTime >= checkInterval) + if (!lastCheckTime || now - lastCheckTime >= static_cast(checkInterval)) { lastCheckTime = now; return true; diff --git a/src/Bot/Engine/Trigger/Trigger.h b/src/Bot/Engine/Trigger/Trigger.h index d32845dc52..1d1d88daaf 100644 --- a/src/Bot/Engine/Trigger/Trigger.h +++ b/src/Bot/Engine/Trigger/Trigger.h @@ -3,7 +3,8 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ -#pragma once +#ifndef MOD_PLAYERBOTS_BOT_ENGINE_TRIGGER_TRIGGER_H +#define MOD_PLAYERBOTS_BOT_ENGINE_TRIGGER_TRIGGER_H #include "Action.h" #include "Common.h" @@ -16,7 +17,7 @@ class Trigger : public AiNamedObject public: Trigger( PlayerbotAI* botAI, - const std::string name = "trigger", + std::string const name = "trigger", int32_t checkInterval = 1 ); @@ -44,7 +45,7 @@ class TriggerNode { public: TriggerNode( - const std::string& name, + std::string const& name, std::vector handlers = {} ) : trigger(nullptr), @@ -54,7 +55,7 @@ class TriggerNode Trigger* getTrigger() { return trigger; } void setTrigger(Trigger* trigger) { this->trigger = trigger; } - const std::string getName() { return name; } + std::string const getName() { return name; } std::vector getHandlers() { @@ -80,5 +81,7 @@ class TriggerNode private: Trigger* trigger; std::vector handlers; - const std::string name; + std::string const name; }; + +#endif // MOD_PLAYERBOTS_BOT_ENGINE_TRIGGER_TRIGGER_H diff --git a/src/Bot/Engine/Value/Value.cpp b/src/Bot/Engine/Value/Value.cpp index c559e03816..cb43e88195 100644 --- a/src/Bot/Engine/Value/Value.cpp +++ b/src/Bot/Engine/Value/Value.cpp @@ -124,7 +124,7 @@ Unit* UnitCalculatedValue::Get() if (checkInterval < 2) { PerfMonitorOperation* pmo = sPerfMonitor->start( - PERF_MON_VALUE, this->getName(), this->context ? &this->context->performanceStack : nullptr); + PerformanceMetric::Value, this->getName(), this->context ? &this->context->performanceStack : nullptr); value = Calculate(); if (pmo) pmo->finish(); @@ -136,7 +136,7 @@ Unit* UnitCalculatedValue::Get() { lastCheckTime = now; PerfMonitorOperation* pmo = sPerfMonitor->start( - PERF_MON_VALUE, this->getName(), this->context ? &this->context->performanceStack : nullptr); + PerformanceMetric::Value, this->getName(), this->context ? &this->context->performanceStack : nullptr); value = Calculate(); if (pmo) pmo->finish(); diff --git a/src/Bot/Engine/Value/Value.h b/src/Bot/Engine/Value/Value.h index b37e162050..97cf2c93a8 100644 --- a/src/Bot/Engine/Value/Value.h +++ b/src/Bot/Engine/Value/Value.h @@ -72,7 +72,7 @@ class CalculatedValue : public UntypedValue, public Value { if (checkInterval < 2) { - // PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_VALUE, this->getName(), + // PerfMonitorOperation* pmo = sPerfMonitor->start(PerformanceMetric::Value, this->getName(), // this->context ? &this->context->performanceStack : nullptr); value = Calculate(); // if (pmo) @@ -84,7 +84,7 @@ class CalculatedValue : public UntypedValue, public Value if (!lastCheckTime || now - lastCheckTime >= checkInterval) { lastCheckTime = now; - // PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_VALUE, this->getName(), + // PerfMonitorOperation* pmo = sPerfMonitor->start(PerformanceMetric::Value, this->getName(), // this->context ? &this->context->performanceStack : nullptr); value = Calculate(); // if (pmo) @@ -105,7 +105,7 @@ class CalculatedValue : public UntypedValue, public Value { if (checkInterval < 2) { - // PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_VALUE, this->getName(), + // PerfMonitorOperation* pmo = sPerfMonitor->start(PerformanceMetric::Value, this->getName(), // this->context ? &this->context->performanceStack : nullptr); value = Calculate(); // if (pmo) @@ -117,7 +117,7 @@ class CalculatedValue : public UntypedValue, public Value if (!lastCheckTime || now - lastCheckTime >= checkInterval) { lastCheckTime = now; - // PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_VALUE, this->getName(), + // PerfMonitorOperation* pmo = sPerfMonitor->start(PerformanceMetric::Value, this->getName(), // this->context ? &this->context->performanceStack : nullptr); value = Calculate(); // if (pmo) @@ -155,7 +155,7 @@ class SingleCalculatedValue : public CalculatedValue this->lastCheckTime = now; PerfMonitorOperation* pmo = sPerfMonitor->start( - PERF_MON_VALUE, this->getName(), this->context ? &this->context->performanceStack : nullptr); + PerformanceMetric::Value, this->getName(), this->context ? &this->context->performanceStack : nullptr); this->value = this->Calculate(); if (pmo) pmo->finish(); diff --git a/src/Bot/Factory/AiFactory.cpp b/src/Bot/Factory/AiFactory.cpp index 8ca3534b1d..c69325ef16 100644 --- a/src/Bot/Factory/AiFactory.cpp +++ b/src/Bot/Factory/AiFactory.cpp @@ -6,6 +6,8 @@ #include "AiFactory.h" #include "BattlegroundMgr.h" +#include "Bot/Core/ManagerRegistry.h" +#include "BotRoleService.h" #include "DKAiObjectContext.h" #include "DruidAiObjectContext.h" #include "Engine.h" @@ -15,7 +17,6 @@ #include "MageAiObjectContext.h" #include "PaladinAiObjectContext.h" #include "PlayerbotAI.h" -#include "PlayerbotAIConfig.h" #include "Playerbots.h" #include "PriestAiObjectContext.h" #include "RogueAiObjectContext.h" @@ -50,6 +51,8 @@ AiObjectContext* AiFactory::createAiObjectContext(Player* player, PlayerbotAI* b return new RogueAiObjectContext(botAI); case CLASS_DEATH_KNIGHT: return new DKAiObjectContext(botAI); + default: + break; } return new AiObjectContext(botAI); @@ -94,6 +97,8 @@ uint8 AiFactory::GetPlayerSpecTab(Player* bot) case CLASS_SHAMAN: tab = SHAMAN_TAB_ELEMENTAL; break; + default: + break; } return tab; @@ -103,7 +108,7 @@ uint8 AiFactory::GetPlayerSpecTab(Player* bot) std::map AiFactory::GetPlayerSpecTabs(Player* bot) { std::map tabs = {{0, 0}, {0, 0}, {0, 0}}; - const PlayerTalentMap& talentMap = bot->GetTalentMap(); + PlayerTalentMap const& talentMap = bot->GetTalentMap(); for (PlayerTalentMap::const_iterator i = talentMap.begin(); i != talentMap.end(); ++i) { uint32 spellId = i->first; @@ -120,7 +125,7 @@ std::map AiFactory::GetPlayerSpecTabs(Player* bot) uint32 const* talentTabIds = GetTalentTabPages(bot->getClass()); - const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); int rank = spellInfo ? spellInfo->GetRank() : 1; if (talentInfo->TalentTab == talentTabIds[0]) tabs[0] += rank; @@ -389,15 +394,17 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa else engine->addStrategiesNoInit("unholy", "unholy aoe", "dps assist", nullptr); break; + default: + break; } - if (PlayerbotAI::IsTank(player, true)) + if (BotRoleService::IsTankStatic(player, true)) engine->addStrategy("tank face", false); - if (PlayerbotAI::IsMelee(player, true) && PlayerbotAI::IsDps(player, true)) + if (BotRoleService::IsMeleeStatic(player, true) && BotRoleService::IsDpsStatic(player, true)) engine->addStrategy("behind", false); - if (PlayerbotAI::IsHeal(player, true)) + if (BotRoleService::IsHealStatic(player, true)) { if (sPlayerbotAIConfig->autoSaveMana) engine->addStrategy("save mana", false); @@ -405,7 +412,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa engine->addStrategy("healer dps", false); } - if (facade->IsRealPlayer() || sRandomPlayerbotMgr->IsRandomBot(player)) + if (facade->IsRealPlayer() || sManagerRegistry.GetRandomBotManager().IsRandomBot(player)) { if (!player->GetGroup()) { @@ -448,7 +455,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa } } } - if (sRandomPlayerbotMgr->IsRandomBot(player)) + if (sManagerRegistry.GetRandomBotManager().IsRandomBot(player)) engine->ChangeStrategy(sPlayerbotAIConfig->randomBotCombatStrategies); else engine->ChangeStrategy(sPlayerbotAIConfig->combatStrategies); @@ -586,10 +593,10 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const "gather", "duel", "pvp", "buff", "mount", "emote", nullptr); } - if (sPlayerbotAIConfig->autoSaveMana && PlayerbotAI::IsHeal(player, true)) + if (sPlayerbotAIConfig->autoSaveMana && BotRoleService::IsHealStatic(player, true)) nonCombatEngine->addStrategy("save mana", false); - if ((sRandomPlayerbotMgr->IsRandomBot(player)) && !player->InBattleground()) + if ((sManagerRegistry.GetRandomBotManager().IsRandomBot(player)) && !player->InBattleground()) { Player* master = facade->GetMaster(); @@ -637,7 +644,7 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const if (master) { PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(master); - if (masterBotAI || sRandomPlayerbotMgr->IsRandomBot(player)) + if (masterBotAI || sManagerRegistry.GetRandomBotManager().IsRandomBot(player)) { // nonCombatEngine->addStrategy("pvp", false); // nonCombatEngine->addStrategy("collision"); @@ -726,7 +733,7 @@ void AiFactory::AddDefaultDeadStrategies(Player* player, PlayerbotAI* const faca (void)facade; // unused and remove warning deadEngine->addStrategiesNoInit("dead", "stay", "chat", "default", "follow", nullptr); - if (sRandomPlayerbotMgr->IsRandomBot(player) && !player->GetGroup()) + if (sManagerRegistry.GetRandomBotManager().IsRandomBot(player) && !player->GetGroup()) deadEngine->removeStrategy("follow", false); } diff --git a/src/Bot/Factory/PlayerbotFactory.cpp b/src/Bot/Factory/PlayerbotFactory.cpp index 67f5909c11..9952970d0f 100644 --- a/src/Bot/Factory/PlayerbotFactory.cpp +++ b/src/Bot/Factory/PlayerbotFactory.cpp @@ -10,6 +10,7 @@ #include "AccountMgr.h" #include "AiFactory.h" +#include "Bot/Core/ManagerRegistry.h" #include "ArenaTeam.h" #include "ArenaTeamMgr.h" #include "DBCStores.h" @@ -42,10 +43,43 @@ #include "World.h" #include "AiObjectContext.h" #include "ItemPackets.h" +#include "BotItemService.h" const uint64 diveMask = (1LL << 7) | (1LL << 44) | (1LL << 37) | (1LL << 38) | (1LL << 26) | (1LL << 30) | (1LL << 27) | (1LL << 33) | (1LL << 24) | (1LL << 34); +// Common item IDs used by bot factory +enum BotFactoryItems +{ + ITEM_HEARTHSTONE = 6948, // Hearthstone + ITEM_SHAMAN_EARTH_TOTEM = 46978 // Shaman Earth Ring Totem +}; + +// Blacklisted enchant spell IDs (test/grandfathered/unobtainable) +enum BlacklistedEnchantSpells +{ + // Test Enchants + SPELL_TEST_ENCHANT_1 = 7218, + SPELL_TEST_ENCHANT_2 = 19927, + SPELL_TEST_ENCHANT_3 = 44119, + SPELL_TEST_ENCHANT_4 = 47147, + SPELL_TEST_ENCHANT_5 = 47181, + SPELL_TEST_ENCHANT_6 = 47242, + SPELL_TEST_ENCHANT_7 = 50358, + SPELL_TEST_ENCHANT_8 = 52639, + // Grandfathered TBC Enchants + SPELL_GRANDFATHERED_TBC_1 = 35791, + SPELL_GRANDFATHERED_TBC_2 = 39405, + // Legendary Arcane Amalgamation + SPELL_LEGENDARY_ARCANE_1 = 15463, + SPELL_LEGENDARY_ARCANE_2 = 15490, + // Naxx40 Sapphiron Shoulder Enchants + SPELL_NAXX40_SHOULDER_1 = 29467, + SPELL_NAXX40_SHOULDER_2 = 29475, + SPELL_NAXX40_SHOULDER_3 = 29480, + SPELL_NAXX40_SHOULDER_4 = 29483 +}; + static std::vector initSlotsOrder = {EQUIPMENT_SLOT_TRINKET1, EQUIPMENT_SLOT_TRINKET2, EQUIPMENT_SLOT_MAINHAND, EQUIPMENT_SLOT_OFFHAND, EQUIPMENT_SLOT_RANGED, EQUIPMENT_SLOT_HEAD, EQUIPMENT_SLOT_SHOULDERS, EQUIPMENT_SLOT_CHEST, EQUIPMENT_SLOT_LEGS, EQUIPMENT_SLOT_HANDS, EQUIPMENT_SLOT_NECK, EQUIPMENT_SLOT_BODY, EQUIPMENT_SLOT_WAIST, @@ -122,17 +156,19 @@ void PlayerbotFactory::Init() uint32 maxStoreSize = sSpellMgr->GetSpellInfoStoreSize(); for (uint32 id = 1; id < maxStoreSize; ++id) { - if (id == 7218 || id == 19927 || id == 44119 || id == 47147 || id == 47181 || - id == 47242 || id == 50358 || id == 52639) // Test Enchants + if (id == SPELL_TEST_ENCHANT_1 || id == SPELL_TEST_ENCHANT_2 || id == SPELL_TEST_ENCHANT_3 || + id == SPELL_TEST_ENCHANT_4 || id == SPELL_TEST_ENCHANT_5 || id == SPELL_TEST_ENCHANT_6 || + id == SPELL_TEST_ENCHANT_7 || id == SPELL_TEST_ENCHANT_8) // Test Enchants continue; - if (id == 35791 || id == 39405) // Grandfathered TBC Enchants + if (id == SPELL_GRANDFATHERED_TBC_1 || id == SPELL_GRANDFATHERED_TBC_2) // Grandfathered TBC Enchants continue; - if (id == 15463 || id == 15490) // Legendary Arcane Amalgamation + if (id == SPELL_LEGENDARY_ARCANE_1 || id == SPELL_LEGENDARY_ARCANE_2) // Legendary Arcane Amalgamation continue; - if (id == 29467 || id == 29475 || id == 29480 || id == 29483) // Naxx40 Sapphiron Shoulder Enchants + if (id == SPELL_NAXX40_SHOULDER_1 || id == SPELL_NAXX40_SHOULDER_2 || + id == SPELL_NAXX40_SHOULDER_3 || id == SPELL_NAXX40_SHOULDER_4) // Naxx40 Sapphiron Shoulder Enchants continue; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(id); @@ -240,8 +276,8 @@ void PlayerbotFactory::Randomize(bool incremental) // LOG_DEBUG("playerbots", "Preparing to {} randomize...", (incremental ? "incremental" : "full")); Prepare(); LOG_DEBUG("playerbots", "Resetting player..."); - PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Reset"); - if (!sPlayerbotAIConfig->equipmentPersistence || level < sPlayerbotAIConfig->equipmentPersistenceLevel) + PerfMonitorOperation* pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Reset"); + if (!sPlayerbotAIConfig->equipmentPersistence || static_cast(level) < sPlayerbotAIConfig->equipmentPersistenceLevel) { bot->resetTalents(true); } @@ -250,7 +286,7 @@ void PlayerbotFactory::Randomize(bool incremental) ClearSkills(); ClearSpells(); ResetQuests(); - if (!sPlayerbotAIConfig->equipmentPersistence || level < sPlayerbotAIConfig->equipmentPersistenceLevel) + if (!sPlayerbotAIConfig->equipmentPersistence || static_cast(level) < sPlayerbotAIConfig->equipmentPersistenceLevel) { ClearAllItems(); } @@ -266,7 +302,7 @@ void PlayerbotFactory::Randomize(bool incremental) if (pmo) pmo->finish(); - // pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Immersive"); + // pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Immersive"); // LOG_INFO("playerbots", "Initializing immersive..."); // InitImmersive(); // if (pmo) @@ -274,7 +310,7 @@ void PlayerbotFactory::Randomize(bool incremental) if (sPlayerbotAIConfig->randomBotPreQuests) { - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Quests"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Quests"); InitInstanceQuests(); InitAttunementQuests(); if (pmo) @@ -282,75 +318,75 @@ void PlayerbotFactory::Randomize(bool incremental) } else { - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Quests"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Quests"); InitAttunementQuests(); if (pmo) pmo->finish(); } LOG_DEBUG("playerbots", "Initializing skills (step 1)..."); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Skills1"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Skills1"); bot->LearnDefaultSkills(); InitSkills(); if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Spells1"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Spells1"); LOG_DEBUG("playerbots", "Initializing spells (step 1)..."); InitClassSpells(); InitAvailableSpells(); if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Talents"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Talents"); LOG_DEBUG("playerbots", "Initializing talents..."); if (!incremental || !sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) { InitTalentsTree(); } - sRandomPlayerbotMgr->SetValue(bot->GetGUID().GetCounter(), "specNo", 0); + sManagerRegistry.GetRandomBotManager().SetValue(bot->GetGUID().GetCounter(), "specNo", 0); if (botAI) { - sPlayerbotRepository->Reset(botAI); + sManagerRegistry.GetBotRepository().Reset(botAI); // botAI->DoSpecificAction("auto talents"); botAI->ResetStrategies(false); // fix wrong stored strategy } if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Spells2"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Spells2"); LOG_DEBUG("playerbots", "Initializing spells (step 2)..."); InitAvailableSpells(); if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Reputation"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Reputation"); LOG_DEBUG("playerbots", "Initializing reputation..."); InitReputation(); if (pmo) pmo->finish(); LOG_DEBUG("playerbots", "Initializing special spells..."); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Spells3"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Spells3"); InitSpecialSpells(); if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Mounts"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Mounts"); LOG_DEBUG("playerbots", "Initializing mounts..."); InitMounts(); // bot->SaveToDB(false, false); if (pmo) pmo->finish(); - // pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Skills2"); + // pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Skills2"); // LOG_INFO("playerbots", "Initializing skills (step 2)..."); // UpdateTradeSkills(); // if (pmo) // pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Equip"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Equip"); LOG_DEBUG("playerbots", "Initializing equipmemt..."); if (!incremental || !sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) @@ -364,51 +400,51 @@ void PlayerbotFactory::Randomize(bool incremental) // if (bot->GetLevel() >= sPlayerbotAIConfig->minEnchantingBotLevel) // { - // pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Enchant"); + // pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Enchant"); // LOG_INFO("playerbots", "Initializing enchant templates..."); // LoadEnchantContainer(); // if (pmo) // pmo->finish(); // } - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Bags"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Bags"); LOG_DEBUG("playerbots", "Initializing bags..."); InitBags(); // bot->SaveToDB(false, false); if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Ammo"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Ammo"); LOG_DEBUG("playerbots", "Initializing ammo..."); InitAmmo(); if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Food"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Food"); LOG_DEBUG("playerbots", "Initializing food..."); InitFood(); if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Potions"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Potions"); LOG_DEBUG("playerbots", "Initializing potions..."); InitPotions(); if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Reagents"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Reagents"); LOG_DEBUG("playerbots", "Initializing reagents..."); InitReagents(); if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Keys"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Keys"); LOG_DEBUG("playerbots", "Initializing keys..."); InitKeyring(); if (pmo) pmo->finish(); - // pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_EqSets"); + // pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_EqSets"); // LOG_DEBUG("playerbots", "Initializing second equipment set..."); // InitSecondEquipmentSet(); // if (pmo) @@ -419,20 +455,20 @@ void PlayerbotFactory::Randomize(bool incremental) ApplyEnchantAndGemsNew(); } // { - // pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_EnchantTemplate"); + // pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_EnchantTemplate"); // LOG_INFO("playerbots", "Initializing enchant templates..."); // ApplyEnchantTemplate(); // if (pmo) // pmo->finish(); // } - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Inventory"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Inventory"); LOG_DEBUG("playerbots", "Initializing inventory..."); // InitInventory(); if (pmo) pmo->finish(); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Consumable"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Consumable"); LOG_DEBUG("playerbots", "Initializing consumables..."); InitConsumables(); if (pmo) @@ -442,7 +478,7 @@ void PlayerbotFactory::Randomize(bool incremental) InitGlyphs(); // bot->SaveToDB(false, false); - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Guilds"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Guilds"); // bot->SaveToDB(false, false); if (sPlayerbotAIConfig->randomBotGuildCount > 0) { @@ -455,7 +491,7 @@ void PlayerbotFactory::Randomize(bool incremental) if (bot->GetLevel() >= 70) { - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Arenas"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Arenas"); // LOG_INFO("playerbots", "Initializing arena teams..."); InitArenaTeam(); if (pmo) @@ -470,7 +506,7 @@ void PlayerbotFactory::Randomize(bool incremental) } if (bot->GetLevel() >= 10) { - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Pet"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Pet"); LOG_DEBUG("playerbots", "Initializing pet..."); InitPet(); // bot->SaveToDB(false, false); @@ -479,7 +515,7 @@ void PlayerbotFactory::Randomize(bool incremental) pmo->finish(); } - pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Save"); + pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "PlayerbotFactory_Save"); LOG_DEBUG("playerbots", "Saving to DB..."); bot->SetMoney(urand(level * 100000, level * 5 * 100000)); bot->SetHealth(bot->GetMaxHealth()); @@ -817,7 +853,7 @@ void PlayerbotFactory::InitPetTalents() int index = urand(0, spells_row.size() - 1); TalentEntry const* talentInfo = spells_row[index]; int maxRank = 0; - for (int rank = 0; rank < std::min((uint32)MAX_TALENT_RANK, (uint32)pet->GetFreeTalentPoints()); ++rank) + for (uint32 rank = 0; rank < std::min((uint32)MAX_TALENT_RANK, (uint32)pet->GetFreeTalentPoints()); ++rank) { uint32 spellId = talentInfo->RankID[rank]; if (!spellId) @@ -954,13 +990,8 @@ void PlayerbotFactory::InitPet() continue; if (co->Name.size() > 21) continue; - uint32 guid = map->GenerateLowGuid(); - uint32 pet_number = sObjectMgr->GeneratePetNumber(); if (bot->GetPetStable() && bot->GetPetStable()->CurrentPet) { - auto petGuid = bot->GetPetStable()->CurrentPet.value(); // To correct the build warnin in VS - // bot->GetPetStable()->CurrentPet.value(); - // bot->GetPetStable()->CurrentPet.reset(); bot->RemovePet(nullptr, PET_SAVE_AS_CURRENT); bot->RemovePet(nullptr, PET_SAVE_NOT_IN_SLOT); } @@ -1071,7 +1102,7 @@ void PlayerbotFactory::ClearSpells() for (PlayerSpellMap::iterator itr = bot->GetSpellMap().begin(); itr != bot->GetSpellMap().end(); ++itr) { uint32 spellId = itr->first; - //const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId); //not used, line marked for removal. + //SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); //not used, line marked for removal. if (itr->second->State == PLAYERSPELL_REMOVED) { continue; @@ -1137,13 +1168,13 @@ void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_templa else { uint32 pointSum = 0; - for (int i = 0; i < MAX_SPECNO; i++) + for (uint32 i = 0; i < MAX_SPECNO; i++) { pointSum += sPlayerbotAIConfig->randomClassSpecProb[cls][i]; } uint32 point = urand(1, pointSum); uint32 currentP = 0; - int i; + uint32 i; for (i = 0; i < MAX_SPECNO; i++) { currentP += sPlayerbotAIConfig->randomClassSpecProb[cls][i]; @@ -1427,6 +1458,8 @@ bool PlayerbotFactory::CheckItemStats(uint8 sp, uint8 ap, uint8 tank) if ((!ap && !tank) || sp > ap || sp > tank) return false; break; + default: + break; } return sp || ap || tank; @@ -1443,6 +1476,8 @@ void PlayerbotFactory::AddItemStats(uint32 mod, uint8& sp, uint8& ap, uint8& tan case ITEM_MOD_SPIRIT: ++sp; break; + default: + break; } switch (mod) @@ -1453,6 +1488,8 @@ void PlayerbotFactory::AddItemStats(uint32 mod, uint8& sp, uint8& ap, uint8& tan case ITEM_MOD_STAMINA: ++tank; break; + default: + break; } switch (mod) @@ -1463,6 +1500,8 @@ void PlayerbotFactory::AddItemStats(uint32 mod, uint8& sp, uint8& ap, uint8& tan case ITEM_MOD_STRENGTH: ++ap; break; + default: + break; } } @@ -1529,6 +1568,8 @@ bool PlayerbotFactory::CanEquipWeapon(ItemTemplate const* proto) proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE) return false; break; + default: + break; } return true; @@ -1722,7 +1763,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) uint32 itemId = oEntry->ItemId[j]; // skip hearthstone - if (itemId == 6948) + if (itemId == ITEM_HEARTHSTONE) continue; // just skip, reported in ObjectMgr::LoadItemTemplates @@ -1792,14 +1833,14 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) } do { - for (uint32 requiredLevel = bot->GetLevel(); requiredLevel > std::max((int32)bot->GetLevel() - delta, 0); + for (uint32 requiredLevel = bot->GetLevel(); requiredLevel > static_cast(std::max(static_cast(bot->GetLevel()) - delta, 0)); requiredLevel--) { for (InventoryType inventoryType : GetPossibleInventoryTypeListBySlot((EquipmentSlots)slot)) { for (uint32 itemId : sRandomItemMgr->GetCachedEquipments(requiredLevel, inventoryType)) { - if (itemId == 46978) // shaman earth ring totem + if (itemId == ITEM_SHAMAN_EARTH_TOTEM) { continue; } @@ -1831,7 +1872,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) if (proto->Class != ITEM_CLASS_WEAPON && proto->Class != ITEM_CLASS_ARMOR) continue; - if (proto->Quality != desiredQuality) + if (static_cast(proto->Quality) != desiredQuality) continue; if (proto->Class == ITEM_CLASS_ARMOR && @@ -1862,7 +1903,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) float bestScoreForSlot = -1; uint32 bestItemForSlot = 0; - for (int index = 0; index < ids.size(); index++) + for (size_t index = 0; index < ids.size(); index++) { uint32 newItemId = ids[index]; @@ -1916,13 +1957,8 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) if (oldItem) continue; - Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true); + bot->EquipNewItem(dest, bestItemForSlot, true); bot->AutoUnequipOffhandIfNeed(); - // if (newItem) - // { - // newItem->AddToWorld(); - // newItem->AddToUpdateQueueOf(bot); - // } } // Secondary init for better equips /// @todo: clean up duplicate code @@ -1956,7 +1992,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) float bestScoreForSlot = -1; uint32 bestItemForSlot = 0; - for (int index = 0; index < ids.size(); index++) + for (size_t index = 0; index < ids.size(); index++) { uint32 newItemId = ids[index]; @@ -1985,13 +2021,8 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) { continue; } - Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true); + bot->EquipNewItem(dest, bestItemForSlot, true); bot->AutoUnequipOffhandIfNeed(); - // if (newItem) - // { - // newItem->AddToWorld(); - // newItem->AddToUpdateQueueOf(bot); - // } } } } @@ -2013,7 +2044,7 @@ bool PlayerbotFactory::IsDesiredReplacement(Item* item) // } uint32 delta = 1 + (80 - bot->GetLevel()) / 10; - return proto->Quality < ITEM_QUALITY_RARE || int32(bot->GetLevel() - requiredLevel) > delta; + return proto->Quality < ITEM_QUALITY_RARE || static_cast(bot->GetLevel() - requiredLevel) > static_cast(delta); } inline Item* StoreNewItemInInventorySlot(Player* player, uint32 newItemId, uint32 count) @@ -2162,12 +2193,7 @@ void PlayerbotFactory::InitBags(bool destroyOld) { continue; } - Item* newItem = bot->EquipNewItem(dest, newItemId, true); - // if (newItem) - // { - // newItem->AddToWorld(); - // newItem->AddToUpdateQueueOf(bot); - // } + bot->EquipNewItem(dest, newItemId, true); } } @@ -2267,7 +2293,7 @@ bool PlayerbotFactory::CanEquipUnseenItem(uint8 slot, uint16& dest, uint32 item) if (Item* pItem = Item::CreateItem(item, 1, bot, false, 0, true)) { - InventoryResult result = botAI ? botAI->CanEquipItem(slot, dest, pItem, true, true) + InventoryResult result = botAI ? botAI->GetServices().GetItemService().CanEquipItem(slot, dest, pItem, true, true) : bot->CanEquipItem(slot, dest, pItem, true, true); pItem->RemoveFromUpdateQueueOf(bot); delete pItem; @@ -2279,8 +2305,8 @@ bool PlayerbotFactory::CanEquipUnseenItem(uint8 slot, uint16& dest, uint32 item) void PlayerbotFactory::InitTradeSkills() { - uint16 firstSkill = sRandomPlayerbotMgr->GetValue(bot, "firstSkill"); - uint16 secondSkill = sRandomPlayerbotMgr->GetValue(bot, "secondSkill"); + uint16 firstSkill = sManagerRegistry.GetRandomBotManager().GetValue(bot, "firstSkill"); + uint16 secondSkill = sManagerRegistry.GetRandomBotManager().GetValue(bot, "secondSkill"); if (!firstSkill || !secondSkill) { std::vector firstSkills; @@ -2332,8 +2358,8 @@ void PlayerbotFactory::InitTradeSkills() break; } - sRandomPlayerbotMgr->SetValue(bot, "firstSkill", firstSkill); - sRandomPlayerbotMgr->SetValue(bot, "secondSkill", secondSkill); + sManagerRegistry.GetRandomBotManager().SetValue(bot, "firstSkill", firstSkill); + sManagerRegistry.GetRandomBotManager().SetValue(bot, "secondSkill", secondSkill); } SetRandomSkill(SKILL_FIRST_AID); @@ -2729,7 +2755,7 @@ void PlayerbotFactory::InitTalents(uint32 specNo) int index = urand(0, spells_row.size() - 1); TalentEntry const* talentInfo = spells_row[index]; int maxRank = 0; - for (int rank = 0; rank < std::min((uint32)MAX_TALENT_RANK, bot->GetFreeTalentPoints()); ++rank) + for (uint32 rank = 0; rank < std::min((uint32)MAX_TALENT_RANK, bot->GetFreeTalentPoints()); ++rank) { uint32 spellId = talentInfo->RankID[rank]; if (!spellId) @@ -2891,7 +2917,6 @@ void PlayerbotFactory::AddPrevQuests(uint32 questId, std::list& questIds void PlayerbotFactory::InitQuests(std::list& questMap, bool withRewardItem) { - uint32 count = 0; for (std::list::iterator i = questMap.begin(); i != questMap.end(); ++i) { uint32 questId = *i; @@ -3076,6 +3101,8 @@ void PlayerbotFactory::InitMounts() slow = {33660, 35020, 35022, 35018}; fast = {35025, 35025, 35027}; break; + default: + break; } switch (bot->GetTeamId()) @@ -3263,7 +3290,7 @@ void PlayerbotFactory::InitFood() } uint32 categories[] = {11, 59}; - for (int i = 0; i < sizeof(categories) / sizeof(uint32); ++i) + for (size_t i = 0; i < sizeof(categories) / sizeof(uint32); ++i) { uint32 category = categories[i]; std::vector& ids = items[category]; @@ -3296,7 +3323,6 @@ void PlayerbotFactory::InitFood() void PlayerbotFactory::InitReagents() { - int specTab = AiFactory::GetPlayerSpecTab(bot); std::vector> items; switch (bot->getClass()) { @@ -3879,11 +3905,15 @@ void PlayerbotFactory::InitInventoryTrade() stacks = urand(1, 3); break; case ITEM_QUALITY_UNCOMMON: + { stacks = 1; int maxStackSize = proto->GetMaxStackSize() / 2; uint32 max = std::max(1, maxStackSize); count = urand(1, max); break; + } + default: + break; } for (uint32 i = 0; i < stacks; i++) @@ -3985,7 +4015,7 @@ void PlayerbotFactory::InitImmersive() std::ostringstream name; name << "immersive_stat_" << i; - uint32 value = sRandomPlayerbotMgr->GetValue(owner, name.str()); + uint32 value = sManagerRegistry.GetRandomBotManager().GetValue(owner, name.str()); if (value) initialized = true; @@ -4038,6 +4068,8 @@ void PlayerbotFactory::InitImmersive() percentMap[STAT_SPIRIT] = 10; percentMap[STAT_STAMINA] = 40; break; + default: + break; } for (uint8 i = 0; i < 5; i++) @@ -4058,7 +4090,7 @@ void PlayerbotFactory::InitImmersive() std::ostringstream name; name << "immersive_stat_" << i; - sRandomPlayerbotMgr->SetValue(owner, name.str(), percentMap[type]); + sManagerRegistry.GetRandomBotManager().SetValue(owner, name.str(), percentMap[type]); } } } @@ -4239,6 +4271,8 @@ void PlayerbotFactory::ApplyEnchantTemplate() case CLASS_PRIEST: ApplyEnchantTemplate(50); break; + default: + break; } } @@ -4279,16 +4313,16 @@ void PlayerbotFactory::ApplyEnchantTemplate(uint8 spec) bot->ApplyEnchantment(pItem, PERM_ENCHANTMENT_SLOT, true); } // botAI->EnchantItemT((*itr).SpellId, (*itr).SlotId); - // const SpellItemEnchantmentEntry* a = sSpellItemEnchantmentStore.LookupEntry(1); + // SpellItemEnchantmentEntry const* a = sSpellItemEnchantmentStore.LookupEntry(1); } -void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destroyOld) +void PlayerbotFactory::ApplyEnchantAndGemsNew(bool /*destroyOld*/) { //int32 bestGemEnchantId[4] = {-1, -1, -1, -1}; // 1, 2, 4, 8 color //not used, line marked for removal. //float bestGemScore[4] = {0, 0, 0, 0}; //not used, line marked for removal. std::vector curCount = GetCurrentGemsCount(); uint8 jewelersCount = 0; - int requiredActive = 2; + uint32 requiredActive = 2; std::vector availableGems; for (const uint32& enchantGem : enchantGemIdCache) { @@ -4296,7 +4330,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destroyOld) if (!gemTemplate) continue; - const GemPropertiesEntry* gemProperties = sGemPropertiesStore.LookupEntry(gemTemplate->GemProperties); + GemPropertiesEntry const* gemProperties = sGemPropertiesStore.LookupEntry(gemTemplate->GemProperties); if (!gemProperties) continue; @@ -4419,8 +4453,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destroyOld) continue; } int32 enchantIdChosen = -1; - int32 colorChosen; - bool jewelersGemChosen; + bool jewelersGemChosen = false; float bestGemScore = -1; for (uint32& enchantGem : availableGems) { @@ -4433,7 +4466,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destroyOld) if (isJewelersGem && jewelersCount >= 3) continue; - const GemPropertiesEntry* gemProperties = sGemPropertiesStore.LookupEntry(gemTemplate->GemProperties); + GemPropertiesEntry const* gemProperties = sGemPropertiesStore.LookupEntry(gemTemplate->GemProperties); if (!gemProperties) continue; @@ -4450,7 +4483,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destroyOld) if (curCount[0] != 0) { // Ensure meta gem activation - for (int i = 1; i < curCount.size(); i++) + for (size_t i = 1; i < curCount.size(); i++) { if (curCount[i] < requiredActive && (gemProperties->color & (1 << i))) { @@ -4460,11 +4493,10 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destroyOld) } } if (socketColor & gemProperties->color) - score *= 1.2; + score *= 1.2f; if (score > bestGemScore) { enchantIdChosen = enchant_id; - colorChosen = gemProperties->color; bestGemScore = score; jewelersGemChosen = isJewelersGem; } diff --git a/src/Bot/Factory/RandomPlayerbotFactory.cpp b/src/Bot/Factory/RandomPlayerbotFactory.cpp index 5a4672f5eb..3e1fef8d3c 100644 --- a/src/Bot/Factory/RandomPlayerbotFactory.cpp +++ b/src/Bot/Factory/RandomPlayerbotFactory.cpp @@ -137,16 +137,24 @@ Player* RandomPlayerbotFactory::CreateRandomBot(WorldSession* session, uint8 cls case SECTION_TYPE_HAIR: hairs.push_back(std::pair(charSection->Type, charSection->Color)); break; + default: + break; } } //uint8 skinColor = skinColors[urand(0, skinColors.size() - 1)]; //not used, line marked for removal. + if (faces.empty() || hairs.empty()) + { + LOG_ERROR("playerbots", "No character customization data found for race: {}, gender: {}", race, gender); + return nullptr; + } + std::pair face = faces[urand(0, faces.size() - 1)]; std::pair hair = hairs[urand(0, hairs.size() - 1)]; bool excludeCheck = (race == RACE_TAUREN) || (race == RACE_DRAENEI) || (gender == GENDER_FEMALE && race != RACE_NIGHTELF && race != RACE_UNDEAD_PLAYER); - uint8 facialHair = excludeCheck ? 0 : facialHairTypes[urand(0, facialHairTypes.size() - 1)]; + uint8 facialHair = excludeCheck ? 0 : (facialHairTypes.empty() ? 0 : facialHairTypes[urand(0, facialHairTypes.size() - 1)]); std::unique_ptr characterInfo = std::make_unique( name, race, cls, gender, face.second, face.first, hair.first, hair.second, facialHair); @@ -211,16 +219,16 @@ std::string const RandomPlayerbotFactory::CreateRandomBotName(NameRaceAndGender // CONLANG NAME GENERATION LOG_ERROR("playerbots", "No more names left for random bots. Attempting conlang name generation."); - const std::string groupCategory = "SCVKRU"; - const std::string groupFormStart[2][4] = {{"SV", "SV", "VK", "RV"}, {"V", "SU", "VS", "RV"}}; - const std::string groupFormMid[2][6] = {{"CV", "CVC", "CVC", "CVK", "VC", "VK"}, + std::string const groupCategory = "SCVKRU"; + std::string const groupFormStart[2][4] = {{"SV", "SV", "VK", "RV"}, {"V", "SU", "VS", "RV"}}; + std::string const groupFormMid[2][6] = {{"CV", "CVC", "CVC", "CVK", "VC", "VK"}, {"CV", "CVC", "CVK", "KVC", "VC", "KV"}}; - const std::string groupFormEnd[2][4] = {{"CV", "VC", "VK", "CV"}, {"RU", "UR", "VR", "V"}}; - const std::string groupLetter[2][6] = { + std::string const groupFormEnd[2][4] = {{"CV", "VC", "VK", "CV"}, {"RU", "UR", "VR", "V"}}; + std::string const groupLetter[2][6] = { // S C V K R U {"dtspkThfS", "bcCdfghjkmnNqqrrlsStTvwxyz", "aaeeiouA", "ppttkkbdg", "lmmnrr", "AEO"}, {"dtskThfS", "bcCdfghjkmmnNqrrlssStTvwyz", "aaaeeiiuAAEIO", "ppttkbbdg", "lmmnrrr", "AEOy"}}; - const std::string replaceRule[2][17] = { + std::string const replaceRule[2][17] = { {"ST", "ka", "ko", "ku", "kr", "S", "T", "C", "N", "jj", "AA", "AI", "A", "E", "O", "I", "aa"}, {"sth", "ca", "co", "cu", "cr", "sh", "th", "ch", "ng", "dg", "A", "ayu", "ai", "ei", "ou", "iu", "ae"}}; @@ -244,17 +252,17 @@ std::string const RandomPlayerbotFactory::CreateRandomBotName(NameRaceAndGender botName += (botName.size() < 2) ? groupFormEnd[gender][rand() % 4] : ""; // Replace Catagory value with random Letter from that Catagory's Letter string for a given bot gender - for (int i = 0; i < botName.size(); i++) + for (size_t i = 0; i < botName.size(); i++) { botName[i] = groupLetter[gender][groupCategory.find(botName[i])] [rand() % groupLetter[gender][groupCategory.find(botName[i])].size()]; } // Itterate over replace rules - for (int i = 0; i < 17; i++) + for (size_t i = 0; i < 17; i++) { - int j = botName.find(replaceRule[0][i]); - while (j > -1) + size_t j = botName.find(replaceRule[0][i]); + while (j != std::string::npos) { botName.replace(j, replaceRule[0][i].size(), replaceRule[1][i]); j = botName.find(replaceRule[0][i]); @@ -277,7 +285,7 @@ std::string const RandomPlayerbotFactory::CreateRandomBotName(NameRaceAndGender botName.clear(); continue; } - return std::move(botName); + return botName; } // TRUE RANDOM NAME GENERATION @@ -302,11 +310,11 @@ std::string const RandomPlayerbotFactory::CreateRandomBotName(NameRaceAndGender botName.clear(); continue; } - return std::move(botName); + return botName; } LOG_ERROR("playerbots", "Random name generation failed."); botName.clear(); - return std::move(botName); + return botName; } // Calculates the total number of required accounts, either using the specified randomBotAccountCount @@ -763,7 +771,7 @@ std::string const RandomPlayerbotFactory::CreateRandomGuildName() if (!result) { LOG_ERROR("playerbots", "No more names left for random guilds"); - return std::move(guildName); + return guildName; } Field* fields = result->Fetch(); @@ -777,13 +785,13 @@ std::string const RandomPlayerbotFactory::CreateRandomGuildName() if (!result) { LOG_ERROR("playerbots", "No more names left for random guilds"); - return std::move(guildName); + return guildName; } fields = result->Fetch(); guildName = fields[0].Get(); - return std::move(guildName); + return guildName; } void RandomPlayerbotFactory::CreateRandomArenaTeams(ArenaType type, uint32 count) @@ -905,7 +913,7 @@ std::string const RandomPlayerbotFactory::CreateRandomArenaTeamName() if (!result) { LOG_ERROR("playerbots", "No more names left for random arena teams"); - return std::move(arenaTeamName); + return arenaTeamName; } Field* fields = result->Fetch(); @@ -920,11 +928,11 @@ std::string const RandomPlayerbotFactory::CreateRandomArenaTeamName() if (!result) { LOG_ERROR("playerbots", "No more names left for random arena teams"); - return std::move(arenaTeamName); + return arenaTeamName; } fields = result->Fetch(); arenaTeamName = fields[0].Get(); - return std::move(arenaTeamName); + return arenaTeamName; } diff --git a/src/Bot/Handler/ChatCommandHandler.cpp b/src/Bot/Handler/ChatCommandHandler.cpp new file mode 100644 index 0000000000..7fb6be2179 --- /dev/null +++ b/src/Bot/Handler/ChatCommandHandler.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "ChatCommandHandler.h" + +#include "PlayerbotAI.h" + +void ChatCommandHandler::HandleCommand(uint32 type, std::string const& text, Player* fromPlayer) +{ + if (_botAI) + { + _botAI->HandleCommand(type, text, fromPlayer); + } +} + +std::string ChatCommandHandler::HandleRemoteCommand(std::string const& command) +{ + if (_botAI) + { + return _botAI->HandleRemoteCommand(command); + } + return ""; +} + +void ChatCommandHandler::QueueChatResponse(ChatQueuedReply const& reply) +{ + if (_botAI) + { + _botAI->QueueChatResponse(reply); + } +} + +bool ChatCommandHandler::IsAllowedCommand(std::string const& text) +{ + if (_botAI) + { + return _botAI->IsAllowedCommand(text); + } + return false; +} diff --git a/src/Bot/Handler/ChatCommandHandler.h b/src/Bot/Handler/ChatCommandHandler.h new file mode 100644 index 0000000000..298e53041a --- /dev/null +++ b/src/Bot/Handler/ChatCommandHandler.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_CHAT_COMMAND_HANDLER_H +#define _PLAYERBOT_CHAT_COMMAND_HANDLER_H + +#include "Bot/Interface/IChatCommandHandler.h" + +class PlayerbotAI; + +/** + * @brief Implementation of IChatCommandHandler + * + * This handler manages chat command processing for bots, + * extracting this functionality from PlayerbotAI for better testability. + * + * The handler delegates to PlayerbotAI methods during the transition period. + */ +class ChatCommandHandler : public IChatCommandHandler +{ +public: + explicit ChatCommandHandler(PlayerbotAI* ai) : _botAI(ai) {} + ~ChatCommandHandler() override = default; + + void HandleCommand(uint32 type, std::string const& text, Player* fromPlayer) override; + std::string HandleRemoteCommand(std::string const& command) override; + void QueueChatResponse(ChatQueuedReply const& reply) override; + bool IsAllowedCommand(std::string const& text) override; + +private: + PlayerbotAI* _botAI; +}; + +#endif diff --git a/src/Bot/Handler/PacketHandler.cpp b/src/Bot/Handler/PacketHandler.cpp new file mode 100644 index 0000000000..bccd48f7d7 --- /dev/null +++ b/src/Bot/Handler/PacketHandler.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "PacketHandler.h" + +#include "PlayerbotAI.h" + +void PacketHandler::HandleBotOutgoingPacket(WorldPacket const& packet) +{ + if (_botAI) + { + _botAI->HandleBotOutgoingPacket(packet); + } +} + +void PacketHandler::HandleMasterIncomingPacket(WorldPacket const& packet) +{ + if (_botAI) + { + _botAI->HandleMasterIncomingPacket(packet); + } +} + +void PacketHandler::HandleMasterOutgoingPacket(WorldPacket const& packet) +{ + if (_botAI) + { + _botAI->HandleMasterOutgoingPacket(packet); + } +} + +void PacketHandler::HandleTeleportAck() +{ + if (_botAI) + { + _botAI->HandleTeleportAck(); + } +} diff --git a/src/Bot/Handler/PacketHandler.h b/src/Bot/Handler/PacketHandler.h new file mode 100644 index 0000000000..098766726d --- /dev/null +++ b/src/Bot/Handler/PacketHandler.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_PACKET_HANDLER_H +#define _PLAYERBOT_PACKET_HANDLER_H + +#include "Bot/Interface/IPacketHandler.h" + +class PlayerbotAI; + +/** + * @brief Implementation of IPacketHandler + * + * This handler manages network packet processing for bots, + * extracting this functionality from PlayerbotAI for better testability. + * + * The handler delegates to PlayerbotAI methods during the transition period. + */ +class PacketHandler : public IPacketHandler +{ +public: + explicit PacketHandler(PlayerbotAI* ai) : _botAI(ai) {} + ~PacketHandler() override = default; + + void HandleBotOutgoingPacket(WorldPacket const& packet) override; + void HandleMasterIncomingPacket(WorldPacket const& packet) override; + void HandleMasterOutgoingPacket(WorldPacket const& packet) override; + void HandleTeleportAck() override; + +private: + PlayerbotAI* _botAI; +}; + +#endif diff --git a/src/Bot/Interface/IBotContext.h b/src/Bot/Interface/IBotContext.h new file mode 100644 index 0000000000..ca133460b7 --- /dev/null +++ b/src/Bot/Interface/IBotContext.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_IBOT_CONTEXT_H +#define _PLAYERBOT_IBOT_CONTEXT_H + +#include "Common.h" +#include + +class Player; +class Unit; +class Creature; +class GameObject; +class WorldObject; +class ObjectGuid; +class WorldPosition; +struct AreaTableEntry; + +enum BotState : int; + +/** + * @brief Interface for bot context access + * + * This interface provides access to the bot, master, and related game objects, + * abstracting away direct dependencies on PlayerbotAI for testability. + */ +class IBotContext +{ +public: + virtual ~IBotContext() = default; + + // Bot and Master access + virtual Player* GetBot() = 0; + virtual Player* GetMaster() = 0; + virtual void SetMaster(Player* newMaster) = 0; + + // State management + virtual BotState GetState() const = 0; + virtual bool IsInCombat() const = 0; + + // Player validation + virtual bool IsRealPlayer() const = 0; + virtual bool HasRealPlayerMaster() const = 0; + virtual bool HasActivePlayerMaster() const = 0; + virtual bool IsAlt() const = 0; + + // Object access + virtual Creature* GetCreature(ObjectGuid guid) = 0; + virtual Unit* GetUnit(ObjectGuid guid) = 0; + virtual Player* GetPlayer(ObjectGuid guid) = 0; + virtual GameObject* GetGameObject(ObjectGuid guid) = 0; + virtual WorldObject* GetWorldObject(ObjectGuid guid) = 0; + + // Location info + virtual AreaTableEntry const* GetCurrentArea() const = 0; + virtual AreaTableEntry const* GetCurrentZone() const = 0; + + // Group access + virtual std::vector GetPlayersInGroup() = 0; + virtual Player* GetGroupLeader() = 0; + + // Utility + virtual bool IsSafe(Player* player) const = 0; + virtual bool IsSafe(WorldObject* obj) const = 0; + virtual bool IsOpposing(Player* player) const = 0; + virtual bool CanMove() const = 0; + + // Player proximity + virtual bool HasPlayerNearby(float range) const = 0; + virtual bool HasPlayerNearby(WorldPosition* pos, float range) const = 0; + virtual bool HasManyPlayersNearby(uint32 triggerValue, float range) const = 0; +}; + +#endif diff --git a/src/Bot/Interface/IBotRepository.h b/src/Bot/Interface/IBotRepository.h new file mode 100644 index 0000000000..1fc4248fca --- /dev/null +++ b/src/Bot/Interface/IBotRepository.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_IBOT_REPOSITORY_H +#define _PLAYERBOT_IBOT_REPOSITORY_H + +#include "Common.h" +#include + +class PlayerbotAI; +class Player; +class ObjectGuid; + +/** + * @brief Interface for bot data persistence + * + * This interface abstracts database operations for bots, + * enabling isolated testing and loose coupling. + */ +class IBotRepository +{ +public: + virtual ~IBotRepository() = default; + + // Bot strategy persistence + virtual void Save(PlayerbotAI* botAI) = 0; + virtual void Load(PlayerbotAI* botAI) = 0; + virtual void Reset(PlayerbotAI* botAI) = 0; + + // Bot data queries + virtual bool HasSavedData(uint32 guid) = 0; + virtual std::string GetSavedValue(uint32 guid, std::string const& key) = 0; + virtual void SetSavedValue(uint32 guid, std::string const& key, std::string const& value) = 0; + virtual void DeleteSavedData(uint32 guid) = 0; +}; + +/** + * @brief Interface for spell repository operations + */ +class ISpellRepository +{ +public: + virtual ~ISpellRepository() = default; + + virtual bool HasSpellData(uint32 spellId) = 0; + virtual void LoadSpellData() = 0; +}; + +/** + * @brief Interface for dungeon repository operations + */ +class IDungeonRepository +{ +public: + virtual ~IDungeonRepository() = default; + + virtual bool HasDungeonData(uint32 mapId) = 0; + virtual void LoadDungeonData() = 0; +}; + +#endif diff --git a/src/Bot/Interface/IChatCommandHandler.h b/src/Bot/Interface/IChatCommandHandler.h new file mode 100644 index 0000000000..6865763f76 --- /dev/null +++ b/src/Bot/Interface/IChatCommandHandler.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_ICHAT_COMMAND_HANDLER_H +#define _PLAYERBOT_ICHAT_COMMAND_HANDLER_H + +#include "Common.h" +#include + +class Player; +struct ChatQueuedReply; + +/** + * @brief Interface for handling chat commands + * + * This interface abstracts command handling operations from PlayerbotAI, + * enabling isolated testing of command processing logic. + */ +class IChatCommandHandler +{ +public: + virtual ~IChatCommandHandler() = default; + + /** + * @brief Handle a chat command from a player + * @param type Message type (whisper, say, etc.) + * @param text Command text + * @param fromPlayer Player sending the command + */ + virtual void HandleCommand(uint32 type, std::string const& text, Player* fromPlayer) = 0; + + /** + * @brief Handle a remote command (from external tools) + * @param command Command string + * @return Response string + */ + virtual std::string HandleRemoteCommand(std::string const& command) = 0; + + /** + * @brief Queue a chat response for later processing + * @param reply The queued reply to send + */ + virtual void QueueChatResponse(ChatQueuedReply const& reply) = 0; + + /** + * @brief Check if a command is allowed without security check + * @param text Command text + * @return true if allowed + */ + virtual bool IsAllowedCommand(std::string const& text) = 0; +}; + +#endif diff --git a/src/Bot/Interface/IChatService.h b/src/Bot/Interface/IChatService.h new file mode 100644 index 0000000000..fd45e490e8 --- /dev/null +++ b/src/Bot/Interface/IChatService.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_ICHAT_SERVICE_H +#define _PLAYERBOT_ICHAT_SERVICE_H + +#include "Common.h" +#include "PlayerbotSecurity.h" +#include + +/** + * @brief Chat channel identifiers for bot communication + */ +enum class ChatChannelType +{ + GUILD, + WORLD, + GENERAL, + TRADE, + LOOKING_FOR_GROUP, + LOCAL_DEFENSE, + WORLD_DEFENSE, + GUILD_RECRUITMENT, + SAY, + WHISPER, + EMOTE, + YELL, + PARTY, + RAID +}; + +/** + * @brief Interface for bot communication + * + * This interface abstracts chat and communication operations from PlayerbotAI, + * enabling isolated testing of chat logic. + */ +class IChatService +{ +public: + virtual ~IChatService() = default; + + // Master communication + virtual bool TellMaster(std::string const& text, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL) = 0; + virtual bool TellMaster(std::ostringstream& stream, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL) = 0; + virtual bool TellMasterNoFacing(std::string const& text, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL) = 0; + virtual bool TellMasterNoFacing(std::ostringstream& stream, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL) = 0; + virtual bool TellError(std::string const& text, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL) = 0; + + // Channel communication + virtual bool SayToGuild(std::string const& msg) = 0; + virtual bool SayToWorld(std::string const& msg) = 0; + virtual bool SayToChannel(std::string const& msg, uint32 channelId) = 0; + virtual bool SayToParty(std::string const& msg) = 0; + virtual bool SayToRaid(std::string const& msg) = 0; + + // Direct communication + virtual bool Say(std::string const& msg) = 0; + virtual bool Yell(std::string const& msg) = 0; + virtual bool Whisper(std::string const& msg, std::string const& receiverName) = 0; + + // Emotes + virtual bool PlaySound(uint32 emote) = 0; + virtual bool PlayEmote(uint32 emote) = 0; + + // Visual feedback + virtual void Ping(float x, float y) = 0; +}; + +#endif diff --git a/src/Bot/Interface/IConfigProvider.h b/src/Bot/Interface/IConfigProvider.h new file mode 100644 index 0000000000..2b1801ca3c --- /dev/null +++ b/src/Bot/Interface/IConfigProvider.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_ICONFIG_PROVIDER_H +#define _PLAYERBOT_ICONFIG_PROVIDER_H + +#include "Common.h" + +/** + * @brief Interface for bot configuration access + * + * This interface abstracts access to bot configuration settings, + * allowing for dependency injection and easier testing. + */ +class IConfigProvider +{ +public: + virtual ~IConfigProvider() = default; + + // Distance settings + virtual float GetSightDistance() const = 0; + virtual float GetSpellDistance() const = 0; + virtual float GetReactDistance() const = 0; + virtual float GetGrindDistance() const = 0; + virtual float GetLootDistance() const = 0; + virtual float GetShootDistance() const = 0; + virtual float GetFleeDistance() const = 0; + virtual float GetTooCloseDistance() const = 0; + virtual float GetMeleeDistance() const = 0; + virtual float GetFollowDistance() const = 0; + virtual float GetWhisperDistance() const = 0; + virtual float GetContactDistance() const = 0; + virtual float GetAoeRadius() const = 0; + virtual float GetRpgDistance() const = 0; + virtual float GetTargetPosRecalcDistance() const = 0; + virtual float GetFarDistance() const = 0; + virtual float GetHealDistance() const = 0; + virtual float GetAggroDistance() const = 0; + + // Timing settings + virtual uint32 GetGlobalCooldown() const = 0; + virtual uint32 GetReactDelay() const = 0; + virtual uint32 GetMaxWaitForMove() const = 0; + virtual uint32 GetExpireActionTime() const = 0; + virtual uint32 GetDispelAuraDuration() const = 0; + virtual uint32 GetPassiveDelay() const = 0; + virtual uint32 GetRepeatDelay() const = 0; + virtual uint32 GetErrorDelay() const = 0; + virtual uint32 GetRpgDelay() const = 0; + virtual uint32 GetSitDelay() const = 0; + virtual uint32 GetReturnDelay() const = 0; + virtual uint32 GetLootDelay() const = 0; + + // Health/Mana thresholds + virtual uint32 GetCriticalHealth() const = 0; + virtual uint32 GetLowHealth() const = 0; + virtual uint32 GetMediumHealth() const = 0; + virtual uint32 GetAlmostFullHealth() const = 0; + virtual uint32 GetLowMana() const = 0; + virtual uint32 GetMediumMana() const = 0; + virtual uint32 GetHighMana() const = 0; + + // Feature flags + virtual bool IsEnabled() const = 0; + virtual bool IsDynamicReactDelay() const = 0; + virtual bool IsAutoSaveMana() const = 0; + virtual uint32 GetSaveManaThreshold() const = 0; + virtual bool IsAutoAvoidAoe() const = 0; + virtual float GetMaxAoeAvoidRadius() const = 0; + virtual bool IsTellWhenAvoidAoe() const = 0; + + // Bot behavior settings + virtual bool IsFleeingEnabled() const = 0; + virtual bool IsRandomBotAutologin() const = 0; + virtual bool IsBotAutologin() const = 0; + + // Logging + virtual bool HasLog(std::string const& fileName) const = 0; +}; + +#endif diff --git a/src/Bot/Interface/IItemService.h b/src/Bot/Interface/IItemService.h new file mode 100644 index 0000000000..0ea518419f --- /dev/null +++ b/src/Bot/Interface/IItemService.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_IITEM_SERVICE_H +#define _PLAYERBOT_IITEM_SERVICE_H + +#include "Common.h" +#include +#include + +class Item; +class Player; +class Unit; +class ObjectGuid; +class Quest; +struct ItemTemplate; + +enum InventoryResult : uint8; + +/** + * @brief Interface for inventory and item management + * + * This interface abstracts item-related operations from PlayerbotAI, + * enabling isolated testing of item logic. + */ +class IItemService +{ +public: + virtual ~IItemService() = default; + + // Item finding + virtual Item* FindPoison() const = 0; + virtual Item* FindAmmo() const = 0; + virtual Item* FindBandage() const = 0; + virtual Item* FindOpenableItem() const = 0; + virtual Item* FindLockedItem() const = 0; + virtual Item* FindConsumable(uint32 itemId) const = 0; + + // Weapon enhancements + virtual Item* FindStoneFor(Item* weapon) const = 0; + virtual Item* FindOilFor(Item* weapon) const = 0; + + // Item use + virtual void ImbueItem(Item* item, uint32 targetFlag, ObjectGuid targetGUID) = 0; + virtual void ImbueItem(Item* item, uint8 targetInventorySlot) = 0; + virtual void ImbueItem(Item* item, Unit* target) = 0; + virtual void ImbueItem(Item* item) = 0; + + // Enchanting + virtual void EnchantItem(uint32 spellId, uint8 slot) = 0; + + // Inventory queries + virtual std::vector GetInventoryAndEquippedItems() const = 0; + virtual std::vector GetInventoryItems() const = 0; + virtual uint32 GetInventoryItemsCountWithId(uint32 itemId) const = 0; + virtual bool HasItemInInventory(uint32 itemId) const = 0; + + // Equipment + virtual InventoryResult CanEquipItem(uint8 slot, uint16& dest, Item* pItem, bool swap, + bool notLoading = true) const = 0; + virtual uint8 FindEquipSlot(ItemTemplate const* proto, uint32 slot, bool swap) const = 0; + virtual uint32 GetEquipGearScore(Player* player = nullptr) const = 0; + + // Quest items + virtual std::vector> GetCurrentQuestsRequiringItemId(uint32 itemId) const = 0; +}; + +#endif diff --git a/src/Bot/Interface/IPacketHandler.h b/src/Bot/Interface/IPacketHandler.h new file mode 100644 index 0000000000..fd2e7243e2 --- /dev/null +++ b/src/Bot/Interface/IPacketHandler.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_IPACKET_HANDLER_H +#define _PLAYERBOT_IPACKET_HANDLER_H + +class WorldPacket; + +/** + * @brief Interface for handling network packets + * + * This interface abstracts packet handling operations from PlayerbotAI, + * enabling isolated testing of packet processing logic. + */ +class IPacketHandler +{ +public: + virtual ~IPacketHandler() = default; + + /** + * @brief Handle packets sent by the bot + * @param packet The outgoing packet from bot + */ + virtual void HandleBotOutgoingPacket(WorldPacket const& packet) = 0; + + /** + * @brief Handle packets received by the master from the server + * @param packet The incoming packet to master + */ + virtual void HandleMasterIncomingPacket(WorldPacket const& packet) = 0; + + /** + * @brief Handle packets sent by the master to the server + * @param packet The outgoing packet from master + */ + virtual void HandleMasterOutgoingPacket(WorldPacket const& packet) = 0; + + /** + * @brief Handle teleport acknowledgment + */ + virtual void HandleTeleportAck() = 0; +}; + +#endif diff --git a/src/Bot/Interface/IRandomBotManager.h b/src/Bot/Interface/IRandomBotManager.h new file mode 100644 index 0000000000..68d689fef5 --- /dev/null +++ b/src/Bot/Interface/IRandomBotManager.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_IRANDOM_BOT_MANAGER_H +#define _PLAYERBOT_IRANDOM_BOT_MANAGER_H + +#include "Common.h" +#include "ObjectGuid.h" +#include +#include + +class Player; + +/** + * @brief Interface for random bot lifecycle management + * + * This interface abstracts random bot operations, + * enabling isolated testing and loose coupling. + */ +class IRandomBotManager +{ +public: + virtual ~IRandomBotManager() = default; + + // Bot queries + virtual bool IsRandomBot(Player* bot) = 0; + virtual bool IsRandomBot(ObjectGuid::LowType guid) = 0; + virtual Player* GetRandomPlayer() = 0; + virtual std::vector GetPlayers() = 0; + virtual uint32 GetActiveBotCount() const = 0; + virtual uint32 GetMaxAllowedBotCount() = 0; + + // Bot lifecycle + virtual void Randomize(Player* bot) = 0; + virtual void RandomizeFirst(Player* bot) = 0; + virtual void Refresh(Player* bot) = 0; + virtual void Revive(Player* bot) = 0; + virtual void Remove(Player* bot) = 0; + virtual void Clear(Player* bot) = 0; + + // Scheduling + virtual void ScheduleTeleport(uint32 bot, uint32 time = 0) = 0; + virtual void ScheduleChangeStrategy(uint32 bot, uint32 time = 0) = 0; + + // Events + virtual void OnPlayerLogin(Player* player) = 0; + virtual void OnPlayerLogout(Player* player) = 0; + + // Value storage (bot-specific persistent data) + virtual uint32 GetValue(Player* bot, std::string const& type) = 0; + virtual uint32 GetValue(uint32 bot, std::string const& type) = 0; + virtual void SetValue(Player* bot, std::string const& type, uint32 value, std::string const& data = "") = 0; + virtual void SetValue(uint32 bot, std::string const& type, uint32 value, std::string const& data = "") = 0; + + // Trading + virtual double GetBuyMultiplier(Player* bot) = 0; + virtual double GetSellMultiplier(Player* bot) = 0; + + // Statistics + virtual void PrintStats() = 0; + virtual float GetActivityPercentage() = 0; + virtual void SetActivityPercentage(float percentage) = 0; +}; + +#endif diff --git a/src/Bot/Interface/IRoleService.h b/src/Bot/Interface/IRoleService.h new file mode 100644 index 0000000000..514f31ae81 --- /dev/null +++ b/src/Bot/Interface/IRoleService.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_IROLE_SERVICE_H +#define _PLAYERBOT_IROLE_SERVICE_H + +#include "Common.h" + +class Player; +class Unit; + +/** + * @brief Bot role types + */ +enum class BotRole : uint8 +{ + NONE = 0x00, + TANK = 0x01, + HEALER = 0x02, + DPS = 0x04 +}; + +/** + * @brief Interface for role detection and management + * + * This interface abstracts role-related operations from PlayerbotAI, + * enabling isolated testing of role logic. + * + * Role detection is based on player spec, talents, and group position. + */ +class IRoleService +{ +public: + virtual ~IRoleService() = default; + + // Basic role detection + virtual bool IsTank(Player* player, bool bySpec = false) const = 0; + virtual bool IsHeal(Player* player, bool bySpec = false) const = 0; + virtual bool IsDps(Player* player, bool bySpec = false) const = 0; + + // Combat style detection + virtual bool IsRanged(Player* player, bool bySpec = false) const = 0; + virtual bool IsMelee(Player* player, bool bySpec = false) const = 0; + virtual bool IsCaster(Player* player, bool bySpec = false) const = 0; + virtual bool IsRangedDps(Player* player, bool bySpec = false) const = 0; + + // Hybrid detection + virtual bool IsCombo(Player* player) const = 0; + + // Tank hierarchy + virtual bool IsBotMainTank(Player* player) const = 0; + virtual bool IsMainTank(Player* player) const = 0; + virtual bool IsAssistTank(Player* player) const = 0; + virtual bool IsAssistTankOfIndex(Player* player, int index, bool ignoreDeadPlayers = false) const = 0; + + // Group role queries + virtual uint32 GetGroupTankNum(Player* player) const = 0; + virtual int32 GetAssistTankIndex(Player* player) const = 0; + virtual int32 GetGroupSlotIndex(Player* player) const = 0; + virtual int32 GetRangedIndex(Player* player) const = 0; + virtual int32 GetRangedDpsIndex(Player* player) const = 0; + virtual int32 GetMeleeIndex(Player* player) const = 0; + virtual int32 GetClassIndex(Player* player, uint8 cls) const = 0; + + // Role assistant index queries + virtual bool IsAssistHealOfIndex(Player* player, int index, bool ignoreDeadPlayers = false) const = 0; + virtual bool IsAssistRangedDpsOfIndex(Player* player, int index, bool ignoreDeadPlayers = false) const = 0; + + // Aggro + virtual bool HasAggro(Unit* unit) const = 0; +}; + +#endif diff --git a/src/Bot/Interface/ISpellService.h b/src/Bot/Interface/ISpellService.h new file mode 100644 index 0000000000..1ae76e3920 --- /dev/null +++ b/src/Bot/Interface/ISpellService.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_ISPELL_SERVICE_H +#define _PLAYERBOT_ISPELL_SERVICE_H + +#include "Common.h" + +class Unit; +class Item; +class Spell; +class Aura; +class SpellInfo; +class GameObject; + +/** + * @brief Interface for spell casting and aura management + * + * This interface abstracts spell-related operations from PlayerbotAI, + * enabling isolated testing of spell logic. + */ +class ISpellService +{ +public: + virtual ~ISpellService() = default; + + // Spell casting by name + virtual bool CanCastSpell(std::string const& name, Unit* target, Item* itemTarget = nullptr) = 0; + virtual bool CastSpell(std::string const& name, Unit* target, Item* itemTarget = nullptr) = 0; + + // Spell casting by ID + virtual bool CanCastSpell(uint32 spellId, Unit* target, bool checkHasSpell = true, Item* itemTarget = nullptr, + Item* castItem = nullptr) = 0; + virtual bool CanCastSpell(uint32 spellId, GameObject* goTarget, bool checkHasSpell = true) = 0; + virtual bool CanCastSpell(uint32 spellId, float x, float y, float z, bool checkHasSpell = true, + Item* itemTarget = nullptr) = 0; + + virtual bool CastSpell(uint32 spellId, Unit* target, Item* itemTarget = nullptr) = 0; + virtual bool CastSpell(uint32 spellId, float x, float y, float z, Item* itemTarget = nullptr) = 0; + + // Aura management + virtual bool HasAura(std::string const& spellName, Unit* player, bool maxStack = false, bool checkIsOwner = false, + int maxAmount = -1, bool checkDuration = false) = 0; + virtual bool HasAura(uint32 spellId, Unit const* player) = 0; + virtual bool HasAnyAuraOf(Unit* player, ...) = 0; + + virtual Aura* GetAura(std::string const& spellName, Unit* unit, bool checkIsOwner = false, + bool checkDuration = false, int checkStack = -1) = 0; + virtual void RemoveAura(std::string const& name) = 0; + virtual void RemoveShapeshift() = 0; + + // Dispel + virtual bool HasAuraToDispel(Unit* player, uint32 dispelType) = 0; + virtual bool CanDispel(SpellInfo const* spellInfo, uint32 dispelType) = 0; + + // Interrupt + virtual bool IsInterruptableSpellCasting(Unit* player, std::string const& spell) = 0; + virtual void InterruptSpell() = 0; + virtual void SpellInterrupted(uint32 spellId) = 0; + + // Cooldown + virtual int32 CalculateGlobalCooldown(uint32 spellId) = 0; + virtual void WaitForSpellCast(Spell* spell) = 0; + + // Vehicle spells + virtual bool CanCastVehicleSpell(uint32 spellId, Unit* target) = 0; + virtual bool CastVehicleSpell(uint32 spellId, Unit* target) = 0; + virtual bool CastVehicleSpell(uint32 spellId, float x, float y, float z) = 0; + virtual bool IsInVehicle(bool canControl = false, bool canCast = false, bool canAttack = false, + bool canTurn = false, bool fixed = false) = 0; +}; + +#endif diff --git a/src/Bot/Interface/ITravelManager.h b/src/Bot/Interface/ITravelManager.h new file mode 100644 index 0000000000..85612d8f88 --- /dev/null +++ b/src/Bot/Interface/ITravelManager.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_ITRAVEL_MANAGER_H +#define _PLAYERBOT_ITRAVEL_MANAGER_H + +#include "Common.h" +#include + +class Player; +class WorldLocation; +class WorldPosition; +class Quest; + +/** + * @brief Interface for travel and navigation management + * + * This interface abstracts travel-related operations, + * enabling isolated testing and loose coupling. + */ +class ITravelManager +{ +public: + virtual ~ITravelManager() = default; + + // Teleportation + virtual void RandomTeleport(Player* bot) = 0; + virtual void RandomTeleportForLevel(Player* bot) = 0; + virtual void RandomTeleportForRpg(Player* bot) = 0; + + // Location queries + virtual uint32 GetZoneLevel(uint16 mapId, float x, float y, float z) = 0; + virtual std::vector GetLocsForLevel(uint8 level) = 0; + + // Quest travel + virtual bool HasDestination(Player* bot) = 0; + virtual void SetQuestDestination(Player* bot, Quest const* quest) = 0; + virtual void ClearDestination(Player* bot) = 0; +}; + +#endif diff --git a/src/Bot/PlayerbotAI.cpp b/src/Bot/PlayerbotAI.cpp index 39db7c5948..428b15b1d8 100644 --- a/src/Bot/PlayerbotAI.cpp +++ b/src/Bot/PlayerbotAI.cpp @@ -11,6 +11,7 @@ #include #include "AiFactory.h" +#include "Bot/Core/ManagerRegistry.h" #include "BudgetValues.h" #include "ChannelMgr.h" #include "CharacterPackets.h" @@ -60,6 +61,15 @@ #include "UpdateTime.h" #include "Vehicle.h" +// Service architecture includes +#include "Bot/Core/BotServiceContainer.h" +#include "Bot/Service/BotChatService.h" +#include "Bot/Service/BotContext.h" +#include "Bot/Service/BotItemService.h" +#include "Bot/Service/BotRoleService.h" +#include "Bot/Service/BotSpellService.h" +#include "Bot/Service/ConfigProvider.h" + const int SPELL_TITAN_GRIP = 49152; std::vector PlayerbotAI::dispel_whitelist = { @@ -148,6 +158,17 @@ PlayerbotAI::PlayerbotAI(Player* bot) accountId = bot->GetSession()->GetAccountId(); + // Initialize service container with all services + services_ = std::make_unique(); + services_->SetContext(std::make_unique(this)); + services_->SetSpellService(std::make_unique(this)); + services_->SetChatService(std::make_unique(this)); + auto roleService = std::make_unique(); + roleService->SetBotContext(this); + services_->SetRoleService(std::move(roleService)); + services_->SetItemService(std::make_unique(this)); + services_->SetConfig(std::make_unique()); + aiObjectContext = AiFactory::createAiObjectContext(bot, this); engines[BOT_STATE_COMBAT] = AiFactory::createCombatEngine(bot, this, aiObjectContext); @@ -275,7 +296,7 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) if (currentSpell) { - const SpellInfo* spellInfo = currentSpell->GetSpellInfo(); + SpellInfo const* spellInfo = currentSpell->GetSpellInfo(); if (spellInfo && currentSpell->getState() == SPELL_STATE_PREPARING) { Unit* spellTarget = currentSpell->m_targets.GetUnitTarget(); @@ -387,7 +408,7 @@ void PlayerbotAI::UpdateAIGroupMaster() Group* group = bot->GetGroup(); - bool IsRandomBot = sRandomPlayerbotMgr->IsRandomBot(bot); + bool IsRandomBot = sManagerRegistry.GetRandomBotManager().IsRandomBot(bot); // If bot is not in group verify that for is RandomBot before clearing master and resetting. if (!group) @@ -451,7 +472,7 @@ void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal std::string const mapString = WorldPosition(bot).isOverworld() ? std::to_string(bot->GetMapId()) : "I"; PerfMonitorOperation* pmo = - sPerfMonitor->start(PERF_MON_TOTAL, "PlayerbotAI::UpdateAIInternal " + mapString); + sPerfMonitor->start(PerformanceMetric::Total, "PlayerbotAI::UpdateAIInternal " + mapString); ExternalEventHelper helper(aiObjectContext); // chat replies @@ -542,7 +563,7 @@ void PlayerbotAI::HandleCommands() continue; } - const std::string& command = it->GetCommand(); + std::string const& command = it->GetCommand(); if (command.empty()) { it = chatCommands.erase(it); @@ -561,7 +582,7 @@ void PlayerbotAI::HandleCommands() } std::map chatMap; -void PlayerbotAI::HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang) +void PlayerbotAI::HandleCommand(uint32 type, std::string const& text, Player& fromPlayer, const uint32 lang) { if (!bot) return; @@ -1607,796 +1628,142 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster) strategyName = "voa"; // Vault of Archavon break; case 631: - strategyName = "icc"; // Icecrown Citadel - break; - case 632: - strategyName = "wotlk-fos"; // The Forge of Souls - break; - case 650: - strategyName = "wotlk-toc"; // Trial of the Champion - break; - case 658: - strategyName = "wotlk-pos"; // Pit of Saron - break; - case 668: - strategyName = "wotlk-hor"; // Halls of Reflection - break; - default: - break; - } - if (strategyName.empty()) - return; - engines[BOT_STATE_COMBAT]->addStrategy(strategyName); - engines[BOT_STATE_NON_COMBAT]->addStrategy(strategyName); - if (tellMaster && !strategyName.empty()) - { - std::ostringstream out; - out << "Added " << strategyName << " instance strategy"; - TellMasterNoFacing(out.str()); - } -} - -bool PlayerbotAI::DoSpecificAction(std::string const name, Event event, bool silent, std::string const qualifier) -{ - std::ostringstream out; - - for (uint8 i = 0; i < BOT_STATE_MAX; i++) - { - ActionResult res = engines[i]->ExecuteAction(name, event, qualifier); - switch (res) - { - case ACTION_RESULT_UNKNOWN: - continue; - case ACTION_RESULT_OK: - if (!silent) - { - PlaySound(TEXT_EMOTE_NOD); - } - return true; - case ACTION_RESULT_IMPOSSIBLE: - out << name << ": impossible"; - if (!silent) - { - TellError(out.str()); - PlaySound(TEXT_EMOTE_NO); - } - return false; - case ACTION_RESULT_USELESS: - out << name << ": useless"; - if (!silent) - { - TellError(out.str()); - PlaySound(TEXT_EMOTE_NO); - } - return false; - case ACTION_RESULT_FAILED: - if (!silent) - { - out << name << ": failed"; - TellError(out.str()); - } - return false; - } - } - - if (!silent) - { - out << name << ": unknown action"; - TellError(out.str()); - } - - return false; -} - -bool PlayerbotAI::PlaySound(uint32 emote) -{ - if (EmotesTextSoundEntry const* soundEntry = FindTextSoundEmoteFor(emote, bot->getRace(), bot->getGender())) - { - bot->PlayDistanceSound(soundEntry->SoundId); - return true; - } - - return false; -} - -bool PlayerbotAI::PlayEmote(uint32 emote) -{ - WorldPacket data(SMSG_TEXT_EMOTE); - data << (TextEmotes)emote; - data << EmoteAction::GetNumberOfEmoteVariants((TextEmotes)emote, bot->getRace(), bot->getGender()); - data << ((master && (sServerFacade->GetDistance2d(bot, master) < 30.0f) && urand(0, 1)) ? master->GetGUID() - : (bot->GetTarget() && urand(0, 1)) ? bot->GetTarget() - : ObjectGuid::Empty); - bot->GetSession()->HandleTextEmoteOpcode(data); - - return false; -} - -bool PlayerbotAI::ContainsStrategy(StrategyType type) -{ - for (uint8 i = 0; i < BOT_STATE_MAX; i++) - { - if (engines[i]->HasStrategyType(type)) - return true; - } - - return false; -} - -bool PlayerbotAI::HasStrategy(std::string const name, BotState type) { return engines[type]->HasStrategy(name); } - -void PlayerbotAI::ResetStrategies(bool load) -{ - for (uint8 i = 0; i < BOT_STATE_MAX; i++) - engines[i]->removeAllStrategies(); - - AiFactory::AddDefaultCombatStrategies(bot, this, engines[BOT_STATE_COMBAT]); - AiFactory::AddDefaultNonCombatStrategies(bot, this, engines[BOT_STATE_NON_COMBAT]); - AiFactory::AddDefaultDeadStrategies(bot, this, engines[BOT_STATE_DEAD]); - if (sPlayerbotAIConfig->applyInstanceStrategies) - ApplyInstanceStrategies(bot->GetMapId()); - - for (uint8 i = 0; i < BOT_STATE_MAX; i++) - engines[i]->Init(); - - // if (load) - // sPlayerbotRepository->Load(this); -} - -bool PlayerbotAI::IsRanged(Player* player, bool bySpec) -{ - PlayerbotAI* botAi = GET_PLAYERBOT_AI(player); - if (!bySpec && botAi) - return botAi->ContainsStrategy(STRATEGY_TYPE_RANGED); - - int tab = AiFactory::GetPlayerSpecTab(player); - switch (player->getClass()) - { - case CLASS_DEATH_KNIGHT: - case CLASS_WARRIOR: - case CLASS_ROGUE: - return false; - break; - case CLASS_DRUID: - if (tab == 1) - { - return false; - } - break; - case CLASS_PALADIN: - if (tab != 0) - { - return false; - } - break; - case CLASS_SHAMAN: - if (tab == 1) - { - return false; - } - break; - } - - return true; -} - -bool PlayerbotAI::IsMelee(Player* player, bool bySpec) { return !IsRanged(player, bySpec); } - -bool PlayerbotAI::IsCaster(Player* player, bool bySpec) -{ - return IsRanged(player, bySpec) && player->getClass() != CLASS_HUNTER; -} - -bool PlayerbotAI::IsCombo(Player* player) -{ - // int tab = AiFactory::GetPlayerSpecTab(player); - return player->getClass() == CLASS_ROGUE || - (player->getClass() == CLASS_DRUID && player->HasAura(768)); // cat druid -} - -bool PlayerbotAI::IsRangedDps(Player* player, bool bySpec) { return IsRanged(player, bySpec) && IsDps(player, bySpec); } - -bool PlayerbotAI::IsAssistHealOfIndex(Player* player, int index, bool ignoreDeadPlayers) -{ - Group* group = player->GetGroup(); - if (!group) - return false; - - int counter = 0; - - // First, assistants - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - if (!member) - continue; - - if (ignoreDeadPlayers && !member->IsAlive()) - continue; - - if (group->IsAssistant(member->GetGUID()) && IsHeal(member)) - { - if (index == counter) - return player == member; - counter++; - } - } - - // If not enough assistants, get non-assistants - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - if (!member) - continue; - - if (ignoreDeadPlayers && !member->IsAlive()) - continue; - - if (!group->IsAssistant(member->GetGUID()) && IsHeal(member)) - { - if (index == counter) - return player == member; - counter++; - } - } - - return false; -} - -bool PlayerbotAI::IsAssistRangedDpsOfIndex(Player* player, int index, bool ignoreDeadPlayers) -{ - Group* group = player->GetGroup(); - if (!group) - return false; - - int counter = 0; - - // First, assistants - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - if (!member) - continue; - - if (ignoreDeadPlayers && !member->IsAlive()) - continue; - - if (group->IsAssistant(member->GetGUID()) && IsRangedDps(member)) - { - if (index == counter) - return player == member; - counter++; - } - } - - // If not enough assistants, get non-assistants - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - if (!member) - continue; - - if (ignoreDeadPlayers && !member->IsAlive()) - continue; - - if (!group->IsAssistant(member->GetGUID()) && IsRangedDps(member)) - { - if (index == counter) - return player == member; - counter++; - } - } - - return false; -} - -bool PlayerbotAI::HasAggro(Unit* unit) -{ - if (!IsValidUnit(unit)) - return false; - - bool isMT = IsMainTank(bot); - Unit* victim = unit->GetVictim(); - if (victim && (victim->GetGUID() == bot->GetGUID() || (!isMT && victim->ToPlayer() && IsTank(victim->ToPlayer())))) - { - return true; - } - return false; -} - -int32 PlayerbotAI::GetAssistTankIndex(Player* player) -{ - Group* group = player->GetGroup(); - if (!group) - { - return -1; - } - - int counter = 0; - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - if (!member) - { - continue; - } - - if (player == member) - { - return counter; - } - - if (IsTank(member, true) && group->IsAssistant(member->GetGUID())) - { - counter++; - } - } - - return 0; -} - -int32 PlayerbotAI::GetGroupSlotIndex(Player* player) -{ - Group* group = bot->GetGroup(); - if (!group) - { - return -1; - } - int counter = 0; - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - - if (!member) - { - continue; - } - - if (player == member) - { - return counter; - } - counter++; - } - return 0; -} - -int32 PlayerbotAI::GetRangedIndex(Player* player) -{ - if (!IsRanged(player)) - { - return -1; - } - Group* group = bot->GetGroup(); - if (!group) - { - return -1; - } - int counter = 0; - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - - if (!member) - { - continue; - } - - if (player == member) - { - return counter; - } - if (IsRanged(member)) - { - counter++; - } - } - return 0; -} - -int32 PlayerbotAI::GetClassIndex(Player* player, uint8 cls) -{ - if (player->getClass() != cls) - { - return -1; - } - Group* group = bot->GetGroup(); - if (!group) - { - return -1; - } - int counter = 0; - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - - if (!member) - { - continue; - } - - if (player == member) - { - return counter; - } - if (member->getClass() == cls) - { - counter++; - } - } - return 0; -} -int32 PlayerbotAI::GetRangedDpsIndex(Player* player) -{ - if (!IsRangedDps(player)) - { - return -1; - } - Group* group = bot->GetGroup(); - if (!group) - { - return -1; - } - int counter = 0; - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - - if (!member) - { - continue; - } - - if (player == member) - { - return counter; - } - if (IsRangedDps(member)) - { - counter++; - } - } - return 0; -} - -int32 PlayerbotAI::GetMeleeIndex(Player* player) -{ - if (IsRanged(player)) - { - return -1; - } - Group* group = bot->GetGroup(); - if (!group) - { - return -1; - } - int counter = 0; - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - - if (!member) - { - continue; - } - - if (player == member) - { - return counter; - } - if (!IsRanged(member)) - { - counter++; - } - } - return 0; -} - -bool PlayerbotAI::IsTank(Player* player, bool bySpec) -{ - PlayerbotAI* botAi = GET_PLAYERBOT_AI(player); - if (!bySpec && botAi) - return botAi->ContainsStrategy(STRATEGY_TYPE_TANK); - - int tab = AiFactory::GetPlayerSpecTab(player); - switch (player->getClass()) - { - case CLASS_DEATH_KNIGHT: - if (tab == DEATH_KNIGHT_TAB_BLOOD) - { - return true; - } - break; - case CLASS_PALADIN: - if (tab == PALADIN_TAB_PROTECTION) - { - return true; - } - break; - case CLASS_WARRIOR: - if (tab == WARRIOR_TAB_PROTECTION) - { - return true; - } - break; - case CLASS_DRUID: - if (tab == DRUID_TAB_FERAL && (player->GetShapeshiftForm() == FORM_BEAR || - player->GetShapeshiftForm() == FORM_DIREBEAR || player->HasAura(16931))) - { - return true; - } - break; - } - return false; -} - -bool PlayerbotAI::IsHeal(Player* player, bool bySpec) -{ - PlayerbotAI* botAi = GET_PLAYERBOT_AI(player); - if (!bySpec && botAi) - return botAi->ContainsStrategy(STRATEGY_TYPE_HEAL); - - int tab = AiFactory::GetPlayerSpecTab(player); - switch (player->getClass()) - { - case CLASS_PRIEST: - if (tab == PRIEST_TAB_DISCIPLINE || tab == PRIEST_TAB_HOLY) - { - return true; - } - break; - case CLASS_DRUID: - if (tab == DRUID_TAB_RESTORATION) - { - return true; - } - break; - case CLASS_SHAMAN: - if (tab == SHAMAN_TAB_RESTORATION) - { - return true; - } - break; - case CLASS_PALADIN: - if (tab == PALADIN_TAB_HOLY) - { - return true; - } - break; - } - return false; -} - -bool PlayerbotAI::IsDps(Player* player, bool bySpec) -{ - PlayerbotAI* botAi = GET_PLAYERBOT_AI(player); - if (!bySpec && botAi) - return botAi->ContainsStrategy(STRATEGY_TYPE_DPS); - - int tab = AiFactory::GetPlayerSpecTab(player); - switch (player->getClass()) - { - case CLASS_MAGE: - case CLASS_WARLOCK: - case CLASS_HUNTER: - case CLASS_ROGUE: - return true; - case CLASS_PRIEST: - if (tab == PRIEST_TAB_SHADOW) - { - return true; - } - break; - case CLASS_DRUID: - if (tab == DRUID_TAB_BALANCE) - { - return true; - } - if (tab == DRUID_TAB_FERAL && !IsTank(player, bySpec)) - { - return true; - } + strategyName = "icc"; // Icecrown Citadel break; - case CLASS_SHAMAN: - if (tab != SHAMAN_TAB_RESTORATION) - { - return true; - } + case 632: + strategyName = "wotlk-fos"; // The Forge of Souls break; - case CLASS_PALADIN: - if (tab == PALADIN_TAB_RETRIBUTION) - { - return true; - } + case 650: + strategyName = "wotlk-toc"; // Trial of the Champion break; - case CLASS_DEATH_KNIGHT: - if (tab != DEATH_KNIGHT_TAB_BLOOD) - { - return true; - } + case 658: + strategyName = "wotlk-pos"; // Pit of Saron break; - case CLASS_WARRIOR: - if (tab != WARRIOR_TAB_PROTECTION) - { - return true; - } + case 668: + strategyName = "wotlk-hor"; // Halls of Reflection + break; + default: break; } - return false; -} - -bool PlayerbotAI::IsMainTank(Player* player) -{ - Group* group = player->GetGroup(); - if (!group) + if (strategyName.empty()) + return; + engines[BOT_STATE_COMBAT]->addStrategy(strategyName); + engines[BOT_STATE_NON_COMBAT]->addStrategy(strategyName); + if (tellMaster && !strategyName.empty()) { - return IsTank(player); + std::ostringstream out; + out << "Added " << strategyName << " instance strategy"; + TellMasterNoFacing(out.str()); } +} - ObjectGuid mainTank = ObjectGuid(); - Group::MemberSlotList const& slots = group->GetMemberSlots(); +bool PlayerbotAI::DoSpecificAction(std::string const name, Event event, bool silent, std::string const qualifier) +{ + std::ostringstream out; - for (Group::member_citerator itr = slots.begin(); itr != slots.end(); ++itr) + for (uint8 i = 0; i < BOT_STATE_MAX; i++) { - if (itr->flags & MEMBER_FLAG_MAINTANK) + ActionResult res = engines[i]->ExecuteAction(name, event, qualifier); + switch (res) { - mainTank = itr->guid; - break; + case ACTION_RESULT_UNKNOWN: + continue; + case ACTION_RESULT_OK: + if (!silent) + { + PlaySound(TEXT_EMOTE_NOD); + } + return true; + case ACTION_RESULT_IMPOSSIBLE: + out << name << ": impossible"; + if (!silent) + { + TellError(out.str()); + PlaySound(TEXT_EMOTE_NO); + } + return false; + case ACTION_RESULT_USELESS: + out << name << ": useless"; + if (!silent) + { + TellError(out.str()); + PlaySound(TEXT_EMOTE_NO); + } + return false; + case ACTION_RESULT_FAILED: + if (!silent) + { + out << name << ": failed"; + TellError(out.str()); + } + return false; + default: + break; } } - if (mainTank != ObjectGuid::Empty) - { - return player->GetGUID() == mainTank; - } - - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + if (!silent) { - Player* member = ref->GetSource(); - if (!member) - { - continue; - } - - if (IsTank(member) && member->IsAlive()) - { - return player->GetGUID() == member->GetGUID(); - } + out << name << ": unknown action"; + TellError(out.str()); } return false; } -bool PlayerbotAI::IsBotMainTank(Player* player) +bool PlayerbotAI::PlaySound(uint32 emote) { - if (!player || !player->IsInWorld() || player->IsDuringRemoveFromWorld()) - return false; - - WorldSession* session = player->GetSession(); - if (!session || !session->IsBot()) - return false; - - if (!IsTank(player)) - return false; - - if (IsMainTank(player)) + if (EmotesTextSoundEntry const* soundEntry = FindTextSoundEmoteFor(emote, bot->getRace(), bot->getGender())) { + bot->PlayDistanceSound(soundEntry->SoundId); return true; } - Group* group = player->GetGroup(); - if (!group) - { - return true; // If no group, consider the bot as main tank - } - - int32 botAssistTankIndex = GetAssistTankIndex(player); - if (botAssistTankIndex == -1) - { - return false; - } - - for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) - { - Player* member = gref->GetSource(); - if (!member) - { - continue; - } - - int32 memberAssistTankIndex = GetAssistTankIndex(member); - if (memberAssistTankIndex == -1) - { - continue; - } - - if (memberAssistTankIndex == botAssistTankIndex && player == member) - { - return true; - } - - if (memberAssistTankIndex < botAssistTankIndex && member->GetSession()->IsBot()) - { - return false; - } + return false; +} - return false; - } +bool PlayerbotAI::PlayEmote(uint32 emote) +{ + WorldPacket data(SMSG_TEXT_EMOTE); + data << (TextEmotes)emote; + data << EmoteAction::GetNumberOfEmoteVariants((TextEmotes)emote, bot->getRace(), bot->getGender()); + data << ((master && (sServerFacade->GetDistance2d(bot, master) < 30.0f) && urand(0, 1)) ? master->GetGUID() + : (bot->GetTarget() && urand(0, 1)) ? bot->GetTarget() + : ObjectGuid::Empty); + bot->GetSession()->HandleTextEmoteOpcode(data); return false; } -uint32 PlayerbotAI::GetGroupTankNum(Player* player) +bool PlayerbotAI::ContainsStrategy(StrategyType type) { - Group* group = player->GetGroup(); - if (!group) + for (uint8 i = 0; i < BOT_STATE_MAX; i++) { - return 0; + if (engines[i]->HasStrategyType(type)) + return true; } - uint32 result = 0; - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - - if (!member) - { - continue; - } - if (IsTank(member) && member->IsAlive()) - { - result++; - } - } - return result; + return false; } -bool PlayerbotAI::IsAssistTank(Player* player) { return IsTank(player) && !IsMainTank(player); } +bool PlayerbotAI::HasStrategy(std::string const name, BotState type) { return engines[type]->HasStrategy(name); } -bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index, bool ignoreDeadPlayers) +void PlayerbotAI::ResetStrategies(bool /*load*/) { - Group* group = player->GetGroup(); - if (!group) - return false; - - int counter = 0; - - // First, assistants - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - if (!member) - continue; - - if (ignoreDeadPlayers && !member->IsAlive()) - continue; - - if (group->IsAssistant(member->GetGUID()) && IsAssistTank(member)) - { - if (index == counter) - return player == member; - counter++; - } - } + for (uint8 i = 0; i < BOT_STATE_MAX; i++) + engines[i]->removeAllStrategies(); - // If not enough assistants, get non-assistants - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - if (!member) - continue; + AiFactory::AddDefaultCombatStrategies(bot, this, engines[BOT_STATE_COMBAT]); + AiFactory::AddDefaultNonCombatStrategies(bot, this, engines[BOT_STATE_NON_COMBAT]); + AiFactory::AddDefaultDeadStrategies(bot, this, engines[BOT_STATE_DEAD]); + if (sPlayerbotAIConfig->applyInstanceStrategies) + ApplyInstanceStrategies(bot->GetMapId()); - if (ignoreDeadPlayers && !member->IsAlive()) - continue; + for (uint8 i = 0; i < BOT_STATE_MAX; i++) + engines[i]->Init(); - if (!group->IsAssistant(member->GetGUID()) && IsAssistTank(member)) - { - if (index == counter) - return player == member; - counter++; - } - } - return false; + // if (load) + // sPlayerbotRepository->Load(this); } namespace acore @@ -2526,19 +1893,19 @@ WorldObject* PlayerbotAI::GetWorldObject(ObjectGuid guid) return ObjectAccessor::GetWorldObject(*bot, guid); } -const AreaTableEntry* PlayerbotAI::GetCurrentArea() +AreaTableEntry const* PlayerbotAI::GetCurrentArea() { return sAreaTableStore.LookupEntry( bot->GetMap()->GetAreaId(bot->GetPhaseMask(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ())); } -const AreaTableEntry* PlayerbotAI::GetCurrentZone() +AreaTableEntry const* PlayerbotAI::GetCurrentZone() { return sAreaTableStore.LookupEntry( bot->GetMap()->GetZoneId(bot->GetPhaseMask(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ())); } -std::string PlayerbotAI::GetLocalizedAreaName(const AreaTableEntry* entry) +std::string PlayerbotAI::GetLocalizedAreaName(AreaTableEntry const* entry) { std::string name; if (entry) @@ -2554,7 +1921,7 @@ std::string PlayerbotAI::GetLocalizedAreaName(const AreaTableEntry* entry) std::string PlayerbotAI::GetLocalizedCreatureName(uint32 entry) { std::string name; - const CreatureLocale* cl = sObjectMgr->GetCreatureLocale(entry); + CreatureLocale const* cl = sObjectMgr->GetCreatureLocale(entry); if (cl) ObjectMgr::GetLocaleString(cl->Name, sWorld->GetDefaultDbcLocale(), name); if (name.empty()) @@ -2569,7 +1936,7 @@ std::string PlayerbotAI::GetLocalizedCreatureName(uint32 entry) std::string PlayerbotAI::GetLocalizedGameObjectName(uint32 entry) { std::string name; - const GameObjectLocale* gl = sObjectMgr->GetGameObjectLocale(entry); + GameObjectLocale const* gl = sObjectMgr->GetGameObjectLocale(entry); if (gl) ObjectMgr::GetLocaleString(gl->Name, sWorld->GetDefaultDbcLocale(), name); if (name.empty()) @@ -2608,7 +1975,7 @@ std::vector PlayerbotAI::GetPlayersInGroup() return members; } -bool PlayerbotAI::SayToGuild(const std::string& msg) +bool PlayerbotAI::SayToGuild(std::string const& msg) { if (msg.empty()) { @@ -2631,7 +1998,7 @@ bool PlayerbotAI::SayToGuild(const std::string& msg) return false; } -bool PlayerbotAI::SayToWorld(const std::string& msg) +bool PlayerbotAI::SayToWorld(std::string const& msg) { if (msg.empty()) { @@ -2652,7 +2019,7 @@ bool PlayerbotAI::SayToWorld(const std::string& msg) return false; } -bool PlayerbotAI::SayToChannel(const std::string& msg, const ChatChannelId& chanId) +bool PlayerbotAI::SayToChannel(std::string const& msg, ChatChannelId const& chanId) { // Checks whether the message or ChannelMgr is valid if (msg.empty()) @@ -2707,7 +2074,7 @@ bool PlayerbotAI::SayToChannel(const std::string& msg, const ChatChannelId& chan return false; } -bool PlayerbotAI::SayToParty(const std::string& msg) +bool PlayerbotAI::SayToParty(std::string const& msg) { if (!bot->GetGroup()) return false; @@ -2724,7 +2091,7 @@ bool PlayerbotAI::SayToParty(const std::string& msg) return true; } -bool PlayerbotAI::SayToRaid(const std::string& msg) +bool PlayerbotAI::SayToRaid(std::string const& msg) { if (!bot->GetGroup() || bot->GetGroup()->isRaidGroup()) return false; @@ -2741,7 +2108,7 @@ bool PlayerbotAI::SayToRaid(const std::string& msg) return true; } -bool PlayerbotAI::Yell(const std::string& msg) +bool PlayerbotAI::Yell(std::string const& msg) { if (bot->GetTeamId() == TeamId::TEAM_ALLIANCE) { @@ -2755,7 +2122,7 @@ bool PlayerbotAI::Yell(const std::string& msg) return true; } -bool PlayerbotAI::Say(const std::string& msg) +bool PlayerbotAI::Say(std::string const& msg) { if (bot->GetTeamId() == TeamId::TEAM_ALLIANCE) { @@ -2769,7 +2136,7 @@ bool PlayerbotAI::Say(const std::string& msg) return true; } -bool PlayerbotAI::Whisper(const std::string& msg, const std::string& receiverName) +bool PlayerbotAI::Whisper(std::string const& msg, std::string const& receiverName) { const auto receiver = ObjectAccessor::FindPlayerByName(receiverName); if (!receiver) @@ -2851,7 +2218,7 @@ bool PlayerbotAI::IsTellAllowed(PlayerbotSecurityLevel securityLevel) if (!GetSecurity()->CheckLevelFor(securityLevel, true, master)) return false; - if (sPlayerbotAIConfig->whisperDistance && !bot->GetGroup() && sRandomPlayerbotMgr->IsRandomBot(bot) && + if (sPlayerbotAIConfig->whisperDistance && !bot->GetGroup() && sManagerRegistry.GetRandomBotManager().IsRandomBot(bot) && master->GetSession()->GetSecurity() < SEC_GAMEMASTER && (bot->GetMapId() != master->GetMapId() || sServerFacade->GetDistance2d(bot, master) > sPlayerbotAIConfig->whisperDistance)) @@ -4021,6 +3388,68 @@ bool PlayerbotAI::CastVehicleSpell(uint32 spellId, Unit* target) return true; } +bool PlayerbotAI::CastVehicleSpell(uint32 spellId, float x, float y, float z) +{ + if (!spellId) + return false; + + Vehicle* vehicle = bot->GetVehicle(); + if (!vehicle) + return false; + + VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(bot); + if (!seat || !(seat->m_flags & VEHICLE_SEAT_FLAG_CAN_CAST)) + return false; + + Unit* vehicleBase = vehicle->GetBase(); + if (!vehicleBase || !vehicleBase->IsAlive()) + return false; + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); + if (!spellInfo) + return false; + + // turn vehicle to face target location + if (seat->CanControl() || (seat->m_flags & VEHICLE_SEAT_FLAG_ALLOW_TURNING)) + { + vehicleBase->SetFacingTo(vehicleBase->GetAngle(x, y)); + } + + Spell* spell = new Spell(vehicleBase, spellInfo, TRIGGERED_NONE); + + SpellCastTargets targets; + WorldLocation dest(bot->GetMapId(), x, y, z, 0.0f); + targets.SetDst(dest); + targets.SetSpeed(30.0f); + float dist = vehicleBase->GetPosition().GetExactDist(dest); + float elev = dist >= 110.0f ? 1.0f : pow(((dist + 10.0f) / 120.0f), 2.0f); + targets.SetElevation(elev); + + if (spellInfo->Targets & TARGET_FLAG_SOURCE_LOCATION) + { + targets.SetSrc(vehicleBase->GetPositionX(), vehicleBase->GetPositionY(), vehicleBase->GetPositionZ()); + } + + spell->prepare(&targets); + + if (seat->CanControl() && vehicleBase->isMoving() && spell->GetCastTime()) + { + vehicleBase->StopMoving(); + SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown); + spell->cancel(); + return false; + } + + if (HasStrategy("debug spell", BOT_STATE_NON_COMBAT)) + { + std::ostringstream out; + out << "Casting Vehicle Spell (location) " << ChatHelper::FormatSpell(spellInfo); + TellMasterNoFacing(out); + } + + return true; +} + bool PlayerbotAI::IsInVehicle(bool canControl, bool canCast, bool canAttack, bool canTurn, bool fixed) { Vehicle* vehicle = bot->GetVehicle(); @@ -4290,7 +3719,7 @@ bool PlayerbotAI::HasRealPlayerMaster() bool PlayerbotAI::HasActivePlayerMaster() { return master && !GET_PLAYERBOT_AI(master); } -bool PlayerbotAI::IsAlt() { return HasRealPlayerMaster() && !sRandomPlayerbotMgr->IsRandomBot(bot); } +bool PlayerbotAI::IsAlt() { return HasRealPlayerMaster() && !sManagerRegistry.GetRandomBotManager().IsRandomBot(bot); } Player* PlayerbotAI::GetGroupLeader() { @@ -4379,7 +3808,7 @@ bool PlayerbotAI::HasPlayerNearby(WorldPosition* pos, float range) { float sqRange = range * range; bool nearPlayer = false; - for (auto& player : sRandomPlayerbotMgr->GetPlayers()) + for (auto& player : sManagerRegistry.GetRandomBotManager().GetPlayers()) { if (!player->IsGameMaster() || player->isGMVisible()) { @@ -4413,7 +3842,7 @@ bool PlayerbotAI::HasManyPlayersNearby(uint32 trigerrValue, float range) float sqRange = range * range; uint32 found = 0; - for (auto& player : sRandomPlayerbotMgr->GetPlayers()) + for (auto& player : sManagerRegistry.GetRandomBotManager().GetPlayers()) { if ((!player->IsGameMaster() || player->isGMVisible()) && sServerFacade->GetDistance2d(player, bot) < sqRange) { @@ -4461,7 +3890,7 @@ inline bool ZoneHasRealPlayers(Player* bot) return false; } - for (Player* player : sRandomPlayerbotMgr->GetPlayers()) + for (Player* player : sManagerRegistry.GetRandomBotManager().GetPlayers()) { if (player->GetMapId() != bot->GetMapId()) continue; @@ -4510,7 +3939,7 @@ bool PlayerbotAI::AllowActive(ActivityType activityType) // which prevents unneeded expensive GameTime calls. if (_isBotInitializing) { - _isBotInitializing = GameTime::GetUptime().count() < sPlayerbotAIConfig->maxRandomBots * 0.11; + _isBotInitializing = GameTime::GetUptime().count() < sPlayerbotAIConfig->maxRandomBots * 0.11f; // no activity allowed during bot initialization if (_isBotInitializing) @@ -4637,7 +4066,7 @@ bool PlayerbotAI::AllowActive(ActivityType activityType) if (!bot->GetGUID()) return false; - for (auto& player : sRandomPlayerbotMgr->GetPlayers()) + for (auto& player : sManagerRegistry.GetRandomBotManager().GetPlayers()) { if (!player || !player->GetSession() || !player->IsInWorld() || player->IsDuringRemoveFromWorld() || player->GetSession()->isLogingOut()) @@ -5016,7 +4445,7 @@ uint32 PlayerbotAI::GetMixedGearScore(Player* player, bool withBags, bool withBa { topGearScore.push_back(gearScore[i]); } - std::sort(topGearScore.begin(), topGearScore.end(), [&](const uint32 lhs, const uint32 rhs) { return lhs > rhs; }); + std::sort(topGearScore.begin(), topGearScore.end(), [](const uint32 lhs, const uint32 rhs) { return lhs > rhs; }); uint32 sum = 0; for (uint32 i = 0; i < std::min((uint32)topGearScore.size(), topN); i++) { @@ -5497,15 +4926,15 @@ Item* PlayerbotAI::FindStoneFor(Item* weapon) const if (!weapon) return nullptr; - const ItemTemplate* item_template = weapon->GetTemplate(); + ItemTemplate const* item_template = weapon->GetTemplate(); if (!item_template) return nullptr; - static const std::vector uPrioritizedSharpStoneIds = { + static std::vector const uPrioritizedSharpStoneIds = { ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, ELEMENTAL_SHARPENING_STONE, DENSE_SHARPENING_STONE, SOLID_SHARPENING_STONE, HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE}; - static const std::vector uPrioritizedWeightStoneIds = { + static std::vector const uPrioritizedWeightStoneIds = { ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE, HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE}; @@ -5546,15 +4975,15 @@ Item* PlayerbotAI::FindOilFor(Item* weapon) const if (!weapon) return nullptr; - const ItemTemplate* item_template = weapon->GetTemplate(); + ItemTemplate const* item_template = weapon->GetTemplate(); if (!item_template) return nullptr; - static const std::vector uPrioritizedWizardOilIds = { + static std::vector const uPrioritizedWizardOilIds = { BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL, BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL}; - static const std::vector uPrioritizedManaOilIds = { + static std::vector const uPrioritizedManaOilIds = { BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL, BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL}; @@ -5562,7 +4991,7 @@ Item* PlayerbotAI::FindOilFor(Item* weapon) const int botClass = bot->getClass(); int specTab = AiFactory::GetPlayerSpecTab(bot); - const std::vector* prioritizedOils = nullptr; + std::vector const* prioritizedOils = nullptr; switch (botClass) { case CLASS_PRIEST: @@ -5782,9 +5211,9 @@ bool PlayerbotAI::HasItemInInventory(uint32 itemId) return false; } -std::vector> PlayerbotAI::GetCurrentQuestsRequiringItemId(uint32 itemId) +std::vector> PlayerbotAI::GetCurrentQuestsRequiringItemId(uint32 itemId) { - std::vector> result; + std::vector> result; if (!itemId) { @@ -5798,7 +5227,7 @@ std::vector> PlayerbotAI::GetCurrentQuestsRequir continue; // QuestStatus status = bot->GetQuestStatus(questId); - const Quest* quest = sObjectMgr->GetQuestTemplate(questId); + Quest const* quest = sObjectMgr->GetQuestTemplate(questId); for (uint8 i = 0; i < std::size(quest->RequiredItemId); ++i) { if (quest->RequiredItemId[i] == itemId) @@ -6094,6 +5523,8 @@ InventoryResult PlayerbotAI::CanEquipItem(uint8 slot, uint16& dest, Item* pItem, case EQUIPMENT_SLOT_TRINKET2: ignore = EQUIPMENT_SLOT_TRINKET1; break; + default: + break; } if (ignore == uint8(NULL_SLOT) || pItem != bot->GetItemByPos(INVENTORY_SLOT_BAG_0, ignore)) @@ -6303,6 +5734,8 @@ uint8 PlayerbotAI::FindEquipSlot(ItemTemplate const* proto, uint32 slot, bool sw if (bot->IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_EQUIP_RELIC)) slots[0] = EQUIPMENT_SLOT_RANGED; break; + default: + break; } break; } @@ -6360,7 +5793,7 @@ ChatChannelSource PlayerbotAI::GetChatChannelSource(Player* bot, uint32 type, st return ChatChannelSource::SRC_UNDEFINED; } - const Channel* channel = cMgr->GetChannel(channelName, bot); + Channel const* channel = cMgr->GetChannel(channelName, bot); if (channel) { switch (channel->GetChannelId()) @@ -6442,7 +5875,7 @@ ChatChannelSource PlayerbotAI::GetChatChannelSource(Player* bot, uint32 type, st return ChatChannelSource::SRC_UNDEFINED; } -bool PlayerbotAI::CheckLocationDistanceByLevel(Player* player, const WorldLocation& loc, bool fromStartUp) +bool PlayerbotAI::CheckLocationDistanceByLevel(Player* player, WorldLocation const& loc, bool fromStartUp) { if (player->GetLevel() > 16) return true; @@ -6471,9 +5904,9 @@ bool PlayerbotAI::CheckLocationDistanceByLevel(Player* player, const WorldLocati return dis <= bound; } -std::vector PlayerbotAI::GetAllCurrentQuests() +std::vector PlayerbotAI::GetAllCurrentQuests() { - std::vector result; + std::vector result; for (uint16 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot) { @@ -6489,9 +5922,9 @@ std::vector PlayerbotAI::GetAllCurrentQuests() return result; } -std::vector PlayerbotAI::GetCurrentIncompleteQuests() +std::vector PlayerbotAI::GetCurrentIncompleteQuests() { - std::vector result; + std::vector result; for (uint16 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot) { @@ -6762,7 +6195,7 @@ void PlayerbotAI::AddTimedEvent(std::function callback, uint32 delayMs) void PlayerbotAI::EvaluateHealerDpsStrategy() { - if (!IsHeal(bot, true)) + if (!BotRoleService::IsHealStatic(bot, true)) return; if (sPlayerbotAIConfig->IsRestrictedHealerDPSMap(bot->GetMapId())) diff --git a/src/Bot/PlayerbotAI.h b/src/Bot/PlayerbotAI.h index b2df4352cc..2a10c66ae5 100644 --- a/src/Bot/PlayerbotAI.h +++ b/src/Bot/PlayerbotAI.h @@ -6,9 +6,11 @@ #ifndef _PLAYERBOT_PLAYERbotAI_H #define _PLAYERBOT_PLAYERbotAI_H +#include #include #include +#include "BotServiceContainer.h" #include "Chat.h" #include "ChatFilter.h" #include "ChatHelper.h" @@ -69,7 +71,7 @@ enum HealingItemId MAJOR_DREAMLESS_SLEEP_POTION = 20002 }; -enum BotState +enum BotState : int { BOT_STATE_COMBAT = 0, BOT_STATE_NON_COMBAT = 1, @@ -369,7 +371,7 @@ class ChatCommandHolder { } - const std::string& GetCommand() { return command; } + std::string const& GetCommand() { return command; } Player* GetOwner() { return owner; } uint32& GetType() { return type; } time_t& GetTime() { return time; } @@ -416,28 +418,6 @@ class PlayerbotAI : public PlayerbotAIBase void ReInitCurrentEngine(); void Reset(bool full = false); void LeaveOrDisbandGroup(); - static bool IsTank(Player* player, bool bySpec = false); - static bool IsHeal(Player* player, bool bySpec = false); - static bool IsDps(Player* player, bool bySpec = false); - static bool IsRanged(Player* player, bool bySpec = false); - static bool IsMelee(Player* player, bool bySpec = false); - static bool IsCaster(Player* player, bool bySpec = false); - static bool IsRangedDps(Player* player, bool bySpec = false); - static bool IsCombo(Player* player); - static bool IsBotMainTank(Player* player); - static bool IsMainTank(Player* player); - static uint32 GetGroupTankNum(Player* player); - static bool IsAssistTank(Player* player); - static bool IsAssistTankOfIndex(Player* player, int index, bool ignoreDeadPlayers = false); - static bool IsAssistHealOfIndex(Player* player, int index, bool ignoreDeadPlayers = false); - static bool IsAssistRangedDpsOfIndex(Player* player, int index, bool ignoreDeadPlayers = false); - bool HasAggro(Unit* unit); - static int32 GetAssistTankIndex(Player* player); - int32 GetGroupSlotIndex(Player* player); - int32 GetRangedIndex(Player* player); - int32 GetClassIndex(Player* player, uint8 cls); - int32 GetRangedDpsIndex(Player* player); - int32 GetMeleeIndex(Player* player); Creature* GetCreature(ObjectGuid guid); Unit* GetUnit(ObjectGuid guid); @@ -447,9 +427,9 @@ class PlayerbotAI : public PlayerbotAIBase // static GameObject* GetGameObject(GameObjectData const* gameObjectData); WorldObject* GetWorldObject(ObjectGuid guid); std::vector GetPlayersInGroup(); - const AreaTableEntry* GetCurrentArea(); - const AreaTableEntry* GetCurrentZone(); - static std::string GetLocalizedAreaName(const AreaTableEntry* entry); + AreaTableEntry const* GetCurrentArea(); + AreaTableEntry const* GetCurrentZone(); + static std::string GetLocalizedAreaName(AreaTableEntry const* entry); static std::string GetLocalizedCreatureName(uint32 entry); static std::string GetLocalizedGameObjectName(uint32 entry); bool TellMaster(std::ostringstream& stream, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL); @@ -459,14 +439,14 @@ class PlayerbotAI : public PlayerbotAIBase bool TellMasterNoFacing(std::string const text, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL); bool TellError(std::string const text, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL); - bool SayToGuild(const std::string& msg); - bool SayToWorld(const std::string& msg); - bool SayToChannel(const std::string& msg, const ChatChannelId& chanId); - bool SayToParty(const std::string& msg); - bool SayToRaid(const std::string& msg); - bool Yell(const std::string& msg); - bool Say(const std::string& msg); - bool Whisper(const std::string& msg, const std::string& receiverName); + bool SayToGuild(std::string const& msg); + bool SayToWorld(std::string const& msg); + bool SayToChannel(std::string const& msg, ChatChannelId const& chanId); + bool SayToParty(std::string const& msg); + bool SayToRaid(std::string const& msg); + bool Yell(std::string const& msg); + bool Say(std::string const& msg); + bool Whisper(std::string const& msg, std::string const& receiverName); void SpellInterrupted(uint32 spellid); int32 CalculateGlobalCooldown(uint32 spellid); @@ -529,6 +509,16 @@ class PlayerbotAI : public PlayerbotAIBase Player* GetBot() { return bot; } Player* GetMaster() { return master; } + + /** + * @brief Get the service container for this bot + * @return Reference to the service container + * + * The service container provides access to all bot services + * through clean interfaces, enabling testability and loose coupling. + */ + BotServiceContainer& GetServices() { return *services_; } + BotServiceContainer const& GetServices() const { return *services_; } Player* FindNewMaster(); // Checks if the bot is really a player. Players always have themselves as master. @@ -557,7 +547,7 @@ class PlayerbotAI : public PlayerbotAIBase bool IsSafe(WorldObject* obj); ChatChannelSource GetChatChannelSource(Player* bot, uint32 type, std::string channelName); - bool CheckLocationDistanceByLevel(Player* player, const WorldLocation &loc, bool fromStartUp = false); + bool CheckLocationDistanceByLevel(Player* player, WorldLocation const& loc, bool fromStartUp = false); bool HasCheat(BotCheatMask mask) { @@ -588,11 +578,11 @@ class PlayerbotAI : public PlayerbotAIBase std::vector GetInventoryItems(); uint32 GetInventoryItemsCountWithId(uint32 itemId); bool HasItemInInventory(uint32 itemId); - std::vector> GetCurrentQuestsRequiringItemId(uint32 itemId); + std::vector> GetCurrentQuestsRequiringItemId(uint32 itemId); uint32 GetReactDelay(); - std::vector GetAllCurrentQuests(); - std::vector GetCurrentIncompleteQuests(); + std::vector GetAllCurrentQuests(); + std::vector GetCurrentIncompleteQuests(); std::set GetAllCurrentQuestIds(); std::set GetCurrentIncompleteQuestIds(); void PetFollow(); @@ -614,13 +604,13 @@ class PlayerbotAI : public PlayerbotAIBase void UpdateAIGroupMaster(); Item* FindItemInInventory(std::function checkItem) const; void HandleCommands(); - void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL); + void HandleCommand(uint32 type, std::string const& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL); bool _isBotInitializing = false; - inline bool IsValidUnit(const Unit* unit) const + inline bool IsValidUnit(Unit const* unit) const { return unit && unit->IsInWorld() && !unit->IsDuringRemoveFromWorld(); } - inline bool IsValidPlayer(const Player* player) const + inline bool IsValidPlayer(Player const* player) const { return player && player->GetSession() && player->IsInWorld() && !player->IsDuringRemoveFromWorld() && !player->IsBeingTeleported(); @@ -633,6 +623,9 @@ class PlayerbotAI : public PlayerbotAIBase Engine* currentEngine; Engine* engines[BOT_STATE_MAX]; BotState currentState; + + // Service container for DI-based architecture + std::unique_ptr services_; ChatHelper chatHelper; std::list chatCommands; std::list chatReplies; diff --git a/src/Bot/PlayerbotMgr.cpp b/src/Bot/PlayerbotMgr.cpp index e01afb0f7c..ee37b973da 100644 --- a/src/Bot/PlayerbotMgr.cpp +++ b/src/Bot/PlayerbotMgr.cpp @@ -3,6 +3,7 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "PlayerbotMgr.h" #include @@ -14,6 +15,7 @@ #include #include +#include "Bot/Core/ManagerRegistry.h" #include "ChannelMgr.h" #include "CharacterCache.h" #include "CharacterPackets.h" @@ -74,7 +76,6 @@ class PlayerbotLoginQueryHolder : public LoginQueryHolder { private: uint32 masterAccountId; - PlayerbotHolder* playerbotHolder; public: PlayerbotLoginQueryHolder(uint32 masterAccount, uint32 accountId, ObjectGuid guid) : LoginQueryHolder(accountId, guid), masterAccountId(masterAccount) @@ -126,7 +127,7 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId return; } uint32 count = mgr->GetPlayerbotsCount() + botLoading.size(); - if (count >= sPlayerbotAIConfig->maxAddedBots) + if (count >= static_cast(sPlayerbotAIConfig->maxAddedBots)) { allowed = false; out << "Failure: You have added too many bots (more than " << sPlayerbotAIConfig->maxAddedBots << ")"; @@ -308,7 +309,7 @@ void PlayerbotMgr::CancelLogout() { WorldPackets::Character::LogoutCancel data = WorldPacket(CMSG_LOGOUT_CANCEL); bot->GetSession()->HandleLogoutCancelOpcode(data); - botAI->TellMaster("Logout cancelled!"); + botAI->GetServices().GetChatService().TellMaster("Logout cancelled!"); } } @@ -395,7 +396,7 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid) return; else if (bot) { - botAI->TellMaster("I'm logging out!"); + botAI->GetServices().GetChatService().TellMaster("I'm logging out!"); WorldPackets::Character::LogoutRequest data = WorldPacket(CMSG_LOGOUT_REQUEST); botWorldSessionPtr->HandleLogoutRequestOpcode(data); if (!bot) @@ -418,7 +419,7 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid) } // if instant logout possible, do it else if (bot && (logout || !botWorldSessionPtr->isLogingOut())) { - botAI->TellMaster("Goodbye!"); + botAI->GetServices().GetChatService().TellMaster("Goodbye!"); RemoveFromPlayerbotsMap(guid); // deletes bot player ptr inside this WorldSession PlayerBotMap botWorldSessionPtr->LogoutPlayer(true); // this will delete the bot Player object and PlayerbotAI object delete botWorldSessionPtr; // finally delete the bot's WorldSession @@ -435,14 +436,14 @@ void PlayerbotHolder::DisablePlayerBot(ObjectGuid guid) { return; } - botAI->TellMaster("Goodbye!"); + botAI->GetServices().GetChatService().TellMaster("Goodbye!"); bot->StopMoving(); bot->GetMotionMaster()->Clear(); Group* group = bot->GetGroup(); if (group && !bot->InBattleground() && !bot->InBattlegroundQueue() && botAI->HasActivePlayerMaster()) { - sPlayerbotRepository->Save(botAI); + sManagerRegistry.GetBotRepository().Save(botAI); } LOG_DEBUG("playerbots", "Bot {} logged out", bot->GetName().c_str()); @@ -554,7 +555,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) { botAI->ResetStrategies(!sRandomPlayerbotMgr->IsRandomBot(bot)); } - sPlayerbotRepository->Load(botAI); + sManagerRegistry.GetBotRepository().Load(botAI); if (master && !master->HasUnitState(UNIT_STATE_IN_FLIGHT)) { @@ -568,7 +569,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) // set delay on login botAI->SetNextCheckDelay(urand(2000, 4000)); - botAI->TellMaster("Hello!", PLAYERBOT_SECURITY_TALK); + botAI->GetServices().GetChatService().TellMaster("Hello!", PLAYERBOT_SECURITY_TALK); // Queue group operations for world thread if (master && master->GetGroup() && !group) @@ -677,7 +678,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) char new_channel_name_buf[100]; //3459 is ID for a zone named "City" (only exists for the sake of using its name) //Currently in magons TBC, if you switch zones, then you join "Trade - " and "GuildRecruitment - " - //which is a core bug, should be "Trade - City" and "GuildRecruitment - City" in both 1.12 and TBC + //which is a core bug, should be "Trade - City" and "GuildRecruitment - City" in both 1.12f and TBC //but if you (actual player) logout in a city and log back in - you join "City" versions snprintf(new_channel_name_buf, 100, channel->pattern[locale], GET_PLAYERBOT_AI(bot)->GetLocalizedAreaName(GetAreaEntryByAreaID(3459)).c_str()); new_channel = cMgr->GetJoinChannel(new_channel_name_buf, channel->ChannelID); @@ -700,16 +701,11 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) } std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, ObjectGuid guid, ObjectGuid masterguid, - bool admin, uint32 masterAccountId, uint32 masterGuildId) + bool admin, uint32 masterAccountId, uint32 /*masterGuildId*/) { if (!sPlayerbotAIConfig->enabled || guid.IsEmpty()) return "bot system is disabled"; - uint32 botAccount = sCharacterCache->GetCharacterAccountIdByGuid(guid); - //bool isRandomBot = sRandomPlayerbotMgr->IsRandomBot(guid.GetCounter()); //not used, line marked for removal. - //bool isRandomAccount = sPlayerbotAIConfig->IsInRandomAccountList(botAccount); //not used, shadowed, line marked for removal. - //bool isMasterAccount = (masterAccountId == botAccount); //not used, line marked for removal. - if (cmd == "add" || cmd == "addaccount" || cmd == "login") { if (ObjectAccessor::FindPlayer(guid)) @@ -722,11 +718,11 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje if (!accountId) return "character not found"; - if (!sPlayerbotAIConfig->allowAccountBots && accountId != masterAccountId && - !(sPlayerbotAIConfig->allowTrustedAccountBots && IsAccountLinked(accountId, masterAccountId))) - { - return "you can only add bots from your own account or linked accounts"; - } + if (!sPlayerbotAIConfig->allowAccountBots && accountId != masterAccountId && + !(sPlayerbotAIConfig->allowTrustedAccountBots && IsAccountLinked(accountId, masterAccountId))) + { + return "you can only add bots from your own account or linked accounts"; + } } AddPlayerBot(guid, masterAccountId); @@ -1183,8 +1179,8 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg return messages; } uint8 teamId = master->GetTeamId(true); - const std::unordered_set &guidCache = sRandomPlayerbotMgr->addclassCache[RandomPlayerbotMgr::GetTeamClassIdx(teamId == TEAM_ALLIANCE, claz)]; - for (const ObjectGuid &guid: guidCache) + std::unordered_set const& guidCache = sRandomPlayerbotMgr->addclassCache[RandomPlayerbotMgr::GetTeamClassIdx(teamId == TEAM_ALLIANCE, claz)]; + for (ObjectGuid const& guid: guidCache) { // If the user requested a specific gender, skip any character that doesn't match. if (gender != -1 && GetOfflinePlayerGender(guid) != gender) @@ -1452,7 +1448,7 @@ std::string const PlayerbotHolder::ListBots(Player* master) return out.str(); } -std::string const PlayerbotHolder::LookupBots(Player* master) +std::string const PlayerbotHolder::LookupBots(Player* /*master*/) { std::list messages; messages.push_back("Classes Available:"); @@ -1574,6 +1570,8 @@ void PlayerbotMgr::HandleMasterIncomingPacket(WorldPacket const& packet) CancelLogout(); break; } + default: + break; } } @@ -1690,7 +1688,7 @@ void PlayerbotMgr::TellError(std::string const botName, std::string const text) errors[text] = names; } -void PlayerbotMgr::CheckTellErrors(uint32 elapsed) +void PlayerbotMgr::CheckTellErrors(uint32 /*elapsed*/) { time_t now = time(nullptr); if ((now - lastErrorTell) < sPlayerbotAIConfig->errorDelay / 1000) @@ -1811,7 +1809,7 @@ PlayerbotMgr* PlayerbotsMgr::GetPlayerbotMgr(Player* player) return nullptr; } -void PlayerbotMgr::HandleSetSecurityKeyCommand(Player* player, const std::string& key) +void PlayerbotMgr::HandleSetSecurityKeyCommand(Player* player, std::string const& key) { uint32 accountId = player->GetSession()->GetAccountId(); @@ -1832,7 +1830,7 @@ void PlayerbotMgr::HandleSetSecurityKeyCommand(Player* player, const std::string ChatHandler(player->GetSession()).PSendSysMessage("Security key set successfully."); } -void PlayerbotMgr::HandleLinkAccountCommand(Player* player, const std::string& accountName, const std::string& key) +void PlayerbotMgr::HandleLinkAccountCommand(Player* player, std::string const& accountName, std::string const& key) { QueryResult result = LoginDatabase.Query("SELECT id FROM account WHERE username = '{}'", accountName); if (!result) @@ -1910,7 +1908,7 @@ void PlayerbotMgr::HandleViewLinkedAccountsCommand(Player* player) } while (result->NextRow()); } -void PlayerbotMgr::HandleUnlinkAccountCommand(Player* player, const std::string& accountName) +void PlayerbotMgr::HandleUnlinkAccountCommand(Player* player, std::string const& accountName) { QueryResult result = LoginDatabase.Query("SELECT id FROM account WHERE username = '{}'", accountName); if (!result) diff --git a/src/Bot/PlayerbotMgr.h b/src/Bot/PlayerbotMgr.h index 7ee576006b..636a257b31 100644 --- a/src/Bot/PlayerbotMgr.h +++ b/src/Bot/PlayerbotMgr.h @@ -83,10 +83,10 @@ class PlayerbotMgr : public PlayerbotHolder void SaveToDB(); - void HandleSetSecurityKeyCommand(Player* player, const std::string& key); - void HandleLinkAccountCommand(Player* player, const std::string& accountName, const std::string& key); + void HandleSetSecurityKeyCommand(Player* player, std::string const& key); + void HandleLinkAccountCommand(Player* player, std::string const& accountName, std::string const& key); void HandleViewLinkedAccountsCommand(Player* player); - void HandleUnlinkAccountCommand(Player* player, const std::string& accountName); + void HandleUnlinkAccountCommand(Player* player, std::string const& accountName); protected: void OnBotLoginInternal(Player* const bot) override; diff --git a/src/Bot/RandomPlayerbotMgr.cpp b/src/Bot/RandomPlayerbotMgr.cpp index 4d589ef1b4..388ea26d76 100644 --- a/src/Bot/RandomPlayerbotMgr.cpp +++ b/src/Bot/RandomPlayerbotMgr.cpp @@ -3,7 +3,9 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ +#include "BotChatService.h" #include "RandomPlayerbotMgr.h" +#include "BotRoleService.h" #include @@ -69,7 +71,7 @@ enum class CityId : uint8 { enum class FactionId : uint8 { ALLIANCE, HORDE, NEUTRAL }; // Map of banker entry → city + faction -static const std::unordered_map> bankerToCity = { +static std::unordered_map const> bankerToCity = { {2455, {CityId::STORMWIND, FactionId::ALLIANCE}}, {2456, {CityId::STORMWIND, FactionId::ALLIANCE}}, {2457, {CityId::STORMWIND, FactionId::ALLIANCE}}, {2460, {CityId::IRONFORGE, FactionId::ALLIANCE}}, {2461, {CityId::IRONFORGE, FactionId::ALLIANCE}}, {5099, {CityId::IRONFORGE, FactionId::ALLIANCE}}, {4155, {CityId::DARNASSUS, FactionId::ALLIANCE}}, {4208, {CityId::DARNASSUS, FactionId::ALLIANCE}}, {4209, {CityId::DARNASSUS, FactionId::ALLIANCE}}, @@ -86,7 +88,7 @@ static const std::unordered_map> bankerToCi }; // Map of city → available banker entries -static const std::unordered_map> cityToBankers = { +static std::unordered_map const> cityToBankers = { {CityId::STORMWIND, {2455, 2456, 2457}}, {CityId::IRONFORGE, {2460, 2461, 5099}}, {CityId::DARNASSUS, {4155, 4208, 4209}}, @@ -354,12 +356,12 @@ void RandomPlayerbotMgr::LogPlayerLocation() } } -void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) +void RandomPlayerbotMgr::UpdateAIInternal(uint32 /*elapsed*/, bool /*minimal*/) { if (totalPmo) totalPmo->finish(); - totalPmo = sPerfMonitor->start(PERF_MON_TOTAL, "RandomPlayerbotMgr::FullTick"); + totalPmo = sPerfMonitor->start(PerformanceMetric::Total, "RandomPlayerbotMgr::FullTick"); if (!sPlayerbotAIConfig->randomBotAutologin || !sPlayerbotAIConfig->enabled) return; @@ -396,14 +398,14 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) // which prevents unneeded expensive GameTime calls. if (_isBotInitializing) { - _isBotInitializing = GameTime::GetUptime().count() < sPlayerbotAIConfig->maxRandomBots * (0.11 + 0.4); + _isBotInitializing = GameTime::GetUptime().count() < sPlayerbotAIConfig->maxRandomBots * (0.11f + 0.4f); } uint32 updateIntervalTurboBoost = _isBotInitializing ? 1 : sPlayerbotAIConfig->randomBotUpdateInterval; SetNextCheckDelay(updateIntervalTurboBoost * (onlineBotFocus + 25) * 10); PerfMonitorOperation* pmo = sPerfMonitor->start( - PERF_MON_TOTAL, + PerformanceMetric::Total, onlineBotCount < maxAllowedBotCount ? "RandomPlayerbotMgr::Login" : "RandomPlayerbotMgr::UpdateAIInternal"); bool realPlayerIsLogged = false; @@ -808,7 +810,7 @@ uint32 RandomPlayerbotMgr::AddRandomBots() } // Lambda to handle bot login logic - auto tryLoginBot = [&](const CharacterInfo& charInfo) -> bool + auto tryLoginBot = [&](CharacterInfo const& charInfo) -> bool { if (GetEventValue(charInfo.guid, "add") || GetEventValue(charInfo.guid, "logout") || @@ -950,7 +952,7 @@ void RandomPlayerbotMgr::LoadBattleMastersCache() LOG_INFO("playerbots", ">> Loaded {} battlemaster entries", count); } -std::vector parseBrackets(const std::string& str) +std::vector parseBrackets(std::string const& str) { std::vector brackets; std::stringstream ss(str); @@ -1441,20 +1443,20 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot) randomTime = urand(1, 2); uint32 randomBotUpdateInterval = _isBotInitializing ? 1 : sPlayerbotAIConfig->randomBotUpdateInterval; - randomTime = urand(std::max(5, static_cast(randomBotUpdateInterval * 0.5)), + randomTime = urand(std::max(5, static_cast(randomBotUpdateInterval * 0.5f)), std::max(12, static_cast(randomBotUpdateInterval * 2))); SetEventValue(bot, "update", 1, randomTime); // do not randomize or teleport immediately after server start (prevent lagging) if (!GetEventValue(bot, "randomize")) { - randomTime = urand(3, std::max(4, static_cast(randomBotUpdateInterval * 0.4))); + randomTime = urand(3, std::max(4, static_cast(randomBotUpdateInterval * 0.4f))); ScheduleRandomize(bot, randomTime); } if (!GetEventValue(bot, "teleport")) { - randomTime = urand(std::max(7, static_cast(randomBotUpdateInterval * 0.7)), - std::max(14, static_cast(randomBotUpdateInterval * 1.4))); + randomTime = urand(std::max(7, static_cast(randomBotUpdateInterval * 0.7f)), + std::max(14, static_cast(randomBotUpdateInterval * 1.4f))); ScheduleTeleport(bot, randomTime); } @@ -1694,7 +1696,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector& tlocs.push_back(WorldPosition(loc)); // Do not teleport to maps disabled in config tlocs.erase(std::remove_if(tlocs.begin(), tlocs.end(), - [bot](WorldPosition l) + [](WorldPosition l) { std::vector::iterator i = find(sPlayerbotAIConfig->randomBotMaps.begin(), @@ -1708,7 +1710,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector& return; } - PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "RandomTeleportByLocations"); + PerfMonitorOperation* pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "RandomTeleportByLocations"); std::shuffle(std::begin(tlocs), std::end(tlocs), RandomEngine::Instance()); for (uint32 i = 0; i < tlocs.size(); i++) @@ -1752,7 +1754,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector& if (!botAI->CheckLocationDistanceByLevel(bot, loc, true)) continue; - const LocaleConstant& locale = sWorld->GetDefaultDbcLocale(); + LocaleConstant const& locale = sWorld->GetDefaultDbcLocale(); LOG_DEBUG("playerbots", "Random teleporting bot {} (level {}) to Map: {} ({}) Zone: {} ({}) Area: {} ({}) ZoneLevel: {} " "AreaLevel: {} {},{},{} ({}/{} " @@ -1939,7 +1941,7 @@ void RandomPlayerbotMgr::PrepareTeleportCache() for (int32 l = (int32)level - (int32)sPlayerbotAIConfig->randomBotTeleLowerLevel; l <= (int32)level + (int32)sPlayerbotAIConfig->randomBotTeleHigherLevel; l++) { - if (l < 1 || l > maxLevel) + if (l < 1 || l > static_cast(maxLevel)) { continue; } @@ -1992,7 +1994,7 @@ void RandomPlayerbotMgr::PrepareTeleportCache() if (tEntry == 3838 || tEntry == 29480) continue; - const FactionTemplateEntry* entry = sFactionTemplateStore.LookupEntry(faction); + FactionTemplateEntry const* entry = sFactionTemplateStore.LookupEntry(faction); WorldLocation loc(mapId, x + cos(orient) * 5.0f, y + sin(orient) * 5.0f, z + 0.5f, orient + M_PI); collected_locs++; @@ -2003,19 +2005,14 @@ void RandomPlayerbotMgr::PrepareTeleportCache() bool forAlliance = !(entry->hostileMask & 2); if (tNpcflag & UNIT_NPC_FLAG_FLIGHTMASTER) { - WorldPosition pos(mapId, x, y, z, orient); - if (forHorde) - sFlightMasterCache->AddHordeFlightMaster(guid, pos); - - if (forAlliance) - sFlightMasterCache->AddAllianceFlightMaster(guid, pos); + sFlightMasterCache->AddFlightMaster(guid, forHorde, forAlliance); } - const AreaTableEntry* area = sAreaTableStore.LookupEntry(map->GetAreaId(PHASEMASK_NORMAL, x, y, z)); + AreaTableEntry const* area = sAreaTableStore.LookupEntry(map->GetAreaId(PHASEMASK_NORMAL, x, y, z)); uint32 zoneId = area->zone ? area->zone : area->ID; if (zone2LevelBracket.find(zoneId) == zone2LevelBracket.end()) continue; LevelBracket bracket = zone2LevelBracket[zoneId]; - for (int i = bracket.low; i <= bracket.high; i++) + for (uint32 i = bracket.low; i <= bracket.high; i++) { if (forHorde) { @@ -2095,7 +2092,7 @@ void RandomPlayerbotMgr::PrepareTeleportCache() bLoc.loc = WorldLocation(mapId, x + cos(orient) * 6.0f, y + sin(orient) * 6.0f, z + 2.0f, orient + M_PI); bLoc.entry = entry; collected_locs++; - for (int32 l = 1; l <= maxLevel; l++) + for (uint32 l = 1; l <= maxLevel; l++) { // Bots 1-60 go to base game bankers (all have minlevel 30 or 45) if (l <=60 && level > 45) @@ -2299,7 +2296,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot) if (bot->InBattleground()) return; - PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "RandomTeleport"); + PerfMonitorOperation* pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "RandomTeleport"); std::vector locs; std::list targets; @@ -2313,7 +2310,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot) for (Unit* unit : targets) { bot->UpdatePosition(*unit); - FleeManager manager(bot, sPlayerbotAIConfig->sightDistance, 0, true); + FleeManager manager(bot, sPlayerbotAIConfig->sightDistance, true); float rx, ry, rz; if (manager.CalculateDestination(&rx, &ry, &rz)) { @@ -2361,7 +2358,7 @@ void RandomPlayerbotMgr::IncreaseLevel(Player* bot) if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); - PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "IncreaseLevel"); + PerfMonitorOperation* pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "IncreaseLevel"); uint32 lastLevel = GetValue(bot, "level"); uint8 level = bot->GetLevel() + 1; if (level > maxLevel) @@ -2400,7 +2397,7 @@ void RandomPlayerbotMgr::RandomizeFirst(Player* bot) minLevel = std::max(minLevel, sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL)); } - PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "RandomizeFirst"); + PerfMonitorOperation* pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "RandomizeFirst"); uint32 level; @@ -2479,7 +2476,7 @@ void RandomPlayerbotMgr::RandomizeMin(Player* bot) if (!botAI) return; - PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "RandomizeMin"); + PerfMonitorOperation* pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "RandomizeMin"); uint32 level = sPlayerbotAIConfig->randomBotMinLevel; SetValue(bot, "level", level); PlayerbotFactory factory(bot, level); @@ -2518,7 +2515,7 @@ void RandomPlayerbotMgr::Clear(Player* bot) factory.ClearEverything(); } -uint32 RandomPlayerbotMgr::GetZoneLevel(uint16 mapId, float teleX, float teleY, float teleZ) +uint32 RandomPlayerbotMgr::GetZoneLevel(uint16 mapId, float teleX, float teleY, float /*teleZ*/) { uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); @@ -2568,7 +2565,7 @@ void RandomPlayerbotMgr::Refresh(Player* bot) LOG_DEBUG("playerbots", "Refreshing bot {} <{}>", bot->GetGUID().ToString().c_str(), bot->GetName().c_str()); - PerfMonitorOperation* pmo = sPerfMonitor->start(PERF_MON_RNDBOT, "Refresh"); + PerfMonitorOperation* pmo = sPerfMonitor->start(PerformanceMetric::RndBot, "Refresh"); botAI->Reset(); @@ -2711,7 +2708,7 @@ std::vector RandomPlayerbotMgr::GetBgBots(uint32 bracket) } while (result->NextRow()); } - return std::move(BgBots); + return BgBots; } CachedEvent* RandomPlayerbotMgr::FindEvent(uint32 bot, std::string const& event) @@ -2849,7 +2846,7 @@ void RandomPlayerbotMgr::SetValue(Player* bot, std::string const& type, uint32 v SetValue(bot->GetGUID().GetCounter(), type, value, data); } -bool RandomPlayerbotMgr::HandlePlayerbotConsoleCommand(ChatHandler* handler, char const* args) +bool RandomPlayerbotMgr::HandlePlayerbotConsoleCommand(ChatHandler* /*handler*/, char const* args) { if (!sPlayerbotAIConfig->enabled) { @@ -3069,7 +3066,7 @@ void RandomPlayerbotMgr::OnPlayerLogin(Player* player) { botAI->SetMaster(player); botAI->ResetStrategies(); - botAI->TellMaster("Hello"); + botAI->GetServices().GetChatService().TellMaster("Hello"); } break; @@ -3272,9 +3269,9 @@ void RandomPlayerbotMgr::PrintStats() else ++engine_dead; - if (botAI->IsHeal(bot, true)) + if (BotRoleService::IsHealStatic(bot, true)) ++heal; - else if (botAI->IsTank(bot, true)) + else if (BotRoleService::IsTankStatic(bot, true)) ++tank; else ++dps; @@ -3386,7 +3383,7 @@ double RandomPlayerbotMgr::GetBuyMultiplier(Player* bot) SetEventValue(id, "buymultiplier", value, validIn); } - return (double)value / 100.0; + return (double)value / 100.0f; } double RandomPlayerbotMgr::GetSellMultiplier(Player* bot) @@ -3401,7 +3398,7 @@ double RandomPlayerbotMgr::GetSellMultiplier(Player* bot) SetEventValue(id, "sellmultiplier", value, validIn); } - return (double)value / 100.0; + return (double)value / 100.0f; } void RandomPlayerbotMgr::AddTradeDiscount(Player* bot, Player* master, int32 value) diff --git a/src/Bot/RandomPlayerbotMgr.h b/src/Bot/RandomPlayerbotMgr.h index 26d09d454e..c30136a670 100644 --- a/src/Bot/RandomPlayerbotMgr.h +++ b/src/Bot/RandomPlayerbotMgr.h @@ -171,7 +171,6 @@ class RandomPlayerbotMgr : public PlayerbotHolder std::map> locsPerLevelCache; std::map> allianceStarterPerLevelCache; std::map> hordeStarterPerLevelCache; - struct LevelBracket { uint32 low; uint32 high; @@ -194,7 +193,7 @@ class RandomPlayerbotMgr : public PlayerbotHolder private: // pid values are set in constructor botPID pid = botPID(1, 50, -50, 0, 0, 0); - float activityMod = 0.25; + float activityMod = 0.25f; bool _isBotInitializing = true; bool _isBotLogging = true; NewRpgStatistic rpgStasticTotal; diff --git a/src/Bot/Service/BotChatService.cpp b/src/Bot/Service/BotChatService.cpp new file mode 100644 index 0000000000..f668eeb23b --- /dev/null +++ b/src/Bot/Service/BotChatService.cpp @@ -0,0 +1,482 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "BotChatService.h" + +#include "Bot/Core/ManagerRegistry.h" +#include "Channel.h" +#include "ChannelMgr.h" +#include "Chat.h" +#include "Group.h" +#include "Guild.h" +#include "GuildMgr.h" +#include "ObjectAccessor.h" +#include "Player.h" +#include "PlayerbotAI.h" +#include "PlayerbotAIConfig.h" +#include "PlayerbotMgr.h" +#include "PlayerbotSecurity.h" +#include "Playerbots.h" +#include "ServerFacade.h" +#include "WorldPacket.h" +#include "WorldSession.h" + +// ============================================================================ +// Static method implementations (main logic) +// ============================================================================ + +bool BotChatService::IsTellAllowedStatic(Player* bot, Player* master, PlayerbotSecurity* security, + PlayerbotSecurityLevel securityLevel) +{ + if (!master || master->IsBeingTeleported()) + return false; + + if (!security->CheckLevelFor(securityLevel, true, master)) + return false; + + if (sPlayerbotAIConfig->whisperDistance && !bot->GetGroup() && sManagerRegistry.GetRandomBotManager().IsRandomBot(bot) && + master->GetSession()->GetSecurity() < SEC_GAMEMASTER && + (bot->GetMapId() != master->GetMapId() || + sServerFacade->GetDistance2d(bot, master) > sPlayerbotAIConfig->whisperDistance)) + return false; + + return true; +} + +bool BotChatService::TellMasterNoFacingStatic(ChatContext const& ctx, std::string const& text, + PlayerbotSecurityLevel securityLevel) +{ + Player* master = ctx.getMaster(); + PlayerbotAI* masterBotAI = nullptr; + if (master) + masterBotAI = GET_PLAYERBOT_AI(master); + + if ((!master || (masterBotAI && !masterBotAI->IsRealPlayer())) && + (sPlayerbotAIConfig->randomBotSayWithoutMaster || ctx.hasStrategy("debug", BOT_STATE_NON_COMBAT))) + { + ctx.bot->Say(text, (ctx.bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH)); + return true; + } + + if (!IsTellAllowedStatic(ctx.bot, master, ctx.security, securityLevel)) + return false; + + time_t lastSaid = (*ctx.whispers)[text]; + + if (!lastSaid || (time(nullptr) - lastSaid) >= sPlayerbotAIConfig->repeatDelay / 1000) + { + (*ctx.whispers)[text] = time(nullptr); + + ChatMsg type = CHAT_MSG_WHISPER; + if (ctx.currentChat->second - time(nullptr) >= 1) + type = ctx.currentChat->first; + + WorldPacket data; + ChatHandler::BuildChatPacket(data, type == CHAT_MSG_ADDON ? CHAT_MSG_PARTY : type, + type == CHAT_MSG_ADDON ? LANG_ADDON : LANG_UNIVERSAL, ctx.bot, nullptr, + text.c_str()); + master->SendDirectMessage(&data); + } + + return true; +} + +bool BotChatService::TellMasterStatic(ChatContext const& ctx, std::string const& text, + PlayerbotSecurityLevel securityLevel) +{ + Player* master = ctx.getMaster(); + if (!master) + { + if (sPlayerbotAIConfig->randomBotSayWithoutMaster) + return TellMasterNoFacingStatic(ctx, text, securityLevel); + } + if (!TellMasterNoFacingStatic(ctx, text, securityLevel)) + return false; + + if (!ctx.bot->isMoving() && !ctx.bot->IsInCombat() && ctx.bot->GetMapId() == master->GetMapId() && + !ctx.bot->HasUnitState(UNIT_STATE_IN_FLIGHT) && !ctx.bot->IsFlying()) + { + if (!ctx.bot->HasInArc(EMOTE_ANGLE_IN_FRONT, master, sPlayerbotAIConfig->sightDistance)) + ctx.bot->SetFacingToObject(master); + + ctx.bot->HandleEmoteCommand(EMOTE_ONESHOT_TALK); + } + + return true; +} + +bool BotChatService::TellErrorStatic(Player* bot, Player* master, PlayerbotSecurity* security, std::string const& text, + PlayerbotSecurityLevel securityLevel) +{ + if (!IsTellAllowedStatic(bot, master, security, securityLevel) || !master || GET_PLAYERBOT_AI(master)) + return false; + + if (PlayerbotMgr* mgr = GET_PLAYERBOT_MGR(master)) + mgr->TellError(bot->GetName(), text); + + return false; +} + +bool BotChatService::SayToGuildStatic(Player* bot, std::string const& msg) +{ + if (msg.empty()) + return false; + + if (bot->GetGuildId()) + { + if (Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId())) + { + if (!guild->HasRankRight(bot, GR_RIGHT_GCHATSPEAK)) + return false; + guild->BroadcastToGuild(bot->GetSession(), false, msg.c_str(), LANG_UNIVERSAL); + return true; + } + } + + return false; +} + +bool BotChatService::SayToWorldStatic(Player* bot, std::string const& msg) +{ + if (msg.empty()) + return false; + + ChannelMgr* cMgr = ChannelMgr::forTeam(bot->GetTeamId()); + if (!cMgr) + return false; + + if (Channel* worldChannel = cMgr->GetChannel("World", bot)) + { + worldChannel->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL); + return true; + } + + return false; +} + +bool BotChatService::SayToChannelStatic(Player* bot, std::string const& msg, uint32 channelId) +{ + if (msg.empty()) + return false; + + ChannelMgr* cMgr = ChannelMgr::forTeam(bot->GetTeamId()); + if (!cMgr) + return false; + + AreaTableEntry const* current_zone = sAreaTableStore.LookupEntry(bot->GetZoneId()); + if (!current_zone) + return false; + + std::string current_str_zone = current_zone->area_name[0]; + + std::mutex socialMutex; + std::lock_guard lock(socialMutex); + + for (auto const& [key, channel] : cMgr->GetChannels()) + { + if (!channel) + continue; + + if (channel->GetChannelId() == channelId) + { + if (channel->GetName().empty()) + continue; + + const auto does_contains = channel->GetName().find(current_str_zone) != std::string::npos; + if (channelId != static_cast(ChatChannelId::LOOKING_FOR_GROUP) && + channelId != static_cast(ChatChannelId::WORLD_DEFENSE) && !does_contains) + { + continue; + } + + if (channel) + { + channel->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL); + return true; + } + } + } + + return false; +} + +bool BotChatService::SayToPartyStatic(Player* bot, std::vector const& recipients, std::string const& msg) +{ + if (!bot->GetGroup()) + return false; + + WorldPacket data; + ChatHandler::BuildChatPacket(data, CHAT_MSG_PARTY, msg.c_str(), LANG_UNIVERSAL, CHAT_TAG_NONE, bot->GetGUID(), + bot->GetName()); + + for (auto* receiver : recipients) + { + sServerFacade->SendPacket(receiver, &data); + } + + return true; +} + +bool BotChatService::SayToRaidStatic(Player* bot, std::vector const& recipients, std::string const& msg) +{ + if (!bot->GetGroup() || bot->GetGroup()->isRaidGroup()) + return false; + + WorldPacket data; + ChatHandler::BuildChatPacket(data, CHAT_MSG_RAID, msg.c_str(), LANG_UNIVERSAL, CHAT_TAG_NONE, bot->GetGUID(), + bot->GetName()); + + for (auto* receiver : recipients) + { + sServerFacade->SendPacket(receiver, &data); + } + + return true; +} + +bool BotChatService::SayStatic(Player* bot, std::string const& msg) +{ + if (bot->GetTeamId() == TeamId::TEAM_ALLIANCE) + { + bot->Say(msg, LANG_COMMON); + } + else + { + bot->Say(msg, LANG_ORCISH); + } + + return true; +} + +bool BotChatService::YellStatic(Player* bot, std::string const& msg) +{ + if (bot->GetTeamId() == TeamId::TEAM_ALLIANCE) + { + bot->Yell(msg, LANG_COMMON); + } + else + { + bot->Yell(msg, LANG_ORCISH); + } + + return true; +} + +bool BotChatService::WhisperStatic(Player* bot, std::string const& msg, std::string const& receiverName) +{ + const auto receiver = ObjectAccessor::FindPlayerByName(receiverName); + if (!receiver) + return false; + + if (bot->GetTeamId() == TeamId::TEAM_ALLIANCE) + { + bot->Whisper(msg, LANG_COMMON, receiver); + } + else + { + bot->Whisper(msg, LANG_ORCISH, receiver); + } + + return true; +} + +bool BotChatService::PlaySoundStatic(Player* bot, uint32 emote) +{ + bot->PlayDistanceSound(emote); + return true; +} + +bool BotChatService::PlayEmoteStatic(Player* bot, uint32 emote) +{ + bot->HandleEmoteCommand(emote); + return true; +} + +void BotChatService::PingStatic(Player* bot, float x, float y) +{ + WorldPacket data(MSG_MINIMAP_PING, (8 + 4 + 4)); + data << bot->GetGUID(); + data << x; + data << y; + + if (bot->GetGroup()) + { + bot->GetGroup()->BroadcastPacket(&data, true, -1, bot->GetGUID()); + } + else + { + bot->GetSession()->SendPacket(&data); + } +} + +// ============================================================================ +// Helper methods +// ============================================================================ + +ChatContext BotChatService::BuildChatContext() const +{ + ChatContext ctx; + if (!_botAI) + { + ctx.bot = nullptr; + ctx.getMaster = []() { return nullptr; }; + ctx.security = nullptr; + ctx.whispers = nullptr; + ctx.currentChat = nullptr; + ctx.hasStrategy = [](std::string const&, int) { return false; }; + ctx.hasRealPlayerMaster = []() { return false; }; + return ctx; + } + + ctx.bot = _botAI->GetBot(); + ctx.getMaster = [this]() { return _botAI->GetMaster(); }; + ctx.security = _botAI->GetSecurity(); + ctx.whispers = nullptr; + ctx.currentChat = nullptr; + ctx.hasStrategy = [](std::string const&, int) { return false; }; + ctx.hasRealPlayerMaster = [this]() { return _botAI->HasRealPlayerMaster(); }; + + return ctx; +} + +// ============================================================================ +// Instance method implementations (IChatService interface) +// These delegate to static methods or PlayerbotAI during transition +// ============================================================================ + +bool BotChatService::TellMaster(std::string const& text, PlayerbotSecurityLevel securityLevel) +{ + if (_botAI) + { + return _botAI->TellMaster(text, securityLevel); + } + return false; +} + +bool BotChatService::TellMaster(std::ostringstream& stream, PlayerbotSecurityLevel securityLevel) +{ + return TellMaster(stream.str(), securityLevel); +} + +bool BotChatService::TellMasterNoFacing(std::string const& text, PlayerbotSecurityLevel securityLevel) +{ + if (_botAI) + { + return _botAI->TellMasterNoFacing(text, securityLevel); + } + return false; +} + +bool BotChatService::TellMasterNoFacing(std::ostringstream& stream, PlayerbotSecurityLevel securityLevel) +{ + return TellMasterNoFacing(stream.str(), securityLevel); +} + +bool BotChatService::TellError(std::string const& text, PlayerbotSecurityLevel securityLevel) +{ + if (_botAI) + { + return _botAI->TellError(text, securityLevel); + } + return false; +} + +bool BotChatService::SayToGuild(std::string const& msg) +{ + if (_botAI) + { + return SayToGuildStatic(_botAI->GetBot(), msg); + } + return false; +} + +bool BotChatService::SayToWorld(std::string const& msg) +{ + if (_botAI) + { + return SayToWorldStatic(_botAI->GetBot(), msg); + } + return false; +} + +bool BotChatService::SayToChannel(std::string const& msg, uint32 channelId) +{ + if (_botAI) + { + return SayToChannelStatic(_botAI->GetBot(), msg, channelId); + } + return false; +} + +bool BotChatService::SayToParty(std::string const& msg) +{ + if (_botAI) + { + return SayToPartyStatic(_botAI->GetBot(), _botAI->GetPlayersInGroup(), msg); + } + return false; +} + +bool BotChatService::SayToRaid(std::string const& msg) +{ + if (_botAI) + { + return SayToRaidStatic(_botAI->GetBot(), _botAI->GetPlayersInGroup(), msg); + } + return false; +} + +bool BotChatService::Say(std::string const& msg) +{ + if (_botAI) + { + return SayStatic(_botAI->GetBot(), msg); + } + return false; +} + +bool BotChatService::Yell(std::string const& msg) +{ + if (_botAI) + { + return YellStatic(_botAI->GetBot(), msg); + } + return false; +} + +bool BotChatService::Whisper(std::string const& msg, std::string const& receiverName) +{ + if (_botAI) + { + return WhisperStatic(_botAI->GetBot(), msg, receiverName); + } + return false; +} + +bool BotChatService::PlaySound(uint32 emote) +{ + if (_botAI) + { + return PlaySoundStatic(_botAI->GetBot(), emote); + } + return false; +} + +bool BotChatService::PlayEmote(uint32 emote) +{ + if (_botAI) + { + return PlayEmoteStatic(_botAI->GetBot(), emote); + } + return false; +} + +void BotChatService::Ping(float x, float y) +{ + if (_botAI) + { + PingStatic(_botAI->GetBot(), x, y); + } +} diff --git a/src/Bot/Service/BotChatService.h b/src/Bot/Service/BotChatService.h new file mode 100644 index 0000000000..d883b22f36 --- /dev/null +++ b/src/Bot/Service/BotChatService.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_BOT_CHAT_SERVICE_H +#define _PLAYERBOT_BOT_CHAT_SERVICE_H + +#include "Bot/Interface/IChatService.h" +#include "SharedDefines.h" + +#include +#include + +class Player; +class PlayerbotAI; +class PlayerbotSecurity; + +/** + * @brief Context for chat operations + * + * Provides all dependencies needed for chat operations without + * requiring direct access to PlayerbotAI. + */ +struct ChatContext +{ + Player* bot; + std::function getMaster; + PlayerbotSecurity* security; + std::map* whispers; + std::pair* currentChat; + std::function hasStrategy; + std::function hasRealPlayerMaster; +}; + +/** + * @brief Implementation of IChatService + * + * This service provides communication functionality for bots. + * + * Static methods are provided for direct access without needing a service instance. + * Instance methods implement the IChatService interface for testability/mockability. + */ +class BotChatService : public IChatService +{ +public: + BotChatService() = default; + explicit BotChatService(PlayerbotAI* ai) : _botAI(ai) {} + ~BotChatService() override = default; + + // ======================================================================== + // Static methods for direct access (main implementations) + // These can be called without a service instance + // ======================================================================== + + // Master communication - static versions + static bool IsTellAllowedStatic(Player* bot, Player* master, PlayerbotSecurity* security, + PlayerbotSecurityLevel securityLevel); + static bool TellMasterNoFacingStatic(ChatContext const& ctx, std::string const& text, + PlayerbotSecurityLevel securityLevel); + static bool TellMasterStatic(ChatContext const& ctx, std::string const& text, PlayerbotSecurityLevel securityLevel); + static bool TellErrorStatic(Player* bot, Player* master, PlayerbotSecurity* security, std::string const& text, + PlayerbotSecurityLevel securityLevel); + + // Channel communication - static versions + static bool SayToGuildStatic(Player* bot, std::string const& msg); + static bool SayToWorldStatic(Player* bot, std::string const& msg); + static bool SayToChannelStatic(Player* bot, std::string const& msg, uint32 channelId); + static bool SayToPartyStatic(Player* bot, std::vector const& recipients, std::string const& msg); + static bool SayToRaidStatic(Player* bot, std::vector const& recipients, std::string const& msg); + + // Direct communication - static versions + static bool SayStatic(Player* bot, std::string const& msg); + static bool YellStatic(Player* bot, std::string const& msg); + static bool WhisperStatic(Player* bot, std::string const& msg, std::string const& receiverName); + + // Emotes and visual - static versions + static bool PlaySoundStatic(Player* bot, uint32 emote); + static bool PlayEmoteStatic(Player* bot, uint32 emote); + static void PingStatic(Player* bot, float x, float y); + + // ======================================================================== + // Instance methods (IChatService interface implementation) + // These call the static methods internally, but provide mockable interface + // ======================================================================== + + // Master communication + bool TellMaster(std::string const& text, PlayerbotSecurityLevel securityLevel) override; + bool TellMaster(std::ostringstream& stream, PlayerbotSecurityLevel securityLevel) override; + bool TellMasterNoFacing(std::string const& text, PlayerbotSecurityLevel securityLevel) override; + bool TellMasterNoFacing(std::ostringstream& stream, PlayerbotSecurityLevel securityLevel) override; + bool TellError(std::string const& text, PlayerbotSecurityLevel securityLevel) override; + + // Channel communication + bool SayToGuild(std::string const& msg) override; + bool SayToWorld(std::string const& msg) override; + bool SayToChannel(std::string const& msg, uint32 channelId) override; + bool SayToParty(std::string const& msg) override; + bool SayToRaid(std::string const& msg) override; + + // Direct communication + bool Say(std::string const& msg) override; + bool Yell(std::string const& msg) override; + bool Whisper(std::string const& msg, std::string const& receiverName) override; + + // Emotes + bool PlaySound(uint32 emote) override; + bool PlayEmote(uint32 emote) override; + + // Visual feedback + void Ping(float x, float y) override; + + // Set the bot context for instance methods that need the bot + void SetBotContext(PlayerbotAI* ai) { _botAI = ai; } + PlayerbotAI* GetBotContext() const { return _botAI; } + +private: + PlayerbotAI* _botAI = nullptr; + + // Helper to build ChatContext from PlayerbotAI + ChatContext BuildChatContext() const; +}; + +#endif diff --git a/src/Bot/Service/BotContext.cpp b/src/Bot/Service/BotContext.cpp new file mode 100644 index 0000000000..f6b015e070 --- /dev/null +++ b/src/Bot/Service/BotContext.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "BotContext.h" + +#include "PlayerbotAI.h" + +Player* BotContext::GetBot() +{ + if (_botAI) + { + return _botAI->GetBot(); + } + return nullptr; +} + +Player* BotContext::GetMaster() +{ + if (_botAI) + { + return _botAI->GetMaster(); + } + return nullptr; +} + +void BotContext::SetMaster(Player* newMaster) +{ + if (_botAI) + { + _botAI->SetMaster(newMaster); + } +} + +BotState BotContext::GetState() const +{ + if (_botAI) + { + return _botAI->GetState(); + } + return BOT_STATE_NON_COMBAT; +} + +bool BotContext::IsInCombat() const +{ + if (_botAI) + { + return _botAI->GetState() == BOT_STATE_COMBAT; + } + return false; +} + +bool BotContext::IsRealPlayer() const +{ + if (_botAI) + { + return _botAI->IsRealPlayer(); + } + return false; +} + +bool BotContext::HasRealPlayerMaster() const +{ + if (_botAI) + { + return _botAI->HasRealPlayerMaster(); + } + return false; +} + +bool BotContext::HasActivePlayerMaster() const +{ + if (_botAI) + { + return _botAI->HasActivePlayerMaster(); + } + return false; +} + +bool BotContext::IsAlt() const +{ + if (_botAI) + { + return _botAI->IsAlt(); + } + return false; +} + +Creature* BotContext::GetCreature(ObjectGuid guid) +{ + if (_botAI) + { + return _botAI->GetCreature(guid); + } + return nullptr; +} + +Unit* BotContext::GetUnit(ObjectGuid guid) +{ + if (_botAI) + { + return _botAI->GetUnit(guid); + } + return nullptr; +} + +Player* BotContext::GetPlayer(ObjectGuid guid) +{ + if (_botAI) + { + return _botAI->GetPlayer(guid); + } + return nullptr; +} + +GameObject* BotContext::GetGameObject(ObjectGuid guid) +{ + if (_botAI) + { + return _botAI->GetGameObject(guid); + } + return nullptr; +} + +WorldObject* BotContext::GetWorldObject(ObjectGuid guid) +{ + if (_botAI) + { + return _botAI->GetWorldObject(guid); + } + return nullptr; +} + +AreaTableEntry const* BotContext::GetCurrentArea() const +{ + if (_botAI) + { + return _botAI->GetCurrentArea(); + } + return nullptr; +} + +AreaTableEntry const* BotContext::GetCurrentZone() const +{ + if (_botAI) + { + return _botAI->GetCurrentZone(); + } + return nullptr; +} + +std::vector BotContext::GetPlayersInGroup() +{ + if (_botAI) + { + return _botAI->GetPlayersInGroup(); + } + return {}; +} + +Player* BotContext::GetGroupLeader() +{ + if (_botAI) + { + return _botAI->GetGroupLeader(); + } + return nullptr; +} + +bool BotContext::IsSafe(Player* player) const +{ + if (_botAI) + { + return _botAI->IsSafe(player); + } + return false; +} + +bool BotContext::IsSafe(WorldObject* obj) const +{ + if (_botAI) + { + return _botAI->IsSafe(obj); + } + return false; +} + +bool BotContext::IsOpposing(Player* player) const +{ + if (_botAI) + { + return _botAI->IsOpposing(player); + } + return false; +} + +bool BotContext::CanMove() const +{ + if (_botAI) + { + return _botAI->CanMove(); + } + return false; +} + +bool BotContext::HasPlayerNearby(float range) const +{ + if (_botAI) + { + return _botAI->HasPlayerNearby(range); + } + return false; +} + +bool BotContext::HasPlayerNearby(WorldPosition* pos, float range) const +{ + if (_botAI) + { + return _botAI->HasPlayerNearby(pos, range); + } + return false; +} + +bool BotContext::HasManyPlayersNearby(uint32 triggerValue, float range) const +{ + if (_botAI) + { + return _botAI->HasManyPlayersNearby(triggerValue, range); + } + return false; +} diff --git a/src/Bot/Service/BotContext.h b/src/Bot/Service/BotContext.h new file mode 100644 index 0000000000..9f7b1d82d1 --- /dev/null +++ b/src/Bot/Service/BotContext.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_BOT_CONTEXT_H +#define _PLAYERBOT_BOT_CONTEXT_H + +#include "Bot/Interface/IBotContext.h" + +class PlayerbotAI; + +/** + * @brief Implementation of IBotContext + * + * This service provides bot context access, + * extracting this functionality from PlayerbotAI for better testability. + * + * The service delegates to PlayerbotAI methods during the transition period. + */ +class BotContext : public IBotContext +{ +public: + explicit BotContext(PlayerbotAI* ai) : _botAI(ai) {} + ~BotContext() override = default; + + // Bot and Master access + Player* GetBot() override; + Player* GetMaster() override; + void SetMaster(Player* newMaster) override; + + // State management + BotState GetState() const override; + bool IsInCombat() const override; + + // Player validation + bool IsRealPlayer() const override; + bool HasRealPlayerMaster() const override; + bool HasActivePlayerMaster() const override; + bool IsAlt() const override; + + // Object access + Creature* GetCreature(ObjectGuid guid) override; + Unit* GetUnit(ObjectGuid guid) override; + Player* GetPlayer(ObjectGuid guid) override; + GameObject* GetGameObject(ObjectGuid guid) override; + WorldObject* GetWorldObject(ObjectGuid guid) override; + + // Location info + AreaTableEntry const* GetCurrentArea() const override; + AreaTableEntry const* GetCurrentZone() const override; + + // Group access + std::vector GetPlayersInGroup() override; + Player* GetGroupLeader() override; + + // Utility + bool IsSafe(Player* player) const override; + bool IsSafe(WorldObject* obj) const override; + bool IsOpposing(Player* player) const override; + bool CanMove() const override; + + // Player proximity + bool HasPlayerNearby(float range) const override; + bool HasPlayerNearby(WorldPosition* pos, float range) const override; + bool HasManyPlayersNearby(uint32 triggerValue, float range) const override; + +private: + PlayerbotAI* _botAI; +}; + +#endif diff --git a/src/Bot/Service/BotItemService.cpp b/src/Bot/Service/BotItemService.cpp new file mode 100644 index 0000000000..9dc9fc824a --- /dev/null +++ b/src/Bot/Service/BotItemService.cpp @@ -0,0 +1,783 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "BotItemService.h" + +#include "AiFactory.h" +#include "Bag.h" +#include "DBCStores.h" +#include "Item.h" +#include "ItemTemplate.h" +#include "ObjectMgr.h" +#include "SharedDefines.h" +#include "Player.h" +#include "PlayerbotAI.h" +#include "QuestDef.h" +#include "SpellMgr.h" +#include "Unit.h" +#include "WorldPacket.h" +#include "WorldSession.h" + +// Stone and oil item IDs are defined in PlayerbotAI.h + +// ============================================================================ +// Static method implementations (main logic) +// ============================================================================ + +Item* BotItemService::FindItemInInventoryStatic(Player* bot, std::function checkItem) +{ + // List out items in the main backpack + for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; ++slot) + { + if (Item* const pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) + { + ItemTemplate const* pItemProto = pItem->GetTemplate(); + if (pItemProto && bot->CanUseItem(pItemProto) == EQUIP_ERR_OK && checkItem(pItemProto)) + return pItem; + } + } + + // List out items in other removable backpacks + for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag) + { + if (Bag const* const pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag)) + { + for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot) + { + if (Item* const pItem = bot->GetItemByPos(bag, slot)) + { + ItemTemplate const* pItemProto = pItem->GetTemplate(); + if (pItemProto && bot->CanUseItem(pItemProto) == EQUIP_ERR_OK && checkItem(pItemProto)) + return pItem; + } + } + } + } + + return nullptr; +} + +Item* BotItemService::FindPoisonStatic(Player* bot) +{ + return FindItemInInventoryStatic( + bot, [](ItemTemplate const* pItemProto) -> bool + { return pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == 6; }); +} + +Item* BotItemService::FindAmmoStatic(Player* bot) +{ + // Get equipped ranged weapon + if (Item* rangedWeapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED)) + { + uint32 weaponSubClass = rangedWeapon->GetTemplate()->SubClass; + uint32 requiredAmmoType = 0; + + // Determine the correct ammo type based on the weapon + switch (weaponSubClass) + { + case ITEM_SUBCLASS_WEAPON_GUN: + requiredAmmoType = ITEM_SUBCLASS_BULLET; + break; + case ITEM_SUBCLASS_WEAPON_BOW: + case ITEM_SUBCLASS_WEAPON_CROSSBOW: + requiredAmmoType = ITEM_SUBCLASS_ARROW; + break; + default: + return nullptr; // Not a ranged weapon that requires ammo + } + + // Search inventory for the correct ammo type + return FindItemInInventoryStatic(bot, [requiredAmmoType](ItemTemplate const* pItemProto) -> bool + { return pItemProto->Class == ITEM_CLASS_PROJECTILE && + pItemProto->SubClass == requiredAmmoType; }); + } + + return nullptr; +} + +Item* BotItemService::FindBandageStatic(Player* bot) +{ + return FindItemInInventoryStatic( + bot, [](ItemTemplate const* pItemProto) -> bool + { return pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == ITEM_SUBCLASS_BANDAGE; }); +} + +Item* BotItemService::FindOpenableItemStatic(Player* bot) +{ + return FindItemInInventoryStatic(bot, [bot](ItemTemplate const* itemTemplate) -> bool + { + return itemTemplate->HasFlag(ITEM_FLAG_HAS_LOOT) && + (itemTemplate->LockID == 0 || + !bot->GetItemByEntry(itemTemplate->ItemId)->IsLocked()); + }); +} + +Item* BotItemService::FindLockedItemStatic(Player* bot) +{ + return FindItemInInventoryStatic(bot, [bot](ItemTemplate const* itemTemplate) -> bool + { + if (!bot->HasSkill(SKILL_LOCKPICKING)) + return false; + + if (itemTemplate->LockID == 0) + return false; + + Item* item = bot->GetItemByEntry(itemTemplate->ItemId); + if (!item || !item->IsLocked()) + return false; + + LockEntry const* lockInfo = sLockStore.LookupEntry(itemTemplate->LockID); + if (!lockInfo) + return false; + + for (uint8 j = 0; j < 8; ++j) + { + if (lockInfo->Type[j] == LOCK_KEY_SKILL) + { + uint32 skillId = SkillByLockType(LockType(lockInfo->Index[j])); + if (skillId == SKILL_LOCKPICKING) + { + uint32 requiredSkill = lockInfo->Skill[j]; + uint32 botSkill = bot->GetSkillValue(SKILL_LOCKPICKING); + return botSkill >= requiredSkill; + } + } + } + + return false; + }); +} + +Item* BotItemService::FindConsumableStatic(Player* bot, uint32 itemId) +{ + return FindItemInInventoryStatic( + bot, [itemId](ItemTemplate const* pItemProto) -> bool + { + return (pItemProto->Class == ITEM_CLASS_CONSUMABLE || pItemProto->Class == ITEM_CLASS_TRADE_GOODS) && + pItemProto->ItemId == itemId; + }); +} + +Item* BotItemService::FindStoneForStatic(Player* bot, Item* weapon) +{ + if (!weapon) + return nullptr; + + ItemTemplate const* item_template = weapon->GetTemplate(); + if (!item_template) + return nullptr; + + static std::vector const uPrioritizedSharpStoneIds = { + ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, ELEMENTAL_SHARPENING_STONE, DENSE_SHARPENING_STONE, + SOLID_SHARPENING_STONE, HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE}; + + static std::vector const uPrioritizedWeightStoneIds = { + ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE, + HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE}; + + Item* stone = nullptr; + ItemTemplate const* pProto = weapon->GetTemplate(); + if (pProto && (pProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || pProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD2 || + pProto->SubClass == ITEM_SUBCLASS_WEAPON_AXE || pProto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2 || + pProto->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER || pProto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM)) + { + for (uint8 i = 0; i < std::size(uPrioritizedSharpStoneIds); ++i) + { + stone = FindConsumableStatic(bot, uPrioritizedSharpStoneIds[i]); + if (stone) + return stone; + } + } + else if (pProto && + (pProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE || pProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE2 || + pProto->SubClass == ITEM_SUBCLASS_WEAPON_STAFF || pProto->SubClass == ITEM_SUBCLASS_WEAPON_FIST)) + { + for (uint8 i = 0; i < std::size(uPrioritizedWeightStoneIds); ++i) + { + stone = FindConsumableStatic(bot, uPrioritizedWeightStoneIds[i]); + if (stone) + return stone; + } + } + + return stone; +} + +Item* BotItemService::FindOilForStatic(Player* bot, Item* weapon) +{ + if (!weapon) + return nullptr; + + ItemTemplate const* item_template = weapon->GetTemplate(); + if (!item_template) + return nullptr; + + static std::vector const uPrioritizedWizardOilIds = { + BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL, + BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL}; + + static std::vector const uPrioritizedManaOilIds = { + BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL, BRILLIANT_WIZARD_OIL, + SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL}; + + Item* oil = nullptr; + int botClass = bot->getClass(); + int specTab = AiFactory::GetPlayerSpecTab(bot); + + std::vector const* prioritizedOils = nullptr; + switch (botClass) + { + case CLASS_PRIEST: + prioritizedOils = (specTab == 2) ? &uPrioritizedWizardOilIds : &uPrioritizedManaOilIds; + break; + case CLASS_MAGE: + prioritizedOils = &uPrioritizedWizardOilIds; + break; + case CLASS_DRUID: + if (specTab == 0) // Balance + prioritizedOils = &uPrioritizedWizardOilIds; + else if (specTab == 1) // Feral + prioritizedOils = nullptr; + else + prioritizedOils = &uPrioritizedManaOilIds; + break; + case CLASS_HUNTER: + prioritizedOils = &uPrioritizedManaOilIds; + break; + case CLASS_PALADIN: + if (specTab == 1) // Protection + prioritizedOils = &uPrioritizedWizardOilIds; + else if (specTab == 2) // Retribution + prioritizedOils = nullptr; + else + prioritizedOils = &uPrioritizedManaOilIds; + break; + default: + prioritizedOils = &uPrioritizedManaOilIds; + break; + } + + if (prioritizedOils) + { + for (auto const& id : *prioritizedOils) + { + oil = FindConsumableStatic(bot, id); + if (oil) + return oil; + } + } + + return oil; +} + +void BotItemService::ImbueItemStatic(Player* bot, Item* item) +{ + ImbueItemStatic(bot, item, TARGET_FLAG_NONE, ObjectGuid::Empty); +} + +void BotItemService::ImbueItemStatic(Player* bot, Item* item, Unit* target) +{ + if (!target || !target->IsInWorld() || target->IsDuringRemoveFromWorld()) + return; + + ImbueItemStatic(bot, item, TARGET_FLAG_UNIT, target->GetGUID()); +} + +void BotItemService::ImbueItemStatic(Player* bot, Item* item, uint8 targetInventorySlot) +{ + if (targetInventorySlot >= EQUIPMENT_SLOT_END) + return; + + Item* const targetItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, targetInventorySlot); + if (!targetItem) + return; + + ImbueItemStatic(bot, item, TARGET_FLAG_ITEM, targetItem->GetGUID()); +} + +void BotItemService::ImbueItemStatic(Player* bot, Item* item, uint32 targetFlag, ObjectGuid targetGUID) +{ + if (!item) + return; + + uint32 glyphIndex = 0; + uint8 castFlags = 0; + uint8 bagIndex = item->GetBagSlot(); + uint8 slot = item->GetSlot(); + uint8 cast_count = 0; + ObjectGuid item_guid = item->GetGUID(); + + uint32 spellId = 0; + for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) + { + if (item->GetTemplate()->Spells[i].SpellId > 0) + { + spellId = item->GetTemplate()->Spells[i].SpellId; + break; + } + } + + WorldPacket* packet = new WorldPacket(CMSG_USE_ITEM); + *packet << bagIndex; + *packet << slot; + *packet << cast_count; + *packet << spellId; + *packet << item_guid; + *packet << glyphIndex; + *packet << castFlags; + *packet << targetFlag; + + if (targetFlag & (TARGET_FLAG_UNIT | TARGET_FLAG_ITEM | TARGET_FLAG_GAMEOBJECT)) + *packet << targetGUID.WriteAsPacked(); + + bot->GetSession()->QueuePacket(packet); +} + +void BotItemService::EnchantItemStatic(Player* bot, uint32 spellid, uint8 slot) +{ + Item* pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); + if (!pItem || !pItem->IsInWorld() || !pItem->GetOwner() || !pItem->GetOwner()->IsInWorld() || + !pItem->GetOwner()->GetSession()) + return; + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid); + if (!spellInfo) + return; + + uint32 enchantid = spellInfo->Effects[0].MiscValue; + if (!enchantid) + return; + + if (!((1 << pItem->GetTemplate()->SubClass) & spellInfo->EquippedItemSubClassMask) && + !((1 << pItem->GetTemplate()->InventoryType) & spellInfo->EquippedItemInventoryTypeMask)) + { + return; + } + + bot->ApplyEnchantment(pItem, PERM_ENCHANTMENT_SLOT, false); + pItem->SetEnchantment(PERM_ENCHANTMENT_SLOT, enchantid, 0, 0); + bot->ApplyEnchantment(pItem, PERM_ENCHANTMENT_SLOT, true); + + LOG_INFO("playerbots", "{}: items was enchanted successfully!", bot->GetName().c_str()); +} + +std::vector BotItemService::GetInventoryAndEquippedItemsStatic(Player* bot) +{ + std::vector items; + + for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) + { + if (Bag* pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + { + for (uint32 j = 0; j < pBag->GetBagSize(); ++j) + { + if (Item* pItem = pBag->GetItemByPos(j)) + { + items.push_back(pItem); + } + } + } + } + + for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i) + { + if (Item* pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + { + items.push_back(pItem); + } + } + + for (int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; ++i) + { + if (Item* pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + { + items.push_back(pItem); + } + } + + for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++) + { + if (Item* pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) + { + items.push_back(pItem); + } + } + + return items; +} + +std::vector BotItemService::GetInventoryItemsStatic(Player* bot) +{ + std::vector items; + + for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) + { + if (Bag* pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + { + for (uint32 j = 0; j < pBag->GetBagSize(); ++j) + { + if (Item* pItem = pBag->GetItemByPos(j)) + { + items.push_back(pItem); + } + } + } + } + + for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i) + { + if (Item* pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + { + items.push_back(pItem); + } + } + + for (int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; ++i) + { + if (Item* pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + { + items.push_back(pItem); + } + } + + return items; +} + +uint32 BotItemService::GetInventoryItemsCountWithIdStatic(Player* bot, uint32 itemId) +{ + uint32 count = 0; + + for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) + { + if (Bag* pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + { + for (uint32 j = 0; j < pBag->GetBagSize(); ++j) + { + if (Item* pItem = pBag->GetItemByPos(j)) + { + if (pItem->GetTemplate()->ItemId == itemId) + { + count += pItem->GetCount(); + } + } + } + } + } + + for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i) + { + if (Item* pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + { + if (pItem->GetTemplate()->ItemId == itemId) + { + count += pItem->GetCount(); + } + } + } + + for (int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; ++i) + { + if (Item* pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + { + if (pItem->GetTemplate()->ItemId == itemId) + { + count += pItem->GetCount(); + } + } + } + + return count; +} + +bool BotItemService::HasItemInInventoryStatic(Player* bot, uint32 itemId) +{ + for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) + { + if (Bag* pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + { + for (uint32 j = 0; j < pBag->GetBagSize(); ++j) + { + if (Item* pItem = pBag->GetItemByPos(j)) + { + if (pItem->GetTemplate()->ItemId == itemId) + { + return true; + } + } + } + } + } + + for (int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i) + { + if (Item* pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + { + if (pItem->GetTemplate()->ItemId == itemId) + { + return true; + } + } + } + + for (int i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; ++i) + { + if (Item* pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + { + if (pItem->GetTemplate()->ItemId == itemId) + { + return true; + } + } + } + + return false; +} + +uint32 BotItemService::GetEquipGearScoreStatic(Player* player) +{ + if (!player) + return 0; + + uint32 gearScore = 0; + uint32 itemCount = 0; + + for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++) + { + // Skip shirt and tabard slots + if (slot == EQUIPMENT_SLOT_BODY || slot == EQUIPMENT_SLOT_TABARD) + continue; + + if (Item* pItem = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) + { + if (ItemTemplate const* proto = pItem->GetTemplate()) + { + gearScore += proto->ItemLevel; + itemCount++; + } + } + } + + return itemCount > 0 ? gearScore / itemCount : 0; +} + +std::vector> BotItemService::GetCurrentQuestsRequiringItemIdStatic(Player* bot, + uint32 itemId) +{ + std::vector> result; + + if (!itemId) + return result; + + for (uint16 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot) + { + uint32 questId = bot->GetQuestSlotQuestId(slot); + if (!questId) + continue; + + Quest const* quest = sObjectMgr->GetQuestTemplate(questId); + if (!quest) + continue; + + for (uint8 i = 0; i < std::size(quest->RequiredItemId); ++i) + { + if (quest->RequiredItemId[i] == itemId) + { + result.push_back(std::pair(quest, quest->RequiredItemId[i])); + break; + } + } + } + + return result; +} + +// ============================================================================ +// Instance method implementations (IItemService interface) +// These call static methods internally +// ============================================================================ + +Item* BotItemService::FindPoison() const +{ + if (_botAI) + { + return FindPoisonStatic(_botAI->GetBot()); + } + return nullptr; +} + +Item* BotItemService::FindAmmo() const +{ + if (_botAI) + { + return FindAmmoStatic(_botAI->GetBot()); + } + return nullptr; +} + +Item* BotItemService::FindBandage() const +{ + if (_botAI) + { + return FindBandageStatic(_botAI->GetBot()); + } + return nullptr; +} + +Item* BotItemService::FindOpenableItem() const +{ + if (_botAI) + { + return FindOpenableItemStatic(_botAI->GetBot()); + } + return nullptr; +} + +Item* BotItemService::FindLockedItem() const +{ + if (_botAI) + { + return FindLockedItemStatic(_botAI->GetBot()); + } + return nullptr; +} + +Item* BotItemService::FindConsumable(uint32 itemId) const +{ + if (_botAI) + { + return FindConsumableStatic(_botAI->GetBot(), itemId); + } + return nullptr; +} + +Item* BotItemService::FindStoneFor(Item* weapon) const +{ + if (_botAI) + { + return FindStoneForStatic(_botAI->GetBot(), weapon); + } + return nullptr; +} + +Item* BotItemService::FindOilFor(Item* weapon) const +{ + if (_botAI) + { + return FindOilForStatic(_botAI->GetBot(), weapon); + } + return nullptr; +} + +void BotItemService::ImbueItem(Item* item, uint32 targetFlag, ObjectGuid targetGUID) +{ + if (_botAI) + { + ImbueItemStatic(_botAI->GetBot(), item, targetFlag, targetGUID); + } +} + +void BotItemService::ImbueItem(Item* item, uint8 targetInventorySlot) +{ + if (_botAI) + { + ImbueItemStatic(_botAI->GetBot(), item, targetInventorySlot); + } +} + +void BotItemService::ImbueItem(Item* item, Unit* target) +{ + if (_botAI) + { + ImbueItemStatic(_botAI->GetBot(), item, target); + } +} + +void BotItemService::ImbueItem(Item* item) +{ + if (_botAI) + { + ImbueItemStatic(_botAI->GetBot(), item); + } +} + +void BotItemService::EnchantItem(uint32 spellId, uint8 slot) +{ + if (_botAI) + { + EnchantItemStatic(_botAI->GetBot(), spellId, slot); + } +} + +std::vector BotItemService::GetInventoryAndEquippedItems() const +{ + if (_botAI) + { + return GetInventoryAndEquippedItemsStatic(_botAI->GetBot()); + } + return {}; +} + +std::vector BotItemService::GetInventoryItems() const +{ + if (_botAI) + { + return GetInventoryItemsStatic(_botAI->GetBot()); + } + return {}; +} + +uint32 BotItemService::GetInventoryItemsCountWithId(uint32 itemId) const +{ + if (_botAI) + { + return GetInventoryItemsCountWithIdStatic(_botAI->GetBot(), itemId); + } + return 0; +} + +bool BotItemService::HasItemInInventory(uint32 itemId) const +{ + if (_botAI) + { + return HasItemInInventoryStatic(_botAI->GetBot(), itemId); + } + return false; +} + +InventoryResult BotItemService::CanEquipItem(uint8 slot, uint16& dest, Item* pItem, bool swap, bool notLoading) const +{ + if (_botAI) + { + return _botAI->CanEquipItem(slot, dest, pItem, swap, notLoading); + } + return EQUIP_ERR_ITEM_NOT_FOUND; +} + +uint8 BotItemService::FindEquipSlot(ItemTemplate const* proto, uint32 slot, bool swap) const +{ + if (_botAI) + { + return _botAI->FindEquipSlot(proto, slot, swap); + } + return 0; +} + +uint32 BotItemService::GetEquipGearScore(Player* player) const +{ + if (_botAI) + { + return GetEquipGearScoreStatic(player ? player : _botAI->GetBot()); + } + return 0; +} + +std::vector> BotItemService::GetCurrentQuestsRequiringItemId(uint32 itemId) const +{ + if (_botAI) + { + return GetCurrentQuestsRequiringItemIdStatic(_botAI->GetBot(), itemId); + } + return {}; +} diff --git a/src/Bot/Service/BotItemService.h b/src/Bot/Service/BotItemService.h new file mode 100644 index 0000000000..6252792315 --- /dev/null +++ b/src/Bot/Service/BotItemService.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_BOT_ITEM_SERVICE_H +#define _PLAYERBOT_BOT_ITEM_SERVICE_H + +#include "Bot/Interface/IItemService.h" + +#include + +class PlayerbotAI; + +/** + * @brief Implementation of IItemService + * + * This service provides inventory and item management for bots. + * + * Static methods are provided for direct access without needing a service instance. + * Instance methods implement the IItemService interface for testability/mockability. + */ +class BotItemService : public IItemService +{ +public: + BotItemService() = default; + explicit BotItemService(PlayerbotAI* ai) : _botAI(ai) {} + ~BotItemService() override = default; + + // ======================================================================== + // Static methods for direct access (main implementations) + // These can be called without a service instance + // ======================================================================== + + // Core item finding helper + static Item* FindItemInInventoryStatic(Player* bot, std::function checkItem); + + // Specific item type finders + static Item* FindPoisonStatic(Player* bot); + static Item* FindAmmoStatic(Player* bot); + static Item* FindBandageStatic(Player* bot); + static Item* FindOpenableItemStatic(Player* bot); + static Item* FindLockedItemStatic(Player* bot); + static Item* FindConsumableStatic(Player* bot, uint32 itemId); + + // Weapon enhancement finders + static Item* FindStoneForStatic(Player* bot, Item* weapon); + static Item* FindOilForStatic(Player* bot, Item* weapon); + + // Item use + static void ImbueItemStatic(Player* bot, Item* item, uint32 targetFlag, ObjectGuid targetGUID); + static void ImbueItemStatic(Player* bot, Item* item, uint8 targetInventorySlot); + static void ImbueItemStatic(Player* bot, Item* item, Unit* target); + static void ImbueItemStatic(Player* bot, Item* item); + + // Enchanting + static void EnchantItemStatic(Player* bot, uint32 spellId, uint8 slot); + + // Inventory queries + static std::vector GetInventoryAndEquippedItemsStatic(Player* bot); + static std::vector GetInventoryItemsStatic(Player* bot); + static uint32 GetInventoryItemsCountWithIdStatic(Player* bot, uint32 itemId); + static bool HasItemInInventoryStatic(Player* bot, uint32 itemId); + + // Equipment + static uint32 GetEquipGearScoreStatic(Player* player); + + // Quest items + static std::vector> GetCurrentQuestsRequiringItemIdStatic(Player* bot, + uint32 itemId); + + // ======================================================================== + // Instance methods (IItemService interface implementation) + // These call the static methods internally, but provide mockable interface + // ======================================================================== + + // Item finding + Item* FindPoison() const override; + Item* FindAmmo() const override; + Item* FindBandage() const override; + Item* FindOpenableItem() const override; + Item* FindLockedItem() const override; + Item* FindConsumable(uint32 itemId) const override; + + // Weapon enhancements + Item* FindStoneFor(Item* weapon) const override; + Item* FindOilFor(Item* weapon) const override; + + // Item use + void ImbueItem(Item* item, uint32 targetFlag, ObjectGuid targetGUID) override; + void ImbueItem(Item* item, uint8 targetInventorySlot) override; + void ImbueItem(Item* item, Unit* target) override; + void ImbueItem(Item* item) override; + + // Enchanting + void EnchantItem(uint32 spellId, uint8 slot) override; + + // Inventory queries + std::vector GetInventoryAndEquippedItems() const override; + std::vector GetInventoryItems() const override; + uint32 GetInventoryItemsCountWithId(uint32 itemId) const override; + bool HasItemInInventory(uint32 itemId) const override; + + // Equipment + InventoryResult CanEquipItem(uint8 slot, uint16& dest, Item* pItem, bool swap, + bool notLoading = true) const override; + uint8 FindEquipSlot(ItemTemplate const* proto, uint32 slot, bool swap) const override; + uint32 GetEquipGearScore(Player* player = nullptr) const override; + + // Quest items + std::vector> GetCurrentQuestsRequiringItemId(uint32 itemId) const override; + + // Set the bot context for instance methods that need the bot + void SetBotContext(PlayerbotAI* ai) { _botAI = ai; } + PlayerbotAI* GetBotContext() const { return _botAI; } + +private: + PlayerbotAI* _botAI = nullptr; +}; + +#endif diff --git a/src/Bot/Service/BotRepositoryAdapter.cpp b/src/Bot/Service/BotRepositoryAdapter.cpp new file mode 100644 index 0000000000..9f4bc9bb97 --- /dev/null +++ b/src/Bot/Service/BotRepositoryAdapter.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "BotRepositoryAdapter.h" + +#include "PlayerbotRepository.h" + +void BotRepositoryAdapter::Save(PlayerbotAI* botAI) +{ + sPlayerbotRepository->Save(botAI); +} + +void BotRepositoryAdapter::Load(PlayerbotAI* botAI) +{ + sPlayerbotRepository->Load(botAI); +} + +void BotRepositoryAdapter::Reset(PlayerbotAI* botAI) +{ + sPlayerbotRepository->Reset(botAI); +} + +bool BotRepositoryAdapter::HasSavedData(uint32 /*guid*/) +{ + // PlayerbotRepository doesn't expose this directly + // This would need to be added to PlayerbotRepository in a future refactor + return false; +} + +std::string BotRepositoryAdapter::GetSavedValue(uint32 /*guid*/, std::string const& /*key*/) +{ + // PlayerbotRepository doesn't expose individual value access + // This would need to be added to PlayerbotRepository in a future refactor + return ""; +} + +void BotRepositoryAdapter::SetSavedValue(uint32 /*guid*/, std::string const& /*key*/, std::string const& /*value*/) +{ + // PlayerbotRepository doesn't expose individual value access + // This would need to be added to PlayerbotRepository in a future refactor +} + +void BotRepositoryAdapter::DeleteSavedData(uint32 /*guid*/) +{ + // PlayerbotRepository doesn't expose this directly + // This would need to be added to PlayerbotRepository in a future refactor +} diff --git a/src/Bot/Service/BotRepositoryAdapter.h b/src/Bot/Service/BotRepositoryAdapter.h new file mode 100644 index 0000000000..4e667bd5fa --- /dev/null +++ b/src/Bot/Service/BotRepositoryAdapter.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_BOT_REPOSITORY_ADAPTER_H +#define _PLAYERBOT_BOT_REPOSITORY_ADAPTER_H + +#include "Bot/Interface/IBotRepository.h" + +/** + * @brief Adapter that wraps PlayerbotRepository singleton behind IBotRepository interface + * + * This adapter allows code to use the IBotRepository interface while + * delegating to the existing PlayerbotRepository singleton. + */ +class BotRepositoryAdapter : public IBotRepository +{ +public: + BotRepositoryAdapter() = default; + ~BotRepositoryAdapter() override = default; + + // Bot strategy persistence + void Save(PlayerbotAI* botAI) override; + void Load(PlayerbotAI* botAI) override; + void Reset(PlayerbotAI* botAI) override; + + // Bot data queries + bool HasSavedData(uint32 guid) override; + std::string GetSavedValue(uint32 guid, std::string const& key) override; + void SetSavedValue(uint32 guid, std::string const& key, std::string const& value) override; + void DeleteSavedData(uint32 guid) override; +}; + +#endif diff --git a/src/Bot/Service/BotRoleService.cpp b/src/Bot/Service/BotRoleService.cpp new file mode 100644 index 0000000000..04f08f155f --- /dev/null +++ b/src/Bot/Service/BotRoleService.cpp @@ -0,0 +1,856 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "BotRoleService.h" + +#include "AiFactory.h" +#include "Group.h" +#include "ObjectGuid.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "Player.h" +#include "Strategy.h" +#include "Unit.h" + +// ============================================================================ +// Static method implementations (main logic) +// ============================================================================ + +bool BotRoleService::IsRangedStatic(Player* player, bool bySpec) +{ + PlayerbotAI* botAi = GET_PLAYERBOT_AI(player); + if (!bySpec && botAi) + return botAi->ContainsStrategy(STRATEGY_TYPE_RANGED); + + int tab = AiFactory::GetPlayerSpecTab(player); + switch (player->getClass()) + { + case CLASS_DEATH_KNIGHT: + case CLASS_WARRIOR: + case CLASS_ROGUE: + return false; + break; + case CLASS_DRUID: + if (tab == DRUID_TAB_FERAL) + { + return false; + } + break; + case CLASS_PALADIN: + if (tab != PALADIN_TAB_HOLY) + { + return false; + } + break; + case CLASS_SHAMAN: + if (tab == SHAMAN_TAB_ENHANCEMENT) + { + return false; + } + break; + default: + break; + } + + return true; +} + +bool BotRoleService::IsMeleeStatic(Player* player, bool bySpec) +{ + return !IsRangedStatic(player, bySpec); +} + +bool BotRoleService::IsCasterStatic(Player* player, bool bySpec) +{ + return IsRangedStatic(player, bySpec) && player->getClass() != CLASS_HUNTER; +} + +bool BotRoleService::IsComboStatic(Player* player) +{ + return player->getClass() == CLASS_ROGUE || + (player->getClass() == CLASS_DRUID && player->HasAura(768)); // cat druid +} + +bool BotRoleService::IsRangedDpsStatic(Player* player, bool bySpec) +{ + return IsRangedStatic(player, bySpec) && IsDpsStatic(player, bySpec); +} + +bool BotRoleService::IsAssistHealOfIndexStatic(Player* player, int index, bool ignoreDeadPlayers) +{ + Group* group = player->GetGroup(); + if (!group) + return false; + + int counter = 0; + + // First, assistants + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member) + continue; + + if (ignoreDeadPlayers && !member->IsAlive()) + continue; + + if (group->IsAssistant(member->GetGUID()) && IsHealStatic(member)) + { + if (index == counter) + return player == member; + counter++; + } + } + + // If not enough assistants, get non-assistants + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member) + continue; + + if (ignoreDeadPlayers && !member->IsAlive()) + continue; + + if (!group->IsAssistant(member->GetGUID()) && IsHealStatic(member)) + { + if (index == counter) + return player == member; + counter++; + } + } + + return false; +} + +bool BotRoleService::IsAssistRangedDpsOfIndexStatic(Player* player, int index, bool ignoreDeadPlayers) +{ + Group* group = player->GetGroup(); + if (!group) + return false; + + int counter = 0; + + // First, assistants + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member) + continue; + + if (ignoreDeadPlayers && !member->IsAlive()) + continue; + + if (group->IsAssistant(member->GetGUID()) && IsRangedDpsStatic(member)) + { + if (index == counter) + return player == member; + counter++; + } + } + + // If not enough assistants, get non-assistants + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member) + continue; + + if (ignoreDeadPlayers && !member->IsAlive()) + continue; + + if (!group->IsAssistant(member->GetGUID()) && IsRangedDpsStatic(member)) + { + if (index == counter) + return player == member; + counter++; + } + } + + return false; +} + +int32 BotRoleService::GetAssistTankIndexStatic(Player* player) +{ + Group* group = player->GetGroup(); + if (!group) + { + return -1; + } + + int counter = 0; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member) + { + continue; + } + + if (player == member) + { + return counter; + } + + if (IsTankStatic(member, true) && group->IsAssistant(member->GetGUID())) + { + counter++; + } + } + + return 0; +} + +bool BotRoleService::IsTankStatic(Player* player, bool bySpec) +{ + PlayerbotAI* botAi = GET_PLAYERBOT_AI(player); + if (!bySpec && botAi) + return botAi->ContainsStrategy(STRATEGY_TYPE_TANK); + + int tab = AiFactory::GetPlayerSpecTab(player); + switch (player->getClass()) + { + case CLASS_DEATH_KNIGHT: + if (tab == DEATH_KNIGHT_TAB_BLOOD) + { + return true; + } + break; + case CLASS_PALADIN: + if (tab == PALADIN_TAB_PROTECTION) + { + return true; + } + break; + case CLASS_WARRIOR: + if (tab == WARRIOR_TAB_PROTECTION) + { + return true; + } + break; + case CLASS_DRUID: + if (tab == DRUID_TAB_FERAL && (player->GetShapeshiftForm() == FORM_BEAR || + player->GetShapeshiftForm() == FORM_DIREBEAR || player->HasAura(16931))) + { + return true; + } + break; + default: + break; + } + return false; +} + +bool BotRoleService::IsHealStatic(Player* player, bool bySpec) +{ + PlayerbotAI* botAi = GET_PLAYERBOT_AI(player); + if (!bySpec && botAi) + return botAi->ContainsStrategy(STRATEGY_TYPE_HEAL); + + int tab = AiFactory::GetPlayerSpecTab(player); + switch (player->getClass()) + { + case CLASS_PRIEST: + if (tab == PRIEST_TAB_DISCIPLINE || tab == PRIEST_TAB_HOLY) + { + return true; + } + break; + case CLASS_DRUID: + if (tab == DRUID_TAB_RESTORATION) + { + return true; + } + break; + case CLASS_SHAMAN: + if (tab == SHAMAN_TAB_RESTORATION) + { + return true; + } + break; + case CLASS_PALADIN: + if (tab == PALADIN_TAB_HOLY) + { + return true; + } + break; + default: + break; + } + return false; +} + +bool BotRoleService::IsDpsStatic(Player* player, bool bySpec) +{ + PlayerbotAI* botAi = GET_PLAYERBOT_AI(player); + if (!bySpec && botAi) + return botAi->ContainsStrategy(STRATEGY_TYPE_DPS); + + int tab = AiFactory::GetPlayerSpecTab(player); + switch (player->getClass()) + { + case CLASS_MAGE: + case CLASS_WARLOCK: + case CLASS_HUNTER: + case CLASS_ROGUE: + return true; + case CLASS_PRIEST: + if (tab == PRIEST_TAB_SHADOW) + { + return true; + } + break; + case CLASS_DRUID: + if (tab == DRUID_TAB_BALANCE) + { + return true; + } + if (tab == DRUID_TAB_FERAL && !IsTankStatic(player, bySpec)) + { + return true; + } + break; + case CLASS_SHAMAN: + if (tab != SHAMAN_TAB_RESTORATION) + { + return true; + } + break; + case CLASS_PALADIN: + if (tab == PALADIN_TAB_RETRIBUTION) + { + return true; + } + break; + case CLASS_DEATH_KNIGHT: + if (tab != DEATH_KNIGHT_TAB_BLOOD) + { + return true; + } + break; + case CLASS_WARRIOR: + if (tab != WARRIOR_TAB_PROTECTION) + { + return true; + } + break; + default: + break; + } + return false; +} + +bool BotRoleService::IsMainTankStatic(Player* player) +{ + Group* group = player->GetGroup(); + if (!group) + { + return IsTankStatic(player); + } + + ObjectGuid mainTank = ObjectGuid(); + Group::MemberSlotList const& slots = group->GetMemberSlots(); + + for (Group::member_citerator itr = slots.begin(); itr != slots.end(); ++itr) + { + if (itr->flags & MEMBER_FLAG_MAINTANK) + { + mainTank = itr->guid; + break; + } + } + + if (mainTank != ObjectGuid::Empty) + { + return player->GetGUID() == mainTank; + } + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member) + { + continue; + } + + if (IsTankStatic(member) && member->IsAlive()) + { + return player->GetGUID() == member->GetGUID(); + } + } + + return false; +} + +bool BotRoleService::IsBotMainTankStatic(Player* player) +{ + if (!player || !player->IsInWorld() || player->IsDuringRemoveFromWorld()) + return false; + + WorldSession* session = player->GetSession(); + if (!session || !session->IsBot()) + return false; + + if (!IsTankStatic(player)) + return false; + + if (IsMainTankStatic(player)) + { + return true; + } + + Group* group = player->GetGroup(); + if (!group) + { + return true; // If no group, consider the bot as main tank + } + + int32 botAssistTankIndex = GetAssistTankIndexStatic(player); + if (botAssistTankIndex == -1) + { + return false; + } + + for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) + { + Player* member = gref->GetSource(); + if (!member) + { + continue; + } + + int32 memberAssistTankIndex = GetAssistTankIndexStatic(member); + if (memberAssistTankIndex == -1) + { + continue; + } + + if (memberAssistTankIndex == botAssistTankIndex && player == member) + { + return true; + } + + if (memberAssistTankIndex < botAssistTankIndex && member->GetSession()->IsBot()) + { + return false; + } + + return false; + } + + return false; +} + +uint32 BotRoleService::GetGroupTankNumStatic(Player* player) +{ + Group* group = player->GetGroup(); + if (!group) + { + return 0; + } + uint32 result = 0; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + + if (!member) + { + continue; + } + + if (IsTankStatic(member) && member->IsAlive()) + { + result++; + } + } + return result; +} + +bool BotRoleService::IsAssistTankStatic(Player* player) +{ + return IsTankStatic(player) && !IsMainTankStatic(player); +} + +bool BotRoleService::IsAssistTankOfIndexStatic(Player* player, int index, bool ignoreDeadPlayers) +{ + Group* group = player->GetGroup(); + if (!group) + { + return false; + } + int counter = 0; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + + if (!member) + { + continue; + } + + if (ignoreDeadPlayers && !member->IsAlive()) + continue; + + if (group->IsAssistant(member->GetGUID()) && IsAssistTankStatic(member)) + { + if (index == counter) + { + return player == member; + } + counter++; + } + } + // not enough + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + + if (!member) + { + continue; + } + + if (ignoreDeadPlayers && !member->IsAlive()) + continue; + + if (!group->IsAssistant(member->GetGUID()) && IsAssistTankStatic(member)) + { + if (index == counter) + { + return player == member; + } + counter++; + } + } + return false; +} + +// ============================================================================ +// Instance method implementations (IRoleService interface) +// These delegate to the static methods or use bot context where needed +// ============================================================================ + +bool BotRoleService::IsTank(Player* player, bool bySpec) const +{ + return IsTankStatic(player, bySpec); +} + +bool BotRoleService::IsHeal(Player* player, bool bySpec) const +{ + return IsHealStatic(player, bySpec); +} + +bool BotRoleService::IsDps(Player* player, bool bySpec) const +{ + return IsDpsStatic(player, bySpec); +} + +bool BotRoleService::IsRanged(Player* player, bool bySpec) const +{ + return IsRangedStatic(player, bySpec); +} + +bool BotRoleService::IsMelee(Player* player, bool bySpec) const +{ + return IsMeleeStatic(player, bySpec); +} + +bool BotRoleService::IsCaster(Player* player, bool bySpec) const +{ + return IsCasterStatic(player, bySpec); +} + +bool BotRoleService::IsRangedDps(Player* player, bool bySpec) const +{ + return IsRangedDpsStatic(player, bySpec); +} + +bool BotRoleService::IsCombo(Player* player) const +{ + return IsComboStatic(player); +} + +bool BotRoleService::IsBotMainTank(Player* player) const +{ + return IsBotMainTankStatic(player); +} + +bool BotRoleService::IsMainTank(Player* player) const +{ + return IsMainTankStatic(player); +} + +bool BotRoleService::IsAssistTank(Player* player) const +{ + return IsAssistTankStatic(player); +} + +bool BotRoleService::IsAssistTankOfIndex(Player* player, int index, bool ignoreDeadPlayers) const +{ + return IsAssistTankOfIndexStatic(player, index, ignoreDeadPlayers); +} + +uint32 BotRoleService::GetGroupTankNum(Player* player) const +{ + return GetGroupTankNumStatic(player); +} + +int32 BotRoleService::GetAssistTankIndex(Player* player) const +{ + return GetAssistTankIndexStatic(player); +} + +int32 BotRoleService::GetGroupSlotIndex(Player* player) const +{ + if (!_botAI) + { + return -1; + } + + Player* bot = _botAI->GetBot(); + if (!bot) + { + return -1; + } + + Group* group = bot->GetGroup(); + if (!group) + { + return -1; + } + int counter = 0; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + + if (!member) + { + continue; + } + + if (player == member) + { + return counter; + } + counter++; + } + return 0; +} + +int32 BotRoleService::GetRangedIndex(Player* player) const +{ + if (!IsRangedStatic(player)) + { + return -1; + } + + if (!_botAI) + { + return -1; + } + + Player* bot = _botAI->GetBot(); + if (!bot) + { + return -1; + } + + Group* group = bot->GetGroup(); + if (!group) + { + return -1; + } + int counter = 0; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + + if (!member) + { + continue; + } + + if (player == member) + { + return counter; + } + if (IsRangedStatic(member)) + { + counter++; + } + } + return 0; +} + +int32 BotRoleService::GetClassIndex(Player* player, uint8 cls) const +{ + if (player->getClass() != cls) + { + return -1; + } + + if (!_botAI) + { + return -1; + } + + Player* bot = _botAI->GetBot(); + if (!bot) + { + return -1; + } + + Group* group = bot->GetGroup(); + if (!group) + { + return -1; + } + int counter = 0; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + + if (!member) + { + continue; + } + + if (player == member) + { + return counter; + } + if (member->getClass() == cls) + { + counter++; + } + } + return 0; +} + +int32 BotRoleService::GetRangedDpsIndex(Player* player) const +{ + if (!IsRangedDpsStatic(player)) + { + return -1; + } + + if (!_botAI) + { + return -1; + } + + Player* bot = _botAI->GetBot(); + if (!bot) + { + return -1; + } + + Group* group = bot->GetGroup(); + if (!group) + { + return -1; + } + int counter = 0; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + + if (!member) + { + continue; + } + + if (player == member) + { + return counter; + } + if (IsRangedDpsStatic(member)) + { + counter++; + } + } + return 0; +} + +int32 BotRoleService::GetMeleeIndex(Player* player) const +{ + if (IsRangedStatic(player)) + { + return -1; + } + + if (!_botAI) + { + return -1; + } + + Player* bot = _botAI->GetBot(); + if (!bot) + { + return -1; + } + + Group* group = bot->GetGroup(); + if (!group) + { + return -1; + } + int counter = 0; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + + if (!member) + { + continue; + } + + if (player == member) + { + return counter; + } + if (!IsRangedStatic(member)) + { + counter++; + } + } + return 0; +} + +bool BotRoleService::IsAssistHealOfIndex(Player* player, int index, bool ignoreDeadPlayers) const +{ + return IsAssistHealOfIndexStatic(player, index, ignoreDeadPlayers); +} + +bool BotRoleService::IsAssistRangedDpsOfIndex(Player* player, int index, bool ignoreDeadPlayers) const +{ + return IsAssistRangedDpsOfIndexStatic(player, index, ignoreDeadPlayers); +} + +bool BotRoleService::HasAggro(Unit* unit) const +{ + if (!_botAI) + { + return false; + } + + Player* bot = _botAI->GetBot(); + if (!bot) + { + return false; + } + + if (!unit || !unit->IsAlive()) + return false; + + bool isMT = IsMainTankStatic(bot); + Unit* victim = unit->GetVictim(); + if (victim && (victim->GetGUID() == bot->GetGUID() || + (!isMT && victim->ToPlayer() && IsTankStatic(victim->ToPlayer())))) + { + return true; + } + return false; +} diff --git a/src/Bot/Service/BotRoleService.h b/src/Bot/Service/BotRoleService.h new file mode 100644 index 0000000000..ff43999d1d --- /dev/null +++ b/src/Bot/Service/BotRoleService.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_BOT_ROLE_SERVICE_H +#define _PLAYERBOT_BOT_ROLE_SERVICE_H + +#include "Bot/Interface/IRoleService.h" + +class PlayerbotAI; + +/** + * @brief Implementation of IRoleService + * + * This service provides role detection and management for bots. + * Role detection is based on player spec, talents, and group position. + * + * Static methods are provided for direct access without needing a service instance. + * Instance methods implement the IRoleService interface for testability/mockability. + */ +class BotRoleService : public IRoleService +{ +public: + BotRoleService() = default; + ~BotRoleService() override = default; + + // ======================================================================== + // Static methods for direct access (main implementations) + // These can be called without a service instance + // ======================================================================== + + // Basic role detection (based on strategy or spec) + static bool IsTankStatic(Player* player, bool bySpec = false); + static bool IsHealStatic(Player* player, bool bySpec = false); + static bool IsDpsStatic(Player* player, bool bySpec = false); + + // Combat style detection + static bool IsRangedStatic(Player* player, bool bySpec = false); + static bool IsMeleeStatic(Player* player, bool bySpec = false); + static bool IsCasterStatic(Player* player, bool bySpec = false); + static bool IsRangedDpsStatic(Player* player, bool bySpec = false); + + // Hybrid detection + static bool IsComboStatic(Player* player); + + // Tank hierarchy + static bool IsBotMainTankStatic(Player* player); + static bool IsMainTankStatic(Player* player); + static bool IsAssistTankStatic(Player* player); + static bool IsAssistTankOfIndexStatic(Player* player, int index, bool ignoreDeadPlayers = false); + + // Group role queries (static - use player's group) + static uint32 GetGroupTankNumStatic(Player* player); + static int32 GetAssistTankIndexStatic(Player* player); + + // Heal/DPS assistant detection + static bool IsAssistHealOfIndexStatic(Player* player, int index, bool ignoreDeadPlayers = false); + static bool IsAssistRangedDpsOfIndexStatic(Player* player, int index, bool ignoreDeadPlayers = false); + + // ======================================================================== + // Instance methods (IRoleService interface implementation) + // These call the static methods internally, but provide mockable interface + // ======================================================================== + + // Basic role detection + bool IsTank(Player* player, bool bySpec = false) const override; + bool IsHeal(Player* player, bool bySpec = false) const override; + bool IsDps(Player* player, bool bySpec = false) const override; + + // Combat style detection + bool IsRanged(Player* player, bool bySpec = false) const override; + bool IsMelee(Player* player, bool bySpec = false) const override; + bool IsCaster(Player* player, bool bySpec = false) const override; + bool IsRangedDps(Player* player, bool bySpec = false) const override; + + // Hybrid detection + bool IsCombo(Player* player) const override; + + // Tank hierarchy + bool IsBotMainTank(Player* player) const override; + bool IsMainTank(Player* player) const override; + bool IsAssistTank(Player* player) const override; + bool IsAssistTankOfIndex(Player* player, int index, bool ignoreDeadPlayers = false) const override; + + // Group role queries + uint32 GetGroupTankNum(Player* player) const override; + int32 GetAssistTankIndex(Player* player) const override; + int32 GetGroupSlotIndex(Player* player) const override; + int32 GetRangedIndex(Player* player) const override; + int32 GetRangedDpsIndex(Player* player) const override; + int32 GetMeleeIndex(Player* player) const override; + int32 GetClassIndex(Player* player, uint8 cls) const override; + + // Role assistant index queries + bool IsAssistHealOfIndex(Player* player, int index, bool ignoreDeadPlayers = false) const override; + bool IsAssistRangedDpsOfIndex(Player* player, int index, bool ignoreDeadPlayers = false) const override; + + // Aggro + bool HasAggro(Unit* unit) const override; + + // Set the bot context for instance methods that need the bot + void SetBotContext(PlayerbotAI* ai) { _botAI = ai; } + PlayerbotAI* GetBotContext() const { return _botAI; } + +private: + PlayerbotAI* _botAI = nullptr; +}; + +#endif diff --git a/src/Bot/Service/BotSpellService.cpp b/src/Bot/Service/BotSpellService.cpp new file mode 100644 index 0000000000..1307ac4af9 --- /dev/null +++ b/src/Bot/Service/BotSpellService.cpp @@ -0,0 +1,586 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "BotSpellService.h" + +#include "DBCStores.h" +#include "Player.h" +#include "PlayerbotAI.h" +#include "PlayerbotAIConfig.h" +#include "SpellAuraEffects.h" +#include "SpellAuras.h" +#include "SpellInfo.h" +#include "SpellMgr.h" +#include "Unit.h" +#include "Util.h" +#include "Vehicle.h" +#include "VehicleDefines.h" + +#ifndef WIN32 +inline int strcmpi(char const* s1, char const* s2) +{ + for (; *s1 && *s2 && (toupper(*s1) == toupper(*s2)); ++s1, ++s2) + { + } + return *s1 - *s2; +} +#endif + +// ============================================================================ +// Static method implementations (main logic) +// ============================================================================ + +bool BotSpellService::IsRealAuraStatic(Player* bot, AuraEffect const* aurEff, Unit const* unit) +{ + if (!unit || !unit->IsInWorld() || unit->IsDuringRemoveFromWorld()) + return false; + + if (!aurEff) + return false; + + if (!unit->IsHostileTo(bot)) + return true; + + SpellInfo const* spellInfo = aurEff->GetSpellInfo(); + if (!spellInfo) + return false; + + uint32 stacks = aurEff->GetBase()->GetStackAmount(); + if (stacks >= spellInfo->StackAmount) + return true; + + if (aurEff->GetCaster() == bot || spellInfo->IsPositive() || + spellInfo->Effects[aurEff->GetEffIndex()].IsAreaAuraEffect()) + return true; + + return false; +} + +bool BotSpellService::HasAuraByNameStatic(Player* bot, std::string const& name, Unit* unit, bool maxStack, + bool checkIsOwner, int maxAuraAmount, bool checkDuration) +{ + if (!unit || !unit->IsInWorld() || unit->IsDuringRemoveFromWorld()) + return false; + + std::wstring wnamepart; + if (!Utf8toWStr(name, wnamepart)) + return false; + + wstrToLower(wnamepart); + + int auraAmount = 0; + + for (uint32 auraType = SPELL_AURA_BIND_SIGHT; auraType < TOTAL_AURAS; auraType++) + { + Unit::AuraEffectList const& auras = unit->GetAuraEffectsByType((AuraType)auraType); + if (auras.empty()) + continue; + + for (AuraEffect const* aurEff : auras) + { + if (!aurEff) + continue; + + SpellInfo const* spellInfo = aurEff->GetSpellInfo(); + if (!spellInfo) + continue; + + std::string_view const auraName = spellInfo->SpellName[0]; + if (auraName.empty() || auraName.length() != wnamepart.length() || !Utf8FitTo(auraName, wnamepart)) + continue; + + if (IsRealAuraStatic(bot, aurEff, unit)) + { + if (checkIsOwner && aurEff->GetCasterGUID() != bot->GetGUID()) + continue; + + if (checkDuration && aurEff->GetBase()->GetDuration() == -1) + continue; + + uint32 maxStackAmount = spellInfo->StackAmount; + uint32 maxProcCharges = spellInfo->ProcCharges; + + if (maxStack) + { + if (maxStackAmount && aurEff->GetBase()->GetStackAmount() >= maxStackAmount) + auraAmount++; + + if (maxProcCharges && aurEff->GetBase()->GetCharges() >= maxProcCharges) + auraAmount++; + } + else + { + auraAmount++; + } + + if (maxAuraAmount < 0 && auraAmount > 0) + return true; + } + } + } + + if (maxAuraAmount >= 0) + { + return auraAmount == maxAuraAmount || (auraAmount > 0 && auraAmount <= maxAuraAmount); + } + + return false; +} + +bool BotSpellService::HasAuraByIdStatic(uint32 spellId, Unit const* unit) +{ + if (!spellId || !unit) + return false; + + return unit->HasAura(spellId); +} + +bool BotSpellService::HasAnyAuraOfStatic(Player* bot, Unit* player, std::vector const& auraNames) +{ + if (!player) + return false; + + for (auto const& name : auraNames) + { + if (HasAuraByNameStatic(bot, name, player)) + return true; + } + + return false; +} + +Aura* BotSpellService::GetAuraStatic(Player* bot, std::string const& name, Unit* unit, bool checkIsOwner, + bool checkDuration, int checkStack) +{ + if (!unit || !unit->IsInWorld() || unit->IsDuringRemoveFromWorld()) + return nullptr; + + std::wstring wnamepart; + if (!Utf8toWStr(name, wnamepart)) + return nullptr; + + wstrToLower(wnamepart); + + for (uint32 auraType = SPELL_AURA_BIND_SIGHT; auraType < TOTAL_AURAS; ++auraType) + { + Unit::AuraEffectList const& auras = unit->GetAuraEffectsByType((AuraType)auraType); + if (auras.empty()) + continue; + + for (AuraEffect const* aurEff : auras) + { + SpellInfo const* spellInfo = aurEff->GetSpellInfo(); + if (!spellInfo) + continue; + + std::string const& auraName = spellInfo->SpellName[0]; + + if (auraName.empty() || auraName.length() != wnamepart.length() || !Utf8FitTo(auraName, wnamepart)) + continue; + + if (!IsRealAuraStatic(bot, aurEff, unit)) + continue; + + if (checkIsOwner && aurEff->GetCasterGUID() != bot->GetGUID()) + continue; + + if (checkDuration && aurEff->GetBase()->GetDuration() == -1) + continue; + + if (checkStack != -1 && aurEff->GetBase()->GetStackAmount() < checkStack) + continue; + + return aurEff->GetBase(); + } + } + + return nullptr; +} + +bool BotSpellService::HasAuraToDispelStatic(Player* bot, Unit* target, uint32 dispelType) +{ + if (!target || !target->IsAlive() || !target->IsInWorld() || target->IsDuringRemoveFromWorld()) + return false; + + if (!bot || !bot->IsAlive()) + return false; + + bool isFriend = bot->IsFriendlyTo(target); + + Unit::VisibleAuraMap const* visibleAuras = target->GetVisibleAuras(); + if (!visibleAuras) + return false; + + for (Unit::VisibleAuraMap::const_iterator itr = visibleAuras->begin(); itr != visibleAuras->end(); ++itr) + { + if (!itr->second) + continue; + + Aura* aura = itr->second->GetBase(); + if (!aura || aura->IsPassive() || aura->IsRemoved()) + continue; + + if (sPlayerbotAIConfig->dispelAuraDuration && aura->GetDuration() && + aura->GetDuration() < (int32)sPlayerbotAIConfig->dispelAuraDuration) + continue; + + SpellInfo const* spellInfo = aura->GetSpellInfo(); + if (!spellInfo) + continue; + + bool isPositiveSpell = spellInfo->IsPositive(); + if (isPositiveSpell && isFriend) + continue; + + if (!isPositiveSpell && !isFriend) + continue; + + // Use an empty whitelist since we don't have access to PlayerbotAI's dispel_whitelist + std::vector emptyWhitelist; + if (CanDispelStatic(spellInfo, dispelType, emptyWhitelist)) + return true; + } + + return false; +} + +bool BotSpellService::CanDispelStatic(SpellInfo const* spellInfo, uint32 dispelType, + std::vector const& dispelWhitelist) +{ + if (spellInfo->Dispel != dispelType) + return false; + + if (!spellInfo->SpellName[0]) + { + return true; + } + + for (auto const& wl : dispelWhitelist) + { + if (strcmpi((const char*)spellInfo->SpellName[0], wl.c_str()) == 0) + { + return false; + } + } + + return !spellInfo->SpellName[0] || (strcmpi((const char*)spellInfo->SpellName[0], "demon skin") && + strcmpi((const char*)spellInfo->SpellName[0], "mage armor") && + strcmpi((const char*)spellInfo->SpellName[0], "ice armor") && + strcmpi((const char*)spellInfo->SpellName[0], "frost armor") && + strcmpi((const char*)spellInfo->SpellName[0], "molten armor") && + strcmpi((const char*)spellInfo->SpellName[0], "demon armor")); +} + +bool BotSpellService::IsInterruptableSpellCastingStatic(Player* /*bot*/, Unit* target, uint32 interruptSpellId) +{ + if (!target || !target->IsInWorld() || target->IsDuringRemoveFromWorld()) + return false; + + if (!interruptSpellId || !target->IsNonMeleeSpellCast(true)) + return false; + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(interruptSpellId); + if (!spellInfo) + return false; + + for (uint8 i = EFFECT_0; i <= EFFECT_2; i++) + { + if ((spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_INTERRUPT) && + spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE) + return true; + + if (spellInfo->Effects[i].Effect == SPELL_EFFECT_INTERRUPT_CAST && + !target->IsImmunedToSpellEffect(spellInfo, i)) + return true; + + if ((spellInfo->Effects[i].Effect == SPELL_EFFECT_APPLY_AURA) && + spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_SILENCE) + return true; + } + + return false; +} + +void BotSpellService::SpellInterruptedStatic(Player* /*bot*/, uint32 /*spellId*/) +{ + // This tracks interrupted spells - implementation would need access to PlayerbotAI's lastInterruptedSpellTime + // For static version, this is a no-op since we can't store state +} + +int32 BotSpellService::CalculateGlobalCooldownStatic(uint32 /*spellId*/) +{ + return sPlayerbotAIConfig->globalCoolDown; +} + +bool BotSpellService::IsInVehicleStatic(Player* bot, bool canControl, bool canCast, bool canAttack, bool canTurn, + bool fixed) +{ + Vehicle* vehicle = bot->GetVehicle(); + if (!vehicle) + return false; + + Unit* vehicleBase = vehicle->GetBase(); + if (!vehicleBase || !vehicleBase->IsAlive()) + return false; + + if (!vehicle->GetVehicleInfo()) + return false; + + VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(bot); + if (!seat) + return false; + + if (!(canControl || canCast || canAttack || canTurn || fixed)) + return true; + + if (canControl) + return seat->CanControl() && !(vehicle->GetVehicleInfo()->m_flags & VEHICLE_FLAG_FIXED_POSITION); + + if (canCast) + return (seat->m_flags & VEHICLE_SEAT_FLAG_CAN_CAST) != 0; + + if (canAttack) + return (seat->m_flags & VEHICLE_SEAT_FLAG_CAN_ATTACK) != 0; + + if (canTurn) + return (seat->m_flags & VEHICLE_SEAT_FLAG_ALLOW_TURNING) != 0; + + if (fixed) + return (vehicle->GetVehicleInfo()->m_flags & VEHICLE_FLAG_FIXED_POSITION) != 0; + + return false; +} + +// ============================================================================ +// Instance method implementations (IChatService interface) +// These delegate to static methods or PlayerbotAI during transition +// ============================================================================ + +bool BotSpellService::CanCastSpell(std::string const& name, Unit* target, Item* itemTarget) +{ + if (_botAI) + { + return _botAI->CanCastSpell(name, target, itemTarget); + } + return false; +} + +bool BotSpellService::CastSpell(std::string const& name, Unit* target, Item* itemTarget) +{ + if (_botAI) + { + return _botAI->CastSpell(name, target, itemTarget); + } + return false; +} + +bool BotSpellService::CanCastSpell(uint32 spellId, Unit* target, bool checkHasSpell, Item* itemTarget, Item* castItem) +{ + if (_botAI) + { + return _botAI->CanCastSpell(spellId, target, checkHasSpell, itemTarget, castItem); + } + return false; +} + +bool BotSpellService::CanCastSpell(uint32 spellId, GameObject* goTarget, bool checkHasSpell) +{ + if (_botAI) + { + return _botAI->CanCastSpell(spellId, goTarget, checkHasSpell); + } + return false; +} + +bool BotSpellService::CanCastSpell(uint32 spellId, float x, float y, float z, bool checkHasSpell, Item* itemTarget) +{ + if (_botAI) + { + return _botAI->CanCastSpell(spellId, x, y, z, checkHasSpell, itemTarget); + } + return false; +} + +bool BotSpellService::CastSpell(uint32 spellId, Unit* target, Item* itemTarget) +{ + if (_botAI) + { + return _botAI->CastSpell(spellId, target, itemTarget); + } + return false; +} + +bool BotSpellService::CastSpell(uint32 spellId, float x, float y, float z, Item* itemTarget) +{ + if (_botAI) + { + return _botAI->CastSpell(spellId, x, y, z, itemTarget); + } + return false; +} + +bool BotSpellService::HasAura(std::string const& spellName, Unit* player, bool maxStack, bool checkIsOwner, + int maxAmount, bool checkDuration) +{ + if (_botAI) + { + return HasAuraByNameStatic(_botAI->GetBot(), spellName, player, maxStack, checkIsOwner, maxAmount, checkDuration); + } + return false; +} + +bool BotSpellService::HasAura(uint32 spellId, Unit const* player) +{ + return HasAuraByIdStatic(spellId, player); +} + +bool BotSpellService::HasAnyAuraOf(Unit* player, ...) +{ + if (!_botAI || !player) + return false; + + va_list vl; + va_start(vl, player); + + const char* cur; + while ((cur = va_arg(vl, const char*)) != nullptr) + { + if (HasAura(cur, player)) + { + va_end(vl); + return true; + } + } + + va_end(vl); + return false; +} + +bool BotSpellService::HasAnyAuraOfVec(Unit* player, std::vector const& auraNames) +{ + if (_botAI) + { + return HasAnyAuraOfStatic(_botAI->GetBot(), player, auraNames); + } + return false; +} + +Aura* BotSpellService::GetAura(std::string const& spellName, Unit* unit, bool checkIsOwner, bool checkDuration, + int checkStack) +{ + if (_botAI) + { + return GetAuraStatic(_botAI->GetBot(), spellName, unit, checkIsOwner, checkDuration, checkStack); + } + return nullptr; +} + +void BotSpellService::RemoveAura(std::string const& name) +{ + if (_botAI) + { + _botAI->RemoveAura(name); + } +} + +void BotSpellService::RemoveShapeshift() +{ + if (_botAI) + { + _botAI->RemoveShapeshift(); + } +} + +bool BotSpellService::HasAuraToDispel(Unit* player, uint32 dispelType) +{ + if (_botAI) + { + return HasAuraToDispelStatic(_botAI->GetBot(), player, dispelType); + } + return false; +} + +bool BotSpellService::CanDispel(SpellInfo const* spellInfo, uint32 dispelType) +{ + if (_botAI) + { + return _botAI->canDispel(spellInfo, dispelType); + } + return false; +} + +bool BotSpellService::IsInterruptableSpellCasting(Unit* player, std::string const& spell) +{ + if (_botAI) + { + return _botAI->IsInterruptableSpellCasting(player, spell); + } + return false; +} + +void BotSpellService::InterruptSpell() +{ + if (_botAI) + { + _botAI->InterruptSpell(); + } +} + +void BotSpellService::SpellInterrupted(uint32 spellId) +{ + if (_botAI) + { + _botAI->SpellInterrupted(spellId); + } +} + +int32 BotSpellService::CalculateGlobalCooldown(uint32 spellId) +{ + return CalculateGlobalCooldownStatic(spellId); +} + +void BotSpellService::WaitForSpellCast(Spell* spell) +{ + if (_botAI) + { + _botAI->WaitForSpellCast(spell); + } +} + +bool BotSpellService::CanCastVehicleSpell(uint32 spellId, Unit* target) +{ + if (_botAI) + { + return _botAI->CanCastVehicleSpell(spellId, target); + } + return false; +} + +bool BotSpellService::CastVehicleSpell(uint32 spellId, Unit* target) +{ + if (_botAI) + { + return _botAI->CastVehicleSpell(spellId, target); + } + return false; +} + +bool BotSpellService::CastVehicleSpell(uint32 spellId, float x, float y, float z) +{ + if (_botAI) + { + return _botAI->CastVehicleSpell(spellId, x, y, z); + } + return false; +} + +bool BotSpellService::IsInVehicle(bool canControl, bool canCast, bool canAttack, bool canTurn, bool fixed) +{ + if (_botAI) + { + return IsInVehicleStatic(_botAI->GetBot(), canControl, canCast, canAttack, canTurn, fixed); + } + return false; +} diff --git a/src/Bot/Service/BotSpellService.h b/src/Bot/Service/BotSpellService.h new file mode 100644 index 0000000000..2da5a8ccff --- /dev/null +++ b/src/Bot/Service/BotSpellService.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_BOT_SPELL_SERVICE_H +#define _PLAYERBOT_BOT_SPELL_SERVICE_H + +#include "Bot/Interface/ISpellService.h" + +#include +#include + +class AiObjectContext; +class AuraEffect; +class ChatHelper; +class Player; +class PlayerbotAI; + +/** + * @brief Context for spell operations + * + * Provides all dependencies needed for spell operations without + * requiring direct access to PlayerbotAI. + */ +struct SpellContext +{ + Player* bot; + AiObjectContext* aiObjectContext; + ChatHelper* chatHelper; + std::function setNextCheckDelay; + std::function hasStrategy; + std::function hasRealPlayerMaster; +}; + +/** + * @brief Implementation of ISpellService + * + * This service provides spell casting and aura management for bots. + * + * Static methods are provided for direct access without needing a service instance. + * Instance methods implement the ISpellService interface for testability/mockability. + */ +class BotSpellService : public ISpellService +{ +public: + BotSpellService() = default; + explicit BotSpellService(PlayerbotAI* ai) : _botAI(ai) {} + ~BotSpellService() override = default; + + // ======================================================================== + // Static methods for direct access (main implementations) + // These can be called without a service instance + // ======================================================================== + + // Aura checking - static versions + static bool IsRealAuraStatic(Player* bot, AuraEffect const* aurEff, Unit const* unit); + static bool HasAuraByNameStatic(Player* bot, std::string const& name, Unit* unit, bool maxStack = false, + bool checkIsOwner = false, int maxAuraAmount = -1, bool checkDuration = false); + static bool HasAuraByIdStatic(uint32 spellId, Unit const* unit); + static bool HasAnyAuraOfStatic(Player* bot, Unit* player, std::vector const& auraNames); + static Aura* GetAuraStatic(Player* bot, std::string const& name, Unit* unit, bool checkIsOwner = false, + bool checkDuration = false, int checkStack = -1); + + // Dispel checking - static versions + static bool HasAuraToDispelStatic(Player* bot, Unit* target, uint32 dispelType); + static bool CanDispelStatic(SpellInfo const* spellInfo, uint32 dispelType, + std::vector const& dispelWhitelist); + + // Interrupt checking - static versions + static bool IsInterruptableSpellCastingStatic(Player* bot, Unit* target, uint32 interruptSpellId); + static void SpellInterruptedStatic(Player* bot, uint32 spellId); + static int32 CalculateGlobalCooldownStatic(uint32 spellId); + + // Vehicle - static versions + static bool IsInVehicleStatic(Player* bot, bool canControl, bool canCast, bool canAttack, bool canTurn, bool fixed); + + // ======================================================================== + // Instance methods (ISpellService interface implementation) + // These delegate to static methods or PlayerbotAI during transition + // ======================================================================== + + // Spell casting by name + bool CanCastSpell(std::string const& name, Unit* target, Item* itemTarget = nullptr) override; + bool CastSpell(std::string const& name, Unit* target, Item* itemTarget = nullptr) override; + + // Spell casting by ID + bool CanCastSpell(uint32 spellId, Unit* target, bool checkHasSpell = true, Item* itemTarget = nullptr, + Item* castItem = nullptr) override; + bool CanCastSpell(uint32 spellId, GameObject* goTarget, bool checkHasSpell = true) override; + bool CanCastSpell(uint32 spellId, float x, float y, float z, bool checkHasSpell = true, + Item* itemTarget = nullptr) override; + + bool CastSpell(uint32 spellId, Unit* target, Item* itemTarget = nullptr) override; + bool CastSpell(uint32 spellId, float x, float y, float z, Item* itemTarget = nullptr) override; + + // Aura management + bool HasAura(std::string const& spellName, Unit* player, bool maxStack = false, bool checkIsOwner = false, + int maxAmount = -1, bool checkDuration = false) override; + bool HasAura(uint32 spellId, Unit const* player) override; + bool HasAnyAuraOf(Unit* player, ...) override; + + // Vector-based aura checking (safer alternative to variadic) + bool HasAnyAuraOfVec(Unit* player, std::vector const& auraNames); + + Aura* GetAura(std::string const& spellName, Unit* unit, bool checkIsOwner = false, bool checkDuration = false, + int checkStack = -1) override; + void RemoveAura(std::string const& name) override; + void RemoveShapeshift() override; + + // Dispel + bool HasAuraToDispel(Unit* player, uint32 dispelType) override; + bool CanDispel(SpellInfo const* spellInfo, uint32 dispelType) override; + + // Interrupt + bool IsInterruptableSpellCasting(Unit* player, std::string const& spell) override; + void InterruptSpell() override; + void SpellInterrupted(uint32 spellId) override; + + // Cooldown + int32 CalculateGlobalCooldown(uint32 spellId) override; + void WaitForSpellCast(Spell* spell) override; + + // Vehicle spells + bool CanCastVehicleSpell(uint32 spellId, Unit* target) override; + bool CastVehicleSpell(uint32 spellId, Unit* target) override; + bool CastVehicleSpell(uint32 spellId, float x, float y, float z) override; + bool IsInVehicle(bool canControl = false, bool canCast = false, bool canAttack = false, bool canTurn = false, + bool fixed = false) override; + + // Set the bot context for instance methods that need the bot + void SetBotContext(PlayerbotAI* ai) { _botAI = ai; } + PlayerbotAI* GetBotContext() const { return _botAI; } + +private: + PlayerbotAI* _botAI = nullptr; +}; + +#endif diff --git a/src/Bot/Service/ConfigProvider.h b/src/Bot/Service/ConfigProvider.h new file mode 100644 index 0000000000..751d868e54 --- /dev/null +++ b/src/Bot/Service/ConfigProvider.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_CONFIG_PROVIDER_H +#define _PLAYERBOT_CONFIG_PROVIDER_H + +#include "Bot/Interface/IConfigProvider.h" +#include "PlayerbotAIConfig.h" + +/** + * @brief Implementation of IConfigProvider that wraps PlayerbotAIConfig + * + * This class provides a testable interface to the PlayerbotAIConfig singleton. + */ +class ConfigProvider : public IConfigProvider +{ +public: + ConfigProvider() = default; + ~ConfigProvider() override = default; + + // Distance settings + float GetSightDistance() const override { return sPlayerbotAIConfig->sightDistance; } + float GetSpellDistance() const override { return sPlayerbotAIConfig->spellDistance; } + float GetReactDistance() const override { return sPlayerbotAIConfig->reactDistance; } + float GetGrindDistance() const override { return sPlayerbotAIConfig->grindDistance; } + float GetLootDistance() const override { return sPlayerbotAIConfig->lootDistance; } + float GetShootDistance() const override { return sPlayerbotAIConfig->shootDistance; } + float GetFleeDistance() const override { return sPlayerbotAIConfig->fleeDistance; } + float GetTooCloseDistance() const override { return sPlayerbotAIConfig->tooCloseDistance; } + float GetMeleeDistance() const override { return sPlayerbotAIConfig->meleeDistance; } + float GetFollowDistance() const override { return sPlayerbotAIConfig->followDistance; } + float GetWhisperDistance() const override { return sPlayerbotAIConfig->whisperDistance; } + float GetContactDistance() const override { return sPlayerbotAIConfig->contactDistance; } + float GetAoeRadius() const override { return sPlayerbotAIConfig->aoeRadius; } + float GetRpgDistance() const override { return sPlayerbotAIConfig->rpgDistance; } + float GetTargetPosRecalcDistance() const override { return sPlayerbotAIConfig->targetPosRecalcDistance; } + float GetFarDistance() const override { return sPlayerbotAIConfig->farDistance; } + float GetHealDistance() const override { return sPlayerbotAIConfig->healDistance; } + float GetAggroDistance() const override { return sPlayerbotAIConfig->aggroDistance; } + + // Timing settings + uint32 GetGlobalCooldown() const override { return sPlayerbotAIConfig->globalCoolDown; } + uint32 GetReactDelay() const override { return sPlayerbotAIConfig->reactDelay; } + uint32 GetMaxWaitForMove() const override { return sPlayerbotAIConfig->maxWaitForMove; } + uint32 GetExpireActionTime() const override { return sPlayerbotAIConfig->expireActionTime; } + uint32 GetDispelAuraDuration() const override { return sPlayerbotAIConfig->dispelAuraDuration; } + uint32 GetPassiveDelay() const override { return sPlayerbotAIConfig->passiveDelay; } + uint32 GetRepeatDelay() const override { return sPlayerbotAIConfig->repeatDelay; } + uint32 GetErrorDelay() const override { return sPlayerbotAIConfig->errorDelay; } + uint32 GetRpgDelay() const override { return sPlayerbotAIConfig->rpgDelay; } + uint32 GetSitDelay() const override { return sPlayerbotAIConfig->sitDelay; } + uint32 GetReturnDelay() const override { return sPlayerbotAIConfig->returnDelay; } + uint32 GetLootDelay() const override { return sPlayerbotAIConfig->lootDelay; } + + // Health/Mana thresholds + uint32 GetCriticalHealth() const override { return sPlayerbotAIConfig->criticalHealth; } + uint32 GetLowHealth() const override { return sPlayerbotAIConfig->lowHealth; } + uint32 GetMediumHealth() const override { return sPlayerbotAIConfig->mediumHealth; } + uint32 GetAlmostFullHealth() const override { return sPlayerbotAIConfig->almostFullHealth; } + uint32 GetLowMana() const override { return sPlayerbotAIConfig->lowMana; } + uint32 GetMediumMana() const override { return sPlayerbotAIConfig->mediumMana; } + uint32 GetHighMana() const override { return sPlayerbotAIConfig->highMana; } + + // Feature flags + bool IsEnabled() const override { return sPlayerbotAIConfig->enabled; } + bool IsDynamicReactDelay() const override { return sPlayerbotAIConfig->dynamicReactDelay; } + bool IsAutoSaveMana() const override { return sPlayerbotAIConfig->autoSaveMana; } + uint32 GetSaveManaThreshold() const override { return sPlayerbotAIConfig->saveManaThreshold; } + bool IsAutoAvoidAoe() const override { return sPlayerbotAIConfig->autoAvoidAoe; } + float GetMaxAoeAvoidRadius() const override { return sPlayerbotAIConfig->maxAoeAvoidRadius; } + bool IsTellWhenAvoidAoe() const override { return sPlayerbotAIConfig->tellWhenAvoidAoe; } + + // Bot behavior settings + bool IsFleeingEnabled() const override { return sPlayerbotAIConfig->fleeingEnabled; } + bool IsRandomBotAutologin() const override { return sPlayerbotAIConfig->randomBotAutologin; } + bool IsBotAutologin() const override { return sPlayerbotAIConfig->botAutologin; } + + // Logging + bool HasLog(std::string const& fileName) const override { return sPlayerbotAIConfig->hasLog(fileName); } +}; + +#endif diff --git a/src/Bot/Service/RandomBotManagerAdapter.cpp b/src/Bot/Service/RandomBotManagerAdapter.cpp new file mode 100644 index 0000000000..461f9e70c1 --- /dev/null +++ b/src/Bot/Service/RandomBotManagerAdapter.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "RandomBotManagerAdapter.h" + +#include "RandomPlayerbotMgr.h" + +bool RandomBotManagerAdapter::IsRandomBot(Player* bot) +{ + return sRandomPlayerbotMgr->IsRandomBot(bot); +} + +bool RandomBotManagerAdapter::IsRandomBot(ObjectGuid::LowType guid) +{ + return sRandomPlayerbotMgr->IsRandomBot(guid); +} + +Player* RandomBotManagerAdapter::GetRandomPlayer() +{ + return sRandomPlayerbotMgr->GetRandomPlayer(); +} + +std::vector RandomBotManagerAdapter::GetPlayers() +{ + return sRandomPlayerbotMgr->GetPlayers(); +} + +uint32 RandomBotManagerAdapter::GetActiveBotCount() const +{ + return sRandomPlayerbotMgr->activeBots; +} + +uint32 RandomBotManagerAdapter::GetMaxAllowedBotCount() +{ + return sRandomPlayerbotMgr->GetMaxAllowedBotCount(); +} + +void RandomBotManagerAdapter::Randomize(Player* bot) +{ + sRandomPlayerbotMgr->Randomize(bot); +} + +void RandomBotManagerAdapter::RandomizeFirst(Player* bot) +{ + sRandomPlayerbotMgr->RandomizeFirst(bot); +} + +void RandomBotManagerAdapter::Refresh(Player* bot) +{ + sRandomPlayerbotMgr->Refresh(bot); +} + +void RandomBotManagerAdapter::Revive(Player* bot) +{ + sRandomPlayerbotMgr->Revive(bot); +} + +void RandomBotManagerAdapter::Remove(Player* bot) +{ + sRandomPlayerbotMgr->Remove(bot); +} + +void RandomBotManagerAdapter::Clear(Player* bot) +{ + sRandomPlayerbotMgr->Clear(bot); +} + +void RandomBotManagerAdapter::ScheduleTeleport(uint32 bot, uint32 time) +{ + sRandomPlayerbotMgr->ScheduleTeleport(bot, time); +} + +void RandomBotManagerAdapter::ScheduleChangeStrategy(uint32 bot, uint32 time) +{ + sRandomPlayerbotMgr->ScheduleChangeStrategy(bot, time); +} + +void RandomBotManagerAdapter::OnPlayerLogin(Player* player) +{ + sRandomPlayerbotMgr->OnPlayerLogin(player); +} + +void RandomBotManagerAdapter::OnPlayerLogout(Player* player) +{ + sRandomPlayerbotMgr->OnPlayerLogout(player); +} + +uint32 RandomBotManagerAdapter::GetValue(Player* bot, std::string const& type) +{ + return sRandomPlayerbotMgr->GetValue(bot, type); +} + +uint32 RandomBotManagerAdapter::GetValue(uint32 bot, std::string const& type) +{ + return sRandomPlayerbotMgr->GetValue(bot, type); +} + +void RandomBotManagerAdapter::SetValue(Player* bot, std::string const& type, uint32 value, std::string const& data) +{ + sRandomPlayerbotMgr->SetValue(bot, type, value, data); +} + +void RandomBotManagerAdapter::SetValue(uint32 bot, std::string const& type, uint32 value, std::string const& data) +{ + sRandomPlayerbotMgr->SetValue(bot, type, value, data); +} + +double RandomBotManagerAdapter::GetBuyMultiplier(Player* bot) +{ + return sRandomPlayerbotMgr->GetBuyMultiplier(bot); +} + +double RandomBotManagerAdapter::GetSellMultiplier(Player* bot) +{ + return sRandomPlayerbotMgr->GetSellMultiplier(bot); +} + +void RandomBotManagerAdapter::PrintStats() +{ + sRandomPlayerbotMgr->PrintStats(); +} + +float RandomBotManagerAdapter::GetActivityPercentage() +{ + return sRandomPlayerbotMgr->getActivityPercentage(); +} + +void RandomBotManagerAdapter::SetActivityPercentage(float percentage) +{ + sRandomPlayerbotMgr->setActivityPercentage(percentage); +} diff --git a/src/Bot/Service/RandomBotManagerAdapter.h b/src/Bot/Service/RandomBotManagerAdapter.h new file mode 100644 index 0000000000..d0e0c05db4 --- /dev/null +++ b/src/Bot/Service/RandomBotManagerAdapter.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_RANDOM_BOT_MANAGER_ADAPTER_H +#define _PLAYERBOT_RANDOM_BOT_MANAGER_ADAPTER_H + +#include "Bot/Interface/IRandomBotManager.h" + +/** + * @brief Adapter that wraps RandomPlayerbotMgr singleton behind IRandomBotManager interface + * + * This adapter allows code to use the IRandomBotManager interface while + * delegating to the existing RandomPlayerbotMgr singleton. + */ +class RandomBotManagerAdapter : public IRandomBotManager +{ +public: + RandomBotManagerAdapter() = default; + ~RandomBotManagerAdapter() override = default; + + // Bot queries + bool IsRandomBot(Player* bot) override; + bool IsRandomBot(ObjectGuid::LowType guid) override; + Player* GetRandomPlayer() override; + std::vector GetPlayers() override; + uint32 GetActiveBotCount() const override; + uint32 GetMaxAllowedBotCount() override; + + // Bot lifecycle + void Randomize(Player* bot) override; + void RandomizeFirst(Player* bot) override; + void Refresh(Player* bot) override; + void Revive(Player* bot) override; + void Remove(Player* bot) override; + void Clear(Player* bot) override; + + // Scheduling + void ScheduleTeleport(uint32 bot, uint32 time = 0) override; + void ScheduleChangeStrategy(uint32 bot, uint32 time = 0) override; + + // Events + void OnPlayerLogin(Player* player) override; + void OnPlayerLogout(Player* player) override; + + // Value storage + uint32 GetValue(Player* bot, std::string const& type) override; + uint32 GetValue(uint32 bot, std::string const& type) override; + void SetValue(Player* bot, std::string const& type, uint32 value, std::string const& data = "") override; + void SetValue(uint32 bot, std::string const& type, uint32 value, std::string const& data = "") override; + + // Trading + double GetBuyMultiplier(Player* bot) override; + double GetSellMultiplier(Player* bot) override; + + // Statistics + void PrintStats() override; + float GetActivityPercentage() override; + void SetActivityPercentage(float percentage) override; +}; + +#endif diff --git a/src/Bot/Service/TravelManagerAdapter.cpp b/src/Bot/Service/TravelManagerAdapter.cpp new file mode 100644 index 0000000000..12ad3a7cf6 --- /dev/null +++ b/src/Bot/Service/TravelManagerAdapter.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "TravelManagerAdapter.h" + +#include "RandomPlayerbotMgr.h" +#include "TravelMgr.h" + +void TravelManagerAdapter::RandomTeleport(Player* bot) +{ + // RandomPlayerbotMgr handles random teleportation + // Note: The actual RandomTeleport method is private in RandomPlayerbotMgr + // Using RandomTeleportForLevel as the public alternative + sRandomPlayerbotMgr->RandomTeleportForLevel(bot); +} + +void TravelManagerAdapter::RandomTeleportForLevel(Player* bot) +{ + sRandomPlayerbotMgr->RandomTeleportForLevel(bot); +} + +void TravelManagerAdapter::RandomTeleportForRpg(Player* bot) +{ + sRandomPlayerbotMgr->RandomTeleportForRpg(bot); +} + +uint32 TravelManagerAdapter::GetZoneLevel(uint16 /*mapId*/, float /*x*/, float /*y*/, float /*z*/) +{ + // This would need access to RandomPlayerbotMgr's private GetZoneLevel method + // For now, return 0 - this can be expanded when the method is made accessible + return 0; +} + +std::vector TravelManagerAdapter::GetLocsForLevel(uint8 level) +{ + // Access the cached locations from RandomPlayerbotMgr + auto const& cache = sRandomPlayerbotMgr->locsPerLevelCache; + auto it = cache.find(level); + if (it != cache.end()) + { + return it->second; + } + return {}; +} + +bool TravelManagerAdapter::HasDestination(Player* bot) +{ + // TravelMgr doesn't have a direct "has destination" check + // This would need to check the bot's travel target + // For now, return false - this can be expanded based on usage + (void)bot; // Suppress unused parameter warning + return false; +} + +void TravelManagerAdapter::SetQuestDestination(Player* bot, Quest const* quest) +{ + // TravelMgr provides getQuestTravelDestinations but doesn't set them directly + // This would need to interact with the bot's AI to set the travel target + (void)bot; + (void)quest; +} + +void TravelManagerAdapter::ClearDestination(Player* bot) +{ + // Clear the bot's travel destination + sTravelMgr->setNullTravelTarget(bot); +} diff --git a/src/Bot/Service/TravelManagerAdapter.h b/src/Bot/Service/TravelManagerAdapter.h new file mode 100644 index 0000000000..f9b054cb8b --- /dev/null +++ b/src/Bot/Service/TravelManagerAdapter.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_TRAVEL_MANAGER_ADAPTER_H +#define _PLAYERBOT_TRAVEL_MANAGER_ADAPTER_H + +#include "Bot/Interface/ITravelManager.h" + +/** + * @brief Adapter that wraps TravelMgr and RandomPlayerbotMgr behind ITravelManager interface + * + * This adapter allows code to use the ITravelManager interface while + * delegating to the existing TravelMgr and RandomPlayerbotMgr singletons. + * + * Note: Some travel operations are split between TravelMgr and RandomPlayerbotMgr + * in the current architecture. This adapter unifies them behind a single interface. + */ +class TravelManagerAdapter : public ITravelManager +{ +public: + TravelManagerAdapter() = default; + ~TravelManagerAdapter() override = default; + + // Teleportation (delegates to RandomPlayerbotMgr) + void RandomTeleport(Player* bot) override; + void RandomTeleportForLevel(Player* bot) override; + void RandomTeleportForRpg(Player* bot) override; + + // Location queries + uint32 GetZoneLevel(uint16 mapId, float x, float y, float z) override; + std::vector GetLocsForLevel(uint8 level) override; + + // Quest travel (delegates to TravelMgr) + bool HasDestination(Player* bot) override; + void SetQuestDestination(Player* bot, Quest const* quest) override; + void ClearDestination(Player* bot) override; +}; + +#endif diff --git a/src/Db/FlightMasterCache.cpp b/src/Db/FlightMasterCache.cpp index c708e09cb8..10e6604f3d 100644 --- a/src/Db/FlightMasterCache.cpp +++ b/src/Db/FlightMasterCache.cpp @@ -1,39 +1,24 @@ -#include "FlightMasterCache.h" - -void FlightMasterCache::AddHordeFlightMaster(uint32 entry, WorldPosition pos) -{ - hordeFlightMasterCache[entry] = pos; -} +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ -void FlightMasterCache::AddAllianceFlightMaster(uint32 entry, WorldPosition pos) -{ - allianceFlightMasterCache[entry] = pos; -} +#include "FlightMasterCache.h" -Creature* FlightMasterCache::GetNearestFlightMaster(Player* bot) +void FlightMasterCache::AddFlightMaster(uint32 guid, bool forHorde, bool forAlliance) { - std::map& flightMasterCache = - (bot->GetTeamId() == ALLIANCE) ? allianceFlightMasterCache : hordeFlightMasterCache; - - Creature* nearestFlightMaster = nullptr; - float nearestDistance = std::numeric_limits::max(); - - for (auto const& [entry, pos] : flightMasterCache) + if (forHorde) + { + hordeFlightMasterCache.push_back(guid); + } + if (forAlliance) { - if (pos.GetMapId() == bot->GetMapId()) - { - float distance = bot->GetExactDist2dSq(pos); - if (distance < nearestDistance) - { - Creature* flightMaster = ObjectAccessor::GetSpawnedCreatureByDBGUID(bot->GetMapId(), entry); - if (flightMaster) - { - nearestDistance = distance; - nearestFlightMaster = flightMaster; - } - } - } + allianceFlightMasterCache.push_back(guid); } +} - return nearestFlightMaster; +void FlightMasterCache::Clear() +{ + allianceFlightMasterCache.clear(); + hordeFlightMasterCache.clear(); } diff --git a/src/Db/FlightMasterCache.h b/src/Db/FlightMasterCache.h index 519d6fc7c8..dc79cadf23 100644 --- a/src/Db/FlightMasterCache.h +++ b/src/Db/FlightMasterCache.h @@ -1,27 +1,36 @@ -#ifndef _PLAYERBOT_FLIGHTMASTER_H -#define _PLAYERBOT_FLIGHTMASTER_H +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ -#include "Creature.h" -#include "Player.h" -#include "TravelMgr.h" +#ifndef _PLAYERBOT_FLIGHTMASTERCACHE_H +#define _PLAYERBOT_FLIGHTMASTERCACHE_H + +#include + +#include "Common.h" class FlightMasterCache { public: - static FlightMasterCache* Instance() + FlightMasterCache() {} + virtual ~FlightMasterCache() {} + static FlightMasterCache* instance() { static FlightMasterCache instance; return &instance; } - Creature* GetNearestFlightMaster(Player* bot); - void AddHordeFlightMaster(uint32 entry, WorldPosition pos); - void AddAllianceFlightMaster(uint32 entry, WorldPosition pos); + void AddFlightMaster(uint32 guid, bool forHorde, bool forAlliance); + std::vector const& GetAllianceFlightMasters() const { return allianceFlightMasterCache; } + std::vector const& GetHordeFlightMasters() const { return hordeFlightMasterCache; } + void Clear(); private: - std::map allianceFlightMasterCache; - std::map hordeFlightMasterCache; + std::vector allianceFlightMasterCache; + std::vector hordeFlightMasterCache; }; -#define sFlightMasterCache FlightMasterCache::Instance() +#define sFlightMasterCache FlightMasterCache::instance() + #endif diff --git a/src/Db/PlayerbotRepository.cpp b/src/Db/PlayerbotRepository.cpp index 886cebb833..d281f39c2e 100644 --- a/src/Db/PlayerbotRepository.cpp +++ b/src/Db/PlayerbotRepository.cpp @@ -68,7 +68,7 @@ void PlayerbotRepository::Save(PlayerbotAI* botAI) SaveValue(guid, "dead", FormatStrategies("dead", botAI->GetStrategies(BOT_STATE_DEAD))); } -std::string const PlayerbotRepository::FormatStrategies(std::string const type, std::vector strategies) +std::string const PlayerbotRepository::FormatStrategies(std::string const /*type*/, std::vector strategies) { std::ostringstream out; for (std::vector::iterator i = strategies.begin(); i != strategies.end(); ++i) diff --git a/src/Mgr/Guild/GuildTaskMgr.cpp b/src/Mgr/Guild/GuildTaskMgr.cpp index 8fb4711dd7..345e6c8491 100644 --- a/src/Mgr/Guild/GuildTaskMgr.cpp +++ b/src/Mgr/Guild/GuildTaskMgr.cpp @@ -5,6 +5,7 @@ #include "GuildTaskMgr.h" +#include "Bot/Core/ManagerRegistry.h" #include "ChatHelper.h" #include "Group.h" #include "GuildMgr.h" @@ -727,6 +728,8 @@ bool GuildTaskMgr::HandleConsoleCommand(ChatHandler* /* handler */, char const* case ITEM_QUALITY_LEGENDARY: name << "yellow"; break; + default: + break; } name << ")"; @@ -749,6 +752,8 @@ bool GuildTaskMgr::HandleConsoleCommand(ChatHandler* /* handler */, char const* case CREATURE_ELITE_RAREELITE: name << "rare elite"; break; + default: + break; } name << ")"; @@ -868,7 +873,7 @@ bool GuildTaskMgr::CheckItemTask(uint32 itemId, uint32 obtained, Player* ownerPl if (!guild) return false; - if (!sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) return false; LOG_DEBUG("playerbots", "{} / {}: checking guild task", guild->GetName().c_str(), ownerPlayer->GetName().c_str()); @@ -1200,7 +1205,7 @@ bool GuildTaskMgr::CheckTaskTransfer(std::string const text, Player* ownerPlayer if (!guild) return false; - if (!sRandomPlayerbotMgr->IsRandomBot(bot)) + if (!sManagerRegistry.GetRandomBotManager().IsRandomBot(bot)) return false; if (text.empty()) diff --git a/src/Mgr/Guild/PlayerbotGuildMgr.cpp b/src/Mgr/Guild/PlayerbotGuildMgr.cpp index c1f7aa5b2d..02bb9fbf79 100644 --- a/src/Mgr/Guild/PlayerbotGuildMgr.cpp +++ b/src/Mgr/Guild/PlayerbotGuildMgr.cpp @@ -107,7 +107,7 @@ std::string PlayerbotGuildMgr::AssignToGuild(Player* player) size_t count = std::count_if( _guildCache.begin(), _guildCache.end(), - [](const std::pair& pair) + [](std::pair const& pair) { return !pair.second.hasRealPlayer; } diff --git a/src/Mgr/Item/ItemVisitors.h b/src/Mgr/Item/ItemVisitors.h index e42b14f89e..84b57b9545 100644 --- a/src/Mgr/Item/ItemVisitors.h +++ b/src/Mgr/Item/ItemVisitors.h @@ -334,6 +334,8 @@ class FindAmmoVisitor : public FindUsableItemVisitor case ITEM_SUBCLASS_WEAPON_CROSSBOW: subClass = ITEM_SUBCLASS_ARROW; break; + default: + break; } if (!subClass) @@ -397,6 +399,8 @@ class FindRecipeVisitor : public FindUsableItemVisitor return skill == SKILL_ENCHANTING; case ITEM_SUBCLASS_FISHING_MANUAL: return skill == SKILL_FISHING; + default: + break; } } diff --git a/src/Mgr/Item/LootObjectStack.cpp b/src/Mgr/Item/LootObjectStack.cpp index a62eb8fe46..c6cf62769d 100644 --- a/src/Mgr/Item/LootObjectStack.cpp +++ b/src/Mgr/Item/LootObjectStack.cpp @@ -11,7 +11,7 @@ #include "Playerbots.h" #include "Unit.h" -#define MAX_LOOT_OBJECT_COUNT 200 +static constexpr uint32 MAX_LOOT_OBJECT_COUNT = 200; LootTarget::LootTarget(ObjectGuid guid) : guid(guid), asOfTime(time(nullptr)) {} @@ -104,7 +104,7 @@ void LootObject::Refresh(Player* bot, ObjectGuid lootGUID) return; } - const ItemTemplate* proto = sObjectMgr->GetItemTemplate(itemId); + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); if (!proto) continue; @@ -120,18 +120,18 @@ void LootObject::Refresh(Player* bot, ObjectGuid lootGUID) return; // Check the main loot template - if (const LootTemplate* lootTemplate = LootTemplates_Gameobject.GetLootFor(lootEntry)) + if (LootTemplate const* lootTemplate = LootTemplates_Gameobject.GetLootFor(lootEntry)) { Loot loot; lootTemplate->Process(loot, LootTemplates_Gameobject, 1, bot); - for (const LootItem& item : loot.items) + for (LootItem const& item : loot.items) { uint32 itemId = item.itemid; if (!itemId) continue; - const ItemTemplate* proto = sObjectMgr->GetItemTemplate(itemId); + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); if (!proto) continue; @@ -142,18 +142,18 @@ void LootObject::Refresh(Player* bot, ObjectGuid lootGUID) } // If this item references another loot table, process it - if (const LootTemplate* refLootTemplate = LootTemplates_Reference.GetLootFor(itemId)) + if (LootTemplate const* refLootTemplate = LootTemplates_Reference.GetLootFor(itemId)) { Loot refLoot; refLootTemplate->Process(refLoot, LootTemplates_Reference, 1, bot); - for (const LootItem& refItem : refLoot.items) + for (LootItem const& refItem : refLoot.items) { uint32 refItemId = refItem.itemid; if (!refItemId) continue; - const ItemTemplate* refProto = sObjectMgr->GetItemTemplate(refItemId); + ItemTemplate const* refProto = sObjectMgr->GetItemTemplate(refItemId); if (!refProto) continue; @@ -208,6 +208,8 @@ void LootObject::Refresh(Player* bot, ObjectGuid lootGUID) case LOCK_KEY_NONE: guid = lootGUID; break; + default: + break; } } } diff --git a/src/Mgr/Item/RandomItemMgr.cpp b/src/Mgr/Item/RandomItemMgr.cpp index 87b8379355..83ca8cbbf7 100644 --- a/src/Mgr/Item/RandomItemMgr.cpp +++ b/src/Mgr/Item/RandomItemMgr.cpp @@ -176,7 +176,7 @@ RandomItemMgr::~RandomItemMgr() predicates.clear(); } -bool RandomItemMgr::HandleConsoleCommand(ChatHandler* handler, char const* args) +bool RandomItemMgr::HandleConsoleCommand(ChatHandler* /*handler*/, char const* args) { if (!args || !*args) { @@ -399,6 +399,8 @@ void RandomItemMgr::AddItemStats(uint32 mod, uint8& sp, uint8& ap, uint8& tank) case ITEM_MOD_SPIRIT: ++sp; break; + default: + break; } switch (mod) @@ -409,6 +411,8 @@ void RandomItemMgr::AddItemStats(uint32 mod, uint8& sp, uint8& ap, uint8& tank) case ITEM_MOD_STAMINA: ++tank; break; + default: + break; } switch (mod) @@ -419,6 +423,8 @@ void RandomItemMgr::AddItemStats(uint32 mod, uint8& sp, uint8& ap, uint8& tank) case ITEM_MOD_STRENGTH: ++ap; break; + default: + break; } } @@ -442,6 +448,8 @@ bool RandomItemMgr::CheckItemStats(uint8 clazz, uint8 sp, uint8 ap, uint8 tank) if (!ap || sp > ap || sp > tank) return false; break; + default: + break; } return sp || ap || tank; @@ -544,6 +552,8 @@ bool RandomItemMgr::ShouldEquipArmorForSpec(uint8 playerclass, uint8 spec, ItemT resultArmorSubClass = {ITEM_SUBCLASS_ARMOR_IDOL, ITEM_SUBCLASS_ARMOR_CLOTH, ITEM_SUBCLASS_ARMOR_LEATHER}; break; } + default: + break; } return resultArmorSubClass.find(proto->SubClass) != resultArmorSubClass.end(); @@ -698,6 +708,8 @@ bool RandomItemMgr::ShouldEquipWeaponForSpec(uint8 playerclass, uint8 spec, Item } break; } + default: + break; } if (slot_mh == EQUIPMENT_SLOT_MAINHAND) @@ -826,6 +838,8 @@ bool RandomItemMgr::CanEquipWeapon(uint8 clazz, ItemTemplate const* proto) proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE) return false; break; + default: + break; } return true; @@ -1596,7 +1610,7 @@ uint32 RandomItemMgr::CalculateStatWeight(uint8 playerclass, uint8 spec, ItemTem statWeight += attackPower; // handle negative stats - if (basicStatsWeight < 0 && (abs(basicStatsWeight) >= statWeight)) + if (basicStatsWeight < 0 && (static_cast(abs(basicStatsWeight)) >= statWeight)) statWeight = 0; else statWeight += basicStatsWeight; @@ -1700,7 +1714,7 @@ std::vector RandomItemMgr::GetQuestIdsForItem(uint32 itemId) } } - return std::move(questIds); + return questIds; } uint32 RandomItemMgr::GetUpgrade(Player* player, std::string spec, uint8 slot, uint32 quality, uint32 itemId) @@ -1782,7 +1796,7 @@ uint32 RandomItemMgr::GetUpgrade(Player* player, std::string spec, uint8 slot, u } // skip no stats trinkets - if (info.weights[specId] == 1 && info.slot == EQUIPMENT_SLOT_NECK || info.slot == EQUIPMENT_SLOT_TRINKET1 || + if ((info.weights[specId] == 1 && info.slot == EQUIPMENT_SLOT_NECK) || info.slot == EQUIPMENT_SLOT_TRINKET1 || info.slot == EQUIPMENT_SLOT_TRINKET2 || info.slot == EQUIPMENT_SLOT_FINGER1 || info.slot == EQUIPMENT_SLOT_FINGER2) continue; @@ -1823,17 +1837,16 @@ uint32 RandomItemMgr::GetUpgrade(Player* player, std::string spec, uint8 slot, u } std::vector RandomItemMgr::GetUpgradeList(Player* player, std::string spec, uint8 slot, uint32 quality, - uint32 itemId, uint32 amount) + uint32 itemId, uint32 /*amount*/) { std::vector listItems; if (!player) - return std::move(listItems); + return listItems; // get old item statWeight uint32 oldStatWeight = 0; - uint32 specId = 0; - uint32 closestUpgrade = 0; uint32 closestUpgradeWeight = 0; + uint32 specId = 0; std::vector classspecs; for (uint32 specNum = 1; specNum < 5; ++specNum) @@ -1848,7 +1861,7 @@ std::vector RandomItemMgr::GetUpgradeList(Player* player, std::string sp } if (!specId) - return std::move(listItems); + return listItems; if (itemId && itemInfoCache.find(itemId) != itemInfoCache.end()) { @@ -1928,21 +1941,17 @@ std::vector RandomItemMgr::GetUpgradeList(Player* player, std::string sp //} listItems.push_back(info.itemId); - // continue; - // pick closest upgrade + // track best upgrade weight for logging if (info.weights[specId] > closestUpgradeWeight) - { - closestUpgrade = info.itemId; closestUpgradeWeight = info.weights[specId]; - } } if (listItems.size()) LOG_INFO("playerbots", "New Items: {}, Old item:%d, New items max: {}", listItems.size(), oldStatWeight, closestUpgradeWeight); - return std::move(listItems); + return listItems; } bool RandomItemMgr::HasStatWeight(uint32 itemId) @@ -2218,7 +2227,7 @@ void RandomItemMgr::BuildEquipCacheNew() if (quest->GetRequiredClasses()) continue; - for (int j = 0; j < quest->GetRewChoiceItemsCount(); j++) + for (uint32 j = 0; j < quest->GetRewChoiceItemsCount(); j++) if (uint32 itemId = quest->RewardChoiceItemId[j]) { ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); @@ -2229,7 +2238,7 @@ void RandomItemMgr::BuildEquipCacheNew() questItemIds.insert(itemId); } - for (int j = 0; j < quest->GetRewItemsCount(); j++) + for (uint32 j = 0; j < quest->GetRewItemsCount(); j++) if (uint32 itemId = quest->RewardItemId[j]) { ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); @@ -2374,7 +2383,7 @@ void RandomItemMgr::BuildPotionCache() if (proto->Duration & 0x80000000) continue; - if (proto->AllowableClass != -1) + if (proto->AllowableClass != static_cast(-1)) continue; bool hybrid = false; @@ -2476,7 +2485,7 @@ void RandomItemMgr::BuildFoodCache() uint32 RandomItemMgr::GetRandomPotion(uint32 level, uint32 effect) { - const std::vector &potions = potionCache[level][effect]; + std::vector const& potions = potionCache[level][effect]; if (potions.empty()) return 0; @@ -2739,7 +2748,7 @@ void RandomItemMgr::BuildRarityCache() { Field* fields = results->Fetch(); float rarity = fields[0].Get(); - if (rarity > 0.01) + if (rarity > 0.01f) { rarityCache[itr.first] = rarity; @@ -2767,7 +2776,7 @@ inline bool IsCraftedBySpellInfo(ItemTemplate const* proto, SpellInfo const* spe continue; } - if (proto->ItemId == spellInfo->Reagent[x]) + if (proto->ItemId == static_cast(spellInfo->Reagent[x])) { return true; } diff --git a/src/Mgr/Item/RandomItemMgr.h b/src/Mgr/Item/RandomItemMgr.h index 216ddf9bfc..7dd83c8b09 100644 --- a/src/Mgr/Item/RandomItemMgr.h +++ b/src/Mgr/Item/RandomItemMgr.h @@ -30,7 +30,7 @@ enum RandomItemType RANDOM_ITEM_GUILD_TASK_REWARD_TRADE_RARE }; -#define MAX_STAT_SCALES 32 +constexpr uint32 MAX_STAT_SCALES = 32; enum ItemSource { diff --git a/src/Mgr/Item/StatsCollector.cpp b/src/Mgr/Item/StatsCollector.cpp index 4f719fea4e..eabac24f90 100644 --- a/src/Mgr/Item/StatsCollector.cpp +++ b/src/Mgr/Item/StatsCollector.cpp @@ -39,7 +39,7 @@ void StatsCollector::CollectItemStats(ItemTemplate const* proto) } stats[STATS_TYPE_ARMOR] += proto->Armor; stats[STATS_TYPE_BLOCK_VALUE] += proto->Block; - for (int i = 0; i < proto->StatsCount; i++) + for (uint32 i = 0; i < proto->StatsCount; i++) { const _ItemStat& stat = proto->ItemStat[i]; const int32& val = stat.ItemStatValue; @@ -71,7 +71,7 @@ void StatsCollector::CollectItemStats(ItemTemplate const* proto) if (proto->socketBonus) { - if (const SpellItemEnchantmentEntry* enchant = sSpellItemEnchantmentStore.LookupEntry(proto->socketBonus)) + if (SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(proto->socketBonus)) CollectEnchantStats(enchant); } } @@ -86,7 +86,7 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s if (SpecialSpellFilter(spellId)) return; - const SpellProcEventEntry* eventEntry = sSpellMgr->GetSpellProcEvent(spellInfo->Id); + SpellProcEventEntry const* eventEntry = sSpellMgr->GetSpellProcEvent(spellInfo->Id); uint32 triggerCooldown = eventEntry ? eventEntry->cooldown : 0; @@ -114,18 +114,18 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s if (spellInfo->StackAmount <= 1) multiplier *= spellInfo->StackAmount * 1; else if (spellInfo->StackAmount <= 5) - multiplier *= 1 + (spellInfo->StackAmount - 1) * 0.75; + multiplier *= 1 + (spellInfo->StackAmount - 1) * 0.75f; else if (spellInfo->StackAmount <= 10) - multiplier *= 4 + (spellInfo->StackAmount - 5) * 0.6; + multiplier *= 4 + (spellInfo->StackAmount - 5) * 0.6f; else if (spellInfo->StackAmount <= 20) - multiplier *= 7 + (spellInfo->StackAmount - 10) * 0.4; + multiplier *= 7 + (spellInfo->StackAmount - 10) * 0.4f; else multiplier *= 11; } for (int i = 0; i < MAX_SPELL_EFFECTS; i++) { - const SpellEffectInfo& effectInfo = spellInfo->Effects[i]; + SpellEffectInfo const& effectInfo = spellInfo->Effects[i]; if (!effectInfo.Effect) continue; switch (effectInfo.Effect) @@ -177,7 +177,7 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s break; float normalizedCd = std::max((float)spellCooldown / 1000, 5.0f); int32 val = AverageValue(effectInfo); - float transfer_multiplier = 0.2; + float transfer_multiplier = 0.2f; stats[STATS_TYPE_MANA_REGENERATION] += (float)val / normalizedCd * multiplier * transfer_multiplier; break; } @@ -195,7 +195,7 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s } else if (type_ & CollectorType::SPELL_DMG) { - float transfer_multiplier = 0.5; + float transfer_multiplier = 0.5f; stats[STATS_TYPE_SPELL_POWER] += (float)val / normalizedCd * multiplier * transfer_multiplier; } break; @@ -357,7 +357,7 @@ bool StatsCollector::SpecialEnchantFilter(uint32 enchantSpellId) bool StatsCollector::CanBeTriggeredByType(SpellInfo const* spellInfo, uint32 procFlags, bool strict) { - const SpellProcEventEntry* eventEntry = sSpellMgr->GetSpellProcEvent(spellInfo->Id); + SpellProcEventEntry const* eventEntry = sSpellMgr->GetSpellProcEvent(spellInfo->Id); uint32 spellFamilyName = 0; if (eventEntry) { @@ -552,7 +552,7 @@ void StatsCollector::CollectByItemStatType(uint32 itemStatType, int32 val) } } -void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float multiplier, bool canNextTrigger, +void StatsCollector::HandleApplyAura(SpellEffectInfo const& effectInfo, float multiplier, bool canNextTrigger, uint32 triggerCooldown) { if (effectInfo.Effect != SPELL_EFFECT_APPLY_AURA) @@ -747,7 +747,7 @@ void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float mu } } -float StatsCollector::AverageValue(const SpellEffectInfo& effectInfo) +float StatsCollector::AverageValue(SpellEffectInfo const& effectInfo) { // float basePointsPerLevel = effectInfo.RealPointsPerLevel; //not used, line marked for removal. float basePoints = effectInfo.BasePoints; diff --git a/src/Mgr/Item/StatsCollector.h b/src/Mgr/Item/StatsCollector.h index 27ef871ec5..5466f09cd6 100644 --- a/src/Mgr/Item/StatsCollector.h +++ b/src/Mgr/Item/StatsCollector.h @@ -78,9 +78,9 @@ class StatsCollector bool SpecialSpellFilter(uint32 spellId); bool SpecialEnchantFilter(uint32 enchantSpellId); - void HandleApplyAura(const SpellEffectInfo& effectInfo, float multiplier, bool canNextTrigger, + void HandleApplyAura(SpellEffectInfo const& effectInfo, float multiplier, bool canNextTrigger, uint32 triggerCooldown); - float AverageValue(const SpellEffectInfo& effectInfo); + float AverageValue(SpellEffectInfo const& effectInfo); private: CollectorType type_; diff --git a/src/Mgr/Item/StatsWeightCalculator.cpp b/src/Mgr/Item/StatsWeightCalculator.cpp index 1b3f3dcfcb..bc5ab36a02 100644 --- a/src/Mgr/Item/StatsWeightCalculator.cpp +++ b/src/Mgr/Item/StatsWeightCalculator.cpp @@ -8,6 +8,34 @@ #include #include "AiFactory.h" + +// Talent spell IDs for stat weight calculations +enum StatWeightTalentSpells +{ + // Hunter talents + SPELL_CAREFUL_AIM = 34484, // Hunter: Careful Aim + SPELL_HUNTER_VS_WILD = 56341, // Hunter: Hunter vs. Wild + + // Warrior talents + SPELL_ARMORED_TO_TEETH = 61222, // Warrior: Armored to the Teeth + SPELL_WARRIOR_SWORD_SPEC = 12785, // Warrior: Sword Specialization + + // Shaman talents + SPELL_MENTAL_QUICKNESS = 51885, // Shaman: Mental Quickness + + // Rogue talents + SPELL_COMBAT_POTENCY = 13964, // Rogue: Combat Potency + + // Death Knight talents + SPELL_THREAT_OF_THASSARIAN = 50138, // Death Knight: Threat of Thassarian + + // Priest talents + SPELL_SHADOW_FOCUS = 15835, // Priest: Shadow Focus + + // Mage talents + SPELL_ARCANE_FOCUS = 12840 // Mage: Arcane Focus +}; +#include "BotRoleService.h" #include "DBCStores.h" #include "ItemEnchantmentMgr.h" #include "ItemTemplate.h" @@ -22,13 +50,13 @@ StatsWeightCalculator::StatsWeightCalculator(Player* player) : player_(player) { - if (PlayerbotAI::IsHeal(player)) + if (BotRoleService::IsHealStatic(player)) type_ = CollectorType::SPELL_HEAL; - else if (PlayerbotAI::IsCaster(player)) + else if (BotRoleService::IsCasterStatic(player)) type_ = CollectorType::SPELL_DMG; - else if (PlayerbotAI::IsTank(player)) + else if (BotRoleService::IsTankStatic(player)) type_ = CollectorType::MELEE_TANK; - else if (PlayerbotAI::IsMelee(player)) + else if (BotRoleService::IsMeleeStatic(player)) type_ = CollectorType::MELEE_DMG; else type_ = CollectorType::RANGED; @@ -225,7 +253,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f; stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f; } - else if (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL && !PlayerbotAI::IsTank(player)) + else if (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL && !BotRoleService::IsTankStatic(player)) { stats_weights_[STATS_TYPE_AGILITY] += 2.2f; stats_weights_[STATS_TYPE_STRENGTH] += 2.4f; @@ -439,19 +467,19 @@ void StatsWeightCalculator::GenerateAdditionalWeights(Player* player) // int tab = AiFactory::GetPlayerSpecTab(player); if (cls == CLASS_HUNTER) { - if (player->HasAura(34484)) + if (player->HasAura(SPELL_CAREFUL_AIM)) stats_weights_[STATS_TYPE_INTELLECT] += 1.1f; - if (player->HasAura(56341)) + if (player->HasAura(SPELL_HUNTER_VS_WILD)) stats_weights_[STATS_TYPE_STAMINA] += 0.3f; } else if (cls == CLASS_WARRIOR) { - if (player->HasAura(61222)) + if (player->HasAura(SPELL_ARMORED_TO_TEETH)) stats_weights_[STATS_TYPE_ARMOR] += 0.03f; } else if (cls == CLASS_SHAMAN) { - if (player->HasAura(51885)) + if (player->HasAura(SPELL_MENTAL_QUICKNESS)) stats_weights_[STATS_TYPE_INTELLECT] += 1.1f; } } @@ -474,7 +502,7 @@ void StatsWeightCalculator::CalculateItemSetMod(Player* player, ItemTemplate con if (itemSet != setId) continue; - const ItemSetEntry* setEntry = sItemSetStore.LookupEntry(setId); + ItemSetEntry const* setEntry = sItemSetStore.LookupEntry(setId); if (!setEntry) continue; @@ -500,7 +528,7 @@ void StatsWeightCalculator::CalculateItemSetMod(Player* player, ItemTemplate con weight_ *= multiplier; } -void StatsWeightCalculator::CalculateSocketBonus(Player* player, ItemTemplate const* proto) +void StatsWeightCalculator::CalculateSocketBonus(Player* /*player*/, ItemTemplate const* proto) { uint32 socketNum = 0; for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + MAX_GEM_SOCKETS; @@ -525,7 +553,7 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) // if (proto->Class == ITEM_CLASS_ARMOR && proto->SubClass >= ITEM_SUBCLASS_ARMOR_CLOTH && // proto->SubClass <= ITEM_SUBCLASS_ARMOR_PLATE && NotBestArmorType(proto->SubClass)) // { - // weight_ *= 1.0; + // weight_ *= 1.0f; // } if (proto->Class == ITEM_CLASS_WEAPON) { @@ -536,7 +564,7 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) if (isDoubleHand) { - weight_ *= 0.5; + weight_ *= 0.5f; // spec without double hand // enhancement, rogue, ice dk, unholy dk, shield tank, fury warrior without titan's grip but with duel wield if (((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && player_->CanDualWield()) || @@ -546,7 +574,7 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) || (cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION))) { - weight_ *= 0.1; + weight_ *= 0.1f; } } // spec with double hand @@ -560,13 +588,13 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) (cls == CLASS_DEATH_KNIGHT && tab == DEATH_KNIGHT_TAB_BLOOD) || (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && !player_->CanDualWield())) { - weight_ *= 0.1; + weight_ *= 0.1f; } // caster's main hand (cannot duel weapon but can equip two-hands stuff) if (cls == CLASS_MAGE || cls == CLASS_PRIEST || cls == CLASS_WARLOCK || cls == CLASS_DRUID || (cls == CLASS_SHAMAN && !player_->CanDualWield())) { - weight_ *= 0.65; + weight_ *= 0.65f; } } // fury with titan's grip @@ -574,37 +602,37 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) proto->SubClass == ITEM_SUBCLASS_WEAPON_STAFF) && (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && player_->CanTitanGrip())) { - weight_ *= 0.1; + weight_ *= 0.1f; } if (cls == CLASS_HUNTER && proto->SubClass == ITEM_SUBCLASS_WEAPON_THROWN) { - weight_ *= 0.1; + weight_ *= 0.1f; } if (lvl >= 10 && cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY) && proto->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER) { - weight_ *= 1.5; + weight_ *= 1.5f; } - if (cls == CLASS_ROGUE && player_->HasAura(13964) && + if (cls == CLASS_ROGUE && player_->HasAura(SPELL_COMBAT_POTENCY) && (proto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE)) { - weight_ *= 1.1; + weight_ *= 1.1f; } - if (cls == CLASS_WARRIOR && player_->HasAura(12785) && + if (cls == CLASS_WARRIOR && player_->HasAura(SPELL_WARRIOR_SWORD_SPEC) && (proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2)) { - weight_ *= 1.1; + weight_ *= 1.1f; } - if (cls == CLASS_DEATH_KNIGHT && player_->HasAura(50138) && !isDoubleHand) + if (cls == CLASS_DEATH_KNIGHT && player_->HasAura(SPELL_THREAT_OF_THASSARIAN) && !isDoubleHand) { - weight_ *= 1.3; + weight_ *= 1.3f; } bool slowDelay = proto->Delay > 2500; if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && slowDelay) - weight_ *= 1.1; + weight_ *= 1.1f; } } @@ -637,9 +665,9 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) player->GetTotalAuraModifier(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT); // suppression (18176) hit_current += player->GetRatingBonusValue(CR_HIT_SPELL); - if (cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW && player->HasAura(15835)) // Shadow Focus + if (cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW && player->HasAura(SPELL_SHADOW_FOCUS)) hit_current += 3; - if (cls == CLASS_MAGE && tab == MAGE_TAB_ARCANE && player->HasAura(12840)) // Arcane Focus + if (cls == CLASS_MAGE && tab == MAGE_TAB_ARCANE && player->HasAura(SPELL_ARCANE_FOCUS)) hit_current += 3; hit_overflow = SPELL_HIT_OVERFLOW; diff --git a/src/Mgr/Move/FleeManager.cpp b/src/Mgr/Move/FleeManager.cpp index 34e17fe391..2cb88e1001 100644 --- a/src/Mgr/Move/FleeManager.cpp +++ b/src/Mgr/Move/FleeManager.cpp @@ -8,11 +8,10 @@ #include "Playerbots.h" #include "ServerFacade.h" -FleeManager::FleeManager(Player* bot, float maxAllowedDistance, float followAngle, bool forceMaxDistance, +FleeManager::FleeManager(Player* bot, float maxAllowedDistance, bool forceMaxDistance, WorldPosition startPosition) : bot(bot), maxAllowedDistance(maxAllowedDistance), - followAngle(followAngle), forceMaxDistance(forceMaxDistance), startPosition(startPosition ? startPosition : WorldPosition(bot)) { @@ -66,7 +65,7 @@ void FleeManager::calculatePossibleDestinations(std::vector& points) float botPosY = startPosition.getY(); float botPosZ = startPosition.getZ(); - FleePoint start(botAI, botPosX, botPosY, botPosZ); + FleePoint start(botPosX, botPosY, botPosZ); calculateDistanceToCreatures(&start); std::vector enemyOri; @@ -85,7 +84,7 @@ void FleeManager::calculatePossibleDestinations(std::vector& points) (maxAllowedDistance - sPlayerbotAIConfig->tooCloseDistance) / 10.0f); for (float dist = maxAllowedDistance; dist >= sPlayerbotAIConfig->tooCloseDistance; dist -= distIncrement) { - float angleIncrement = std::max(M_PI / 20, M_PI / 4 / (1.0 + dist - sPlayerbotAIConfig->tooCloseDistance)); + float angleIncrement = std::max(M_PI / 20, M_PI / 4 / (1.0f + dist - sPlayerbotAIConfig->tooCloseDistance)); for (float add = 0.0f; add < M_PI / 4 + angleIncrement; add += angleIncrement) { for (float angle = add; angle < add + 2 * static_cast(M_PI) + angleIncrement; @@ -110,7 +109,7 @@ void FleeManager::calculatePossibleDestinations(std::vector& points) if (!bot->IsWithinLOS(x, y, z) || (target && !target->IsWithinLOS(x, y, z))) continue; - FleePoint* point = new FleePoint(botAI, x, y, z); + FleePoint* point = new FleePoint(x, y, z); calculateDistanceToCreatures(point); if (sServerFacade->IsDistanceGreaterOrEqualThan(point->minDistance - start.minDistance, diff --git a/src/Mgr/Move/FleeManager.h b/src/Mgr/Move/FleeManager.h index 79629769f0..26366d2ea5 100644 --- a/src/Mgr/Move/FleeManager.h +++ b/src/Mgr/Move/FleeManager.h @@ -17,8 +17,8 @@ class PlayerbotAI; class FleePoint { public: - FleePoint(PlayerbotAI* botAI, float x, float y, float z) - : x(x), y(y), z(z), sumDistance(0.0f), minDistance(0.0f), botAI(botAI) + FleePoint(float x, float y, float z) + : x(x), y(y), z(z), sumDistance(0.0f), minDistance(0.0f) { } @@ -28,15 +28,12 @@ class FleePoint float sumDistance; float minDistance; - -private: - PlayerbotAI* botAI; }; class FleeManager { public: - FleeManager(Player* bot, float maxAllowedDistance, float followAngle, bool forceMaxDistance = false, + FleeManager(Player* bot, float maxAllowedDistance, bool forceMaxDistance = false, WorldPosition startPosition = WorldPosition()); bool CalculateDestination(float* rx, float* ry, float* rz); @@ -51,7 +48,6 @@ class FleeManager Player* bot; float maxAllowedDistance; - [[maybe_unused]] float followAngle; // unused - whipowill bool forceMaxDistance; WorldPosition startPosition; }; diff --git a/src/Mgr/Security/PlayerbotSecurity.cpp b/src/Mgr/Security/PlayerbotSecurity.cpp index 68bb2db29a..1c0e1f349c 100644 --- a/src/Mgr/Security/PlayerbotSecurity.cpp +++ b/src/Mgr/Security/PlayerbotSecurity.cpp @@ -8,6 +8,7 @@ #include "LFGMgr.h" #include "PlayerbotAIConfig.h" #include "Playerbots.h" +#include "BotItemService.h" PlayerbotSecurity::PlayerbotSecurity(Player* const bot) : bot(bot) { @@ -95,8 +96,8 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea } } - int32 botGS = static_cast(botAI->GetEquipGearScore(bot)); - int32 fromGS = static_cast(botAI->GetEquipGearScore(from)); + int32 botGS = static_cast(botAI->GetServices().GetItemService().GetEquipGearScore(bot)); + int32 fromGS = static_cast(botAI->GetServices().GetItemService().GetEquipGearScore(from)); if (sPlayerbotAIConfig->gearscorecheck && botGS && bot->GetLevel() > 15 && botGS > fromGS) { @@ -211,8 +212,8 @@ bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent, break; case PLAYERBOT_DENY_GEARSCORE: { - int botGS = int(botAI->GetEquipGearScore(bot)); - int fromGS = int(botAI->GetEquipGearScore(from)); + int botGS = int(botAI->GetServices().GetItemService().GetEquipGearScore(bot)); + int fromGS = int(botAI->GetServices().GetItemService().GetEquipGearScore(from)); int diff = (100 * (botGS - fromGS) / botGS); int req = 12 * sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) / from->GetLevel(); diff --git a/src/Mgr/Talent/Talentspec.cpp b/src/Mgr/Talent/Talentspec.cpp index 06e70b841c..0a74762323 100644 --- a/src/Mgr/Talent/Talentspec.cpp +++ b/src/Mgr/Talent/Talentspec.cpp @@ -93,12 +93,14 @@ bool TalentSpec::CheckTalents(uint32 level, std::ostringstream* out) SpellInfo const* spellInfodep = nullptr; for (auto& dep : talents) + { if (dep.talentInfo->TalentID == entry.talentInfo->DependsOn) { spellInfodep = sSpellMgr->GetSpellInfo(dep.talentInfo->RankID[0]); if (dep.rank >= entry.talentInfo->DependsOnRank) found = true; } + } if (!found) { @@ -140,7 +142,7 @@ bool TalentSpec::CheckTalents(uint32 level, std::ostringstream* out) } // Set the talents for the bots to the current spec. -void TalentSpec::ApplyTalents(Player* bot, std::ostringstream* out) +void TalentSpec::ApplyTalents(Player* bot, std::ostringstream* /*out*/) { for (auto& entry : talents) { @@ -236,6 +238,8 @@ void TalentSpec::SortTalents(std::vector& talents, uint32 sortB { return sortTalentMap(i, j, tabSort); }); break; } + default: + break; } } @@ -317,7 +321,7 @@ std::vector TalentSpec::GetTalentTree(uint32 tabpag if (entry.tabPage() == tabpage) retList.push_back(entry); - return std::move(retList); + return retList; } uint32 TalentSpec::GetTalentPoints(int32 tabpage) { return GetTalentPoints(talents, tabpage); }; @@ -330,7 +334,7 @@ uint32 TalentSpec::GetTalentPoints(std::vector& talents, int32 uint32 tPoints = 0; for (auto& entry : talents) - if (entry.tabPage() == tabpage) + if (entry.tabPage() == static_cast(tabpage)) tPoints = tPoints + entry.rank; return tPoints; @@ -368,7 +372,7 @@ std::string const TalentSpec::GetTalentLink() if (treeLink[2] != "0") link = link + "-" + treeLink[2]; - return std::move(link); + return link; } uint32 TalentSpec::highestTree() @@ -395,7 +399,7 @@ uint32 TalentSpec::highestTree() return 0; } -std::string const TalentSpec::FormatSpec(Player* bot) +std::string const TalentSpec::FormatSpec(Player* /*bot*/) { // uint8 cls = bot->getClass(); //not used, (used in lined 403), line marked for removal. @@ -444,9 +448,9 @@ std::vector TalentSpec::SubTalentList(std::vector(ABSOLUTE_DIST)) newentry.rank = std::abs(int32(newentry.rank - oldentry.rank)); - else if (reverse == ADDED_POINTS || reverse == REMOVED_POINTS) + else if (reverse == static_cast(ADDED_POINTS) || reverse == static_cast(REMOVED_POINTS)) newentry.rank = std::max(0u, (newentry.rank - oldentry.rank) * (reverse / 2)); else newentry.rank = (newentry.rank - oldentry.rank) * reverse; diff --git a/src/Mgr/Talent/Talentspec.h b/src/Mgr/Talent/Talentspec.h index 8cb63e44d9..17bf48b784 100644 --- a/src/Mgr/Talent/Talentspec.h +++ b/src/Mgr/Talent/Talentspec.h @@ -11,13 +11,13 @@ struct TalentEntry; struct TalentTabEntry; -#define SORT_BY_DEFAULT 0 -#define SORT_BY_POINTS_TREE 1 -#define ABSOLUTE_DIST 0 -#define SUBSTRACT_OLD_NEW 1 -#define SUBSTRACT_NEW_OLD -1 -#define ADDED_POINTS 2 -#define REMOVED_POINTS -2 +constexpr int SORT_BY_DEFAULT = 0; +constexpr int SORT_BY_POINTS_TREE = 1; +constexpr int ABSOLUTE_DIST = 0; +constexpr int SUBSTRACT_OLD_NEW = 1; +constexpr int SUBSTRACT_NEW_OLD = -1; +constexpr int ADDED_POINTS = 2; +constexpr int REMOVED_POINTS = -2; // unused currently class TalentSpec diff --git a/src/Mgr/Text/PlayerbotTextMgr.cpp b/src/Mgr/Text/PlayerbotTextMgr.cpp index 1dce9a29a5..1cc8ff76b7 100644 --- a/src/Mgr/Text/PlayerbotTextMgr.cpp +++ b/src/Mgr/Text/PlayerbotTextMgr.cpp @@ -8,7 +8,7 @@ #include "Playerbots.h" #include "WorldSessionMgr.h" -void PlayerbotTextMgr::replaceAll(std::string& str, const std::string& from, const std::string& to) +void PlayerbotTextMgr::replaceAll(std::string& str, std::string const& from, std::string const& to) { if (from.empty()) return; diff --git a/src/Mgr/Text/PlayerbotTextMgr.h b/src/Mgr/Text/PlayerbotTextMgr.h index f4ec2c403c..df0d6b68d5 100644 --- a/src/Mgr/Text/PlayerbotTextMgr.h +++ b/src/Mgr/Text/PlayerbotTextMgr.h @@ -87,7 +87,7 @@ class PlayerbotTextMgr std::map placeholders); void LoadBotTexts(); void LoadBotTextChance(); - static void replaceAll(std::string& str, const std::string& from, const std::string& to); + static void replaceAll(std::string& str, std::string const& from, std::string const& to); bool rollTextChance(std::string text); uint32 GetLocalePriority(); diff --git a/src/Mgr/Travel/TravelMgr.cpp b/src/Mgr/Travel/TravelMgr.cpp index ab70576502..ee6fb029e0 100644 --- a/src/Mgr/Travel/TravelMgr.cpp +++ b/src/Mgr/Travel/TravelMgr.cpp @@ -33,7 +33,7 @@ WorldPosition::WorldPosition(std::string const str) m_positionZ = std::stof(tokens[3]); m_orientation = std::stof(tokens[4]); } - catch (const std::exception&) + catch (std::exception const&) { m_mapId = 0; m_positionX = 0.0f; @@ -44,7 +44,7 @@ WorldPosition::WorldPosition(std::string const str) } } -WorldPosition::WorldPosition(uint32 mapId, const Position& pos) +WorldPosition::WorldPosition(uint32 mapId, Position const& pos) : WorldLocation(mapId, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()) { } @@ -117,15 +117,15 @@ WorldPosition::WorldPosition(std::vector list, WorldPositionConst } WorldPosition::WorldPosition(uint32 mapid, GridCoord grid) - : WorldLocation(mapid, (int32(grid.x_coord) - CENTER_GRID_ID - 0.5) * SIZE_OF_GRIDS + CENTER_GRID_OFFSET, - (int32(grid.y_coord) - CENTER_GRID_ID - 0.5) * SIZE_OF_GRIDS + CENTER_GRID_OFFSET, 0, 0) + : WorldLocation(mapid, (int32(grid.x_coord) - CENTER_GRID_ID - 0.5f) * SIZE_OF_GRIDS + CENTER_GRID_OFFSET, + (int32(grid.y_coord) - CENTER_GRID_ID - 0.5f) * SIZE_OF_GRIDS + CENTER_GRID_OFFSET, 0, 0) { } WorldPosition::WorldPosition(uint32 mapid, CellCoord cell) : WorldLocation( - mapid, (int32(cell.x_coord) - CENTER_GRID_CELL_ID - 0.5) * SIZE_OF_GRID_CELL + CENTER_GRID_CELL_OFFSET, - (int32(cell.y_coord) - CENTER_GRID_CELL_ID - 0.5) * SIZE_OF_GRID_CELL + CENTER_GRID_CELL_OFFSET, 0, 0) + mapid, (int32(cell.x_coord) - CENTER_GRID_CELL_ID - 0.5f) * SIZE_OF_GRID_CELL + CENTER_GRID_CELL_OFFSET, + (int32(cell.y_coord) - CENTER_GRID_CELL_ID - 0.5f) * SIZE_OF_GRID_CELL + CENTER_GRID_CELL_OFFSET, 0, 0) { } @@ -134,14 +134,14 @@ WorldPosition::WorldPosition(uint32 mapid, mGridCoord grid) { } -void WorldPosition::set(const WorldLocation& pos) { WorldRelocate(pos); } +void WorldPosition::set(WorldLocation const& pos) { WorldRelocate(pos); } -void WorldPosition::set(const WorldPosition& pos) +void WorldPosition::set(WorldPosition const& pos) { WorldRelocate(pos.m_mapId, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()); } -void WorldPosition::set(const WorldObject* pos) +void WorldPosition::set(WorldObject const* pos) { WorldRelocate(pos->GetMapId(), pos->GetPositionX(), pos->GetPositionY(), pos->GetPositionZ(), pos->GetOrientation()); } @@ -164,14 +164,14 @@ WorldPosition::operator bool() const return GetMapId() != 0 || GetPositionX() != 0 || GetPositionY() != 0 || GetPositionZ() != 0; } -bool operator==(WorldPosition const& p1, const WorldPosition& p2) +bool operator==(WorldPosition const& p1, WorldPosition const& p2) { return p1.GetMapId() == p2.GetMapId() && p2.GetPositionX() == p1.GetPositionX() && p2.GetPositionY() == p1.GetPositionY() && p2.GetPositionZ() == p1.GetPositionZ() && p2.GetOrientation() == p1.GetOrientation(); } -bool operator!=(WorldPosition const& p1, const WorldPosition& p2) { return !(p1 == p2); } +bool operator!=(WorldPosition const& p1, WorldPosition const& p2) { return !(p1 == p2); } WorldPosition& WorldPosition::operator+=(WorldPosition const& p1) { @@ -237,7 +237,7 @@ WorldPosition WorldPosition::offset(WorldPosition* center) float WorldPosition::size() { - return sqrt(pow(GetPositionX(), 2.0) + pow(GetPositionY(), 2.0) + pow(GetPositionZ(), 2.0)); + return sqrt(pow(GetPositionX(), 2.0f) + pow(GetPositionY(), 2.0f) + pow(GetPositionZ(), 2.0f)); } float WorldPosition::distance(WorldPosition* center) @@ -388,7 +388,7 @@ std::string const WorldPosition::to_string() return out.str(); } -std::vector WorldPosition::split(const std::string& s, char delimiter) +std::vector WorldPosition::split(std::string const& s, char delimiter) { std::vector tokens; std::string token; @@ -415,6 +415,9 @@ void WorldPosition::printWKT(std::vector points, std::ostringstre break; case 2: out << "\"POLYGON(("; + break; + default: + break; } for (auto& p : points) @@ -479,10 +482,10 @@ std::string const WorldPosition::getAreaName(bool fullName, bool zoneName) } } - return std::move(areaName); + return areaName; } -std::set WorldPosition::getTransports(uint32 entry) +std::set WorldPosition::getTransports(uint32 /*entry*/) { /* if (!entry) @@ -648,8 +651,6 @@ void WorldPosition::loadMapAndVMap(uint32 mapId, uint8 x, uint8 y) if (!sTravelMgr->isBadVmap(mapId, x, y)) { // load VMAPs for current map/grid... - const MapEntry* i_mapEntry = sMapStore.LookupEntry(mapId); - //const char* mapName = i_mapEntry ? i_mapEntry->name[sWorld->GetDefaultDbcLocale()] : "UNNAMEDMAP\x0"; //not used, (usage are commented out below), line marked for removal. int vmapLoadResult = VMAP::VMapFactory::createOrGetVMapMgr()->loadMap( (sWorld->GetDataPath() + "vmaps").c_str(), mapId, x, y); @@ -669,6 +670,8 @@ void WorldPosition::loadMapAndVMap(uint32 mapId, uint8 x, uint8 y) // LOG_INFO("playerbots", "Ignored VMAP name:{}, id:{}, x:{}, y:{} (vmap rep.: x:{}, y:{})", // mapName, mapId, x, y, x, y); break; + default: + break; } if (sPlayerbotAIConfig->hasLog(fileName)) @@ -1207,7 +1210,7 @@ bool QuestObjectiveTravelDestination::isActive(Player* bot) GuidVector targets = AI_VALUE(GuidVector, "possible targets"); for (auto& target : targets) - if (target.GetEntry() == getEntry() && target.IsCreature() && botAI->GetCreature(target) && + if (static_cast(target.GetEntry()) == getEntry() && target.IsCreature() && botAI->GetCreature(target) && botAI->GetCreature(target)->IsAlive()) return true; @@ -1259,7 +1262,7 @@ bool RpgTravelDestination::isActive(Player* bot) for (ObjectGuid const guid : ignoreList) { - if (guid.GetEntry() == getEntry()) + if (static_cast(guid.GetEntry()) == getEntry()) { return false; } @@ -1379,7 +1382,7 @@ bool BossTravelDestination::isActive(Player* bot) float levelMod = botPowerLevel / 500.0f; //(0-0.2f) float levelBoost = botPowerLevel / 50.0f; //(0-2.0f) - int32 maxLevel = botLevel + 3.0; + int32 maxLevel = botLevel + 3.0f; if ((int32)cInfo->MaxLevel > maxLevel) //@lvl5 max = 3, @lvl60 max = 57 return false; @@ -1405,7 +1408,7 @@ bool BossTravelDestination::isActive(Player* bot) GuidVector targets = AI_VALUE(GuidVector, "possible targets"); for (auto& target : targets) - if (target.GetEntry() == getEntry() && target.IsCreature() && botAI->GetCreature(target) && + if (static_cast(target.GetEntry()) == getEntry() && target.IsCreature() && botAI->GetCreature(target) && botAI->GetCreature(target)->IsAlive()) return true; @@ -1498,10 +1501,10 @@ TravelDestination* TravelTarget::getDestination() { return tDestination; } void TravelTarget::setStatus(TravelStatus status) { - m_status = status; + travelStatus = status; startTime = getMSTime(); - switch (m_status) + switch (travelStatus) { case TRAVEL_STATUS_NONE: case TRAVEL_STATUS_PREPARE: @@ -1523,7 +1526,7 @@ void TravelTarget::setStatus(TravelStatus status) bool TravelTarget::isActive() { - if (m_status == TRAVEL_STATUS_NONE || m_status == TRAVEL_STATUS_EXPIRED || m_status == TRAVEL_STATUS_PREPARE) + if (travelStatus == TRAVEL_STATUS_NONE || travelStatus == TRAVEL_STATUS_EXPIRED || travelStatus == TRAVEL_STATUS_PREPARE) return false; if (forced && isTraveling()) @@ -1535,7 +1538,7 @@ bool TravelTarget::isActive() return false; } - if (m_status == TRAVEL_STATUS_COOLDOWN) + if (travelStatus == TRAVEL_STATUS_COOLDOWN) return true; if (isTraveling()) @@ -1553,11 +1556,11 @@ bool TravelTarget::isActive() return true; }; -uint32 TravelTarget::getMaxTravelTime() { return (1000.0 * distance(bot)) / bot->GetSpeed(MOVE_RUN); } +uint32 TravelTarget::getMaxTravelTime() { return (1000.0f * distance(bot)) / bot->GetSpeed(MOVE_RUN); } bool TravelTarget::isTraveling() { - if (m_status != TRAVEL_STATUS_TRAVEL) + if (travelStatus != TRAVEL_STATUS_TRAVEL) return false; if (!tDestination->isActive(bot) && !forced) // Target has become invalid. Stop. @@ -1587,7 +1590,7 @@ bool TravelTarget::isTraveling() bool TravelTarget::isWorking() { - if (m_status != TRAVEL_STATUS_WORK) + if (travelStatus != TRAVEL_STATUS_WORK) return false; if (!tDestination->isActive(bot)) // Target has become invalid. Stop. @@ -1619,7 +1622,7 @@ bool TravelTarget::isWorking() bool TravelTarget::isPreparing() { - if (m_status != TRAVEL_STATUS_PREPARE) + if (travelStatus != TRAVEL_STATUS_PREPARE) return false; return true; @@ -2423,7 +2426,7 @@ void TravelMgr::LoadQuestTravelTable() std::vector ppath; for (auto& n : nodes) - ppath.push_back(WorldPosition(n->mapid, n->x, n->y, n->z, 0.0)); + ppath.push_back(WorldPosition(n->mapid, n->x, n->y, n->z, 0.0f)); float totalTime = startPos.getPathLength(ppath) / (450 * 8.0f); @@ -2613,7 +2616,7 @@ void TravelMgr::LoadQuestTravelTable() } else { - TravelNodePath travelPath(0.1f, 0.0, (uint8) TravelNodePathType::transport, entry, + TravelNodePath travelPath(0.1f, 0.0f, (uint8) TravelNodePathType::transport, entry, true); travelPath.setPathAndCost(ppath, moveSpeed); node->setPathTo(prevNode, travelPath); ppath.clear(); ppath.push_back(pos); } @@ -2641,7 +2644,7 @@ void TravelMgr::LoadQuestTravelTable() TravelNode* node = sTravelNodeMap->getNode(&pos, nullptr, 5.0f); if (node != prevNode) { - TravelNodePath travelPath(0.1f, 0.0, (uint8) TravelNodePathType::transport, entry, + TravelNodePath travelPath(0.1f, 0.0f, (uint8) TravelNodePathType::transport, entry, true); travelPath.setPathAndCost(ppath, moveSpeed); node->setPathTo(prevNode, travelPath); } } @@ -3409,7 +3412,7 @@ void TravelMgr::LoadQuestTravelTable() { Strategy* strat = con->GetStrategy(stratName); - const std::vector defaultActions = strat->getDefaultActions(); + std::vector const defaultActions = strat->getDefaultActions(); if (defaultActions.size() > 0) { @@ -3637,7 +3640,7 @@ void TravelMgr::LoadQuestTravelTable() if (!pos->getMap()->GetHeightInRange(nx, ny, nz, 5000.0f)) // GetHeight can fail continue; - WorldPosition npos = WorldPosition(pos->getMapId(), nx, ny, nz, 0.0); + WorldPosition npos = WorldPosition(pos->getMapId(), nx, ny, nz, 0.0f); uint32 area = path.getArea(npos.getMapId(), npos.getX(), npos.getY(), npos.getZ()); std::ostringstream out; @@ -3783,7 +3786,7 @@ uint32 TravelMgr::getDialogStatus(Player* pPlayer, int32 questgiver, Quest const // Selects a random WorldPosition from a list. Use a distance weighted distribution. std::vector TravelMgr::getNextPoint(WorldPosition* center, std::vector points, - uint32 amount) + uint32 /*amount*/) { std::vector retVec; @@ -3801,9 +3804,9 @@ std::vector TravelMgr::getNextPoint(WorldPosition* center, std:: // List of weights based on distance (Gausian curve that starts at 100 and lower to 1 at 1000 distance) // std::transform(retVec.begin(), retVec.end(), std::back_inserter(weights), [center](WorldPosition point) { return - // 1 + 1000 * exp(-1 * pow(point.distance(center) / 400.0, 2)); }); + // 1 + 1000 * exp(-1 * pow(point.distance(center) / 400.0f, 2)); }); - // List of weights based on distance (Twice the distance = half the weight). Caps out at 200.0000 range. + // List of weights based on distance (Twice the distance = half the weight). Caps out at 200.0000f range. std::transform(retVec.begin(), retVec.end(), std::back_inserter(weights), [center](WorldPosition* point) { return static_cast(200000.f / (1.f + point->distance(center))); }); @@ -4037,7 +4040,7 @@ std::vector TravelMgr::getRpgTravelDestinations(Player* bot, retTravelLocations.push_back(dest); } - return std::move(retTravelLocations); + return retTravelLocations; } std::vector TravelMgr::getExploreTravelDestinations(Player* bot, bool ignoreFull, diff --git a/src/Mgr/Travel/TravelMgr.h b/src/Mgr/Travel/TravelMgr.h index 39a79a4078..27ca823d48 100644 --- a/src/Mgr/Travel/TravelMgr.h +++ b/src/Mgr/Travel/TravelMgr.h @@ -90,7 +90,7 @@ class WorldPosition : public WorldLocation : WorldLocation(mapid, x, y, z, orientation) { } - WorldPosition(uint32 mapId, const Position& pos); + WorldPosition(uint32 mapId, Position const& pos); WorldPosition(WorldObject const* wo); WorldPosition(std::vector list, WorldPositionConst conType); WorldPosition(std::vector list, WorldPositionConst conType); @@ -99,9 +99,9 @@ class WorldPosition : public WorldLocation WorldPosition(uint32 mapid, mGridCoord grid); //Setters - void set(const WorldLocation& pos); - void set(const WorldObject* wo); - void set(const WorldPosition& pos); + void set(WorldLocation const& pos); + void set(WorldObject const* wo); + void set(WorldPosition const& pos); void setMapId(uint32 id); void setX(float x); void setY(float y); @@ -114,8 +114,8 @@ class WorldPosition : public WorldLocation // Getters operator bool() const; - friend bool operator==(WorldPosition const& p1, const WorldPosition& p2); - friend bool operator!=(WorldPosition const& p1, const WorldPosition& p2); + friend bool operator==(WorldPosition const& p1, WorldPosition const& p2); + friend bool operator!=(WorldPosition const& p1, WorldPosition const& p2); WorldPosition& operator=(WorldPosition const&) = default; WorldPosition& operator+=(WorldPosition const& p1); @@ -131,7 +131,7 @@ class WorldPosition : public WorldLocation std::string const print(); std::string const to_string(); - std::vector split(const std::string& s, char delimiter); + std::vector split(std::string const& s, char delimiter); void printWKT(std::vector points, std::ostringstream& out, uint32 dim = 0, bool loop = false); void printWKT(std::ostringstream& out) { printWKT({*this}, out); } @@ -277,7 +277,7 @@ class WorldPosition : public WorldLocation // Display functions WorldPosition getDisplayLocation(); - float getDisplayX() { return getDisplayLocation().getY() * -1.0; } + float getDisplayX() { return getDisplayLocation().getY() * -1.0f; } float getDisplayY() { return getDisplayLocation().getX(); } @@ -753,9 +753,9 @@ class BossTravelDestination : public TravelDestination class TravelTarget : AiObject { public: - TravelTarget(PlayerbotAI* botAI) : AiObject(botAI), m_status(TRAVEL_STATUS_NONE), startTime(getMSTime()){}; + TravelTarget(PlayerbotAI* botAI) : AiObject(botAI), travelStatus(TRAVEL_STATUS_NONE), startTime(getMSTime()){}; TravelTarget(PlayerbotAI* botAI, TravelDestination* tDestination1, WorldPosition* wPosition1) - : AiObject(botAI), m_status(TRAVEL_STATUS_NONE), startTime(getMSTime()) + : AiObject(botAI), travelStatus(TRAVEL_STATUS_NONE), startTime(getMSTime()) { setTarget(tDestination1, wPosition1); } @@ -818,7 +818,7 @@ class TravelTarget : AiObject bool isPreparing(); bool isMaxRetry(bool isMove) { return isMove ? (moveRetryCount > 5) : (extendRetryCount > 5); } - TravelStatus getStatus() { return m_status; } + TravelStatus getStatus() { return travelStatus; } TravelState getTravelState(); @@ -827,7 +827,7 @@ class TravelTarget : AiObject bool isForced() { return forced; } protected: - TravelStatus m_status; + TravelStatus travelStatus; uint32 startTime; uint32 statusTime = 0; diff --git a/src/Mgr/Travel/TravelNode.cpp b/src/Mgr/Travel/TravelNode.cpp index d3c03ed97e..84ba6925c8 100644 --- a/src/Mgr/Travel/TravelNode.cpp +++ b/src/Mgr/Travel/TravelNode.cpp @@ -135,7 +135,7 @@ float TravelNodePath::getCost(Player* bot, uint32 cGold) swimSpeed = bot->GetSpeed(MOVE_SWIM); if (bot->HasSpell(1066)) - swimSpeed *= 1.5; + swimSpeed *= 1.5f; uint32 level = bot->GetLevel(); bool isAlliance = Unit::GetFactionReactionTo(bot->GetFactionTemplateEntry(), @@ -152,9 +152,9 @@ float TravelNodePath::getCost(Player* bot, uint32 cGold) factionAnnoyance = (maxLevelCreature[1] - level) - 10; if (mobAnnoyance > 0) - modifier += 0.1 * mobAnnoyance; // For each level the whole path takes 10% longer. + modifier += 0.1f * mobAnnoyance; // For each level the whole path takes 10% longer. if (factionAnnoyance > 0) - modifier += 0.3 * factionAnnoyance; // For each level the whole path takes 10% longer. + modifier += 0.3f * factionAnnoyance; // For each level the whole path takes 10% longer. } } else if (getPathType() == TravelNodePathType::flightPath) @@ -364,7 +364,7 @@ std::vector TravelNode::getNodeMap(bool importantOnly, std::vector< } } - return std::move(closeList); + return closeList; } bool TravelNode::isUselessLink(TravelNode* farNode) @@ -392,7 +392,7 @@ bool TravelNode::isUselessLink(TravelNode* farNode) if (nearNode->hasLinkTo(farNode)) { // Is it quicker to go past second node to reach first node instead of going directly? - if (nearLength + nearNode->linkDistanceTo(farNode) < farLength * 1.1) + if (nearLength + nearNode->linkDistanceTo(farNode) < farLength * 1.1f) return true; } else @@ -406,7 +406,7 @@ bool TravelNode::isUselessLink(TravelNode* farNode) continue; // Is it quicker to go past second (and multiple) nodes to reach the first node instead of going directly? - if (nearLength + route.getTotalDistance() < farLength * 1.1) + if (nearLength + route.getTotalDistance() < farLength * 1.1f) return true; } } @@ -486,7 +486,7 @@ bool TravelNode::cropUselessLinks() if (firstNode->hasLinkTo(secondNode)) { //Is it quicker to go past first node to reach second node instead of going directly? - if (firstLength + firstNode->linkLengthTo(secondNode) < secondLength * 1.1) + if (firstLength + firstNode->linkLengthTo(secondNode) < secondLength * 1.1f) { if (secondNode->hasLinkTo(this) && !firstNode->hasLinkTo(this)) continue; @@ -505,7 +505,7 @@ bool TravelNode::cropUselessLinks() continue; //Is it quicker to go past first (and multiple) nodes to reach the second node instead of going - directly? if (firstLength + route.getLength() < secondLength * 1.1) + directly? if (firstLength + route.getLength() < secondLength * 1.1f) { if (secondNode->hasLinkTo(this) && !firstNode->hasLinkTo(this)) continue; @@ -534,7 +534,7 @@ bool TravelNode::cropUselessLinks() if (firstNode->hasLinkTo(secondNode)) { //Is it quicker to go past first node to reach second node instead of going directly? - if (firstLength + firstNode->linkLengthTo(secondNode) < secondLength * 1.1) + if (firstLength + firstNode->linkLengthTo(secondNode) < secondLength * 1.1f) { if (secondNode->hasLinkTo(this) && !firstNode->hasLinkTo(this)) continue; @@ -553,7 +553,7 @@ bool TravelNode::cropUselessLinks() continue; //Is it quicker to go past first (and multiple) nodes to reach the second node instead of going - directly? if (firstLength + route.getLength() < secondLength * 1.1) + directly? if (firstLength + route.getLength() < secondLength * 1.1f) { if (secondNode->hasLinkTo(this) && !firstNode->hasLinkTo(this)) continue; @@ -1163,7 +1163,7 @@ std::vector TravelNodeMap::getNodes(WorldPosition pos, float range) [pos](TravelNode* i, TravelNode* j) { return i->getPosition()->distance(pos) < j->getPosition()->distance(pos); }); - return std::move(retVec); + return retVec; } TravelNode* TravelNodeMap::getNode(WorldPosition pos, [[maybe_unused]] std::vector& ppath, Unit* bot, @@ -1247,14 +1247,14 @@ TravelNodeRoute TravelNodeMap::getRoute(TravelNode* start, TravelNode* goal, Pla childNode = &m_stubs.insert(std::make_pair(portNode, TravelNodeStub(portNode))).first->second; - childNode->m_g = 10 * MINUTE; - childNode->m_h = childNode->dataNode->fDist(goal) / botSpeed; - childNode->m_f = childNode->m_g + childNode->m_h; + childNode->g = 10 * MINUTE; + childNode->h = childNode->dataNode->fDist(goal) / botSpeed; + childNode->f = childNode->g + childNode->h; // childNode->parent = startStub; open.push_back(childNode); std::push_heap(open.begin(), open.end(), - [](TravelNodeStub* i, TravelNodeStub* j) { return i->m_f < j->m_f; }); + [](TravelNodeStub* i, TravelNodeStub* j) { return i->f < j->f; }); childNode->open = true; } } @@ -1263,19 +1263,19 @@ TravelNodeRoute TravelNodeMap::getRoute(TravelNode* start, TravelNode* goal, Pla if (open.size() == 0 && !start->hasRouteTo(goal)) return TravelNodeRoute(); - std::make_heap(open.begin(), open.end(), [](TravelNodeStub* i, TravelNodeStub* j) { return i->m_f < j->m_f; }); + std::make_heap(open.begin(), open.end(), [](TravelNodeStub* i, TravelNodeStub* j) { return i->f < j->f; }); open.push_back(startStub); - std::push_heap(open.begin(), open.end(), [](TravelNodeStub* i, TravelNodeStub* j) { return i->m_f < j->m_f; }); + std::push_heap(open.begin(), open.end(), [](TravelNodeStub* i, TravelNodeStub* j) { return i->f < j->f; }); startStub->open = true; while (!open.empty()) { - std::sort(open.begin(), open.end(), [](TravelNodeStub* i, TravelNodeStub* j) { return i->m_f < j->m_f; }); + std::sort(open.begin(), open.end(), [](TravelNodeStub* i, TravelNodeStub* j) { return i->f < j->f; }); currentNode = open.front(); // pop n node from open for which f is minimal - std::pop_heap(open.begin(), open.end(), [](TravelNodeStub* i, TravelNodeStub* j) { return i->m_f < j->m_f; }); + std::pop_heap(open.begin(), open.end(), [](TravelNodeStub* i, TravelNodeStub* j) { return i->f < j->f; }); open.pop_back(); currentNode->open = false; @@ -1310,16 +1310,16 @@ TravelNodeRoute TravelNodeMap::getRoute(TravelNode* start, TravelNode* goal, Pla continue; childNode = &m_stubs.insert(std::make_pair(linkNode, TravelNodeStub(linkNode))).first->second; - g = currentNode->m_g + linkCost; // stance from start + distance between the two nodes + g = currentNode->g + linkCost; // stance from start + distance between the two nodes if ((childNode->open || childNode->close) && - childNode->m_g <= g) // n' is already in opend or closed with a lower cost g(n') + childNode->g <= g) // n' is already in opend or closed with a lower cost g(n') continue; // consider next successor h = childNode->dataNode->fDist(goal) / botSpeed; f = g + h; // compute f(n') - childNode->m_f = f; - childNode->m_g = g; - childNode->m_h = h; + childNode->f = f; + childNode->g = g; + childNode->h = h; childNode->parent = currentNode; if (bot && !bot->isTaxiCheater()) @@ -1332,7 +1332,7 @@ TravelNodeRoute TravelNodeMap::getRoute(TravelNode* start, TravelNode* goal, Pla { open.push_back(childNode); std::push_heap(open.begin(), open.end(), - [](TravelNodeStub* i, TravelNodeStub* j) { return i->m_f < j->m_f; }); + [](TravelNodeStub* i, TravelNodeStub* j) { return i->f < j->f; }); childNode->open = true; } } @@ -1919,7 +1919,7 @@ void TravelNodeMap::generateTransportNodes() } else { - TravelNodePath travelPath(0.1f, 0.0, (uint8)TravelNodePathType::transport, itr.first, true); + TravelNodePath travelPath(0.1f, 0.0f, (uint8)TravelNodePathType::transport, itr.first, true); travelPath.setPathAndCost(ppath, moveSpeed); node->setPathTo(prevNode, travelPath); ppath.clear(); @@ -1950,7 +1950,7 @@ void TravelNodeMap::generateTransportNodes() if (node != prevNode) { - TravelNodePath travelPath(0.1f, 0.0, (uint8)TravelNodePathType::transport, itr.first, + TravelNodePath travelPath(0.1f, 0.0f, (uint8)TravelNodePathType::transport, itr.first, true); travelPath.setPathAndCost(ppath, moveSpeed); @@ -2075,7 +2075,7 @@ void TravelNodeMap::generateTaxiPaths() std::vector ppath; for (auto& n : nodes) - ppath.push_back(WorldPosition(n->mapid, n->x, n->y, n->z, 0.0)); + ppath.push_back(WorldPosition(n->mapid, n->x, n->y, n->z, 0.0f)); float totalTime = startPos.getPathLength(ppath) / (450 * 8.0f); @@ -2511,9 +2511,9 @@ void TravelNodeMap::loadNodeStore() void TravelNodeMap::calcMapOffset() { mapOffsets.push_back(std::make_pair(0, WorldPosition(0, 0, 0, 0, 0))); - mapOffsets.push_back(std::make_pair(1, WorldPosition(1, -3680.0, 13670.0, 0, 0))); - mapOffsets.push_back(std::make_pair(530, WorldPosition(530, 15000.0, -20000.0, 0, 0))); - mapOffsets.push_back(std::make_pair(571, WorldPosition(571, 10000.0, 5000.0, 0, 0))); + mapOffsets.push_back(std::make_pair(1, WorldPosition(1, -3680.0f, 13670.0f, 0, 0))); + mapOffsets.push_back(std::make_pair(530, WorldPosition(530, 15000.0f, -20000.0f, 0, 0))); + mapOffsets.push_back(std::make_pair(571, WorldPosition(571, 10000.0f, 5000.0f, 0, 0))); std::vector mapIds; diff --git a/src/Mgr/Travel/TravelNode.h b/src/Mgr/Travel/TravelNode.h index 3a86cbb169..ca05364630 100644 --- a/src/Mgr/Travel/TravelNode.h +++ b/src/Mgr/Travel/TravelNode.h @@ -467,7 +467,7 @@ class TravelNodeStub TravelNodeStub(TravelNode* dataNode1) { dataNode = dataNode1; } TravelNode* dataNode; - float m_f = 0.0, m_g = 0.0, m_h = 0.0; + float f = 0.0f, g = 0.0f, h = 0.0f; // A* algorithm cost variables bool open = false, close = false; TravelNodeStub* parent = nullptr; uint32 currentGold = 0; diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index b9ef257d95..8b3f58d313 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -392,7 +392,7 @@ bool PlayerbotAIConfig::Initialize() } } - randomChangeMultiplier = sConfigMgr->GetOption("AiPlayerbot.RandomChangeMultiplier", 1.0); + randomChangeMultiplier = sConfigMgr->GetOption("AiPlayerbot.RandomChangeMultiplier", 1.0f); randomBotCombatStrategies = sConfigMgr->GetOption("AiPlayerbot.RandomBotCombatStrategies", ""); randomBotNonCombatStrategies = sConfigMgr->GetOption("AiPlayerbot.RandomBotNonCombatStrategies", ""); @@ -549,7 +549,7 @@ bool PlayerbotAIConfig::Initialize() reviveBotWhenSummoned = sConfigMgr->GetOption("AiPlayerbot.ReviveBotWhenSummoned", 1); botRepairWhenSummon = sConfigMgr->GetOption("AiPlayerbot.BotRepairWhenSummon", true); autoInitOnly = sConfigMgr->GetOption("AiPlayerbot.AutoInitOnly", false); - autoInitEquipLevelLimitRatio = sConfigMgr->GetOption("AiPlayerbot.AutoInitEquipLevelLimitRatio", 1.0); + autoInitEquipLevelLimitRatio = sConfigMgr->GetOption("AiPlayerbot.AutoInitEquipLevelLimitRatio", 1.0f); maxAddedBots = sConfigMgr->GetOption("AiPlayerbot.MaxAddedBots", 40); addClassCommand = sConfigMgr->GetOption("AiPlayerbot.AddClassCommand", 1); @@ -581,7 +581,7 @@ bool PlayerbotAIConfig::Initialize() autoGearQualityLimit = sConfigMgr->GetOption("AiPlayerbot.AutoGearQualityLimit", 3); autoGearScoreLimit = sConfigMgr->GetOption("AiPlayerbot.AutoGearScoreLimit", 0); - randomBotXPRate = sConfigMgr->GetOption("AiPlayerbot.RandomBotXPRate", 1.0); + randomBotXPRate = sConfigMgr->GetOption("AiPlayerbot.RandomBotXPRate", 1.0f); randomBotAllianceRatio = sConfigMgr->GetOption("AiPlayerbot.RandomBotAllianceRatio", 50); randomBotHordeRatio = sConfigMgr->GetOption("AiPlayerbot.RandomBotHordeRatio", 50); disableDeathKnightLogin = sConfigMgr->GetOption("AiPlayerbot.DisableDeathKnightLogin", 0); @@ -606,7 +606,7 @@ bool PlayerbotAIConfig::Initialize() randombotStartingLevel = sConfigMgr->GetOption("AiPlayerbot.RandombotStartingLevel", 1); enablePeriodicOnlineOffline = sConfigMgr->GetOption("AiPlayerbot.EnablePeriodicOnlineOffline", false); enableRandomBotTrading = sConfigMgr->GetOption("AiPlayerbot.EnableRandomBotTrading", 1); - periodicOnlineOfflineRatio = sConfigMgr->GetOption("AiPlayerbot.PeriodicOnlineOfflineRatio", 2.0); + periodicOnlineOfflineRatio = sConfigMgr->GetOption("AiPlayerbot.PeriodicOnlineOfflineRatio", 2.0f); gearscorecheck = sConfigMgr->GetOption("AiPlayerbot.GearScoreCheck", false); randomBotPreQuests = sConfigMgr->GetOption("AiPlayerbot.PreQuests", false); @@ -854,7 +854,7 @@ void PlayerbotAIConfig::loadWorldBuff() } } -static std::vector split(const std::string& str, const std::string& pattern) +static std::vector split(std::string const& str, std::string const& pattern) { std::vector res; if (str == "") @@ -898,7 +898,7 @@ std::vector> PlayerbotAIConfig::ParseTempTalentsOrder(uint32 spells[talentTabInfo->tabpage].push_back(talentInfo); } - for (int tab = 0; tab < 3; tab++) + for (size_t tab = 0; tab < 3; tab++) { if (tab_links.size() <= tab) { @@ -907,7 +907,7 @@ std::vector> PlayerbotAIConfig::ParseTempTalentsOrder(uint32 std::sort(spells[tab].begin(), spells[tab].end(), [&](TalentEntry const* lhs, TalentEntry const* rhs) { return lhs->Row != rhs->Row ? lhs->Row < rhs->Row : lhs->Col < rhs->Col; }); - for (int i = 0; i < tab_links[tab].size(); i++) + for (size_t i = 0; i < tab_links[tab].size(); i++) { if (i >= spells[tab].size()) { @@ -956,7 +956,7 @@ std::vector> PlayerbotAIConfig::ParseTempPetTalentsOrder(uin std::sort(spells.begin(), spells.end(), [&](TalentEntry const* lhs, TalentEntry const* rhs) { return lhs->Row != rhs->Row ? lhs->Row < rhs->Row : lhs->Col < rhs->Col; }); - for (int i = 0; i < tab_link.size(); i++) + for (size_t i = 0; i < tab_link.size(); i++) { if (i >= spells.size()) { diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index fb112fc907..ed3407c2c4 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -57,7 +57,7 @@ enum NewRpgStatus : int RPG_STATUS_END = 8 }; -#define MAX_SPECNO 20 +constexpr uint32 MAX_SPECNO = 20; class PlayerbotAIConfig { @@ -65,10 +65,32 @@ class PlayerbotAIConfig PlayerbotAIConfig(){}; static PlayerbotAIConfig* instance() { + if (testInstance_) + return testInstance_; static PlayerbotAIConfig instance; return &instance; } + /** + * @brief Set a test instance for dependency injection during tests + * @param inst Pointer to test instance, or nullptr to reset to default + * + * This allows tests to inject mock configuration without modifying + * the global singleton. Call with nullptr to restore normal behavior. + */ + static void SetTestInstance(PlayerbotAIConfig* inst) { testInstance_ = inst; } + + /** + * @brief Check if a test instance is currently active + * @return true if using a test instance + */ + static bool HasTestInstance() { return testInstance_ != nullptr; } + +private: + static inline PlayerbotAIConfig* testInstance_ = nullptr; + +public: + bool Initialize(); bool IsInRandomAccountList(uint32 id); bool IsInRandomQuestItemList(uint32 id); diff --git a/src/RefactorFlags.h b/src/RefactorFlags.h new file mode 100644 index 0000000000..76a48701b1 --- /dev/null +++ b/src/RefactorFlags.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_REFACTOR_FLAGS_H +#define _PLAYERBOT_REFACTOR_FLAGS_H + +// Feature flags for incremental refactoring +// Set to 1 to enable new implementation, 0 to use legacy code + +// Phase 1: Configuration System +#define USE_NEW_CONFIG_SYSTEM 0 + +// Phase 3: Service Extraction +#define USE_NEW_ROLE_SERVICE 0 +#define USE_NEW_SPELL_SERVICE 0 +#define USE_NEW_CHAT_SERVICE 0 +#define USE_NEW_ITEM_SERVICE 0 + +// Phase 4: Handler Extraction +#define USE_NEW_PACKET_HANDLER 0 +#define USE_NEW_CHAT_COMMAND_HANDLER 0 + +// Phase 6: Manager Refactoring +#define USE_NEW_MANAGER_REGISTRY 0 + +#endif diff --git a/src/Script/PlayerbotCommandScript.cpp b/src/Script/PlayerbotCommandScript.cpp index 4e3c5611f2..cf9f7c77f8 100644 --- a/src/Script/PlayerbotCommandScript.cpp +++ b/src/Script/PlayerbotCommandScript.cpp @@ -72,7 +72,7 @@ class playerbots_commandscript : public CommandScript return GuildTaskMgr::HandleConsoleCommand(handler, args); } - static bool HandlePerfMonCommand(ChatHandler* handler, char const* args) + static bool HandlePerfMonCommand(ChatHandler* /*handler*/, char const* args) { if (!strcmp(args, "reset")) { diff --git a/src/Script/Playerbots.cpp b/src/Script/Playerbots.cpp index 8a8e1e0b73..dd066be38f 100644 --- a/src/Script/Playerbots.cpp +++ b/src/Script/Playerbots.cpp @@ -34,6 +34,11 @@ #include "cmath" #include "BattleGroundTactics.h" +#include "Bot/Core/ManagerRegistry.h" +#include "Bot/Service/TravelManagerAdapter.h" +#include "Bot/Service/RandomBotManagerAdapter.h" +#include "Bot/Service/BotRepositoryAdapter.h" + class PlayerbotsDatabaseScript : public DatabaseScript { public: @@ -99,7 +104,7 @@ class PlayerbotsPlayerScript : public PlayerScript if (!player->GetSession()->IsBot()) { sPlayerbotsMgr->AddPlayerbotData(player, false); - sRandomPlayerbotMgr->OnPlayerLogin(player); + sManagerRegistry.GetRandomBotManager().OnPlayerLogin(player); // Before modifying the following messages, please make sure it does not violate the AGPLv3.0 license // especially if you are distributing a repack or hosting a public server @@ -115,7 +120,7 @@ class PlayerbotsPlayerScript : public PlayerScript if (sPlayerbotAIConfig->enabled || sPlayerbotAIConfig->randomBotAutologin) { std::string roundedTime = - std::to_string(std::ceil((sPlayerbotAIConfig->maxRandomBots * 0.11 / 60) * 10) / 10.0); + std::to_string(std::ceil((sPlayerbotAIConfig->maxRandomBots * 0.11f / 60) * 10) / 10.0f); roundedTime = roundedTime.substr(0, roundedTime.find('.') + 2); ChatHandler(player->GetSession()).SendSysMessage( @@ -216,7 +221,7 @@ class PlayerbotsPlayerScript : public PlayerScript return true; } - bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Guild* guild) override + bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Guild* /*guild*/) override { if (type == CHAT_MSG_GUILD) { @@ -254,7 +259,7 @@ class PlayerbotsPlayerScript : public PlayerScript bool OnPlayerBeforeAchievementComplete(Player* player, AchievementEntry const* achievement) override { - if ((sRandomPlayerbotMgr->IsRandomBot(player) || sRandomPlayerbotMgr->IsAddclassBot(player)) && + if ((sManagerRegistry.GetRandomBotManager().IsRandomBot(player) || sRandomPlayerbotMgr->IsAddclassBot(player)) && (achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL))) { return false; @@ -266,11 +271,11 @@ class PlayerbotsPlayerScript : public PlayerScript void OnPlayerGiveXP(Player* player, uint32& amount, Unit* /*victim*/, uint8 /*xpSource*/) override { // early return - if (sPlayerbotAIConfig->randomBotXPRate == 1.0 || !player) + if (sPlayerbotAIConfig->randomBotXPRate == 1.0f || !player) return; // no XP multiplier, when player is no bot. - if (!player->GetSession()->IsBot() || !sRandomPlayerbotMgr->IsRandomBot(player)) + if (!player->GetSession()->IsBot() || !sManagerRegistry.GetRandomBotManager().IsRandomBot(player)) return; // no XP multiplier, when bot is in a group with a real player. @@ -367,6 +372,12 @@ class PlayerbotsWorldScript : public WorldScript sPlayerbotSpellRepository->Initialize(); + // Initialize ManagerRegistry with production adapters + sManagerRegistry.SetTravelManager(std::make_shared()); + sManagerRegistry.SetRandomBotManager(std::make_shared()); + sManagerRegistry.SetBotRepository(std::make_shared()); + + LOG_INFO("server.loading", ">> ManagerRegistry initialized with production adapters"); LOG_INFO("server.loading", "Playerbots World Thread Processor initialized"); } @@ -433,7 +444,7 @@ class PlayerbotsScript : public PlayerbotScript } } - void OnPlayerbotUpdate(uint32 diff) override + void OnPlayerbotUpdate(uint32 /*diff*/) override { sRandomPlayerbotMgr->UpdateSessions(); // Per-bot updates only } @@ -456,7 +467,7 @@ class PlayerbotsScript : public PlayerbotScript } } - sRandomPlayerbotMgr->OnPlayerLogout(player); + sManagerRegistry.GetRandomBotManager().OnPlayerLogout(player); } void OnPlayerbotLogoutBots() override diff --git a/src/Script/WorldThr/PlayerbotOperations.h b/src/Script/WorldThr/PlayerbotOperations.h index cd8851902c..c121262c4d 100644 --- a/src/Script/WorldThr/PlayerbotOperations.h +++ b/src/Script/WorldThr/PlayerbotOperations.h @@ -6,6 +6,7 @@ #ifndef _PLAYERBOT_OPERATIONS_H #define _PLAYERBOT_OPERATIONS_H +#include "Bot/Core/ManagerRegistry.h" #include "Group.h" #include "GroupMgr.h" #include "GuildMgr.h" @@ -274,7 +275,7 @@ class ArenaGroupFormationOperation : public PlayerbotOperation } // Step 1: Remove all members from their existing groups - for (const ObjectGuid& memberGuid : m_memberGuids) + for (ObjectGuid const& memberGuid : m_memberGuids) { Player* member = ObjectAccessor::FindPlayer(memberGuid); if (!member) @@ -313,7 +314,7 @@ class ArenaGroupFormationOperation : public PlayerbotOperation // Step 4: Add members to the new group uint32 addedMembers = 0; - for (const ObjectGuid& memberGuid : m_memberGuids) + for (ObjectGuid const& memberGuid : m_memberGuids) { Player* member = ObjectAccessor::FindPlayer(memberGuid); if (!member) @@ -349,7 +350,7 @@ class ArenaGroupFormationOperation : public PlayerbotOperation } // Step 5: Teleport members to leader and reset AI - for (const ObjectGuid& memberGuid : m_memberGuids) + for (ObjectGuid const& memberGuid : m_memberGuids) { Player* member = ObjectAccessor::FindPlayer(memberGuid); if (!member || !newGroup->IsMember(memberGuid)) @@ -418,7 +419,7 @@ class BotLogoutGroupCleanupOperation : public PlayerbotOperation Group* group = bot->GetGroup(); if (group && !bot->InBattleground() && !bot->InBattlegroundQueue() && botAI->HasActivePlayerMaster()) - sPlayerbotRepository->Save(botAI); + sManagerRegistry.GetBotRepository().Save(botAI); return true; } diff --git a/src/Util/BroadcastHelper.cpp b/src/Util/BroadcastHelper.cpp index 19979d3d9f..0a5e9f4b13 100644 --- a/src/Util/BroadcastHelper.cpp +++ b/src/Util/BroadcastHelper.cpp @@ -259,7 +259,7 @@ bool BroadcastHelper::BroadcastLootingItem(PlayerbotAI* ai, Player* bot, ItemTem return false; } -bool BroadcastHelper::BroadcastQuestAccepted(PlayerbotAI* ai, Player* bot, const Quest* quest) +bool BroadcastHelper::BroadcastQuestAccepted(PlayerbotAI* ai, Player* bot, Quest const* quest) { if (!sPlayerbotAIConfig->enableBroadcasts) return false; @@ -326,7 +326,7 @@ bool BroadcastHelper::BroadcastQuestUpdateAddKill(PlayerbotAI* ai, Player* bot, return false; } -bool BroadcastHelper::BroadcastQuestUpdateAddItem(PlayerbotAI* ai, Player* bot, Quest const* quest, uint32 availableCount, uint32 requiredCount, const ItemTemplate* proto) +bool BroadcastHelper::BroadcastQuestUpdateAddItem(PlayerbotAI* ai, Player* bot, Quest const* quest, uint32 availableCount, uint32 requiredCount, ItemTemplate const* proto) { if (!sPlayerbotAIConfig->enableBroadcasts) return false; @@ -795,7 +795,7 @@ bool BroadcastHelper::BroadcastSuggestGrindReputation(PlayerbotAI* ai, std::vect return false; } -bool BroadcastHelper::BroadcastSuggestSell(PlayerbotAI* ai, const ItemTemplate* proto, uint32 count, uint32 price, Player* bot) +bool BroadcastHelper::BroadcastSuggestSell(PlayerbotAI* ai, ItemTemplate const* proto, uint32 count, uint32 price, Player* bot) { if (!sPlayerbotAIConfig->enableBroadcasts) return false; diff --git a/src/Util/BroadcastHelper.h b/src/Util/BroadcastHelper.h index 099d6bf192..9f8dcf9138 100644 --- a/src/Util/BroadcastHelper.h +++ b/src/Util/BroadcastHelper.h @@ -1,4 +1,10 @@ -#pragma once +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef MOD_PLAYERBOTS_UTIL_BROADCAST_HELPER_H +#define MOD_PLAYERBOTS_UTIL_BROADCAST_HELPER_H class PlayerbotAI; class Player; @@ -38,12 +44,12 @@ class BroadcastHelper static bool BroadcastLootingItem( PlayerbotAI* ai, Player* bot, - const ItemTemplate* proto + ItemTemplate const* proto ); static bool BroadcastQuestAccepted( PlayerbotAI* ai, Player* bot, - const Quest* quest + Quest const* quest ); static bool BroadcastQuestUpdateAddKill( PlayerbotAI* ai, @@ -59,7 +65,7 @@ class BroadcastHelper Quest const* quest, uint32_t availableCount, uint32_t requiredCount, - const ItemTemplate* proto + ItemTemplate const* proto ); static bool BroadcastQuestUpdateFailedTimer( PlayerbotAI* ai, @@ -124,7 +130,7 @@ class BroadcastHelper ); static bool BroadcastSuggestSell( PlayerbotAI* ai, - const ItemTemplate* proto, + ItemTemplate const* proto, uint32_t count, uint32_t price, Player* bot @@ -145,4 +151,6 @@ class BroadcastHelper PlayerbotAI* ai, Player* bot ); -}; \ No newline at end of file +}; + +#endif // MOD_PLAYERBOTS_UTIL_BROADCAST_HELPER_H \ No newline at end of file diff --git a/src/Util/ServerFacade.cpp b/src/Util/ServerFacade.cpp index d69944c04f..d0381ac793 100644 --- a/src/Util/ServerFacade.cpp +++ b/src/Util/ServerFacade.cpp @@ -39,7 +39,7 @@ bool ServerFacade::IsDistanceGreaterOrEqualThan(float dist1, float dist2) { retu bool ServerFacade::IsDistanceLessOrEqualThan(float dist1, float dist2) { return !IsDistanceGreaterThan(dist1, dist2); } -void ServerFacade::SetFacingTo(Player* bot, WorldObject* wo, bool force) +void ServerFacade::SetFacingTo(Player* bot, WorldObject* wo, bool /*force*/) { if (!bot) return; diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000000..130df1d2ad --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,2 @@ +# Build directory +build/ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000000..a021be95ff --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,97 @@ +# mod-playerbots test configuration +# This CMakeLists.txt is intended for standalone testing with Google Test + +cmake_minimum_required(VERSION 3.16) +project(mod-playerbots-tests) + +# Google Test +include(FetchContent) +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG v1.14.0 +) +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +enable_testing() + +# Include directories +# IMPORTANT: fixtures must come FIRST to provide test stubs for AzerothCore headers +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/fixtures + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/mocks + ${CMAKE_CURRENT_SOURCE_DIR}/../src + ${CMAKE_CURRENT_SOURCE_DIR}/../src/Bot + ${CMAKE_CURRENT_SOURCE_DIR}/../src/Bot/Core + ${CMAKE_CURRENT_SOURCE_DIR}/../src/Bot/Engine + ${CMAKE_CURRENT_SOURCE_DIR}/../src/Bot/Interface + ${CMAKE_CURRENT_SOURCE_DIR}/../src/Bot/Service + ${CMAKE_CURRENT_SOURCE_DIR}/../src/Bot/Handler +) + +# Test sources +set(TEST_SOURCES + TestMain.cpp + + # Bot Service tests + unit/Bot/Service/ConfigProviderTest.cpp + unit/Bot/Service/RoleServiceTest.cpp + + # Bot Core tests + unit/Bot/Core/ManagerRegistryTest.cpp + + # Utility logic tests + unit/HealthThresholdTest.cpp + + # AI Base pattern tests + unit/Ai/Trigger/TriggerLogicTest.cpp + unit/Ai/Value/ValueLogicTest.cpp + unit/Ai/Action/ActionLogicTest.cpp + + # AI Engine tests + unit/Ai/Engine/StrategyLogicTest.cpp + unit/Ai/Engine/EngineTickTest.cpp + unit/Ai/Engine/MultiplierTest.cpp + unit/Ai/Engine/StateTransitionTest.cpp + + # AI Combat tests + unit/Ai/Combat/TargetSelectionTest.cpp + unit/Ai/Combat/SpellRotationTest.cpp + unit/Ai/Combat/GroupCoordinationTest.cpp + unit/Ai/Combat/BuffManagementTest.cpp + unit/Ai/Combat/CrowdControlTest.cpp + + # AI Movement tests + unit/Ai/Movement/PositioningTest.cpp + + # AI Resource tests + unit/Ai/Resource/ResourceManagementTest.cpp + + # AI Pet tests + unit/Ai/Pet/PetManagementTest.cpp + + # AI Dungeon/Raid tests + unit/Ai/Dungeon/DungeonTacticsTest.cpp + + # Integration tests + unit/Ai/Integration/ActionChainTest.cpp +) + +# Create test executable +add_executable(playerbots_tests ${TEST_SOURCES}) + +# Force include the test prelude header for all test files +target_compile_options(playerbots_tests PRIVATE + -include "${CMAKE_CURRENT_SOURCE_DIR}/fixtures/TestPrelude.h" +) + +target_link_libraries(playerbots_tests + GTest::gtest_main + GTest::gmock +) + +include(GoogleTest) +gtest_discover_tests(playerbots_tests) diff --git a/test/TestMain.cpp b/test/TestMain.cpp new file mode 100644 index 0000000000..a371e50eae --- /dev/null +++ b/test/TestMain.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include + +/** + * @brief Main entry point for mod-playerbots unit tests + * + * This test suite is designed to run independently of the AzerothCore server, + * testing isolated components through mocks and interfaces. + */ +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/fixtures/AcoreTypes.h b/test/fixtures/AcoreTypes.h new file mode 100644 index 0000000000..e1f85572b7 --- /dev/null +++ b/test/fixtures/AcoreTypes.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +/** + * @brief Minimal AzerothCore type stubs for unit testing + * + * This file provides the basic type definitions normally found in AzerothCore's + * Common.h, allowing unit tests to compile without the full server dependencies. + */ + +#ifndef _TEST_ACORE_TYPES_H +#define _TEST_ACORE_TYPES_H + +#include +#include + +// Basic integer types from AzerothCore +using int8 = int8_t; +using int16 = int16_t; +using int32 = int32_t; +using int64 = int64_t; +using uint8 = uint8_t; +using uint16 = uint16_t; +using uint32 = uint32_t; +using uint64 = uint64_t; + +// ObjectGuid stub for testing (simplified) +class ObjectGuid +{ +public: + using LowType = uint32; + + ObjectGuid() : _guid(0) {} + explicit ObjectGuid(uint64 guid) : _guid(guid) {} + + bool IsEmpty() const { return _guid == 0; } + uint64 GetRawValue() const { return _guid; } + LowType GetCounter() const { return static_cast(_guid & 0xFFFFFFFF); } + + bool operator==(ObjectGuid const& other) const { return _guid == other._guid; } + bool operator!=(ObjectGuid const& other) const { return _guid != other._guid; } + bool operator<(ObjectGuid const& other) const { return _guid < other._guid; } + + static ObjectGuid Empty; + +private: + uint64 _guid; +}; + +// WorldLocation stub for testing +struct WorldLocation +{ + uint32 mapId{0}; + float x{0.0f}; + float y{0.0f}; + float z{0.0f}; + float orientation{0.0f}; + + WorldLocation() = default; + WorldLocation(uint32 map, float px, float py, float pz, float o = 0.0f) + : mapId(map), x(px), y(py), z(pz), orientation(o) {} +}; + +#endif diff --git a/test/fixtures/Common.h b/test/fixtures/Common.h new file mode 100644 index 0000000000..ad8be127c7 --- /dev/null +++ b/test/fixtures/Common.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +/** + * @brief Stub for AzerothCore's Common.h + * + * Provides minimal type definitions for unit testing without full server dependencies. + */ + +#ifndef _TEST_COMMON_H +#define _TEST_COMMON_H + +#include "AcoreTypes.h" + +#endif diff --git a/test/fixtures/ObjectGuid.h b/test/fixtures/ObjectGuid.h new file mode 100644 index 0000000000..db19e7c7a2 --- /dev/null +++ b/test/fixtures/ObjectGuid.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +/** + * @brief Stub for AzerothCore's ObjectGuid.h for unit testing + */ + +#ifndef _TEST_OBJECTGUID_H +#define _TEST_OBJECTGUID_H + +#include "AcoreTypes.h" + +// ObjectGuid is already defined in AcoreTypes.h + +#endif diff --git a/test/fixtures/PlayerbotSecurity.h b/test/fixtures/PlayerbotSecurity.h new file mode 100644 index 0000000000..2aa57797b0 --- /dev/null +++ b/test/fixtures/PlayerbotSecurity.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +/** + * @brief Test fixture stub for PlayerbotSecurity.h + * + * This provides the minimum definitions needed for unit testing + * without requiring the full AzerothCore context. + */ + +#ifndef _PLAYERBOT_PLAYERBOTSECURITY_H +#define _PLAYERBOT_PLAYERBOTSECURITY_H + +#include "Common.h" + +class Player; + +enum PlayerbotSecurityLevel : uint32 +{ + PLAYERBOT_SECURITY_DENY_ALL = 0, + PLAYERBOT_SECURITY_TALK = 1, + PLAYERBOT_SECURITY_INVITE = 2, + PLAYERBOT_SECURITY_ALLOW_ALL = 3 +}; + +enum DenyReason +{ + PLAYERBOT_DENY_NONE, + PLAYERBOT_DENY_LOW_LEVEL, + PLAYERBOT_DENY_GEARSCORE, + PLAYERBOT_DENY_NOT_YOURS, + PLAYERBOT_DENY_IS_BOT, + PLAYERBOT_DENY_OPPOSING, + PLAYERBOT_DENY_DEAD, + PLAYERBOT_DENY_FAR, + PLAYERBOT_DENY_INVITE, + PLAYERBOT_DENY_FULL_GROUP, + PLAYERBOT_DENY_NOT_LEADER, + PLAYERBOT_DENY_IS_LEADER, + PLAYERBOT_DENY_BG, + PLAYERBOT_DENY_LFG +}; + +#endif diff --git a/test/fixtures/TestPrelude.h b/test/fixtures/TestPrelude.h new file mode 100644 index 0000000000..eac79446af --- /dev/null +++ b/test/fixtures/TestPrelude.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +/** + * @brief Common header for all unit tests + * + * Include this header at the top of test files to get access to + * AzerothCore type stubs and other common test utilities. + */ + +#ifndef _TEST_PRELUDE_H +#define _TEST_PRELUDE_H + +#include "AcoreTypes.h" + +#endif diff --git a/test/fixtures/TriggerTestFixture.h b/test/fixtures/TriggerTestFixture.h new file mode 100644 index 0000000000..a57f295acf --- /dev/null +++ b/test/fixtures/TriggerTestFixture.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_TRIGGER_TEST_FIXTURE_H +#define _PLAYERBOT_TRIGGER_TEST_FIXTURE_H + +#include +#include +#include "MockPlayerbotAI.h" +#include "MockPlayerbotAIConfig.h" + +/** + * @brief Base test fixture for testing Trigger classes + * + * This fixture provides common setup for testing trigger conditions + * without requiring a running game server. + * + * Usage: + * @code + * class MyTriggerTest : public TriggerTestFixture + * { + * protected: + * void SetUp() override + * { + * TriggerTestFixture::SetUp(); + * // Additional setup + * } + * }; + * + * TEST_F(MyTriggerTest, ShouldActivateWhenConditionMet) + * { + * // Arrange + * EXPECT_CALL(mockConfig, GetCriticalHealth()).WillOnce(Return(20)); + * + * // Act & Assert + * // ... test trigger activation + * } + * @endcode + */ +class TriggerTestFixture : public ::testing::Test +{ +protected: + void SetUp() override + { + mockConfig.SetupDefaults(); + } + + void TearDown() override + { + // Cleanup if needed + } + + MockConfigProvider mockConfig; + MockPlayerbotAI mockAI; +}; + +/** + * @brief Test fixture for testing Value classes + * + * Similar to TriggerTestFixture but focused on Value calculations. + */ +class ValueTestFixture : public ::testing::Test +{ +protected: + void SetUp() override + { + mockConfig.SetupDefaults(); + } + + void TearDown() override + { + // Cleanup if needed + } + + MockConfigProvider mockConfig; + MockPlayerbotAI mockAI; +}; + +/** + * @brief Test fixture for testing Action classes + * + * Provides mocks needed for testing bot actions. + */ +class ActionTestFixture : public ::testing::Test +{ +protected: + void SetUp() override + { + mockConfig.SetupDefaults(); + } + + void TearDown() override + { + // Cleanup if needed + } + + MockConfigProvider mockConfig; + MockPlayerbotAI mockAI; +}; + +#endif diff --git a/test/mocks/MockBotServices.h b/test/mocks/MockBotServices.h new file mode 100644 index 0000000000..4d260f3319 --- /dev/null +++ b/test/mocks/MockBotServices.h @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_MOCK_BOT_SERVICES_H +#define _PLAYERBOT_MOCK_BOT_SERVICES_H + +#include +#include "Bot/Interface/IBotContext.h" +#include "Bot/Interface/IChatService.h" +#include "Bot/Interface/IItemService.h" +#include "Bot/Interface/IRoleService.h" +#include "Bot/Interface/ISpellService.h" + +/** + * @brief Mock implementation of IBotContext for testing + */ +class MockBotContext : public IBotContext +{ +public: + MOCK_METHOD(Player*, GetBot, (), (override)); + MOCK_METHOD(Player*, GetMaster, (), (override)); + MOCK_METHOD(void, SetMaster, (Player* newMaster), (override)); + MOCK_METHOD(BotState, GetState, (), (const, override)); + MOCK_METHOD(bool, IsInCombat, (), (const, override)); + MOCK_METHOD(bool, IsRealPlayer, (), (const, override)); + MOCK_METHOD(bool, HasRealPlayerMaster, (), (const, override)); + MOCK_METHOD(bool, HasActivePlayerMaster, (), (const, override)); + MOCK_METHOD(bool, IsAlt, (), (const, override)); + MOCK_METHOD(Creature*, GetCreature, (ObjectGuid guid), (override)); + MOCK_METHOD(Unit*, GetUnit, (ObjectGuid guid), (override)); + MOCK_METHOD(Player*, GetPlayer, (ObjectGuid guid), (override)); + MOCK_METHOD(GameObject*, GetGameObject, (ObjectGuid guid), (override)); + MOCK_METHOD(WorldObject*, GetWorldObject, (ObjectGuid guid), (override)); + MOCK_METHOD(AreaTableEntry const*, GetCurrentArea, (), (const, override)); + MOCK_METHOD(AreaTableEntry const*, GetCurrentZone, (), (const, override)); + MOCK_METHOD(std::vector, GetPlayersInGroup, (), (override)); + MOCK_METHOD(Player*, GetGroupLeader, (), (override)); + MOCK_METHOD(bool, IsSafe, (Player* player), (const, override)); + MOCK_METHOD(bool, IsSafe, (WorldObject* obj), (const, override)); + MOCK_METHOD(bool, IsOpposing, (Player* player), (const, override)); + MOCK_METHOD(bool, CanMove, (), (const, override)); + MOCK_METHOD(bool, HasPlayerNearby, (float range), (const, override)); + MOCK_METHOD(bool, HasPlayerNearby, (WorldPosition* pos, float range), (const, override)); + MOCK_METHOD(bool, HasManyPlayersNearby, (uint32 triggerValue, float range), (const, override)); +}; + +/** + * @brief Mock implementation of IRoleService for testing + */ +class MockRoleService : public IRoleService +{ +public: + MOCK_METHOD(bool, IsTank, (Player* player, bool bySpec), (const, override)); + MOCK_METHOD(bool, IsHeal, (Player* player, bool bySpec), (const, override)); + MOCK_METHOD(bool, IsDps, (Player* player, bool bySpec), (const, override)); + MOCK_METHOD(bool, IsRanged, (Player* player, bool bySpec), (const, override)); + MOCK_METHOD(bool, IsMelee, (Player* player, bool bySpec), (const, override)); + MOCK_METHOD(bool, IsCaster, (Player* player, bool bySpec), (const, override)); + MOCK_METHOD(bool, IsRangedDps, (Player* player, bool bySpec), (const, override)); + MOCK_METHOD(bool, IsCombo, (Player* player), (const, override)); + MOCK_METHOD(bool, IsBotMainTank, (Player* player), (const, override)); + MOCK_METHOD(bool, IsMainTank, (Player* player), (const, override)); + MOCK_METHOD(bool, IsAssistTank, (Player* player), (const, override)); + MOCK_METHOD(bool, IsAssistTankOfIndex, (Player* player, int index, bool ignoreDeadPlayers), (const, override)); + MOCK_METHOD(uint32, GetGroupTankNum, (Player* player), (const, override)); + MOCK_METHOD(int32, GetAssistTankIndex, (Player* player), (const, override)); + MOCK_METHOD(int32, GetGroupSlotIndex, (Player* player), (const, override)); + MOCK_METHOD(int32, GetRangedIndex, (Player* player), (const, override)); + MOCK_METHOD(int32, GetRangedDpsIndex, (Player* player), (const, override)); + MOCK_METHOD(int32, GetMeleeIndex, (Player* player), (const, override)); + MOCK_METHOD(int32, GetClassIndex, (Player* player, uint8 cls), (const, override)); + MOCK_METHOD(bool, IsAssistHealOfIndex, (Player* player, int index, bool ignoreDeadPlayers), (const, override)); + MOCK_METHOD(bool, IsAssistRangedDpsOfIndex, (Player* player, int index, bool ignoreDeadPlayers), (const, override)); + MOCK_METHOD(bool, HasAggro, (Unit* unit), (const, override)); +}; + +/** + * @brief Mock implementation of ISpellService for testing + */ +class MockSpellService : public ISpellService +{ +public: + MOCK_METHOD(bool, CanCastSpell, (std::string const& name, Unit* target, Item* itemTarget), (override)); + MOCK_METHOD(bool, CastSpell, (std::string const& name, Unit* target, Item* itemTarget), (override)); + MOCK_METHOD(bool, CanCastSpell, + (uint32 spellId, Unit* target, bool checkHasSpell, Item* itemTarget, Item* castItem), (override)); + MOCK_METHOD(bool, CanCastSpell, (uint32 spellId, GameObject* goTarget, bool checkHasSpell), (override)); + MOCK_METHOD(bool, CanCastSpell, (uint32 spellId, float x, float y, float z, bool checkHasSpell, Item* itemTarget), + (override)); + MOCK_METHOD(bool, CastSpell, (uint32 spellId, Unit* target, Item* itemTarget), (override)); + MOCK_METHOD(bool, CastSpell, (uint32 spellId, float x, float y, float z, Item* itemTarget), (override)); + MOCK_METHOD(bool, HasAura, + (std::string const& spellName, Unit* player, bool maxStack, bool checkIsOwner, int maxAmount, + bool checkDuration), + (override)); + MOCK_METHOD(bool, HasAura, (uint32 spellId, Unit const* player), (override)); + // NOTE: HasAnyAuraOf uses variadic args (...) which GMock cannot mock. + // Provide a stub implementation that returns false. + bool HasAnyAuraOf(Unit* /*player*/, ...) override { return false; } + MOCK_METHOD(Aura*, GetAura, + (std::string const& spellName, Unit* unit, bool checkIsOwner, bool checkDuration, int checkStack), + (override)); + MOCK_METHOD(void, RemoveAura, (std::string const& name), (override)); + MOCK_METHOD(void, RemoveShapeshift, (), (override)); + MOCK_METHOD(bool, HasAuraToDispel, (Unit * player, uint32 dispelType), (override)); + MOCK_METHOD(bool, CanDispel, (SpellInfo const* spellInfo, uint32 dispelType), (override)); + MOCK_METHOD(bool, IsInterruptableSpellCasting, (Unit * player, std::string const& spell), (override)); + MOCK_METHOD(void, InterruptSpell, (), (override)); + MOCK_METHOD(void, SpellInterrupted, (uint32 spellId), (override)); + MOCK_METHOD(int32, CalculateGlobalCooldown, (uint32 spellId), (override)); + MOCK_METHOD(void, WaitForSpellCast, (Spell * spell), (override)); + MOCK_METHOD(bool, CanCastVehicleSpell, (uint32 spellId, Unit* target), (override)); + MOCK_METHOD(bool, CastVehicleSpell, (uint32 spellId, Unit* target), (override)); + MOCK_METHOD(bool, CastVehicleSpell, (uint32 spellId, float x, float y, float z), (override)); + MOCK_METHOD(bool, IsInVehicle, (bool canControl, bool canCast, bool canAttack, bool canTurn, bool fixed), + (override)); +}; + +/** + * @brief Mock implementation of IChatService for testing + */ +class MockChatService : public IChatService +{ +public: + MOCK_METHOD(bool, TellMaster, (std::string const& text, PlayerbotSecurityLevel securityLevel), (override)); + MOCK_METHOD(bool, TellMaster, (std::ostringstream & stream, PlayerbotSecurityLevel securityLevel), (override)); + MOCK_METHOD(bool, TellMasterNoFacing, (std::string const& text, PlayerbotSecurityLevel securityLevel), (override)); + MOCK_METHOD(bool, TellMasterNoFacing, (std::ostringstream & stream, PlayerbotSecurityLevel securityLevel), + (override)); + MOCK_METHOD(bool, TellError, (std::string const& text, PlayerbotSecurityLevel securityLevel), (override)); + MOCK_METHOD(bool, SayToGuild, (std::string const& msg), (override)); + MOCK_METHOD(bool, SayToWorld, (std::string const& msg), (override)); + MOCK_METHOD(bool, SayToChannel, (std::string const& msg, uint32 channelId), (override)); + MOCK_METHOD(bool, SayToParty, (std::string const& msg), (override)); + MOCK_METHOD(bool, SayToRaid, (std::string const& msg), (override)); + MOCK_METHOD(bool, Say, (std::string const& msg), (override)); + MOCK_METHOD(bool, Yell, (std::string const& msg), (override)); + MOCK_METHOD(bool, Whisper, (std::string const& msg, std::string const& receiverName), (override)); + MOCK_METHOD(bool, PlaySound, (uint32 emote), (override)); + MOCK_METHOD(bool, PlayEmote, (uint32 emote), (override)); + MOCK_METHOD(void, Ping, (float x, float y), (override)); +}; + +/** + * @brief Mock implementation of IItemService for testing + */ +class MockItemService : public IItemService +{ +public: + MOCK_METHOD(Item*, FindPoison, (), (const, override)); + MOCK_METHOD(Item*, FindAmmo, (), (const, override)); + MOCK_METHOD(Item*, FindBandage, (), (const, override)); + MOCK_METHOD(Item*, FindOpenableItem, (), (const, override)); + MOCK_METHOD(Item*, FindLockedItem, (), (const, override)); + MOCK_METHOD(Item*, FindConsumable, (uint32 itemId), (const, override)); + MOCK_METHOD(Item*, FindStoneFor, (Item * weapon), (const, override)); + MOCK_METHOD(Item*, FindOilFor, (Item * weapon), (const, override)); + MOCK_METHOD(void, ImbueItem, (Item * item, uint32 targetFlag, ObjectGuid targetGUID), (override)); + MOCK_METHOD(void, ImbueItem, (Item * item, uint8 targetInventorySlot), (override)); + MOCK_METHOD(void, ImbueItem, (Item * item, Unit* target), (override)); + MOCK_METHOD(void, ImbueItem, (Item * item), (override)); + MOCK_METHOD(void, EnchantItem, (uint32 spellId, uint8 slot), (override)); + MOCK_METHOD(std::vector, GetInventoryAndEquippedItems, (), (const, override)); + MOCK_METHOD(std::vector, GetInventoryItems, (), (const, override)); + MOCK_METHOD(uint32, GetInventoryItemsCountWithId, (uint32 itemId), (const, override)); + MOCK_METHOD(bool, HasItemInInventory, (uint32 itemId), (const, override)); + MOCK_METHOD(InventoryResult, CanEquipItem, (uint8 slot, uint16& dest, Item* pItem, bool swap, bool notLoading), + (const, override)); + MOCK_METHOD(uint8, FindEquipSlot, (ItemTemplate const* proto, uint32 slot, bool swap), (const, override)); + MOCK_METHOD(uint32, GetEquipGearScore, (Player* player), (const, override)); + MOCK_METHOD((std::vector>), GetCurrentQuestsRequiringItemId, (uint32 itemId), + (const, override)); +}; + +#endif diff --git a/test/mocks/MockManagers.h b/test/mocks/MockManagers.h new file mode 100644 index 0000000000..bbb2e90ffb --- /dev/null +++ b/test/mocks/MockManagers.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_MOCK_MANAGERS_H +#define _PLAYERBOT_MOCK_MANAGERS_H + +#include +#include "Bot/Interface/ITravelManager.h" +#include "Bot/Interface/IRandomBotManager.h" +#include "Bot/Interface/IBotRepository.h" + +/** + * @brief Mock implementation of ITravelManager for testing + */ +class MockTravelManager : public ITravelManager +{ +public: + MOCK_METHOD(void, RandomTeleport, (Player* bot), (override)); + MOCK_METHOD(void, RandomTeleportForLevel, (Player* bot), (override)); + MOCK_METHOD(void, RandomTeleportForRpg, (Player* bot), (override)); + MOCK_METHOD(uint32, GetZoneLevel, (uint16 mapId, float x, float y, float z), (override)); + MOCK_METHOD(std::vector, GetLocsForLevel, (uint8 level), (override)); + MOCK_METHOD(bool, HasDestination, (Player* bot), (override)); + MOCK_METHOD(void, SetQuestDestination, (Player* bot, Quest const* quest), (override)); + MOCK_METHOD(void, ClearDestination, (Player* bot), (override)); +}; + +/** + * @brief Mock implementation of IRandomBotManager for testing + */ +class MockRandomBotManager : public IRandomBotManager +{ +public: + // Bot queries + MOCK_METHOD(bool, IsRandomBot, (Player* bot), (override)); + MOCK_METHOD(bool, IsRandomBot, (ObjectGuid::LowType guid), (override)); + MOCK_METHOD(Player*, GetRandomPlayer, (), (override)); + MOCK_METHOD(std::vector, GetPlayers, (), (override)); + MOCK_METHOD(uint32, GetActiveBotCount, (), (const, override)); + MOCK_METHOD(uint32, GetMaxAllowedBotCount, (), (override)); + + // Bot lifecycle + MOCK_METHOD(void, Randomize, (Player* bot), (override)); + MOCK_METHOD(void, RandomizeFirst, (Player* bot), (override)); + MOCK_METHOD(void, Refresh, (Player* bot), (override)); + MOCK_METHOD(void, Revive, (Player* bot), (override)); + MOCK_METHOD(void, Remove, (Player* bot), (override)); + MOCK_METHOD(void, Clear, (Player* bot), (override)); + + // Scheduling + MOCK_METHOD(void, ScheduleTeleport, (uint32 bot, uint32 time), (override)); + MOCK_METHOD(void, ScheduleChangeStrategy, (uint32 bot, uint32 time), (override)); + + // Events + MOCK_METHOD(void, OnPlayerLogin, (Player* player), (override)); + MOCK_METHOD(void, OnPlayerLogout, (Player* player), (override)); + + // Value storage + MOCK_METHOD(uint32, GetValue, (Player* bot, std::string const& type), (override)); + MOCK_METHOD(uint32, GetValue, (uint32 bot, std::string const& type), (override)); + MOCK_METHOD(void, SetValue, (Player* bot, std::string const& type, uint32 value, std::string const& data), + (override)); + MOCK_METHOD(void, SetValue, (uint32 bot, std::string const& type, uint32 value, std::string const& data), + (override)); + + // Trading + MOCK_METHOD(double, GetBuyMultiplier, (Player* bot), (override)); + MOCK_METHOD(double, GetSellMultiplier, (Player* bot), (override)); + + // Statistics + MOCK_METHOD(void, PrintStats, (), (override)); + MOCK_METHOD(float, GetActivityPercentage, (), (override)); + MOCK_METHOD(void, SetActivityPercentage, (float percentage), (override)); +}; + +/** + * @brief Mock implementation of IBotRepository for testing + */ +class MockBotRepository : public IBotRepository +{ +public: + MOCK_METHOD(void, Save, (PlayerbotAI* botAI), (override)); + MOCK_METHOD(void, Load, (PlayerbotAI* botAI), (override)); + MOCK_METHOD(void, Reset, (PlayerbotAI* botAI), (override)); + MOCK_METHOD(bool, HasSavedData, (uint32 guid), (override)); + MOCK_METHOD(std::string, GetSavedValue, (uint32 guid, std::string const& key), (override)); + MOCK_METHOD(void, SetSavedValue, (uint32 guid, std::string const& key, std::string const& value), (override)); + MOCK_METHOD(void, DeleteSavedData, (uint32 guid), (override)); +}; + +#endif diff --git a/test/mocks/MockPlayerbotAI.h b/test/mocks/MockPlayerbotAI.h new file mode 100644 index 0000000000..40d71fb4ed --- /dev/null +++ b/test/mocks/MockPlayerbotAI.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_MOCK_PLAYERBOT_AI_H +#define _PLAYERBOT_MOCK_PLAYERBOT_AI_H + +#include +#include + +// Forward declarations for types used in the interface +class Player; +class Unit; +class Item; +class Spell; +class Aura; +class SpellInfo; + +/** + * @brief Mock interface for PlayerbotAI + * + * This mock is used for testing components that depend on PlayerbotAI + * without requiring a full game server context. + * + * Note: This is a simplified interface for testing purposes. + * The actual PlayerbotAI class has many more methods. + */ +class IMockPlayerbotAI +{ +public: + virtual ~IMockPlayerbotAI() = default; + + // Role detection (static methods in real class) + virtual bool IsTank() const = 0; + virtual bool IsHeal() const = 0; + virtual bool IsDps() const = 0; + virtual bool IsRanged() const = 0; + virtual bool IsMelee() const = 0; + virtual bool IsCaster() const = 0; + virtual bool IsMainTank() const = 0; + + // Spell casting + virtual bool CanCastSpell(std::string const& name, Unit* target) = 0; + virtual bool CastSpell(std::string const& name, Unit* target) = 0; + virtual bool HasAura(std::string const& spellName, Unit* player) = 0; + + // Communication + virtual bool TellMaster(std::string const& text) = 0; + virtual bool TellError(std::string const& text) = 0; + + // Bot access + virtual Player* GetBot() = 0; + virtual Player* GetMaster() = 0; +}; + +/** + * @brief Google Mock implementation of IMockPlayerbotAI + */ +class MockPlayerbotAI : public IMockPlayerbotAI +{ +public: + // Role detection + MOCK_METHOD(bool, IsTank, (), (const, override)); + MOCK_METHOD(bool, IsHeal, (), (const, override)); + MOCK_METHOD(bool, IsDps, (), (const, override)); + MOCK_METHOD(bool, IsRanged, (), (const, override)); + MOCK_METHOD(bool, IsMelee, (), (const, override)); + MOCK_METHOD(bool, IsCaster, (), (const, override)); + MOCK_METHOD(bool, IsMainTank, (), (const, override)); + + // Spell casting + MOCK_METHOD(bool, CanCastSpell, (std::string const& name, Unit* target), (override)); + MOCK_METHOD(bool, CastSpell, (std::string const& name, Unit* target), (override)); + MOCK_METHOD(bool, HasAura, (std::string const& spellName, Unit* player), (override)); + + // Communication + MOCK_METHOD(bool, TellMaster, (std::string const& text), (override)); + MOCK_METHOD(bool, TellError, (std::string const& text), (override)); + + // Bot access + MOCK_METHOD(Player*, GetBot, (), (override)); + MOCK_METHOD(Player*, GetMaster, (), (override)); +}; + +#endif diff --git a/test/mocks/MockPlayerbotAIConfig.h b/test/mocks/MockPlayerbotAIConfig.h new file mode 100644 index 0000000000..faa4765334 --- /dev/null +++ b/test/mocks/MockPlayerbotAIConfig.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_MOCK_PLAYERBOT_AI_CONFIG_H +#define _PLAYERBOT_MOCK_PLAYERBOT_AI_CONFIG_H + +#include +#include "Bot/Interface/IConfigProvider.h" + +/** + * @brief Mock implementation of IConfigProvider for testing + * + * This mock allows tests to inject specific configuration values + * without depending on the PlayerbotAIConfig singleton. + */ +class MockConfigProvider : public IConfigProvider +{ +public: + // Distance settings + MOCK_METHOD(float, GetSightDistance, (), (const, override)); + MOCK_METHOD(float, GetSpellDistance, (), (const, override)); + MOCK_METHOD(float, GetReactDistance, (), (const, override)); + MOCK_METHOD(float, GetGrindDistance, (), (const, override)); + MOCK_METHOD(float, GetLootDistance, (), (const, override)); + MOCK_METHOD(float, GetShootDistance, (), (const, override)); + MOCK_METHOD(float, GetFleeDistance, (), (const, override)); + MOCK_METHOD(float, GetTooCloseDistance, (), (const, override)); + MOCK_METHOD(float, GetMeleeDistance, (), (const, override)); + MOCK_METHOD(float, GetFollowDistance, (), (const, override)); + MOCK_METHOD(float, GetWhisperDistance, (), (const, override)); + MOCK_METHOD(float, GetContactDistance, (), (const, override)); + MOCK_METHOD(float, GetAoeRadius, (), (const, override)); + MOCK_METHOD(float, GetRpgDistance, (), (const, override)); + MOCK_METHOD(float, GetTargetPosRecalcDistance, (), (const, override)); + MOCK_METHOD(float, GetFarDistance, (), (const, override)); + MOCK_METHOD(float, GetHealDistance, (), (const, override)); + MOCK_METHOD(float, GetAggroDistance, (), (const, override)); + + // Timing settings + MOCK_METHOD(uint32, GetGlobalCooldown, (), (const, override)); + MOCK_METHOD(uint32, GetReactDelay, (), (const, override)); + MOCK_METHOD(uint32, GetMaxWaitForMove, (), (const, override)); + MOCK_METHOD(uint32, GetExpireActionTime, (), (const, override)); + MOCK_METHOD(uint32, GetDispelAuraDuration, (), (const, override)); + MOCK_METHOD(uint32, GetPassiveDelay, (), (const, override)); + MOCK_METHOD(uint32, GetRepeatDelay, (), (const, override)); + MOCK_METHOD(uint32, GetErrorDelay, (), (const, override)); + MOCK_METHOD(uint32, GetRpgDelay, (), (const, override)); + MOCK_METHOD(uint32, GetSitDelay, (), (const, override)); + MOCK_METHOD(uint32, GetReturnDelay, (), (const, override)); + MOCK_METHOD(uint32, GetLootDelay, (), (const, override)); + + // Health/Mana thresholds + MOCK_METHOD(uint32, GetCriticalHealth, (), (const, override)); + MOCK_METHOD(uint32, GetLowHealth, (), (const, override)); + MOCK_METHOD(uint32, GetMediumHealth, (), (const, override)); + MOCK_METHOD(uint32, GetAlmostFullHealth, (), (const, override)); + MOCK_METHOD(uint32, GetLowMana, (), (const, override)); + MOCK_METHOD(uint32, GetMediumMana, (), (const, override)); + MOCK_METHOD(uint32, GetHighMana, (), (const, override)); + + // Feature flags + MOCK_METHOD(bool, IsEnabled, (), (const, override)); + MOCK_METHOD(bool, IsDynamicReactDelay, (), (const, override)); + MOCK_METHOD(bool, IsAutoSaveMana, (), (const, override)); + MOCK_METHOD(uint32, GetSaveManaThreshold, (), (const, override)); + MOCK_METHOD(bool, IsAutoAvoidAoe, (), (const, override)); + MOCK_METHOD(float, GetMaxAoeAvoidRadius, (), (const, override)); + MOCK_METHOD(bool, IsTellWhenAvoidAoe, (), (const, override)); + + // Bot behavior settings + MOCK_METHOD(bool, IsFleeingEnabled, (), (const, override)); + MOCK_METHOD(bool, IsRandomBotAutologin, (), (const, override)); + MOCK_METHOD(bool, IsBotAutologin, (), (const, override)); + + // Logging + MOCK_METHOD(bool, HasLog, (std::string const& fileName), (const, override)); + + /** + * @brief Set up default return values for common configuration + * + * Call this in test setup to get reasonable defaults without + * having to mock every method. + */ + void SetupDefaults() + { + using ::testing::Return; + + // Distance defaults + ON_CALL(*this, GetSightDistance()).WillByDefault(Return(75.0f)); + ON_CALL(*this, GetSpellDistance()).WillByDefault(Return(26.0f)); + ON_CALL(*this, GetReactDistance()).WillByDefault(Return(150.0f)); + ON_CALL(*this, GetGrindDistance()).WillByDefault(Return(100.0f)); + ON_CALL(*this, GetLootDistance()).WillByDefault(Return(15.0f)); + ON_CALL(*this, GetShootDistance()).WillByDefault(Return(26.0f)); + ON_CALL(*this, GetFleeDistance()).WillByDefault(Return(20.0f)); + ON_CALL(*this, GetTooCloseDistance()).WillByDefault(Return(5.0f)); + ON_CALL(*this, GetMeleeDistance()).WillByDefault(Return(1.5f)); + ON_CALL(*this, GetFollowDistance()).WillByDefault(Return(1.5f)); + ON_CALL(*this, GetWhisperDistance()).WillByDefault(Return(6000.0f)); + ON_CALL(*this, GetContactDistance()).WillByDefault(Return(0.5f)); + ON_CALL(*this, GetAoeRadius()).WillByDefault(Return(5.0f)); + ON_CALL(*this, GetRpgDistance()).WillByDefault(Return(200.0f)); + ON_CALL(*this, GetTargetPosRecalcDistance()).WillByDefault(Return(0.1f)); + ON_CALL(*this, GetFarDistance()).WillByDefault(Return(20.0f)); + ON_CALL(*this, GetHealDistance()).WillByDefault(Return(38.0f)); + ON_CALL(*this, GetAggroDistance()).WillByDefault(Return(22.0f)); + + // Timing defaults + ON_CALL(*this, GetGlobalCooldown()).WillByDefault(Return(1500)); + ON_CALL(*this, GetReactDelay()).WillByDefault(Return(100)); + ON_CALL(*this, GetMaxWaitForMove()).WillByDefault(Return(5000)); + ON_CALL(*this, GetExpireActionTime()).WillByDefault(Return(5000)); + ON_CALL(*this, GetDispelAuraDuration()).WillByDefault(Return(2000)); + ON_CALL(*this, GetPassiveDelay()).WillByDefault(Return(4000)); + ON_CALL(*this, GetRepeatDelay()).WillByDefault(Return(5000)); + ON_CALL(*this, GetErrorDelay()).WillByDefault(Return(100)); + ON_CALL(*this, GetRpgDelay()).WillByDefault(Return(10000)); + ON_CALL(*this, GetSitDelay()).WillByDefault(Return(30000)); + ON_CALL(*this, GetReturnDelay()).WillByDefault(Return(3000)); + ON_CALL(*this, GetLootDelay()).WillByDefault(Return(1000)); + + // Health/Mana thresholds + ON_CALL(*this, GetCriticalHealth()).WillByDefault(Return(20)); + ON_CALL(*this, GetLowHealth()).WillByDefault(Return(45)); + ON_CALL(*this, GetMediumHealth()).WillByDefault(Return(65)); + ON_CALL(*this, GetAlmostFullHealth()).WillByDefault(Return(85)); + ON_CALL(*this, GetLowMana()).WillByDefault(Return(15)); + ON_CALL(*this, GetMediumMana()).WillByDefault(Return(40)); + ON_CALL(*this, GetHighMana()).WillByDefault(Return(80)); + + // Feature flags + ON_CALL(*this, IsEnabled()).WillByDefault(Return(true)); + ON_CALL(*this, IsDynamicReactDelay()).WillByDefault(Return(false)); + ON_CALL(*this, IsAutoSaveMana()).WillByDefault(Return(false)); + ON_CALL(*this, GetSaveManaThreshold()).WillByDefault(Return(50)); + ON_CALL(*this, IsAutoAvoidAoe()).WillByDefault(Return(true)); + ON_CALL(*this, GetMaxAoeAvoidRadius()).WillByDefault(Return(10.0f)); + ON_CALL(*this, IsTellWhenAvoidAoe()).WillByDefault(Return(true)); + + // Bot behavior + ON_CALL(*this, IsFleeingEnabled()).WillByDefault(Return(true)); + ON_CALL(*this, IsRandomBotAutologin()).WillByDefault(Return(false)); + ON_CALL(*this, IsBotAutologin()).WillByDefault(Return(false)); + + // Logging + ON_CALL(*this, HasLog(::testing::_)).WillByDefault(Return(false)); + } +}; + +#endif diff --git a/test/unit/Ai/Action/ActionLogicTest.cpp b/test/unit/Ai/Action/ActionLogicTest.cpp new file mode 100644 index 0000000000..5e3ca63d79 --- /dev/null +++ b/test/unit/Ai/Action/ActionLogicTest.cpp @@ -0,0 +1,443 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include +#include +#include + +/** + * @brief Tests for action execution logic patterns + * + * These tests validate the core execution patterns used by actions + * without requiring actual game objects. + */ + +/** + * @brief Tests for action precondition checking + */ +class ActionPreconditionTest : public ::testing::Test +{ +protected: + struct ActionState + { + bool hasTarget; + bool inRange; + bool hasResources; + bool offCooldown; + bool notCasting; + bool targetAlive; + }; + + /** + * @brief Check if attack action can execute + */ + bool CanExecuteAttack(ActionState const& state) + { + return state.hasTarget && + state.targetAlive && + state.inRange && + state.hasResources && + state.offCooldown && + state.notCasting; + } + + /** + * @brief Check if heal action can execute + */ + bool CanExecuteHeal(ActionState const& state) + { + return state.hasTarget && + state.targetAlive && + state.inRange && + state.hasResources && + state.offCooldown && + state.notCasting; + } + + /** + * @brief Check if resurrection action can execute + */ + bool CanExecuteResurrect(ActionState const& state) + { + return state.hasTarget && + !state.targetAlive && // Target must be dead + state.inRange && + state.hasResources && + state.offCooldown && + state.notCasting; + } +}; + +TEST_F(ActionPreconditionTest, AttackRequiresAllConditions) +{ + // All conditions met + ActionState ready{true, true, true, true, true, true}; + EXPECT_TRUE(CanExecuteAttack(ready)); + + // Missing target + ActionState noTarget{false, true, true, true, true, true}; + EXPECT_FALSE(CanExecuteAttack(noTarget)); + + // Out of range + ActionState outOfRange{true, false, true, true, true, true}; + EXPECT_FALSE(CanExecuteAttack(outOfRange)); + + // No resources + ActionState noResources{true, true, false, true, true, true}; + EXPECT_FALSE(CanExecuteAttack(noResources)); + + // On cooldown + ActionState onCooldown{true, true, true, false, true, true}; + EXPECT_FALSE(CanExecuteAttack(onCooldown)); + + // Already casting + ActionState casting{true, true, true, true, false, true}; + EXPECT_FALSE(CanExecuteAttack(casting)); + + // Target dead + ActionState targetDead{true, true, true, true, true, false}; + EXPECT_FALSE(CanExecuteAttack(targetDead)); +} + +TEST_F(ActionPreconditionTest, ResurrectRequiresDeadTarget) +{ + // Dead target - should work + ActionState deadTarget{true, true, true, true, true, false}; + EXPECT_TRUE(CanExecuteResurrect(deadTarget)); + + // Alive target - should not work + ActionState aliveTarget{true, true, true, true, true, true}; + EXPECT_FALSE(CanExecuteResurrect(aliveTarget)); +} + +/** + * @brief Tests for action result handling + */ +class ActionResultTest : public ::testing::Test +{ +protected: + enum class ActionResult + { + Success, + Fail, + Impossible, + Retry + }; + + struct ExecutionContext + { + bool targetValid; + bool spellReady; + bool inRange; + bool interrupted; + }; + + /** + * @brief Simulate action execution + */ + ActionResult ExecuteAction(ExecutionContext const& ctx) + { + if (!ctx.targetValid) + return ActionResult::Impossible; + + if (!ctx.spellReady) + return ActionResult::Retry; + + if (!ctx.inRange) + return ActionResult::Fail; + + if (ctx.interrupted) + return ActionResult::Fail; + + return ActionResult::Success; + } +}; + +TEST_F(ActionResultTest, SuccessfulExecution) +{ + ExecutionContext ctx{true, true, true, false}; + EXPECT_EQ(ActionResult::Success, ExecuteAction(ctx)); +} + +TEST_F(ActionResultTest, InvalidTargetIsImpossible) +{ + ExecutionContext ctx{false, true, true, false}; + EXPECT_EQ(ActionResult::Impossible, ExecuteAction(ctx)); +} + +TEST_F(ActionResultTest, SpellNotReadyIsRetry) +{ + ExecutionContext ctx{true, false, true, false}; + EXPECT_EQ(ActionResult::Retry, ExecuteAction(ctx)); +} + +TEST_F(ActionResultTest, OutOfRangeIsFail) +{ + ExecutionContext ctx{true, true, false, false}; + EXPECT_EQ(ActionResult::Fail, ExecuteAction(ctx)); +} + +TEST_F(ActionResultTest, InterruptedIsFail) +{ + ExecutionContext ctx{true, true, true, true}; + EXPECT_EQ(ActionResult::Fail, ExecuteAction(ctx)); +} + +/** + * @brief Tests for action queue logic + */ +class ActionQueueTest : public ::testing::Test +{ +protected: + struct QueuedAction + { + std::string name; + float priority; + bool executable; + }; + + /** + * @brief Select next action from queue + */ + std::string SelectNextAction(std::vector& queue) + { + // Sort by priority (highest first) + std::sort(queue.begin(), queue.end(), + [](QueuedAction const& a, QueuedAction const& b) { + return a.priority > b.priority; + }); + + // Find first executable + for (auto const& action : queue) + { + if (action.executable) + return action.name; + } + + return ""; + } + + /** + * @brief Remove completed action from queue + */ + void RemoveAction(std::vector& queue, std::string const& name) + { + queue.erase( + std::remove_if(queue.begin(), queue.end(), + [&name](QueuedAction const& a) { return a.name == name; }), + queue.end()); + } +}; + +TEST_F(ActionQueueTest, SelectsHighestPriorityExecutable) +{ + std::vector queue = { + {"low_priority", 10.0f, true}, + {"high_priority", 100.0f, true}, + {"medium_priority", 50.0f, true}, + }; + + EXPECT_EQ("high_priority", SelectNextAction(queue)); +} + +TEST_F(ActionQueueTest, SkipsNonExecutable) +{ + std::vector queue = { + {"low_priority", 10.0f, true}, + {"high_priority", 100.0f, false}, // Not executable + {"medium_priority", 50.0f, true}, + }; + + EXPECT_EQ("medium_priority", SelectNextAction(queue)); +} + +TEST_F(ActionQueueTest, ReturnsEmptyWhenNoneExecutable) +{ + std::vector queue = { + {"action1", 10.0f, false}, + {"action2", 100.0f, false}, + }; + + EXPECT_EQ("", SelectNextAction(queue)); +} + +TEST_F(ActionQueueTest, RemoveActionWorks) +{ + std::vector queue = { + {"action1", 10.0f, true}, + {"action2", 50.0f, true}, + {"action3", 100.0f, true}, + }; + + RemoveAction(queue, "action2"); + EXPECT_EQ(2u, queue.size()); + + bool found = false; + for (auto const& a : queue) + { + if (a.name == "action2") + found = true; + } + EXPECT_FALSE(found); +} + +/** + * @brief Tests for action timing logic + */ +class ActionTimingTest : public ::testing::Test +{ +protected: + /** + * @brief Calculate remaining cooldown + */ + uint32 GetRemainingCooldown(uint32 cooldownStart, uint32 cooldownDuration, uint32 currentTime) + { + if (cooldownStart == 0) + return 0; + + uint32 cooldownEnd = cooldownStart + cooldownDuration; + if (currentTime >= cooldownEnd) + return 0; + + return cooldownEnd - currentTime; + } + + /** + * @brief Check if within GCD + */ + bool IsOnGCD(uint32 lastSpellTime, uint32 gcdDuration, uint32 currentTime) + { + if (lastSpellTime == 0) + return false; + + return (currentTime - lastSpellTime) < gcdDuration; + } + + /** + * @brief Calculate estimated cast end time + */ + uint32 GetCastEndTime(uint32 castStartTime, uint32 castDuration) + { + return castStartTime + castDuration; + } +}; + +TEST_F(ActionTimingTest, RemainingCooldown) +{ + uint32 cooldownStart = 1000; + uint32 cooldownDuration = 5000; + + // During cooldown + EXPECT_EQ(3000u, GetRemainingCooldown(cooldownStart, cooldownDuration, 3000)); + + // At end of cooldown + EXPECT_EQ(0u, GetRemainingCooldown(cooldownStart, cooldownDuration, 6000)); + + // After cooldown + EXPECT_EQ(0u, GetRemainingCooldown(cooldownStart, cooldownDuration, 10000)); + + // No cooldown set + EXPECT_EQ(0u, GetRemainingCooldown(0, cooldownDuration, 3000)); +} + +TEST_F(ActionTimingTest, GCDCheck) +{ + uint32 gcdDuration = 1500; + + // During GCD + EXPECT_TRUE(IsOnGCD(1000, gcdDuration, 1500)); + EXPECT_TRUE(IsOnGCD(1000, gcdDuration, 2000)); + + // After GCD + EXPECT_FALSE(IsOnGCD(1000, gcdDuration, 2500)); + EXPECT_FALSE(IsOnGCD(1000, gcdDuration, 3000)); + + // Never cast + EXPECT_FALSE(IsOnGCD(0, gcdDuration, 1000)); +} + +TEST_F(ActionTimingTest, CastEndTime) +{ + EXPECT_EQ(3500u, GetCastEndTime(1000, 2500)); + EXPECT_EQ(1000u, GetCastEndTime(1000, 0)); // Instant cast +} + +/** + * @brief Tests for movement action logic + */ +class MovementActionTest : public ::testing::Test +{ +protected: + struct Position + { + float x, y, z; + }; + + /** + * @brief Calculate direction vector + */ + Position CalculateDirection(Position const& from, Position const& to) + { + float dx = to.x - from.x; + float dy = to.y - from.y; + float dz = to.z - from.z; + + float length = std::sqrt(dx * dx + dy * dy + dz * dz); + if (length < 0.0001f) + return {0.0f, 0.0f, 0.0f}; + + return {dx / length, dy / length, dz / length}; + } + + /** + * @brief Calculate position after moving towards target + */ + Position MoveTowards(Position const& from, Position const& to, float distance) + { + Position dir = CalculateDirection(from, to); + return { + from.x + dir.x * distance, + from.y + dir.y * distance, + from.z + dir.z * distance + }; + } +}; + +TEST_F(MovementActionTest, DirectionCalculation) +{ + Position from{0.0f, 0.0f, 0.0f}; + Position to{10.0f, 0.0f, 0.0f}; + + Position dir = CalculateDirection(from, to); + + EXPECT_FLOAT_EQ(1.0f, dir.x); + EXPECT_FLOAT_EQ(0.0f, dir.y); + EXPECT_FLOAT_EQ(0.0f, dir.z); +} + +TEST_F(MovementActionTest, SamePositionGivesZeroDirection) +{ + Position pos{5.0f, 5.0f, 5.0f}; + Position dir = CalculateDirection(pos, pos); + + EXPECT_FLOAT_EQ(0.0f, dir.x); + EXPECT_FLOAT_EQ(0.0f, dir.y); + EXPECT_FLOAT_EQ(0.0f, dir.z); +} + +TEST_F(MovementActionTest, MoveTowardsCalculation) +{ + Position from{0.0f, 0.0f, 0.0f}; + Position to{10.0f, 0.0f, 0.0f}; + + Position newPos = MoveTowards(from, to, 5.0f); + + EXPECT_FLOAT_EQ(5.0f, newPos.x); + EXPECT_FLOAT_EQ(0.0f, newPos.y); + EXPECT_FLOAT_EQ(0.0f, newPos.z); +} + diff --git a/test/unit/Ai/Combat/BuffManagementTest.cpp b/test/unit/Ai/Combat/BuffManagementTest.cpp new file mode 100644 index 0000000000..13852fbeb8 --- /dev/null +++ b/test/unit/Ai/Combat/BuffManagementTest.cpp @@ -0,0 +1,544 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief Tests for buff management logic + */ + +/** + * @brief Tests for self-buff management + */ +class SelfBuffTest : public ::testing::Test +{ +protected: + struct Buff + { + uint32 spellId; + std::string name; + uint32 durationMs; + uint32 remainingMs; + bool isActive; + }; + + struct BuffContext + { + std::vector requiredBuffs; + bool inCombat; + bool isMounted; + float manaPercent; + }; + + BuffContext context_; + + void SetUp() override + { + context_.requiredBuffs = { + {1, "Arcane Intellect", 1800000, 0, false}, + {2, "Mage Armor", 1800000, 0, false}, + {3, "Ice Barrier", 60000, 0, false}, + }; + context_.inCombat = false; + context_.isMounted = false; + context_.manaPercent = 100.0f; + } + + bool NeedsRefresh(Buff const& buff, uint32 refreshThreshold = 300000) + { + if (!buff.isActive) + return true; + return buff.remainingMs < refreshThreshold; + } + + std::vector GetMissingBuffs() + { + std::vector missing; + for (auto const& buff : context_.requiredBuffs) + { + if (NeedsRefresh(buff)) + missing.push_back(buff.spellId); + } + return missing; + } + + bool ShouldBuffNow() + { + if (context_.isMounted) + return false; + + // Don't buff with low mana unless out of combat + if (context_.manaPercent < 30.0f && context_.inCombat) + return false; + + return !GetMissingBuffs().empty(); + } + + uint32 GetNextBuffToApply() + { + auto missing = GetMissingBuffs(); + if (missing.empty()) + return 0; + + // Priority: shortest duration buffs first (they need more upkeep) + uint32 bestSpell = 0; + uint32 shortestDuration = UINT32_MAX; + + for (uint32 spellId : missing) + { + for (auto const& buff : context_.requiredBuffs) + { + if (buff.spellId == spellId && buff.durationMs < shortestDuration) + { + shortestDuration = buff.durationMs; + bestSpell = spellId; + } + } + } + + return bestSpell; + } +}; + +TEST_F(SelfBuffTest, DetectsMissingBuffs) +{ + auto missing = GetMissingBuffs(); + EXPECT_EQ(3u, missing.size()); +} + +TEST_F(SelfBuffTest, DetectsExpiringBuffs) +{ + context_.requiredBuffs[0].isActive = true; + context_.requiredBuffs[0].remainingMs = 200000; // Below threshold + context_.requiredBuffs[1].isActive = true; + context_.requiredBuffs[1].remainingMs = 1000000; // Above threshold + + auto missing = GetMissingBuffs(); + EXPECT_EQ(2u, missing.size()); // First and third +} + +TEST_F(SelfBuffTest, DoesNotBuffWhenMounted) +{ + context_.isMounted = true; + EXPECT_FALSE(ShouldBuffNow()); +} + +TEST_F(SelfBuffTest, DoesNotBuffWithLowManaInCombat) +{ + context_.inCombat = true; + context_.manaPercent = 20.0f; + EXPECT_FALSE(ShouldBuffNow()); +} + +TEST_F(SelfBuffTest, BuffsWithLowManaOutOfCombat) +{ + context_.inCombat = false; + context_.manaPercent = 20.0f; + EXPECT_TRUE(ShouldBuffNow()); +} + +TEST_F(SelfBuffTest, PrioritizesShortDurationBuffs) +{ + uint32 next = GetNextBuffToApply(); + EXPECT_EQ(3u, next); // Ice Barrier has 60s duration +} + +/** + * @brief Tests for group buff management + */ +class GroupBuffTest : public ::testing::Test +{ +protected: + struct GroupMember + { + uint32 guid; + std::string playerClass; + bool hasBuff; + float distance; + bool isAlive; + }; + + struct GroupBuffInfo + { + uint32 singleSpellId; + uint32 groupSpellId; + std::set validClasses; + uint32 singleManaCost; + uint32 groupManaCost; + }; + + std::vector group_; + GroupBuffInfo buffInfo_; + + void SetUp() override + { + group_ = { + {1, "Warrior", false, 10.0f, true}, + {2, "Mage", false, 15.0f, true}, + {3, "Priest", false, 20.0f, true}, + {4, "Rogue", false, 25.0f, true}, + {5, "Druid", false, 30.0f, true}, + }; + + // Arcane Intellect example + buffInfo_ = { + 1, // Single target spell + 2, // Group spell + {"Mage", "Priest", "Warlock", "Druid", "Paladin", "Shaman"}, + 500, // Single mana cost + 2000, // Group mana cost + }; + } + + std::vector GetMembersNeedingBuff() + { + std::vector needing; + for (auto const& member : group_) + { + if (!member.isAlive) + continue; + if (member.hasBuff) + continue; + if (buffInfo_.validClasses.find(member.playerClass) == buffInfo_.validClasses.end()) + continue; + needing.push_back(member.guid); + } + return needing; + } + + bool ShouldUseGroupBuff(float manaPercent) + { + auto needing = GetMembersNeedingBuff(); + + if (needing.size() < 3) + return false; // Not worth group mana cost + + // Check if we have enough mana + return manaPercent >= 40.0f; + } + + uint32 GetClosestMemberNeedingBuff(float maxRange) + { + uint32 closest = 0; + float closestDist = maxRange + 1.0f; + + for (auto const& member : group_) + { + if (!member.isAlive || member.hasBuff) + continue; + if (buffInfo_.validClasses.find(member.playerClass) == buffInfo_.validClasses.end()) + continue; + if (member.distance > maxRange) + continue; + + if (member.distance < closestDist) + { + closestDist = member.distance; + closest = member.guid; + } + } + + return closest; + } +}; + +TEST_F(GroupBuffTest, IdentifiesMembersNeedingBuff) +{ + auto needing = GetMembersNeedingBuff(); + // Mage, Priest, Druid can receive Arcane Intellect + EXPECT_EQ(3u, needing.size()); +} + +TEST_F(GroupBuffTest, ExcludesDeadMembers) +{ + group_[1].isAlive = false; // Mage dead + auto needing = GetMembersNeedingBuff(); + EXPECT_EQ(2u, needing.size()); +} + +TEST_F(GroupBuffTest, ExcludesAlreadyBuffed) +{ + group_[1].hasBuff = true; // Mage already has buff + auto needing = GetMembersNeedingBuff(); + EXPECT_EQ(2u, needing.size()); +} + +TEST_F(GroupBuffTest, UseGroupBuffWhenManyNeed) +{ + EXPECT_TRUE(ShouldUseGroupBuff(100.0f)); +} + +TEST_F(GroupBuffTest, DontUseGroupBuffWhenFewNeed) +{ + group_[1].hasBuff = true; + group_[2].hasBuff = true; + EXPECT_FALSE(ShouldUseGroupBuff(100.0f)); +} + +TEST_F(GroupBuffTest, DontUseGroupBuffWithLowMana) +{ + EXPECT_FALSE(ShouldUseGroupBuff(20.0f)); +} + +TEST_F(GroupBuffTest, FindsClosestMember) +{ + uint32 closest = GetClosestMemberNeedingBuff(40.0f); + EXPECT_EQ(2u, closest); // Mage at 15.0f is closest valid target +} + +/** + * @brief Tests for buff priority and stacking + */ +class BuffPriorityTest : public ::testing::Test +{ +protected: + struct BuffSlot + { + uint32 spellId; + std::string category; // "food", "flask", "elixir_battle", "elixir_guardian" + float effectValue; + bool isActive; + }; + + std::map categoryLimits_; + std::vector buffs_; + + void SetUp() override + { + categoryLimits_ = { + {"food", 1}, + {"flask", 1}, + {"elixir_battle", 1}, + {"elixir_guardian", 1}, + }; + + buffs_ = { + {1, "food", 40.0f, false}, // Food buff + {2, "flask", 125.0f, false}, // Flask + {3, "elixir_battle", 60.0f, false}, // Battle elixir + {4, "elixir_guardian", 50.0f, false}, // Guardian elixir + }; + } + + int GetActiveCountInCategory(std::string const& category) + { + int count = 0; + for (auto const& buff : buffs_) + { + if (buff.category == category && buff.isActive) + count++; + } + return count; + } + + bool CanApplyBuff(std::string const& category) + { + auto limitIt = categoryLimits_.find(category); + if (limitIt == categoryLimits_.end()) + return true; + + return GetActiveCountInCategory(category) < limitIt->second; + } + + bool FlaskExcludesElixirs() + { + // When flask is active, can't use battle/guardian elixirs + for (auto const& buff : buffs_) + { + if (buff.category == "flask" && buff.isActive) + return true; + } + return false; + } + + bool CanUseElixir() + { + return !FlaskExcludesElixirs(); + } + + std::vector GetOptimalConsumables(bool hasFlask) + { + std::vector result; + + if (hasFlask) + { + // Use flask + result.push_back(2); + } + else + { + // Use both elixirs + result.push_back(3); + result.push_back(4); + } + + // Always use food + result.push_back(1); + + return result; + } +}; + +TEST_F(BuffPriorityTest, CategoryLimitEnforced) +{ + EXPECT_TRUE(CanApplyBuff("food")); + + buffs_[0].isActive = true; + EXPECT_FALSE(CanApplyBuff("food")); +} + +TEST_F(BuffPriorityTest, FlaskBlocksElixirs) +{ + EXPECT_TRUE(CanUseElixir()); + + buffs_[1].isActive = true; // Flask active + EXPECT_FALSE(CanUseElixir()); +} + +TEST_F(BuffPriorityTest, OptimalWithFlask) +{ + auto optimal = GetOptimalConsumables(true); + EXPECT_EQ(2u, optimal.size()); // Flask + Food + + bool hasFlask = std::find(optimal.begin(), optimal.end(), 2) != optimal.end(); + bool hasFood = std::find(optimal.begin(), optimal.end(), 1) != optimal.end(); + EXPECT_TRUE(hasFlask); + EXPECT_TRUE(hasFood); +} + +TEST_F(BuffPriorityTest, OptimalWithoutFlask) +{ + auto optimal = GetOptimalConsumables(false); + EXPECT_EQ(3u, optimal.size()); // Both elixirs + Food + + bool hasBattle = std::find(optimal.begin(), optimal.end(), 3) != optimal.end(); + bool hasGuardian = std::find(optimal.begin(), optimal.end(), 4) != optimal.end(); + bool hasFood = std::find(optimal.begin(), optimal.end(), 1) != optimal.end(); + EXPECT_TRUE(hasBattle); + EXPECT_TRUE(hasGuardian); + EXPECT_TRUE(hasFood); +} + +/** + * @brief Tests for debuff tracking + */ +class DebuffTrackingTest : public ::testing::Test +{ +protected: + struct Debuff + { + uint32 spellId; + std::string name; + uint32 remainingMs; + uint32 stackCount; + uint32 maxStacks; + bool isDispellable; + std::string dispelType; // "magic", "poison", "disease", "curse" + }; + + struct DebuffContext + { + std::vector debuffs; + bool canDispelMagic; + bool canDispelPoison; + bool canDispelDisease; + bool canDispelCurse; + }; + + DebuffContext context_; + + void SetUp() override + { + context_.debuffs = { + {1, "Curse of Agony", 20000, 1, 1, true, "curse"}, + {2, "Corruption", 15000, 1, 1, true, "magic"}, + {3, "Deadly Poison", 12000, 3, 5, true, "poison"}, + }; + context_.canDispelMagic = true; + context_.canDispelPoison = false; + context_.canDispelDisease = false; + context_.canDispelCurse = true; + } + + bool CanDispel(Debuff const& debuff) + { + if (!debuff.isDispellable) + return false; + + if (debuff.dispelType == "magic") + return context_.canDispelMagic; + if (debuff.dispelType == "poison") + return context_.canDispelPoison; + if (debuff.dispelType == "disease") + return context_.canDispelDisease; + if (debuff.dispelType == "curse") + return context_.canDispelCurse; + + return false; + } + + std::vector GetDispellableDebuffs() + { + std::vector result; + for (auto const& debuff : context_.debuffs) + { + if (CanDispel(debuff)) + result.push_back(debuff.spellId); + } + return result; + } + + uint32 GetHighestPriorityDispel() + { + Debuff const* best = nullptr; + float bestScore = -1.0f; + + for (auto const& debuff : context_.debuffs) + { + if (!CanDispel(debuff)) + continue; + + // Priority based on remaining time (shorter = more urgent) + // and stack count (more stacks = more urgent) + float score = (1.0f / std::max(1u, debuff.remainingMs)) * 1000000.0f; + score += debuff.stackCount * 10.0f; + + if (score > bestScore) + { + best = &debuff; + bestScore = score; + } + } + + return best ? best->spellId : 0; + } +}; + +TEST_F(DebuffTrackingTest, IdentifiesDispellable) +{ + auto dispellable = GetDispellableDebuffs(); + EXPECT_EQ(2u, dispellable.size()); // Curse and Magic, not Poison +} + +TEST_F(DebuffTrackingTest, CannotDispelWhenMissingAbility) +{ + context_.canDispelCurse = false; + auto dispellable = GetDispellableDebuffs(); + EXPECT_EQ(1u, dispellable.size()); // Only Magic +} + +TEST_F(DebuffTrackingTest, PrioritizesUrgentDebuffs) +{ + // Corruption has shortest duration + uint32 priority = GetHighestPriorityDispel(); + EXPECT_EQ(2u, priority); +} + diff --git a/test/unit/Ai/Combat/CrowdControlTest.cpp b/test/unit/Ai/Combat/CrowdControlTest.cpp new file mode 100644 index 0000000000..2d4117f4f8 --- /dev/null +++ b/test/unit/Ai/Combat/CrowdControlTest.cpp @@ -0,0 +1,434 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include +#include +#include +#include + +/** + * @brief Tests for crowd control management logic + */ + +/** + * @brief Tests for CC target selection + */ +class CcTargetSelectionTest : public ::testing::Test +{ +protected: + struct CcTarget + { + uint32 guid; + std::string creatureType; // "humanoid", "beast", "demon", etc. + bool isElite; + bool isBoss; + float healthPercent; + float distance; + bool alreadyCCd; + bool immune; + }; + + struct CcAbility + { + uint32 spellId; + std::string name; + std::set validCreatureTypes; + float maxRange; + uint32 durationMs; + bool breakOnDamage; + }; + + CcAbility ability_; + std::vector targets_; + + void SetUp() override + { + // Polymorph + ability_ = { + 1, + "Polymorph", + {"humanoid", "beast", "critter"}, + 30.0f, + 50000, + true + }; + + targets_ = { + {1, "humanoid", false, false, 100.0f, 20.0f, false, false}, + {2, "humanoid", true, false, 100.0f, 25.0f, false, false}, + {3, "demon", false, false, 100.0f, 15.0f, false, false}, + {4, "humanoid", false, true, 100.0f, 10.0f, false, false}, // Boss + {5, "humanoid", false, false, 100.0f, 35.0f, false, false}, // Out of range + }; + } + + bool CanCcTarget(CcTarget const& target, CcAbility const& ability) + { + if (target.immune) + return false; + if (target.isBoss) + return false; + if (target.alreadyCCd) + return false; + if (target.distance > ability.maxRange) + return false; + + if (ability.validCreatureTypes.find(target.creatureType) == ability.validCreatureTypes.end()) + return false; + + return true; + } + + std::vector GetValidCcTargets() + { + std::vector result; + for (auto const& target : targets_) + { + if (CanCcTarget(target, ability_)) + result.push_back(target.guid); + } + return result; + } + + uint32 SelectBestCcTarget() + { + CcTarget const* best = nullptr; + float bestScore = -1.0f; + + for (auto const& target : targets_) + { + if (!CanCcTarget(target, ability_)) + continue; + + float score = 100.0f; + + // Prefer elites (more dangerous) + if (target.isElite) + score += 50.0f; + + // Prefer full health (will be active longer) + score += target.healthPercent * 0.5f; + + // Prefer closer targets (faster CC application) + score -= target.distance; + + if (score > bestScore) + { + best = ⌖ + bestScore = score; + } + } + + return best ? best->guid : 0; + } +}; + +TEST_F(CcTargetSelectionTest, ExcludesInvalidCreatureTypes) +{ + auto valid = GetValidCcTargets(); + + bool hasDemon = std::find(valid.begin(), valid.end(), 3) != valid.end(); + EXPECT_FALSE(hasDemon); +} + +TEST_F(CcTargetSelectionTest, ExcludesBosses) +{ + auto valid = GetValidCcTargets(); + + bool hasBoss = std::find(valid.begin(), valid.end(), 4) != valid.end(); + EXPECT_FALSE(hasBoss); +} + +TEST_F(CcTargetSelectionTest, ExcludesOutOfRange) +{ + auto valid = GetValidCcTargets(); + + bool hasFar = std::find(valid.begin(), valid.end(), 5) != valid.end(); + EXPECT_FALSE(hasFar); +} + +TEST_F(CcTargetSelectionTest, IncludesValidTargets) +{ + auto valid = GetValidCcTargets(); + + bool hasNormal = std::find(valid.begin(), valid.end(), 1) != valid.end(); + bool hasElite = std::find(valid.begin(), valid.end(), 2) != valid.end(); + EXPECT_TRUE(hasNormal); + EXPECT_TRUE(hasElite); +} + +TEST_F(CcTargetSelectionTest, PrioritizesElites) +{ + uint32 best = SelectBestCcTarget(); + EXPECT_EQ(2u, best); // Elite humanoid +} + +TEST_F(CcTargetSelectionTest, ExcludesAlreadyCCd) +{ + targets_[1].alreadyCCd = true; // Elite already CC'd + uint32 best = SelectBestCcTarget(); + EXPECT_EQ(1u, best); // Falls back to normal humanoid +} + +/** + * @brief Tests for CC break avoidance + */ +class CcBreakAvoidanceTest : public ::testing::Test +{ +protected: + struct CrowdControlledMob + { + uint32 guid; + std::string ccType; + uint32 remainingMs; + bool breakOnDamage; + bool breakOnMovement; + }; + + struct DamageAction + { + uint32 targetGuid; + bool isAoe; + float aoeRadius; + float aoeX, aoeY; + }; + + struct Position + { + float x, y; + }; + + std::vector ccMobs_; + std::map mobPositions_; + + void SetUp() override + { + ccMobs_ = { + {1, "polymorph", 30000, true, false}, + {2, "sap", 45000, true, true}, + {3, "fear", 8000, true, true}, + }; + + mobPositions_ = { + {1, {10.0f, 10.0f}}, + {2, {20.0f, 10.0f}}, + {3, {30.0f, 10.0f}}, + {100, {15.0f, 10.0f}}, // Non-CC'd target + }; + } + + float Distance(Position const& a, Position const& b) + { + float dx = a.x - b.x; + float dy = a.y - b.y; + return std::sqrt(dx * dx + dy * dy); + } + + bool WouldBreakCc(DamageAction const& action) + { + for (auto const& cc : ccMobs_) + { + if (!cc.breakOnDamage) + continue; + + // Direct damage + if (action.targetGuid == cc.guid) + return true; + + // AoE damage + if (action.isAoe) + { + auto posIt = mobPositions_.find(cc.guid); + if (posIt != mobPositions_.end()) + { + Position aoeCenter{action.aoeX, action.aoeY}; + if (Distance(aoeCenter, posIt->second) <= action.aoeRadius) + return true; + } + } + } + + return false; + } + + bool IsSafeToAoe(float aoeX, float aoeY, float aoeRadius) + { + for (auto const& cc : ccMobs_) + { + if (!cc.breakOnDamage) + continue; + + auto posIt = mobPositions_.find(cc.guid); + if (posIt != mobPositions_.end()) + { + Position aoeCenter{aoeX, aoeY}; + if (Distance(aoeCenter, posIt->second) <= aoeRadius) + return false; + } + } + return true; + } +}; + +TEST_F(CcBreakAvoidanceTest, DirectDamageBreaksCc) +{ + DamageAction direct{1, false, 0.0f, 0.0f, 0.0f}; + EXPECT_TRUE(WouldBreakCc(direct)); +} + +TEST_F(CcBreakAvoidanceTest, DirectDamageOnNonCcIsSafe) +{ + DamageAction direct{100, false, 0.0f, 0.0f, 0.0f}; + EXPECT_FALSE(WouldBreakCc(direct)); +} + +TEST_F(CcBreakAvoidanceTest, AoeDamageCanBreakCc) +{ + // AoE centered at 15, 10 with radius 10 would hit mob at 10, 10 + DamageAction aoe{0, true, 10.0f, 15.0f, 10.0f}; + EXPECT_TRUE(WouldBreakCc(aoe)); +} + +TEST_F(CcBreakAvoidanceTest, AoeAwayFromCcIsSafe) +{ + // AoE centered at 100, 100 - far from all CC'd mobs + DamageAction aoe{0, true, 10.0f, 100.0f, 100.0f}; + EXPECT_FALSE(WouldBreakCc(aoe)); +} + +TEST_F(CcBreakAvoidanceTest, SafeAoeCheck) +{ + EXPECT_FALSE(IsSafeToAoe(15.0f, 10.0f, 10.0f)); // Would hit mob 1 + EXPECT_TRUE(IsSafeToAoe(100.0f, 100.0f, 10.0f)); // Safe location +} + +/** + * @brief Tests for CC duration tracking + */ +class CcDurationTrackingTest : public ::testing::Test +{ +protected: + struct CcInstance + { + uint32 mobGuid; + uint32 spellId; + uint32 appliedAt; + uint32 baseDuration; + float diminishingFactor; + }; + + std::vector activeCC_; + std::map diminishingReturns_; // mobGuid -> DR category count + + void ApplyCc(uint32 mobGuid, uint32 spellId, uint32 duration, uint32 currentTime, int drCategory) + { + // Calculate diminishing returns + float drFactor = 1.0f; + auto drIt = diminishingReturns_.find(mobGuid); + if (drIt != diminishingReturns_.end()) + { + int count = drIt->second; + if (count == 1) + drFactor = 0.5f; + else if (count == 2) + drFactor = 0.25f; + else if (count >= 3) + drFactor = 0.0f; // Immune + } + + if (drFactor > 0.0f) + { + activeCC_.push_back({mobGuid, spellId, currentTime, duration, drFactor}); + } + + // Increment DR + diminishingReturns_[mobGuid]++; + } + + uint32 GetRemainingDuration(uint32 mobGuid, uint32 currentTime) + { + for (auto const& cc : activeCC_) + { + if (cc.mobGuid == mobGuid) + { + uint32 effectiveDuration = static_cast(cc.baseDuration * cc.diminishingFactor); + uint32 endTime = cc.appliedAt + effectiveDuration; + if (currentTime < endTime) + return endTime - currentTime; + } + } + return 0; + } + + bool IsImmune(uint32 mobGuid) + { + auto drIt = diminishingReturns_.find(mobGuid); + if (drIt == diminishingReturns_.end()) + return false; + return drIt->second >= 3; + } + + void ResetDiminishingReturns() + { + diminishingReturns_.clear(); + } +}; + +TEST_F(CcDurationTrackingTest, FirstCcFullDuration) +{ + ApplyCc(1, 100, 10000, 0, 1); + EXPECT_EQ(10000u, GetRemainingDuration(1, 0)); + EXPECT_EQ(5000u, GetRemainingDuration(1, 5000)); +} + +TEST_F(CcDurationTrackingTest, SecondCcHalfDuration) +{ + ApplyCc(1, 100, 10000, 0, 1); + activeCC_.clear(); // Remove first CC + ApplyCc(1, 100, 10000, 0, 1); + EXPECT_EQ(5000u, GetRemainingDuration(1, 0)); // Half duration +} + +TEST_F(CcDurationTrackingTest, ThirdCcQuarterDuration) +{ + ApplyCc(1, 100, 10000, 0, 1); + activeCC_.clear(); + ApplyCc(1, 100, 10000, 0, 1); + activeCC_.clear(); + ApplyCc(1, 100, 10000, 0, 1); + EXPECT_EQ(2500u, GetRemainingDuration(1, 0)); // Quarter duration +} + +TEST_F(CcDurationTrackingTest, FourthCcImmune) +{ + for (int i = 0; i < 3; i++) + { + ApplyCc(1, 100, 10000, 0, 1); + activeCC_.clear(); + } + + EXPECT_TRUE(IsImmune(1)); + + ApplyCc(1, 100, 10000, 0, 1); + EXPECT_EQ(0u, GetRemainingDuration(1, 0)); // Immune +} + +TEST_F(CcDurationTrackingTest, ResetDiminishing) +{ + for (int i = 0; i < 3; i++) + { + ApplyCc(1, 100, 10000, 0, 1); + activeCC_.clear(); + } + + EXPECT_TRUE(IsImmune(1)); + + ResetDiminishingReturns(); + + EXPECT_FALSE(IsImmune(1)); +} + diff --git a/test/unit/Ai/Combat/GroupCoordinationTest.cpp b/test/unit/Ai/Combat/GroupCoordinationTest.cpp new file mode 100644 index 0000000000..9dabd1313d --- /dev/null +++ b/test/unit/Ai/Combat/GroupCoordinationTest.cpp @@ -0,0 +1,495 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include +#include +#include +#include + +/** + * @brief Tests for group coordination logic + */ + +/** + * @brief Tests for tank threat management + */ +class TankThreatTest : public ::testing::Test +{ +protected: + struct GroupMember + { + uint32 guid; + std::string role; // "tank", "healer", "dps" + float threatOnTarget; + bool hasAggro; + }; + + struct ThreatContext + { + std::vector members; + uint32 mainTankGuid; + float threatLeadThreshold; // How much threat lead tank should have + }; + + ThreatContext context_; + + void SetUp() override + { + context_.members = { + {1, "tank", 10000.0f, true}, + {2, "healer", 3000.0f, false}, + {3, "dps", 8000.0f, false}, + {4, "dps", 7500.0f, false}, + }; + context_.mainTankGuid = 1; + context_.threatLeadThreshold = 1.1f; // 10% lead + } + + float GetTankThreat() + { + for (auto const& m : context_.members) + { + if (m.guid == context_.mainTankGuid) + return m.threatOnTarget; + } + return 0.0f; + } + + float GetHighestNonTankThreat() + { + float highest = 0.0f; + for (auto const& m : context_.members) + { + if (m.guid != context_.mainTankGuid) + highest = std::max(highest, m.threatOnTarget); + } + return highest; + } + + bool TankHasThreatLead() + { + float tankThreat = GetTankThreat(); + float otherThreat = GetHighestNonTankThreat(); + return tankThreat >= otherThreat * context_.threatLeadThreshold; + } + + bool ShouldDpsHoldBack(uint32 dpsGuid) + { + float tankThreat = GetTankThreat(); + + for (auto const& m : context_.members) + { + if (m.guid == dpsGuid) + { + // Hold back if within 90% of tank threat + return m.threatOnTarget >= tankThreat * 0.9f; + } + } + return false; + } + + bool ShouldTankTaunt() + { + // Taunt if someone else has aggro + for (auto const& m : context_.members) + { + if (m.guid != context_.mainTankGuid && m.hasAggro) + return true; + } + return false; + } +}; + +TEST_F(TankThreatTest, TankHasInitialThreatLead) +{ + EXPECT_TRUE(TankHasThreatLead()); +} + +TEST_F(TankThreatTest, ThreatLeadLostWhenDpsCloses) +{ + context_.members[2].threatOnTarget = 9500.0f; // DPS at 95% + EXPECT_FALSE(TankHasThreatLead()); +} + +TEST_F(TankThreatTest, DpsHoldsBackNearThreatCap) +{ + context_.members[2].threatOnTarget = 9200.0f; // DPS at 92% + EXPECT_TRUE(ShouldDpsHoldBack(3)); +} + +TEST_F(TankThreatTest, DpsDoesntHoldBackWhenSafe) +{ + context_.members[2].threatOnTarget = 5000.0f; // DPS at 50% + EXPECT_FALSE(ShouldDpsHoldBack(3)); +} + +TEST_F(TankThreatTest, TankTauntsWhenLosingAggro) +{ + context_.members[0].hasAggro = false; + context_.members[2].hasAggro = true; + EXPECT_TRUE(ShouldTankTaunt()); +} + +TEST_F(TankThreatTest, TankDoesntTauntWithAggro) +{ + EXPECT_FALSE(ShouldTankTaunt()); +} + +/** + * @brief Tests for healer triage + */ +class HealerTriageTest : public ::testing::Test +{ +protected: + struct PatientInfo + { + uint32 guid; + std::string role; + float healthPercent; + float incomingDamagePerSecond; + bool hasCriticalDebuff; + int existingHots; + }; + + struct TriageContext + { + std::vector patients; + float healerManaPercent; + bool isCombat; + }; + + TriageContext context_; + + void SetUp() override + { + context_.patients = { + {1, "tank", 60.0f, 5000.0f, false, 0}, + {2, "healer", 80.0f, 1000.0f, false, 0}, + {3, "dps", 40.0f, 2000.0f, false, 0}, + {4, "dps", 90.0f, 500.0f, false, 0}, + }; + context_.healerManaPercent = 80.0f; + context_.isCombat = true; + } + + enum class TriageCategory + { + Critical, // Immediate heal needed + Urgent, // Needs heal soon + Stable, // Can wait + Healthy // No heal needed + }; + + TriageCategory CategorizePatient(PatientInfo const& patient) + { + // Critical: tank below 40% or anyone below 25% + if (patient.role == "tank" && patient.healthPercent < 40.0f) + return TriageCategory::Critical; + if (patient.healthPercent < 25.0f) + return TriageCategory::Critical; + if (patient.hasCriticalDebuff) + return TriageCategory::Critical; + + // Urgent: tank below 60% or anyone below 50% + if (patient.role == "tank" && patient.healthPercent < 60.0f) + return TriageCategory::Urgent; + if (patient.healthPercent < 50.0f) + return TriageCategory::Urgent; + + // Stable: below 80% + if (patient.healthPercent < 80.0f) + return TriageCategory::Stable; + + return TriageCategory::Healthy; + } + + float CalculateHealUrgency(PatientInfo const& patient) + { + float urgency = 100.0f - patient.healthPercent; + + // Role weighting + if (patient.role == "tank") + urgency *= 1.5f; + else if (patient.role == "healer") + urgency *= 1.3f; + + // Incoming damage consideration + float timeToKill = (patient.healthPercent / 100.0f) * 10000.0f / + std::max(1.0f, patient.incomingDamagePerSecond); + if (timeToKill < 3.0f) + urgency *= 2.0f; + + // Already has HoTs + urgency -= patient.existingHots * 10.0f; + + return std::max(0.0f, urgency); + } + + uint32 SelectHealTarget() + { + PatientInfo const* best = nullptr; + float bestUrgency = -1.0f; + + for (auto const& patient : context_.patients) + { + float urgency = CalculateHealUrgency(patient); + if (urgency > bestUrgency) + { + best = &patient; + bestUrgency = urgency; + } + } + + return best ? best->guid : 0; + } +}; + +TEST_F(HealerTriageTest, CategorizeTankLowHealth) +{ + PatientInfo tank{1, "tank", 35.0f, 5000.0f, false, 0}; + EXPECT_EQ(TriageCategory::Critical, CategorizePatient(tank)); +} + +TEST_F(HealerTriageTest, CategorizeDpsCritical) +{ + PatientInfo dps{3, "dps", 20.0f, 2000.0f, false, 0}; + EXPECT_EQ(TriageCategory::Critical, CategorizePatient(dps)); +} + +TEST_F(HealerTriageTest, CategorizeHealthy) +{ + PatientInfo healthy{4, "dps", 95.0f, 100.0f, false, 0}; + EXPECT_EQ(TriageCategory::Healthy, CategorizePatient(healthy)); +} + +TEST_F(HealerTriageTest, CriticalDebuffIsCritical) +{ + PatientInfo debuffed{4, "dps", 80.0f, 100.0f, true, 0}; + EXPECT_EQ(TriageCategory::Critical, CategorizePatient(debuffed)); +} + +TEST_F(HealerTriageTest, TankPrioritized) +{ + // Tank at 60%, DPS at 40% - tank should still be priority + context_.patients[0].healthPercent = 60.0f; + context_.patients[2].healthPercent = 40.0f; + + // Calculate urgencies + float tankUrgency = CalculateHealUrgency(context_.patients[0]); + float dpsUrgency = CalculateHealUrgency(context_.patients[2]); + + // Tank urgency should be comparable despite higher health + // due to role weighting and incoming damage + EXPECT_GT(tankUrgency, 0.0f); +} + +TEST_F(HealerTriageTest, HoTsReduceUrgency) +{ + context_.patients[0].existingHots = 3; + float withHots = CalculateHealUrgency(context_.patients[0]); + + context_.patients[0].existingHots = 0; + float withoutHots = CalculateHealUrgency(context_.patients[0]); + + EXPECT_LT(withHots, withoutHots); +} + +/** + * @brief Tests for DPS assist target + */ +class DpsAssistTest : public ::testing::Test +{ +protected: + struct AssistContext + { + uint32 mainTankTargetGuid; + uint32 assistTargetGuid; + uint32 markedTargetGuid; + std::map targetHealth; // guid -> health% + }; + + AssistContext context_; + + void SetUp() override + { + context_.mainTankTargetGuid = 100; + context_.assistTargetGuid = 0; + context_.markedTargetGuid = 101; + context_.targetHealth = { + {100, 80.0f}, + {101, 90.0f}, + {102, 100.0f}, + }; + } + + uint32 DetermineAssistTarget(bool followMarks, bool focusFire) + { + // Priority 1: Marked target (skull, etc.) + if (followMarks && context_.markedTargetGuid != 0) + { + auto it = context_.targetHealth.find(context_.markedTargetGuid); + if (it != context_.targetHealth.end() && it->second > 0) + return context_.markedTargetGuid; + } + + // Priority 2: Assist target (if set) + if (context_.assistTargetGuid != 0) + { + auto it = context_.targetHealth.find(context_.assistTargetGuid); + if (it != context_.targetHealth.end() && it->second > 0) + return context_.assistTargetGuid; + } + + // Priority 3: Main tank's target + if (context_.mainTankTargetGuid != 0) + return context_.mainTankTargetGuid; + + // Priority 4: Focus fire on lowest health (if enabled) + if (focusFire) + { + uint32 lowestGuid = 0; + float lowestHealth = 101.0f; + for (auto const& [guid, health] : context_.targetHealth) + { + if (health > 0 && health < lowestHealth) + { + lowestHealth = health; + lowestGuid = guid; + } + } + return lowestGuid; + } + + return 0; + } +}; + +TEST_F(DpsAssistTest, FollowsMarkedTarget) +{ + EXPECT_EQ(101u, DetermineAssistTarget(true, false)); +} + +TEST_F(DpsAssistTest, IgnoresMarksWhenDisabled) +{ + EXPECT_EQ(100u, DetermineAssistTarget(false, false)); // Falls to tank target +} + +TEST_F(DpsAssistTest, UsesAssistTarget) +{ + context_.assistTargetGuid = 102; + context_.markedTargetGuid = 0; + EXPECT_EQ(102u, DetermineAssistTarget(true, false)); +} + +TEST_F(DpsAssistTest, FallsBackToTankTarget) +{ + context_.markedTargetGuid = 0; + context_.assistTargetGuid = 0; + EXPECT_EQ(100u, DetermineAssistTarget(true, false)); +} + +TEST_F(DpsAssistTest, FocusFireOnLowest) +{ + context_.markedTargetGuid = 0; + context_.assistTargetGuid = 0; + context_.mainTankTargetGuid = 0; + EXPECT_EQ(100u, DetermineAssistTarget(false, true)); // 80% is lowest +} + +/** + * @brief Tests for group role assignment + */ +class RoleAssignmentTest : public ::testing::Test +{ +protected: + struct GroupMember + { + uint32 guid; + std::string playerClass; + std::string spec; + std::string assignedRole; + }; + + std::vector group_; + + void SetUp() override + { + group_ = { + {1, "Warrior", "Protection", ""}, + {2, "Priest", "Holy", ""}, + {3, "Mage", "Frost", ""}, + {4, "Rogue", "Combat", ""}, + {5, "Druid", "Restoration", ""}, + }; + } + + std::string DetermineRole(std::string const& playerClass, std::string const& spec) + { + // Tank specs + if (playerClass == "Warrior" && spec == "Protection") + return "tank"; + if (playerClass == "Paladin" && spec == "Protection") + return "tank"; + if (playerClass == "Druid" && spec == "Feral") + return "tank"; // Simplified - could be DPS + if (playerClass == "Death Knight" && spec == "Blood") + return "tank"; + + // Healer specs + if (playerClass == "Priest" && (spec == "Holy" || spec == "Discipline")) + return "healer"; + if (playerClass == "Paladin" && spec == "Holy") + return "healer"; + if (playerClass == "Druid" && spec == "Restoration") + return "healer"; + if (playerClass == "Shaman" && spec == "Restoration") + return "healer"; + + // Everything else is DPS + return "dps"; + } + + void AssignRoles() + { + for (auto& member : group_) + { + member.assignedRole = DetermineRole(member.playerClass, member.spec); + } + } + + int CountRole(std::string const& role) + { + int count = 0; + for (auto const& member : group_) + { + if (member.assignedRole == role) + count++; + } + return count; + } +}; + +TEST_F(RoleAssignmentTest, AssignsCorrectRoles) +{ + AssignRoles(); + + EXPECT_EQ("tank", group_[0].assignedRole); // Prot Warrior + EXPECT_EQ("healer", group_[1].assignedRole); // Holy Priest + EXPECT_EQ("dps", group_[2].assignedRole); // Frost Mage + EXPECT_EQ("dps", group_[3].assignedRole); // Combat Rogue + EXPECT_EQ("healer", group_[4].assignedRole); // Resto Druid +} + +TEST_F(RoleAssignmentTest, CountsRolesCorrectly) +{ + AssignRoles(); + + EXPECT_EQ(1, CountRole("tank")); + EXPECT_EQ(2, CountRole("healer")); + EXPECT_EQ(2, CountRole("dps")); +} + diff --git a/test/unit/Ai/Combat/SpellRotationTest.cpp b/test/unit/Ai/Combat/SpellRotationTest.cpp new file mode 100644 index 0000000000..9176af02d1 --- /dev/null +++ b/test/unit/Ai/Combat/SpellRotationTest.cpp @@ -0,0 +1,477 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include +#include +#include +#include + +/** + * @brief Tests for spell rotation and selection logic + */ + +/** + * @brief Tests for basic spell selection + */ +class SpellSelectionTest : public ::testing::Test +{ +protected: + struct SpellInfo + { + uint32 id; + std::string name; + uint32 manaCost; + uint32 cooldown; // ms + uint32 castTime; // ms + float minRange; + float maxRange; + float damage; + bool requiresBehind; + bool isAoe; + int comboPointCost; + int comboPointsGenerated; + }; + + struct SpellState + { + uint32 cooldownRemaining; + bool isUsable; + }; + + struct CastContext + { + float currentMana; + float maxMana; + float targetDistance; + bool isBehindTarget; + int comboPoints; + bool targetIsMoving; + int enemyCount; + }; + + std::map spells_; + std::map spellStates_; + CastContext context_; + + void SetUp() override + { + // Define test spells + spells_[1] = {1, "Sinister Strike", 45, 0, 0, 0, 5, 100, false, false, 0, 1}; + spells_[2] = {2, "Backstab", 60, 0, 0, 0, 5, 200, true, false, 0, 1}; + spells_[3] = {3, "Eviscerate", 35, 0, 0, 0, 5, 500, false, false, 5, 0}; + spells_[4] = {4, "Fan of Knives", 50, 10000, 0, 0, 10, 150, false, true, 0, 0}; + spells_[5] = {5, "Ambush", 60, 0, 0, 0, 5, 300, true, false, 0, 2}; + + // All spells ready by default + for (auto const& [id, _] : spells_) + { + spellStates_[id] = {0, true}; + } + + context_ = {100.0f, 100.0f, 3.0f, false, 0, false, 1}; + } + + bool CanCastSpell(uint32 spellId) + { + auto spellIt = spells_.find(spellId); + if (spellIt == spells_.end()) + return false; + + auto stateIt = spellStates_.find(spellId); + if (stateIt == spellStates_.end()) + return false; + + SpellInfo const& spell = spellIt->second; + SpellState const& state = stateIt->second; + + // Cooldown check + if (state.cooldownRemaining > 0) + return false; + + // Mana check + if (context_.currentMana < spell.manaCost) + return false; + + // Range check + if (context_.targetDistance < spell.minRange || context_.targetDistance > spell.maxRange) + return false; + + // Position check + if (spell.requiresBehind && !context_.isBehindTarget) + return false; + + // Combo point check + if (spell.comboPointCost > context_.comboPoints) + return false; + + // Moving target + cast time + if (context_.targetIsMoving && spell.castTime > 0) + return false; + + return state.isUsable; + } + + std::vector GetUsableSpells() + { + std::vector result; + for (auto const& [id, _] : spells_) + { + if (CanCastSpell(id)) + result.push_back(id); + } + return result; + } +}; + +TEST_F(SpellSelectionTest, BasicSpellAvailable) +{ + EXPECT_TRUE(CanCastSpell(1)); // Sinister Strike +} + +TEST_F(SpellSelectionTest, OnCooldownNotAvailable) +{ + spellStates_[1].cooldownRemaining = 5000; + EXPECT_FALSE(CanCastSpell(1)); +} + +TEST_F(SpellSelectionTest, NotEnoughMana) +{ + context_.currentMana = 30.0f; + EXPECT_FALSE(CanCastSpell(1)); // Costs 45 +} + +TEST_F(SpellSelectionTest, OutOfRange) +{ + context_.targetDistance = 10.0f; + EXPECT_FALSE(CanCastSpell(1)); // Max range 5 +} + +TEST_F(SpellSelectionTest, RequiresBehind) +{ + EXPECT_FALSE(CanCastSpell(2)); // Backstab requires behind + + context_.isBehindTarget = true; + EXPECT_TRUE(CanCastSpell(2)); +} + +TEST_F(SpellSelectionTest, ComboPointRequirement) +{ + EXPECT_FALSE(CanCastSpell(3)); // Eviscerate needs 5 CP + + context_.comboPoints = 5; + EXPECT_TRUE(CanCastSpell(3)); +} + +/** + * @brief Tests for rotation priority + */ +class RotationPriorityTest : public ::testing::Test +{ +protected: + struct RotationSpell + { + uint32 id; + std::string name; + float basePriority; + bool isFinisher; + bool isBuilder; + bool isCooldown; + bool isMaintenance; // DoT/Buff that needs to be kept up + }; + + struct RotationContext + { + int comboPoints; + bool hasMaintenanceBuff; + bool targetHasDot; + float targetHealthPercent; + bool cooldownsActive; + }; + + std::vector rotation_; + RotationContext context_; + + void SetUp() override + { + rotation_ = { + {1, "Slice and Dice", 100.0f, false, false, false, true}, + {2, "Rupture", 85.0f, true, false, false, true}, // DoT finisher, slightly lower than damage finisher + {3, "Eviscerate", 90.0f, true, false, false, false}, // Damage finisher, higher base + {4, "Mutilate", 50.0f, false, true, false, false}, + {5, "Cold Blood", 70.0f, false, false, true, false}, // Base priority low, cooldown bonus makes it high + }; + + context_ = {0, true, true, 100.0f, false}; + } + + float CalculateSpellPriority(RotationSpell const& spell) + { + float priority = spell.basePriority; + + // Maintenance buffs highest priority when missing + if (spell.isMaintenance) + { + if (spell.name == "Slice and Dice" && !context_.hasMaintenanceBuff) + priority += 150.0f; + if (spell.name == "Rupture" && !context_.targetHasDot) + priority += 100.0f; + } + + // Finishers at max combo points + if (spell.isFinisher && context_.comboPoints >= 5) + priority += 50.0f; + + // Builders when low on combo points + if (spell.isBuilder && context_.comboPoints < 5) + priority += 60.0f; + + // Cooldowns during burn phases + if (spell.isCooldown && !context_.cooldownsActive) + priority += 100.0f; + + // Execute phase bonus for damage finishers + if (spell.name == "Eviscerate" && context_.targetHealthPercent <= 35.0f) + priority += 50.0f; + + return priority; + } + + std::string GetNextSpell() + { + RotationSpell const* best = nullptr; + float bestPriority = -1.0f; + + for (auto const& spell : rotation_) + { + float priority = CalculateSpellPriority(spell); + if (priority > bestPriority) + { + best = &spell; + bestPriority = priority; + } + } + + return best ? best->name : ""; + } +}; + +TEST_F(RotationPriorityTest, MissingBuffHighPriority) +{ + context_.hasMaintenanceBuff = false; + context_.comboPoints = 5; + EXPECT_EQ("Slice and Dice", GetNextSpell()); +} + +TEST_F(RotationPriorityTest, MissingDotHighPriority) +{ + context_.targetHasDot = false; + context_.comboPoints = 5; + EXPECT_EQ("Rupture", GetNextSpell()); +} + +TEST_F(RotationPriorityTest, CooldownsWhenAvailable) +{ + context_.cooldownsActive = false; + EXPECT_EQ("Cold Blood", GetNextSpell()); +} + +TEST_F(RotationPriorityTest, BuilderWhenLowComboPoints) +{ + context_.cooldownsActive = true; // No CD priority + context_.comboPoints = 2; + EXPECT_EQ("Mutilate", GetNextSpell()); +} + +TEST_F(RotationPriorityTest, FinisherAtMaxComboPoints) +{ + context_.cooldownsActive = true; + context_.comboPoints = 5; + EXPECT_EQ("Eviscerate", GetNextSpell()); +} + +TEST_F(RotationPriorityTest, ExecutePhaseBonus) +{ + context_.cooldownsActive = true; + context_.comboPoints = 5; + context_.targetHealthPercent = 20.0f; + // Eviscerate gets execute bonus + EXPECT_EQ("Eviscerate", GetNextSpell()); +} + +/** + * @brief Tests for GCD and cast timing + */ +class SpellTimingTest : public ::testing::Test +{ +protected: + struct SpellTiming + { + uint32 gcdDuration; + uint32 castTime; + uint32 channelDuration; + bool isInstant; + }; + + struct TimingState + { + uint32 gcdEndTime; + uint32 castEndTime; + uint32 channelEndTime; + }; + + TimingState state_; + + void SetUp() override + { + state_ = {0, 0, 0}; + } + + bool CanStartCast(uint32 currentTime) + { + if (currentTime < state_.gcdEndTime) + return false; + if (currentTime < state_.castEndTime) + return false; + if (currentTime < state_.channelEndTime) + return false; + return true; + } + + void StartCast(uint32 currentTime, SpellTiming const& spell) + { + state_.gcdEndTime = currentTime + spell.gcdDuration; + + if (spell.channelDuration > 0) + state_.channelEndTime = currentTime + spell.channelDuration; + else if (!spell.isInstant) + state_.castEndTime = currentTime + spell.castTime; + } + + uint32 GetNextActionTime() + { + return std::max({state_.gcdEndTime, state_.castEndTime, state_.channelEndTime}); + } +}; + +TEST_F(SpellTimingTest, CanCastInitially) +{ + EXPECT_TRUE(CanStartCast(0)); +} + +TEST_F(SpellTimingTest, GcdPreventsNextCast) +{ + SpellTiming instant{1500, 0, 0, true}; + StartCast(0, instant); + + EXPECT_FALSE(CanStartCast(500)); + EXPECT_FALSE(CanStartCast(1000)); + EXPECT_TRUE(CanStartCast(1500)); +} + +TEST_F(SpellTimingTest, CastTimePreventsNextCast) +{ + SpellTiming hardCast{1500, 2500, 0, false}; + StartCast(0, hardCast); + + EXPECT_FALSE(CanStartCast(2000)); // Still casting + EXPECT_TRUE(CanStartCast(2500)); // Cast finished +} + +TEST_F(SpellTimingTest, ChannelPreventsNextCast) +{ + SpellTiming channel{1500, 0, 3000, false}; + StartCast(0, channel); + + EXPECT_FALSE(CanStartCast(2000)); // Still channeling + EXPECT_TRUE(CanStartCast(3000)); // Channel finished +} + +TEST_F(SpellTimingTest, NextActionTime) +{ + SpellTiming hardCast{1500, 2500, 0, false}; + StartCast(0, hardCast); + + EXPECT_EQ(2500u, GetNextActionTime()); // Cast > GCD +} + +/** + * @brief Tests for proc-based rotation adjustments + */ +class ProcHandlingTest : public ::testing::Test +{ +protected: + struct Proc + { + std::string name; + uint32 spellToUse; + float priorityBonus; + uint32 duration; + uint32 activatedAt; + }; + + std::vector activeProcs_; + + void ActivateProc(std::string const& name, uint32 spellId, float bonus, uint32 duration, uint32 currentTime) + { + activeProcs_.push_back({name, spellId, bonus, duration, currentTime}); + } + + void ExpireProcs(uint32 currentTime) + { + activeProcs_.erase( + std::remove_if(activeProcs_.begin(), activeProcs_.end(), + [currentTime](Proc const& p) { + return currentTime >= p.activatedAt + p.duration; + }), + activeProcs_.end()); + } + + bool HasProc(std::string const& name) + { + return std::any_of(activeProcs_.begin(), activeProcs_.end(), + [&name](Proc const& p) { return p.name == name; }); + } + + float GetProcBonusForSpell(uint32 spellId) + { + float bonus = 0.0f; + for (auto const& proc : activeProcs_) + { + if (proc.spellToUse == spellId) + bonus += proc.priorityBonus; + } + return bonus; + } +}; + +TEST_F(ProcHandlingTest, ProcActivates) +{ + ActivateProc("Hot Streak", 1, 100.0f, 5000, 0); + EXPECT_TRUE(HasProc("Hot Streak")); +} + +TEST_F(ProcHandlingTest, ProcExpires) +{ + ActivateProc("Hot Streak", 1, 100.0f, 5000, 0); + ExpireProcs(6000); + EXPECT_FALSE(HasProc("Hot Streak")); +} + +TEST_F(ProcHandlingTest, ProcBoostsPriority) +{ + ActivateProc("Hot Streak", 1, 100.0f, 5000, 0); + EXPECT_FLOAT_EQ(100.0f, GetProcBonusForSpell(1)); + EXPECT_FLOAT_EQ(0.0f, GetProcBonusForSpell(2)); +} + +TEST_F(ProcHandlingTest, MultipleProcsStack) +{ + ActivateProc("Proc1", 1, 50.0f, 5000, 0); + ActivateProc("Proc2", 1, 75.0f, 5000, 0); + EXPECT_FLOAT_EQ(125.0f, GetProcBonusForSpell(1)); +} + diff --git a/test/unit/Ai/Combat/TargetSelectionTest.cpp b/test/unit/Ai/Combat/TargetSelectionTest.cpp new file mode 100644 index 0000000000..61e783818b --- /dev/null +++ b/test/unit/Ai/Combat/TargetSelectionTest.cpp @@ -0,0 +1,473 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include +#include +#include +#include + +/** + * @brief Tests for target selection and prioritization logic + */ + +/** + * @brief Tests for enemy target prioritization + */ +class EnemyPrioritizationTest : public ::testing::Test +{ +protected: + struct EnemyTarget + { + uint32 guid; + std::string name; + float healthPercent; + float distance; + bool isCasting; + bool isElite; + bool isBoss; + bool hasAggro; + float threatToTank; + bool isCrowdControlled; + int debuffCount; + }; + + struct TargetContext + { + bool isTank; + bool isInterrupter; + uint32 currentTargetGuid; + }; + + TargetContext context_; + + void SetUp() override + { + context_ = {false, false, 0}; + } + + /** + * @brief Calculate target priority score + */ + float CalculatePriority(EnemyTarget const& target, TargetContext const& ctx) + { + if (target.isCrowdControlled) + return -1000.0f; // Never attack CC'd targets + + float score = 100.0f; + + // Current target bonus (reduce target switching) + if (target.guid == ctx.currentTargetGuid) + score += 20.0f; + + // Health-based priority (lower health = higher priority for focus fire) + score += (100.0f - target.healthPercent) * 0.5f; + + // Distance penalty + score -= target.distance * 0.5f; + + // Boss/Elite priority + if (target.isBoss) + score += 50.0f; + else if (target.isElite) + score += 25.0f; + + // Interrupt priority + if (target.isCasting && ctx.isInterrupter) + score += 100.0f; + + // Tank threat considerations + if (ctx.isTank) + { + // Prioritize loose mobs (not on tank) + if (!target.hasAggro) + score += 75.0f; + } + else + { + // DPS should attack tank's target + if (target.hasAggro) + score += 30.0f; + } + + // Debuff stacking bonus + score += target.debuffCount * 5.0f; + + return score; + } + + /** + * @brief Select best target from list + */ + uint32 SelectBestTarget(std::vector const& enemies) + { + EnemyTarget const* best = nullptr; + float bestScore = -9999.0f; + + for (auto const& enemy : enemies) + { + float score = CalculatePriority(enemy, context_); + if (score > bestScore) + { + best = &enemy; + bestScore = score; + } + } + + return best ? best->guid : 0; + } +}; + +TEST_F(EnemyPrioritizationTest, AvoidsCrowdControlled) +{ + std::vector enemies = { + {1, "mob1", 50.0f, 10.0f, false, false, false, true, 100.0f, true, 0}, // CC'd + {2, "mob2", 100.0f, 20.0f, false, false, false, true, 100.0f, false, 0}, // Not CC'd + }; + + EXPECT_EQ(2u, SelectBestTarget(enemies)); +} + +TEST_F(EnemyPrioritizationTest, PrioritizesLowHealth) +{ + std::vector enemies = { + {1, "mob1", 80.0f, 10.0f, false, false, false, true, 100.0f, false, 0}, + {2, "mob2", 20.0f, 10.0f, false, false, false, true, 100.0f, false, 0}, + }; + + EXPECT_EQ(2u, SelectBestTarget(enemies)); +} + +TEST_F(EnemyPrioritizationTest, PrioritizesBoss) +{ + std::vector enemies = { + {1, "mob", 50.0f, 10.0f, false, false, false, true, 100.0f, false, 0}, + {2, "boss", 100.0f, 10.0f, false, false, true, true, 100.0f, false, 0}, + }; + + EXPECT_EQ(2u, SelectBestTarget(enemies)); +} + +TEST_F(EnemyPrioritizationTest, InterrupterPrioritizesCaster) +{ + context_.isInterrupter = true; + + std::vector enemies = { + {1, "melee", 30.0f, 10.0f, false, false, false, true, 100.0f, false, 0}, + {2, "caster", 80.0f, 10.0f, true, false, false, true, 100.0f, false, 0}, + }; + + EXPECT_EQ(2u, SelectBestTarget(enemies)); +} + +TEST_F(EnemyPrioritizationTest, TankPrioritizesLooseMobs) +{ + context_.isTank = true; + + std::vector enemies = { + {1, "controlled", 50.0f, 10.0f, false, false, false, true, 100.0f, false, 0}, + {2, "loose", 50.0f, 10.0f, false, false, false, false, 0.0f, false, 0}, + }; + + EXPECT_EQ(2u, SelectBestTarget(enemies)); +} + +TEST_F(EnemyPrioritizationTest, DpsPrioritizesTankTarget) +{ + context_.isTank = false; + + std::vector enemies = { + {1, "tanked", 80.0f, 10.0f, false, false, false, true, 100.0f, false, 0}, + {2, "loose", 50.0f, 10.0f, false, false, false, false, 0.0f, false, 0}, + }; + + EXPECT_EQ(1u, SelectBestTarget(enemies)); +} + +TEST_F(EnemyPrioritizationTest, CurrentTargetBonus) +{ + context_.currentTargetGuid = 1; + + std::vector enemies = { + {1, "current", 70.0f, 10.0f, false, false, false, true, 100.0f, false, 0}, + {2, "other", 65.0f, 10.0f, false, false, false, true, 100.0f, false, 0}, + }; + + // Current target slightly higher health but should still be selected + EXPECT_EQ(1u, SelectBestTarget(enemies)); +} + +/** + * @brief Tests for healing target selection + */ +class HealTargetSelectionTest : public ::testing::Test +{ +protected: + struct HealTarget + { + uint32 guid; + std::string name; + float healthPercent; + float distance; + bool isTank; + bool isHealer; + bool isDps; + bool hasCriticalDebuff; + bool isInLineOfSight; + int incomingHeals; + }; + + /** + * @brief Calculate heal priority + */ + float CalculateHealPriority(HealTarget const& target, float maxRange) + { + if (!target.isInLineOfSight) + return -1000.0f; + + if (target.distance > maxRange) + return -1000.0f; + + float score = 100.0f; + + // Health deficit is primary factor + float healthDeficit = 100.0f - target.healthPercent; + score += healthDeficit * 2.0f; + + // Role priority: Tank > Healer > DPS + if (target.isTank) + score += 50.0f; + else if (target.isHealer) + score += 30.0f; + + // Critical debuff bonus + if (target.hasCriticalDebuff) + score += 40.0f; + + // Incoming heals penalty (avoid overhealing) + score -= target.incomingHeals * 10.0f; + + // Distance penalty (prefer closer targets) + score -= target.distance * 0.5f; + + return score; + } + + uint32 SelectHealTarget(std::vector const& targets, float maxRange) + { + HealTarget const* best = nullptr; + float bestScore = -9999.0f; + + for (auto const& target : targets) + { + float score = CalculateHealPriority(target, maxRange); + if (score > bestScore) + { + best = ⌖ + bestScore = score; + } + } + + return best ? best->guid : 0; + } +}; + +TEST_F(HealTargetSelectionTest, PrioritizesLowHealth) +{ + std::vector targets = { + {1, "healthy", 90.0f, 10.0f, false, false, true, false, true, 0}, + {2, "injured", 30.0f, 10.0f, false, false, true, false, true, 0}, + }; + + EXPECT_EQ(2u, SelectHealTarget(targets, 40.0f)); +} + +TEST_F(HealTargetSelectionTest, PrioritizesTank) +{ + std::vector targets = { + {1, "dps", 50.0f, 10.0f, false, false, true, false, true, 0}, + {2, "tank", 60.0f, 10.0f, true, false, false, false, true, 0}, + }; + + EXPECT_EQ(2u, SelectHealTarget(targets, 40.0f)); +} + +TEST_F(HealTargetSelectionTest, SkipsOutOfRange) +{ + std::vector targets = { + {1, "far", 10.0f, 50.0f, false, false, true, false, true, 0}, + {2, "close", 80.0f, 10.0f, false, false, true, false, true, 0}, + }; + + EXPECT_EQ(2u, SelectHealTarget(targets, 40.0f)); +} + +TEST_F(HealTargetSelectionTest, SkipsOutOfSight) +{ + std::vector targets = { + {1, "hidden", 10.0f, 10.0f, false, false, true, false, false, 0}, + {2, "visible", 80.0f, 10.0f, false, false, true, false, true, 0}, + }; + + EXPECT_EQ(2u, SelectHealTarget(targets, 40.0f)); +} + +TEST_F(HealTargetSelectionTest, AccountsForIncomingHeals) +{ + std::vector targets = { + {1, "being_healed", 40.0f, 10.0f, false, false, true, false, true, 3}, + {2, "not_healed", 50.0f, 10.0f, false, false, true, false, true, 0}, + }; + + EXPECT_EQ(2u, SelectHealTarget(targets, 40.0f)); +} + +TEST_F(HealTargetSelectionTest, CriticalDebuffBonus) +{ + std::vector targets = { + {1, "normal", 50.0f, 10.0f, false, false, true, false, true, 0}, + {2, "debuffed", 60.0f, 10.0f, false, false, true, true, true, 0}, + }; + + EXPECT_EQ(2u, SelectHealTarget(targets, 40.0f)); +} + +/** + * @brief Tests for AoE target selection + */ +class AoeTargetSelectionTest : public ::testing::Test +{ +protected: + struct Position + { + float x, y; + }; + + struct AoeTarget + { + uint32 guid; + Position pos; + bool isCrowdControlled; + }; + + float Distance(Position const& a, Position const& b) + { + float dx = a.x - b.x; + float dy = a.y - b.y; + return std::sqrt(dx * dx + dy * dy); + } + + /** + * @brief Count targets that would be hit by AoE at position + */ + int CountTargetsInAoe(std::vector const& targets, Position const& center, float radius) + { + int count = 0; + for (auto const& target : targets) + { + if (target.isCrowdControlled) + continue; + if (Distance(center, target.pos) <= radius) + count++; + } + return count; + } + + /** + * @brief Find best AoE center position + */ + Position FindBestAoePosition(std::vector const& targets, float radius) + { + Position bestPos{0.0f, 0.0f}; + int bestCount = 0; + + // Simple approach: check each target as potential center + for (auto const& target : targets) + { + if (target.isCrowdControlled) + continue; + + int count = CountTargetsInAoe(targets, target.pos, radius); + if (count > bestCount) + { + bestCount = count; + bestPos = target.pos; + } + } + + return bestPos; + } + + /** + * @brief Check if AoE is worth using + */ + bool ShouldUseAoe(std::vector const& targets, float radius, int minTargets) + { + // Find max targets possible + int maxTargets = 0; + for (auto const& target : targets) + { + if (target.isCrowdControlled) + continue; + + int count = CountTargetsInAoe(targets, target.pos, radius); + maxTargets = std::max(maxTargets, count); + } + + return maxTargets >= minTargets; + } +}; + +TEST_F(AoeTargetSelectionTest, CountsTargetsInRadius) +{ + std::vector targets = { + {1, {0.0f, 0.0f}, false}, + {2, {5.0f, 0.0f}, false}, + {3, {20.0f, 0.0f}, false}, + }; + + EXPECT_EQ(2, CountTargetsInAoe(targets, {0.0f, 0.0f}, 10.0f)); +} + +TEST_F(AoeTargetSelectionTest, ExcludesCrowdControlled) +{ + std::vector targets = { + {1, {0.0f, 0.0f}, false}, + {2, {5.0f, 0.0f}, true}, // CC'd + {3, {3.0f, 0.0f}, false}, + }; + + EXPECT_EQ(2, CountTargetsInAoe(targets, {0.0f, 0.0f}, 10.0f)); +} + +TEST_F(AoeTargetSelectionTest, FindsBestCenter) +{ + std::vector targets = { + {1, {0.0f, 0.0f}, false}, + {2, {5.0f, 5.0f}, false}, + {3, {6.0f, 5.0f}, false}, + {4, {5.0f, 6.0f}, false}, + {5, {50.0f, 50.0f}, false}, + }; + + Position best = FindBestAoePosition(targets, 5.0f); + // Best position should be near the cluster of 3 + EXPECT_GT(best.x, 3.0f); + EXPECT_LT(best.x, 8.0f); +} + +TEST_F(AoeTargetSelectionTest, AoeThresholdMet) +{ + std::vector targets = { + {1, {0.0f, 0.0f}, false}, + {2, {5.0f, 0.0f}, false}, + {3, {3.0f, 0.0f}, false}, + }; + + EXPECT_TRUE(ShouldUseAoe(targets, 10.0f, 3)); + EXPECT_FALSE(ShouldUseAoe(targets, 10.0f, 4)); +} + diff --git a/test/unit/Ai/Dungeon/DungeonTacticsTest.cpp b/test/unit/Ai/Dungeon/DungeonTacticsTest.cpp new file mode 100644 index 0000000000..bf5f07ecc5 --- /dev/null +++ b/test/unit/Ai/Dungeon/DungeonTacticsTest.cpp @@ -0,0 +1,520 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief Tests for dungeon and raid tactics logic + */ + +/** + * @brief Tests for boss mechanic detection + */ +class BossMechanicTest : public ::testing::Test +{ +protected: + enum class MechanicType + { + GroundAoe, + RaidWideAoe, + Cleave, + Charge, + Fear, + Enrage, + Interrupt, + SpreadDebuff, + StackDebuff + }; + + struct BossMechanic + { + MechanicType type; + uint32 spellId; + std::string name; + uint32 castTimeMs; + bool isCastable; + bool isInterruptable; + }; + + struct BossContext + { + std::string bossName; + uint32 currentCastSpellId; + float castProgress; // 0.0 to 1.0 + std::vector mechanics; + float bossHealthPercent; + std::set activeMechanics; + }; + + BossContext context_; + + void SetUp() override + { + context_ = { + "Test Boss", 0, 0.0f, + { + {MechanicType::GroundAoe, 1, "Fire Bomb", 2000, true, false}, + {MechanicType::Interrupt, 2, "Shadow Bolt Volley", 3000, true, true}, + {MechanicType::Cleave, 3, "Cleave", 0, false, false}, + {MechanicType::Enrage, 4, "Enrage", 0, true, false}, + }, + 100.0f, + {} + }; + } + + BossMechanic const* GetCurrentCastMechanic() + { + for (auto const& m : context_.mechanics) + { + if (m.spellId == context_.currentCastSpellId) + return &m; + } + return nullptr; + } + + bool ShouldInterrupt() + { + auto* mechanic = GetCurrentCastMechanic(); + if (!mechanic) + return false; + + return mechanic->isInterruptable && context_.castProgress < 0.9f; + } + + bool ShouldMoveFromCleave() + { + return context_.activeMechanics.find(MechanicType::Cleave) != context_.activeMechanics.end(); + } + + bool IsBossEnraging() + { + // Check for enrage at low health + if (context_.bossHealthPercent < 30.0f) + { + for (auto const& m : context_.mechanics) + { + if (m.type == MechanicType::Enrage) + return true; + } + } + return context_.activeMechanics.find(MechanicType::Enrage) != context_.activeMechanics.end(); + } +}; + +TEST_F(BossMechanicTest, DetectsInterruptableSpell) +{ + context_.currentCastSpellId = 2; // Shadow Bolt Volley + context_.castProgress = 0.5f; + + EXPECT_TRUE(ShouldInterrupt()); +} + +TEST_F(BossMechanicTest, DoesNotInterruptNonInterruptable) +{ + context_.currentCastSpellId = 1; // Fire Bomb (not interruptable) + context_.castProgress = 0.5f; + + EXPECT_FALSE(ShouldInterrupt()); +} + +TEST_F(BossMechanicTest, DoesNotInterruptTooLate) +{ + context_.currentCastSpellId = 2; + context_.castProgress = 0.95f; // Almost done casting + + EXPECT_FALSE(ShouldInterrupt()); +} + +TEST_F(BossMechanicTest, DetectsCleave) +{ + context_.activeMechanics.insert(MechanicType::Cleave); + EXPECT_TRUE(ShouldMoveFromCleave()); +} + +TEST_F(BossMechanicTest, DetectsEnrage) +{ + context_.bossHealthPercent = 25.0f; + EXPECT_TRUE(IsBossEnraging()); +} + +/** + * @brief Tests for boss phase transitions + */ +class BossPhaseTest : public ::testing::Test +{ +protected: + struct BossPhase + { + int phaseNumber; + float healthThreshold; // Phase changes at this health % + std::vector abilities; + std::string positioningStrategy; + }; + + struct BossEncounter + { + std::string name; + std::vector phases; + float currentHealth; + int currentPhase; + }; + + BossEncounter encounter_; + + void SetUp() override + { + encounter_ = { + "Multi-Phase Boss", + { + {1, 100.0f, {"Basic Attack", "Cleave"}, "stack"}, + {2, 70.0f, {"Fire Phase", "AoE"}, "spread"}, + {3, 35.0f, {"Enrage", "All Abilities"}, "kite"}, + }, + 100.0f, + 1 + }; + } + + int DeterminePhase() + { + for (int i = encounter_.phases.size() - 1; i >= 0; i--) + { + if (encounter_.currentHealth <= encounter_.phases[i].healthThreshold) + return encounter_.phases[i].phaseNumber; + } + return 1; + } + + bool IsPhaseTransition() + { + int newPhase = DeterminePhase(); + return newPhase != encounter_.currentPhase; + } + + std::string GetPositioningStrategy() + { + int phase = DeterminePhase(); + for (auto const& p : encounter_.phases) + { + if (p.phaseNumber == phase) + return p.positioningStrategy; + } + return "default"; + } + + std::vector GetActiveAbilities() + { + int phase = DeterminePhase(); + for (auto const& p : encounter_.phases) + { + if (p.phaseNumber == phase) + return p.abilities; + } + return {}; + } +}; + +TEST_F(BossPhaseTest, StartsInPhase1) +{ + EXPECT_EQ(1, DeterminePhase()); +} + +TEST_F(BossPhaseTest, TransitionsToPhase2) +{ + encounter_.currentHealth = 65.0f; + EXPECT_EQ(2, DeterminePhase()); +} + +TEST_F(BossPhaseTest, TransitionsToPhase3) +{ + encounter_.currentHealth = 30.0f; + EXPECT_EQ(3, DeterminePhase()); +} + +TEST_F(BossPhaseTest, DetectsPhaseTransition) +{ + encounter_.currentPhase = 1; + encounter_.currentHealth = 65.0f; + EXPECT_TRUE(IsPhaseTransition()); +} + +TEST_F(BossPhaseTest, NoTransitionWhenSamePhase) +{ + encounter_.currentPhase = 1; + encounter_.currentHealth = 80.0f; + EXPECT_FALSE(IsPhaseTransition()); +} + +TEST_F(BossPhaseTest, PositioningChangesWithPhase) +{ + encounter_.currentHealth = 100.0f; + EXPECT_EQ("stack", GetPositioningStrategy()); + + encounter_.currentHealth = 65.0f; + EXPECT_EQ("spread", GetPositioningStrategy()); + + encounter_.currentHealth = 30.0f; + EXPECT_EQ("kite", GetPositioningStrategy()); +} + +/** + * @brief Tests for dungeon pull management + */ +class DungeonPullTest : public ::testing::Test +{ +protected: + struct MobPack + { + uint32 packId; + int mobCount; + bool hasPatrol; + bool hasCaster; + bool hasHealer; + std::vector linkedPacks; // Packs that will aggro together + }; + + struct PullContext + { + std::vector packs; + uint32 currentPackId; + int maxMobsPerPull; + bool hasCrowdControl; + }; + + PullContext context_; + + void SetUp() override + { + context_ = { + { + {1, 3, false, true, false, {}}, + {2, 4, true, false, true, {3}}, + {3, 2, false, false, false, {2}}, + {4, 5, false, true, true, {}}, + }, + 0, + 4, + true + }; + } + + int GetTotalMobsInPull(uint32 packId) + { + int total = 0; + std::set processedPacks; + + std::vector toProcess = {packId}; + while (!toProcess.empty()) + { + uint32 current = toProcess.back(); + toProcess.pop_back(); + + if (processedPacks.find(current) != processedPacks.end()) + continue; + processedPacks.insert(current); + + for (auto const& pack : context_.packs) + { + if (pack.packId == current) + { + total += pack.mobCount; + for (uint32 linked : pack.linkedPacks) + toProcess.push_back(linked); + break; + } + } + } + + return total; + } + + bool IsSafePull(uint32 packId) + { + return GetTotalMobsInPull(packId) <= context_.maxMobsPerPull; + } + + uint32 GetPriorityTarget(uint32 packId) + { + for (auto const& pack : context_.packs) + { + if (pack.packId == packId) + { + // Priority: Healer > Caster > others + if (pack.hasHealer) + return 1; // Healer target + if (pack.hasCaster) + return 2; // Caster target + return 0; // No priority + } + } + return 0; + } + + bool ShouldWaitForPatrol(uint32 packId) + { + for (auto const& pack : context_.packs) + { + if (pack.packId == packId) + return pack.hasPatrol; + } + return false; + } +}; + +TEST_F(DungeonPullTest, CountsMobsInPack) +{ + EXPECT_EQ(3, GetTotalMobsInPull(1)); +} + +TEST_F(DungeonPullTest, CountsLinkedPacks) +{ + // Pack 2 (4 mobs) + Pack 3 (2 mobs) = 6 + EXPECT_EQ(6, GetTotalMobsInPull(2)); +} + +TEST_F(DungeonPullTest, SafePullCheck) +{ + EXPECT_TRUE(IsSafePull(1)); // 3 mobs < 4 max + EXPECT_FALSE(IsSafePull(2)); // 6 mobs > 4 max + EXPECT_FALSE(IsSafePull(4)); // 5 mobs > 4 max +} + +TEST_F(DungeonPullTest, PriorityTargets) +{ + EXPECT_EQ(2u, GetPriorityTarget(1)); // Has caster + EXPECT_EQ(1u, GetPriorityTarget(2)); // Has healer + EXPECT_EQ(0u, GetPriorityTarget(3)); // No priority + EXPECT_EQ(1u, GetPriorityTarget(4)); // Has both, healer priority +} + +TEST_F(DungeonPullTest, PatrolWaiting) +{ + EXPECT_FALSE(ShouldWaitForPatrol(1)); + EXPECT_TRUE(ShouldWaitForPatrol(2)); +} + +/** + * @brief Tests for specific boss mechanics + */ +class SpecificMechanicTest : public ::testing::Test +{ +protected: + struct Position + { + float x, y; + }; + + float Distance(Position const& a, Position const& b) + { + float dx = a.x - b.x; + float dy = a.y - b.y; + return std::sqrt(dx * dx + dy * dy); + } + + // Heigan Dance: Safe zones alternate + bool IsInSafeZone_Heigan(Position const& pos, int phase) + { + // Simplified: zone 0-3 based on X position + int zone = static_cast(pos.x / 10.0f) % 4; + int safeZone = phase % 4; + return zone == safeZone; + } + + // Kel'Thuzad: Stay spread, avoid void zones + bool IsSafePosition_KT(Position const& pos, std::vector const& voidZones, float voidRadius) + { + for (auto const& vz : voidZones) + { + if (Distance(pos, vz) < voidRadius) + return false; + } + return true; + } + + // Generic: Line of Sight mechanic + bool HasLineOfSight(Position const& player, Position const& boss, std::vector const& pillars, float pillarRadius) + { + for (auto const& pillar : pillars) + { + // Check if pillar center is within pillarRadius of the line segment from player to boss + // Using point-to-line-segment distance formula + float dx = boss.x - player.x; + float dy = boss.y - player.y; + float lineLenSq = dx * dx + dy * dy; + + if (lineLenSq < 0.001f) + continue; // Player and boss at same position + + // Project pillar onto line, clamped to segment + float t = std::max(0.0f, std::min(1.0f, + ((pillar.x - player.x) * dx + (pillar.y - player.y) * dy) / lineLenSq)); + + // Closest point on line segment to pillar + float closestX = player.x + t * dx; + float closestY = player.y + t * dy; + + // Distance from pillar center to closest point + float distSq = (pillar.x - closestX) * (pillar.x - closestX) + + (pillar.y - closestY) * (pillar.y - closestY); + + if (distSq < pillarRadius * pillarRadius) + return false; // Pillar blocks LoS + } + return true; + } +}; + +TEST_F(SpecificMechanicTest, HeiganDanceSafeZone) +{ + Position pos{5.0f, 0.0f}; // Zone 0 + + EXPECT_TRUE(IsInSafeZone_Heigan(pos, 0)); + EXPECT_FALSE(IsInSafeZone_Heigan(pos, 1)); + EXPECT_FALSE(IsInSafeZone_Heigan(pos, 2)); + EXPECT_FALSE(IsInSafeZone_Heigan(pos, 3)); +} + +TEST_F(SpecificMechanicTest, HeiganDancePhaseChange) +{ + Position zone1{15.0f, 0.0f}; // Zone 1 + + EXPECT_FALSE(IsInSafeZone_Heigan(zone1, 0)); + EXPECT_TRUE(IsInSafeZone_Heigan(zone1, 1)); +} + +TEST_F(SpecificMechanicTest, KTVoidZoneAvoidance) +{ + std::vector voidZones = {{10.0f, 10.0f}, {30.0f, 30.0f}}; + float voidRadius = 5.0f; + + Position safe{50.0f, 50.0f}; + Position unsafe{12.0f, 10.0f}; + + EXPECT_TRUE(IsSafePosition_KT(safe, voidZones, voidRadius)); + EXPECT_FALSE(IsSafePosition_KT(unsafe, voidZones, voidRadius)); +} + +TEST_F(SpecificMechanicTest, LineOfSightMechanic) +{ + Position player{0.0f, 0.0f}; + Position boss{100.0f, 0.0f}; + std::vector pillars = {{50.0f, 0.0f}}; + float pillarRadius = 3.0f; + + // Pillar directly between player and boss + EXPECT_FALSE(HasLineOfSight(player, boss, pillars, pillarRadius)); + + // Move player to side + player = {0.0f, 20.0f}; + EXPECT_TRUE(HasLineOfSight(player, boss, pillars, pillarRadius)); +} + diff --git a/test/unit/Ai/Engine/EngineTickTest.cpp b/test/unit/Ai/Engine/EngineTickTest.cpp new file mode 100644 index 0000000000..fd176085ab --- /dev/null +++ b/test/unit/Ai/Engine/EngineTickTest.cpp @@ -0,0 +1,368 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include +#include +#include +#include + +/** + * @brief Tests for engine tick and action selection logic + */ + +/** + * @brief Tests for action relevance checking + */ +class ActionRelevanceTest : public ::testing::Test +{ +protected: + struct ActionContext + { + bool hasTarget; + bool inCombat; + bool hasResources; + float healthPercent; + float manaPercent; + float targetDistance; + }; + + struct ActionDefinition + { + std::string name; + std::function isRelevant; + std::function isPossible; + float basePriority; + }; + + std::vector actions_; + ActionContext context_; + + void SetUp() override + { + // Define test actions + actions_ = { + {"heal_self", + [](ActionContext const& ctx) { return ctx.healthPercent < 50.0f; }, + [](ActionContext const& ctx) { return ctx.manaPercent >= 10.0f; }, + 90.0f}, + {"attack", + [](ActionContext const& ctx) { return ctx.hasTarget && ctx.inCombat; }, + [](ActionContext const& ctx) { return ctx.targetDistance <= 30.0f; }, + 50.0f}, + {"follow", + [](ActionContext const& ctx) { return !ctx.inCombat && ctx.targetDistance > 5.0f; }, + [](ActionContext const& ctx) { return true; }, + 20.0f}, + {"emergency_heal", + [](ActionContext const& ctx) { return ctx.healthPercent < 20.0f; }, + [](ActionContext const& ctx) { return ctx.manaPercent >= 5.0f; }, + 100.0f}, + }; + + // Default context + context_ = {true, true, true, 80.0f, 100.0f, 10.0f}; + } + + std::vector GetRelevantActions() + { + std::vector result; + for (auto const& action : actions_) + { + if (action.isRelevant(context_)) + result.push_back(action.name); + } + return result; + } + + std::vector GetPossibleActions() + { + std::vector result; + for (auto const& action : actions_) + { + if (action.isRelevant(context_) && action.isPossible(context_)) + result.push_back(action.name); + } + return result; + } + + std::string SelectBestAction() + { + ActionDefinition const* best = nullptr; + float bestPriority = -1.0f; + + for (auto const& action : actions_) + { + if (action.isRelevant(context_) && action.isPossible(context_)) + { + if (action.basePriority > bestPriority) + { + best = &action; + bestPriority = action.basePriority; + } + } + } + + return best ? best->name : ""; + } +}; + +TEST_F(ActionRelevanceTest, FullHealthNoHealRelevant) +{ + context_.healthPercent = 100.0f; + auto relevant = GetRelevantActions(); + + bool hasHeal = std::find(relevant.begin(), relevant.end(), "heal_self") != relevant.end(); + EXPECT_FALSE(hasHeal); +} + +TEST_F(ActionRelevanceTest, LowHealthHealRelevant) +{ + context_.healthPercent = 40.0f; + auto relevant = GetRelevantActions(); + + bool hasHeal = std::find(relevant.begin(), relevant.end(), "heal_self") != relevant.end(); + EXPECT_TRUE(hasHeal); +} + +TEST_F(ActionRelevanceTest, CriticalHealthEmergencyHeal) +{ + context_.healthPercent = 15.0f; + std::string best = SelectBestAction(); + EXPECT_EQ("emergency_heal", best); +} + +TEST_F(ActionRelevanceTest, InCombatAttackRelevant) +{ + auto relevant = GetRelevantActions(); + bool hasAttack = std::find(relevant.begin(), relevant.end(), "attack") != relevant.end(); + EXPECT_TRUE(hasAttack); +} + +TEST_F(ActionRelevanceTest, OutOfCombatAttackNotRelevant) +{ + context_.inCombat = false; + auto relevant = GetRelevantActions(); + + bool hasAttack = std::find(relevant.begin(), relevant.end(), "attack") != relevant.end(); + EXPECT_FALSE(hasAttack); +} + +TEST_F(ActionRelevanceTest, OutOfRangeAttackNotPossible) +{ + context_.targetDistance = 50.0f; + auto possible = GetPossibleActions(); + + bool hasAttack = std::find(possible.begin(), possible.end(), "attack") != possible.end(); + EXPECT_FALSE(hasAttack); +} + +/** + * @brief Tests for engine tick timing + */ +class EngineTickTimingTest : public ::testing::Test +{ +protected: + struct TickState + { + uint32 lastTickTime; + uint32 tickInterval; + uint32 minTickInterval; + bool isPaused; + }; + + TickState state_; + + void SetUp() override + { + state_ = {0, 100, 50, false}; + } + + bool ShouldTick(uint32 currentTime) + { + if (state_.isPaused) + return false; + + uint32 elapsed = currentTime - state_.lastTickTime; + return elapsed >= state_.tickInterval; + } + + void DoTick(uint32 currentTime) + { + state_.lastTickTime = currentTime; + } + + uint32 GetNextTickTime() + { + return state_.lastTickTime + state_.tickInterval; + } + + void SetTickInterval(uint32 interval) + { + state_.tickInterval = std::max(interval, state_.minTickInterval); + } +}; + +TEST_F(EngineTickTimingTest, TicksAtInterval) +{ + EXPECT_TRUE(ShouldTick(100)); + DoTick(100); + EXPECT_FALSE(ShouldTick(150)); + EXPECT_TRUE(ShouldTick(200)); +} + +TEST_F(EngineTickTimingTest, PausedDoesNotTick) +{ + state_.isPaused = true; + EXPECT_FALSE(ShouldTick(1000)); +} + +TEST_F(EngineTickTimingTest, NextTickTimeCalculation) +{ + DoTick(100); + EXPECT_EQ(200u, GetNextTickTime()); +} + +TEST_F(EngineTickTimingTest, MinTickIntervalEnforced) +{ + SetTickInterval(10); + EXPECT_EQ(state_.minTickInterval, state_.tickInterval); +} + +/** + * @brief Tests for action execution queue + */ +class ActionExecutionQueueTest : public ::testing::Test +{ +protected: + enum class ActionResult + { + Success, + Failed, + Impossible, + InProgress + }; + + struct QueuedAction + { + std::string name; + float priority; + int retryCount; + int maxRetries; + ActionResult lastResult; + }; + + std::vector queue_; + + void AddAction(std::string const& name, float priority, int maxRetries = 3) + { + queue_.push_back({name, priority, 0, maxRetries, ActionResult::InProgress}); + } + + void SortQueue() + { + std::sort(queue_.begin(), queue_.end(), + [](QueuedAction const& a, QueuedAction const& b) { + return a.priority > b.priority; + }); + } + + QueuedAction* GetNextAction() + { + SortQueue(); + for (auto& action : queue_) + { + if (action.lastResult != ActionResult::Impossible && + action.retryCount < action.maxRetries) + { + return &action; + } + } + return nullptr; + } + + void RecordResult(std::string const& name, ActionResult result) + { + for (auto& action : queue_) + { + if (action.name == name) + { + action.lastResult = result; + if (result == ActionResult::Failed) + action.retryCount++; + break; + } + } + } + + void ClearCompleted() + { + queue_.erase( + std::remove_if(queue_.begin(), queue_.end(), + [](QueuedAction const& a) { + return a.lastResult == ActionResult::Success || + a.lastResult == ActionResult::Impossible || + a.retryCount >= a.maxRetries; + }), + queue_.end()); + } +}; + +TEST_F(ActionExecutionQueueTest, SelectsHighestPriority) +{ + AddAction("low", 10.0f); + AddAction("high", 100.0f); + AddAction("medium", 50.0f); + + auto* next = GetNextAction(); + ASSERT_NE(nullptr, next); + EXPECT_EQ("high", next->name); +} + +TEST_F(ActionExecutionQueueTest, SkipsImpossibleActions) +{ + AddAction("impossible", 100.0f); + AddAction("possible", 50.0f); + RecordResult("impossible", ActionResult::Impossible); + + auto* next = GetNextAction(); + ASSERT_NE(nullptr, next); + EXPECT_EQ("possible", next->name); +} + +TEST_F(ActionExecutionQueueTest, RetriesFailedActions) +{ + AddAction("flaky", 100.0f, 3); + RecordResult("flaky", ActionResult::Failed); + RecordResult("flaky", ActionResult::Failed); + + auto* next = GetNextAction(); + ASSERT_NE(nullptr, next); + EXPECT_EQ("flaky", next->name); + EXPECT_EQ(2, next->retryCount); +} + +TEST_F(ActionExecutionQueueTest, StopsAfterMaxRetries) +{ + AddAction("always_fails", 100.0f, 2); + RecordResult("always_fails", ActionResult::Failed); + RecordResult("always_fails", ActionResult::Failed); + + auto* next = GetNextAction(); + EXPECT_EQ(nullptr, next); +} + +TEST_F(ActionExecutionQueueTest, ClearCompletedRemovesSuccessful) +{ + AddAction("success", 100.0f); + AddAction("pending", 50.0f); + RecordResult("success", ActionResult::Success); + ClearCompleted(); + + EXPECT_EQ(1u, queue_.size()); + EXPECT_EQ("pending", queue_[0].name); +} + diff --git a/test/unit/Ai/Engine/MultiplierTest.cpp b/test/unit/Ai/Engine/MultiplierTest.cpp new file mode 100644 index 0000000000..b74b71aa41 --- /dev/null +++ b/test/unit/Ai/Engine/MultiplierTest.cpp @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include +#include +#include +#include + +/** + * @brief Tests for action priority multiplier calculations + */ + +/** + * @brief Tests for basic multiplier calculations + */ +class MultiplierCalculationTest : public ::testing::Test +{ +protected: + struct MultiplierContext + { + float healthPercent; + float manaPercent; + float targetHealthPercent; + bool inCombat; + bool hasAggro; + int groupSize; + float threatPercent; + }; + + MultiplierContext context_; + + void SetUp() override + { + context_ = {100.0f, 100.0f, 100.0f, true, false, 5, 50.0f}; + } + + /** + * @brief Calculate heal urgency multiplier + * Lower health = higher multiplier + */ + float GetHealUrgencyMultiplier(float healthPercent) + { + if (healthPercent >= 80.0f) + return 1.0f; + if (healthPercent >= 50.0f) + return 1.5f; + if (healthPercent >= 30.0f) + return 2.0f; + if (healthPercent >= 15.0f) + return 3.0f; + return 5.0f; // Critical + } + + /** + * @brief Calculate threat management multiplier + * Higher threat = higher priority for threat reduction + */ + float GetThreatMultiplier(float threatPercent, bool isTank) + { + if (isTank) + return 1.0f; // Tanks don't reduce threat + + if (threatPercent >= 100.0f) + return 5.0f; // Emergency! + if (threatPercent >= 90.0f) + return 3.0f; + if (threatPercent >= 80.0f) + return 2.0f; + if (threatPercent >= 70.0f) + return 1.5f; + return 1.0f; + } + + /** + * @brief Calculate mana conservation multiplier + * Lower mana = prefer efficient spells + */ + float GetManaConservationMultiplier(float manaPercent) + { + if (manaPercent >= 80.0f) + return 1.0f; + if (manaPercent >= 50.0f) + return 1.2f; + if (manaPercent >= 30.0f) + return 1.5f; + if (manaPercent >= 15.0f) + return 2.0f; + return 3.0f; // Very low mana + } + + /** + * @brief Calculate group size scaling multiplier + */ + float GetGroupScalingMultiplier(int groupSize, bool isAoeAction) + { + if (!isAoeAction) + return 1.0f; + + if (groupSize >= 20) // Raid + return 2.0f; + if (groupSize >= 10) + return 1.5f; + if (groupSize >= 5) + return 1.2f; + return 1.0f; + } +}; + +TEST_F(MultiplierCalculationTest, HealUrgencyAtFullHealth) +{ + EXPECT_FLOAT_EQ(1.0f, GetHealUrgencyMultiplier(100.0f)); + EXPECT_FLOAT_EQ(1.0f, GetHealUrgencyMultiplier(80.0f)); +} + +TEST_F(MultiplierCalculationTest, HealUrgencyScalesWithDamage) +{ + EXPECT_FLOAT_EQ(1.5f, GetHealUrgencyMultiplier(60.0f)); + EXPECT_FLOAT_EQ(2.0f, GetHealUrgencyMultiplier(40.0f)); + EXPECT_FLOAT_EQ(3.0f, GetHealUrgencyMultiplier(20.0f)); +} + +TEST_F(MultiplierCalculationTest, HealUrgencyCritical) +{ + EXPECT_FLOAT_EQ(5.0f, GetHealUrgencyMultiplier(10.0f)); + EXPECT_FLOAT_EQ(5.0f, GetHealUrgencyMultiplier(5.0f)); +} + +TEST_F(MultiplierCalculationTest, ThreatMultiplierForDps) +{ + EXPECT_FLOAT_EQ(1.0f, GetThreatMultiplier(50.0f, false)); + EXPECT_FLOAT_EQ(1.5f, GetThreatMultiplier(75.0f, false)); + EXPECT_FLOAT_EQ(2.0f, GetThreatMultiplier(85.0f, false)); + EXPECT_FLOAT_EQ(3.0f, GetThreatMultiplier(95.0f, false)); + EXPECT_FLOAT_EQ(5.0f, GetThreatMultiplier(110.0f, false)); +} + +TEST_F(MultiplierCalculationTest, TankIgnoresThreatMultiplier) +{ + EXPECT_FLOAT_EQ(1.0f, GetThreatMultiplier(100.0f, true)); + EXPECT_FLOAT_EQ(1.0f, GetThreatMultiplier(150.0f, true)); +} + +TEST_F(MultiplierCalculationTest, ManaConservation) +{ + EXPECT_FLOAT_EQ(1.0f, GetManaConservationMultiplier(100.0f)); + EXPECT_FLOAT_EQ(1.2f, GetManaConservationMultiplier(60.0f)); + EXPECT_FLOAT_EQ(1.5f, GetManaConservationMultiplier(40.0f)); + EXPECT_FLOAT_EQ(2.0f, GetManaConservationMultiplier(20.0f)); + EXPECT_FLOAT_EQ(3.0f, GetManaConservationMultiplier(10.0f)); +} + +TEST_F(MultiplierCalculationTest, GroupScalingForAoe) +{ + EXPECT_FLOAT_EQ(1.0f, GetGroupScalingMultiplier(3, true)); + EXPECT_FLOAT_EQ(1.2f, GetGroupScalingMultiplier(5, true)); + EXPECT_FLOAT_EQ(1.5f, GetGroupScalingMultiplier(10, true)); + EXPECT_FLOAT_EQ(2.0f, GetGroupScalingMultiplier(25, true)); +} + +TEST_F(MultiplierCalculationTest, GroupScalingIgnoredForSingleTarget) +{ + EXPECT_FLOAT_EQ(1.0f, GetGroupScalingMultiplier(25, false)); +} + +/** + * @brief Tests for combined multiplier calculations + */ +class CombinedMultiplierTest : public ::testing::Test +{ +protected: + struct ActionPriority + { + std::string name; + float basePriority; + std::vector multipliers; + }; + + /** + * @brief Calculate final priority with all multipliers + */ + float CalculateFinalPriority(ActionPriority const& action) + { + float finalPriority = action.basePriority; + for (float mult : action.multipliers) + { + finalPriority *= mult; + } + return finalPriority; + } + + /** + * @brief Compare actions and select highest priority + */ + std::string SelectBestAction(std::vector const& actions) + { + ActionPriority const* best = nullptr; + float bestPriority = -1.0f; + + for (auto const& action : actions) + { + float priority = CalculateFinalPriority(action); + if (priority > bestPriority) + { + best = &action; + bestPriority = priority; + } + } + + return best ? best->name : ""; + } +}; + +TEST_F(CombinedMultiplierTest, SingleMultiplier) +{ + ActionPriority action{"test", 100.0f, {2.0f}}; + EXPECT_FLOAT_EQ(200.0f, CalculateFinalPriority(action)); +} + +TEST_F(CombinedMultiplierTest, MultipleMultipliers) +{ + ActionPriority action{"test", 100.0f, {2.0f, 1.5f, 1.2f}}; + EXPECT_FLOAT_EQ(360.0f, CalculateFinalPriority(action)); +} + +TEST_F(CombinedMultiplierTest, NoMultipliers) +{ + ActionPriority action{"test", 100.0f, {}}; + EXPECT_FLOAT_EQ(100.0f, CalculateFinalPriority(action)); +} + +TEST_F(CombinedMultiplierTest, MultiplierAffectsSelection) +{ + std::vector actions = { + {"heal", 50.0f, {3.0f}}, // 150 + {"attack", 100.0f, {1.0f}}, // 100 + }; + + EXPECT_EQ("heal", SelectBestAction(actions)); +} + +TEST_F(CombinedMultiplierTest, HighBaseCanBeatMultiplied) +{ + std::vector actions = { + {"heal", 50.0f, {1.5f}}, // 75 + {"attack", 100.0f, {1.0f}}, // 100 + }; + + EXPECT_EQ("attack", SelectBestAction(actions)); +} + +/** + * @brief Tests for situational multipliers + */ +class SituationalMultiplierTest : public ::testing::Test +{ +protected: + struct BossPhase + { + int phase; + bool isBurning; // High damage phase + bool needsInterrupt; + bool needsSpread; + bool needsStack; + }; + + BossPhase currentPhase_; + + void SetUp() override + { + currentPhase_ = {1, false, false, false, false}; + } + + float GetBossPhaseMultiplier(std::string const& actionType) + { + if (currentPhase_.isBurning) + { + if (actionType == "heal") + return 2.0f; + if (actionType == "defensive_cooldown") + return 3.0f; + } + + if (currentPhase_.needsInterrupt && actionType == "interrupt") + return 5.0f; + + if (currentPhase_.needsSpread && actionType == "spread") + return 4.0f; + + if (currentPhase_.needsStack && actionType == "stack") + return 4.0f; + + return 1.0f; + } + + float GetExecutePhaseMultiplier(float targetHealthPercent, std::string const& actionType) + { + if (targetHealthPercent <= 20.0f && actionType == "execute") + return 3.0f; + if (targetHealthPercent <= 35.0f && actionType == "execute") + return 2.0f; + return 1.0f; + } +}; + +TEST_F(SituationalMultiplierTest, NormalPhaseNoBonus) +{ + EXPECT_FLOAT_EQ(1.0f, GetBossPhaseMultiplier("heal")); + EXPECT_FLOAT_EQ(1.0f, GetBossPhaseMultiplier("attack")); +} + +TEST_F(SituationalMultiplierTest, BurningPhaseBoostsHealing) +{ + currentPhase_.isBurning = true; + EXPECT_FLOAT_EQ(2.0f, GetBossPhaseMultiplier("heal")); + EXPECT_FLOAT_EQ(3.0f, GetBossPhaseMultiplier("defensive_cooldown")); +} + +TEST_F(SituationalMultiplierTest, InterruptPhaseBoostsInterrupt) +{ + currentPhase_.needsInterrupt = true; + EXPECT_FLOAT_EQ(5.0f, GetBossPhaseMultiplier("interrupt")); +} + +TEST_F(SituationalMultiplierTest, SpreadStackMechanics) +{ + currentPhase_.needsSpread = true; + EXPECT_FLOAT_EQ(4.0f, GetBossPhaseMultiplier("spread")); + + currentPhase_.needsSpread = false; + currentPhase_.needsStack = true; + EXPECT_FLOAT_EQ(4.0f, GetBossPhaseMultiplier("stack")); +} + +TEST_F(SituationalMultiplierTest, ExecutePhase) +{ + EXPECT_FLOAT_EQ(1.0f, GetExecutePhaseMultiplier(50.0f, "execute")); + EXPECT_FLOAT_EQ(2.0f, GetExecutePhaseMultiplier(30.0f, "execute")); + EXPECT_FLOAT_EQ(3.0f, GetExecutePhaseMultiplier(15.0f, "execute")); +} + diff --git a/test/unit/Ai/Engine/StateTransitionTest.cpp b/test/unit/Ai/Engine/StateTransitionTest.cpp new file mode 100644 index 0000000000..fd4b820077 --- /dev/null +++ b/test/unit/Ai/Engine/StateTransitionTest.cpp @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include +#include +#include +#include + +/** + * @brief Tests for bot state machine transitions + */ + +/** + * @brief Basic state machine tests + */ +class BotStateMachineTest : public ::testing::Test +{ +protected: + enum class BotState + { + Idle, + Following, + Combat, + Fleeing, + Dead, + Looting, + Resting, + Mounted + }; + + struct StateContext + { + bool isAlive; + bool inCombat; + bool hasLoot; + bool needsRest; + bool masterMounted; + float healthPercent; + float manaPercent; + float distanceToMaster; + float distanceToEnemy; + }; + + BotState currentState_; + StateContext context_; + std::map> validTransitions_; + + void SetUp() override + { + currentState_ = BotState::Idle; + context_ = {true, false, false, false, false, 100.0f, 100.0f, 5.0f, 50.0f}; + + // Define valid state transitions + validTransitions_[BotState::Idle] = { + BotState::Following, BotState::Combat, BotState::Dead, + BotState::Resting, BotState::Mounted + }; + validTransitions_[BotState::Following] = { + BotState::Idle, BotState::Combat, BotState::Dead, + BotState::Mounted + }; + validTransitions_[BotState::Combat] = { + BotState::Idle, BotState::Fleeing, BotState::Dead, + BotState::Looting + }; + validTransitions_[BotState::Fleeing] = { + BotState::Combat, BotState::Dead, BotState::Idle + }; + validTransitions_[BotState::Dead] = { + BotState::Idle // After resurrection + }; + validTransitions_[BotState::Looting] = { + BotState::Idle, BotState::Combat + }; + validTransitions_[BotState::Resting] = { + BotState::Idle, BotState::Combat + }; + validTransitions_[BotState::Mounted] = { + BotState::Idle, BotState::Following, BotState::Combat + }; + } + + bool CanTransition(BotState from, BotState to) + { + auto it = validTransitions_.find(from); + if (it == validTransitions_.end()) + return false; + return it->second.find(to) != it->second.end(); + } + + bool TryTransition(BotState newState) + { + if (!CanTransition(currentState_, newState)) + return false; + currentState_ = newState; + return true; + } + + BotState DetermineDesiredState() + { + // Death takes priority + if (!context_.isAlive) + return BotState::Dead; + + // Combat check + if (context_.inCombat) + { + if (context_.healthPercent < 20.0f) + return BotState::Fleeing; + return BotState::Combat; + } + + // Post-combat + if (context_.hasLoot) + return BotState::Looting; + + // Rest if needed + if (context_.needsRest && + (context_.healthPercent < 80.0f || context_.manaPercent < 80.0f)) + return BotState::Resting; + + // Follow master + if (context_.masterMounted) + return BotState::Mounted; + + if (context_.distanceToMaster > 10.0f) + return BotState::Following; + + return BotState::Idle; + } +}; + +TEST_F(BotStateMachineTest, InitialStateIsIdle) +{ + EXPECT_EQ(BotState::Idle, currentState_); +} + +TEST_F(BotStateMachineTest, ValidTransitionSucceeds) +{ + EXPECT_TRUE(TryTransition(BotState::Following)); + EXPECT_EQ(BotState::Following, currentState_); +} + +TEST_F(BotStateMachineTest, InvalidTransitionFails) +{ + currentState_ = BotState::Dead; + EXPECT_FALSE(TryTransition(BotState::Combat)); // Can't fight when dead + EXPECT_EQ(BotState::Dead, currentState_); +} + +TEST_F(BotStateMachineTest, DeathTakesPriority) +{ + context_.isAlive = false; + context_.inCombat = true; // Even in combat + EXPECT_EQ(BotState::Dead, DetermineDesiredState()); +} + +TEST_F(BotStateMachineTest, FleeAtLowHealth) +{ + context_.inCombat = true; + context_.healthPercent = 15.0f; + EXPECT_EQ(BotState::Fleeing, DetermineDesiredState()); +} + +TEST_F(BotStateMachineTest, CombatAtNormalHealth) +{ + context_.inCombat = true; + context_.healthPercent = 80.0f; + EXPECT_EQ(BotState::Combat, DetermineDesiredState()); +} + +TEST_F(BotStateMachineTest, LootAfterCombat) +{ + context_.hasLoot = true; + EXPECT_EQ(BotState::Looting, DetermineDesiredState()); +} + +TEST_F(BotStateMachineTest, RestWhenNeeded) +{ + context_.needsRest = true; + context_.manaPercent = 30.0f; + EXPECT_EQ(BotState::Resting, DetermineDesiredState()); +} + +TEST_F(BotStateMachineTest, MountWhenMasterMounts) +{ + context_.masterMounted = true; + EXPECT_EQ(BotState::Mounted, DetermineDesiredState()); +} + +TEST_F(BotStateMachineTest, FollowWhenFar) +{ + context_.distanceToMaster = 25.0f; + EXPECT_EQ(BotState::Following, DetermineDesiredState()); +} + +/** + * @brief Tests for combat state substates + */ +class CombatSubstateTest : public ::testing::Test +{ +protected: + enum class CombatSubstate + { + Engaging, // Moving to target + Fighting, // In melee range, attacking + Kiting, // Keeping distance + Positioning, // Moving to optimal position + Waiting, // Waiting for cooldowns/resources + Interrupting // Priority interrupt + }; + + struct CombatContext + { + bool isMelee; + bool hasTarget; + float distanceToTarget; + float optimalRange; + bool spellReady; + bool needsInterrupt; + bool targetCasting; + }; + + CombatContext context_; + + void SetUp() override + { + context_ = {true, true, 5.0f, 5.0f, true, false, false}; + } + + CombatSubstate DetermineCombatSubstate() + { + if (!context_.hasTarget) + return CombatSubstate::Waiting; + + // Interrupt takes priority + if (context_.needsInterrupt && context_.targetCasting) + return CombatSubstate::Interrupting; + + // Check range + float rangeDiff = context_.distanceToTarget - context_.optimalRange; + + if (context_.isMelee) + { + if (rangeDiff > 5.0f) + return CombatSubstate::Engaging; + if (rangeDiff > 0.5f) + return CombatSubstate::Positioning; + } + else // Ranged + { + if (context_.distanceToTarget < 8.0f) + return CombatSubstate::Kiting; + if (context_.distanceToTarget > context_.optimalRange + 10.0f) + return CombatSubstate::Positioning; + } + + if (!context_.spellReady) + return CombatSubstate::Waiting; + + return CombatSubstate::Fighting; + } +}; + +TEST_F(CombatSubstateTest, MeleeEngaging) +{ + context_.distanceToTarget = 30.0f; + EXPECT_EQ(CombatSubstate::Engaging, DetermineCombatSubstate()); +} + +TEST_F(CombatSubstateTest, MeleeFighting) +{ + context_.distanceToTarget = 5.0f; + EXPECT_EQ(CombatSubstate::Fighting, DetermineCombatSubstate()); +} + +TEST_F(CombatSubstateTest, MeleePositioning) +{ + context_.distanceToTarget = 8.0f; // Slightly out of range + EXPECT_EQ(CombatSubstate::Positioning, DetermineCombatSubstate()); +} + +TEST_F(CombatSubstateTest, RangedKiting) +{ + context_.isMelee = false; + context_.optimalRange = 30.0f; + context_.distanceToTarget = 5.0f; // Too close + EXPECT_EQ(CombatSubstate::Kiting, DetermineCombatSubstate()); +} + +TEST_F(CombatSubstateTest, RangedPositioning) +{ + context_.isMelee = false; + context_.optimalRange = 30.0f; + context_.distanceToTarget = 50.0f; // Too far + EXPECT_EQ(CombatSubstate::Positioning, DetermineCombatSubstate()); +} + +TEST_F(CombatSubstateTest, InterruptPriority) +{ + context_.needsInterrupt = true; + context_.targetCasting = true; + EXPECT_EQ(CombatSubstate::Interrupting, DetermineCombatSubstate()); +} + +TEST_F(CombatSubstateTest, WaitingForCooldown) +{ + context_.spellReady = false; + EXPECT_EQ(CombatSubstate::Waiting, DetermineCombatSubstate()); +} + +/** + * @brief Tests for state transition events + */ +class StateTransitionEventTest : public ::testing::Test +{ +protected: + enum class StateEvent + { + EnterCombat, + LeaveCombat, + Die, + Resurrect, + StartResting, + FinishResting, + Mount, + Dismount + }; + + struct EventLog + { + std::vector events; + }; + + EventLog log_; + + void OnEnterCombat() { log_.events.push_back(StateEvent::EnterCombat); } + void OnLeaveCombat() { log_.events.push_back(StateEvent::LeaveCombat); } + void OnDie() { log_.events.push_back(StateEvent::Die); } + void OnResurrect() { log_.events.push_back(StateEvent::Resurrect); } + + void SimulateStateChange(std::string const& from, std::string const& to) + { + if (to == "combat" && from != "combat") + OnEnterCombat(); + if (from == "combat" && to != "combat") + OnLeaveCombat(); + if (to == "dead") + OnDie(); + if (from == "dead") + OnResurrect(); + } + + bool HasEvent(StateEvent event) + { + return std::find(log_.events.begin(), log_.events.end(), event) != log_.events.end(); + } + + size_t EventCount() { return log_.events.size(); } +}; + +TEST_F(StateTransitionEventTest, EnterCombatEventFires) +{ + SimulateStateChange("idle", "combat"); + EXPECT_TRUE(HasEvent(StateEvent::EnterCombat)); +} + +TEST_F(StateTransitionEventTest, LeaveCombatEventFires) +{ + SimulateStateChange("combat", "idle"); + EXPECT_TRUE(HasEvent(StateEvent::LeaveCombat)); +} + +TEST_F(StateTransitionEventTest, DieEventFires) +{ + SimulateStateChange("combat", "dead"); + EXPECT_TRUE(HasEvent(StateEvent::Die)); + EXPECT_TRUE(HasEvent(StateEvent::LeaveCombat)); +} + +TEST_F(StateTransitionEventTest, ResurrectEventFires) +{ + SimulateStateChange("dead", "idle"); + EXPECT_TRUE(HasEvent(StateEvent::Resurrect)); +} + +TEST_F(StateTransitionEventTest, NoEventForSameState) +{ + SimulateStateChange("combat", "combat"); + EXPECT_EQ(0u, EventCount()); +} + diff --git a/test/unit/Ai/Engine/StrategyLogicTest.cpp b/test/unit/Ai/Engine/StrategyLogicTest.cpp new file mode 100644 index 0000000000..0a003dc7b0 --- /dev/null +++ b/test/unit/Ai/Engine/StrategyLogicTest.cpp @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief Tests for strategy activation and management logic + */ + +/** + * @brief Tests for basic strategy activation + */ +class StrategyActivationTest : public ::testing::Test +{ +protected: + struct Strategy + { + std::string name; + bool enabled; + std::vector conflicts; // Strategies that can't be active together + std::vector requires; // Strategies that must also be active + }; + + std::map strategies_; + + void SetUp() override + { + // Define test strategies + strategies_["dps"] = {"dps", false, {"tank", "heal"}, {}}; + strategies_["tank"] = {"tank", false, {"dps", "heal"}, {}}; + strategies_["heal"] = {"heal", false, {"dps", "tank"}, {}}; + strategies_["pvp"] = {"pvp", false, {"pve"}, {}}; // pvp conflicts with pve + strategies_["pve"] = {"pve", false, {"pvp"}, {}}; // pve conflicts with pvp + strategies_["aoe"] = {"aoe", false, {}, {"dps"}}; // Requires dps + strategies_["stealth"] = {"stealth", false, {"mount"}, {}}; + strategies_["mount"] = {"mount", false, {"stealth", "combat"}, {}}; + strategies_["combat"] = {"combat", false, {"mount"}, {}}; + } + + bool HasConflict(std::string const& strategyName) + { + auto it = strategies_.find(strategyName); + if (it == strategies_.end()) + return false; + + for (auto const& conflict : it->second.conflicts) + { + auto conflictIt = strategies_.find(conflict); + if (conflictIt != strategies_.end() && conflictIt->second.enabled) + return true; + } + return false; + } + + bool HasRequirements(std::string const& strategyName) + { + auto it = strategies_.find(strategyName); + if (it == strategies_.end()) + return false; + + for (auto const& req : it->second.requires) + { + auto reqIt = strategies_.find(req); + if (reqIt == strategies_.end() || !reqIt->second.enabled) + return false; + } + return true; + } + + bool CanActivate(std::string const& strategyName) + { + return !HasConflict(strategyName) && HasRequirements(strategyName); + } + + bool Activate(std::string const& strategyName) + { + if (!CanActivate(strategyName)) + return false; + + auto it = strategies_.find(strategyName); + if (it != strategies_.end()) + { + it->second.enabled = true; + return true; + } + return false; + } + + void Deactivate(std::string const& strategyName) + { + auto it = strategies_.find(strategyName); + if (it != strategies_.end()) + it->second.enabled = false; + } + + bool IsActive(std::string const& strategyName) + { + auto it = strategies_.find(strategyName); + return it != strategies_.end() && it->second.enabled; + } +}; + +TEST_F(StrategyActivationTest, CanActivateWithNoConflicts) +{ + EXPECT_TRUE(CanActivate("dps")); + EXPECT_TRUE(Activate("dps")); + EXPECT_TRUE(IsActive("dps")); +} + +TEST_F(StrategyActivationTest, CannotActivateWithConflict) +{ + Activate("dps"); + EXPECT_FALSE(CanActivate("tank")); + EXPECT_FALSE(Activate("tank")); + EXPECT_FALSE(IsActive("tank")); +} + +TEST_F(StrategyActivationTest, CanActivateAfterDeactivatingConflict) +{ + Activate("dps"); + Deactivate("dps"); + EXPECT_TRUE(CanActivate("tank")); + EXPECT_TRUE(Activate("tank")); +} + +TEST_F(StrategyActivationTest, RequirementsEnforced) +{ + // aoe requires dps + EXPECT_FALSE(CanActivate("aoe")); + EXPECT_FALSE(Activate("aoe")); + + Activate("dps"); + EXPECT_TRUE(CanActivate("aoe")); + EXPECT_TRUE(Activate("aoe")); +} + +TEST_F(StrategyActivationTest, MultipleNonConflictingStrategies) +{ + EXPECT_TRUE(Activate("dps")); + EXPECT_TRUE(Activate("pve")); + EXPECT_TRUE(IsActive("dps")); + EXPECT_TRUE(IsActive("pve")); +} + +TEST_F(StrategyActivationTest, MutualExclusion) +{ + Activate("pvp"); + EXPECT_FALSE(CanActivate("pve")); + + Deactivate("pvp"); + Activate("pve"); + EXPECT_FALSE(CanActivate("pvp")); +} + +/** + * @brief Tests for strategy state management + */ +class StrategyStateTest : public ::testing::Test +{ +protected: + enum class BotState + { + NonCombat, + Combat, + Dead, + Reaction + }; + + struct StrategySet + { + std::set strategies; + }; + + std::map stateStrategies_; + + void SetUp() override + { + stateStrategies_[BotState::NonCombat] = {{"follow", "buff", "food"}}; + stateStrategies_[BotState::Combat] = {{"dps", "threat", "interrupt"}}; + stateStrategies_[BotState::Dead] = {{"release", "revive"}}; + stateStrategies_[BotState::Reaction] = {{"flee", "emergency_heal"}}; + } + + std::set GetStrategiesForState(BotState state) + { + auto it = stateStrategies_.find(state); + if (it != stateStrategies_.end()) + return it->second.strategies; + return {}; + } + + bool HasStrategyInState(BotState state, std::string const& strategy) + { + auto strategies = GetStrategiesForState(state); + return strategies.find(strategy) != strategies.end(); + } +}; + +TEST_F(StrategyStateTest, NonCombatStrategies) +{ + auto strategies = GetStrategiesForState(BotState::NonCombat); + EXPECT_EQ(3u, strategies.size()); + EXPECT_TRUE(HasStrategyInState(BotState::NonCombat, "follow")); + EXPECT_TRUE(HasStrategyInState(BotState::NonCombat, "buff")); + EXPECT_FALSE(HasStrategyInState(BotState::NonCombat, "dps")); +} + +TEST_F(StrategyStateTest, CombatStrategies) +{ + auto strategies = GetStrategiesForState(BotState::Combat); + EXPECT_TRUE(HasStrategyInState(BotState::Combat, "dps")); + EXPECT_TRUE(HasStrategyInState(BotState::Combat, "interrupt")); + EXPECT_FALSE(HasStrategyInState(BotState::Combat, "follow")); +} + +TEST_F(StrategyStateTest, DeadStrategies) +{ + auto strategies = GetStrategiesForState(BotState::Dead); + EXPECT_TRUE(HasStrategyInState(BotState::Dead, "release")); + EXPECT_TRUE(HasStrategyInState(BotState::Dead, "revive")); +} + +/** + * @brief Tests for strategy priority ordering + */ +class StrategyPriorityTest : public ::testing::Test +{ +protected: + struct PrioritizedStrategy + { + std::string name; + int priority; // Higher = more important + bool enabled; + }; + + std::vector strategies_; + + void SetUp() override + { + strategies_ = { + {"emergency_heal", 100, true}, + {"interrupt", 90, true}, + {"dispel", 80, true}, + {"dps", 50, true}, + {"buff", 30, true}, + {"follow", 10, true}, + }; + } + + std::vector GetOrderedStrategies() + { + std::vector enabled; + std::copy_if(strategies_.begin(), strategies_.end(), + std::back_inserter(enabled), + [](PrioritizedStrategy const& s) { return s.enabled; }); + + std::sort(enabled.begin(), enabled.end(), + [](PrioritizedStrategy const& a, PrioritizedStrategy const& b) { + return a.priority > b.priority; + }); + + std::vector result; + for (auto const& s : enabled) + result.push_back(s.name); + return result; + } + + void DisableStrategy(std::string const& name) + { + for (auto& s : strategies_) + { + if (s.name == name) + s.enabled = false; + } + } +}; + +TEST_F(StrategyPriorityTest, OrderedByPriority) +{ + auto ordered = GetOrderedStrategies(); + ASSERT_EQ(6u, ordered.size()); + EXPECT_EQ("emergency_heal", ordered[0]); + EXPECT_EQ("interrupt", ordered[1]); + EXPECT_EQ("follow", ordered[5]); +} + +TEST_F(StrategyPriorityTest, DisabledStrategiesExcluded) +{ + DisableStrategy("interrupt"); + auto ordered = GetOrderedStrategies(); + EXPECT_EQ(5u, ordered.size()); + + bool found = false; + for (auto const& s : ordered) + { + if (s == "interrupt") + found = true; + } + EXPECT_FALSE(found); +} + diff --git a/test/unit/Ai/Integration/ActionChainTest.cpp b/test/unit/Ai/Integration/ActionChainTest.cpp new file mode 100644 index 0000000000..41d5d7b529 --- /dev/null +++ b/test/unit/Ai/Integration/ActionChainTest.cpp @@ -0,0 +1,624 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief Integration tests for complete action chains + */ + +/** + * @brief Tests for Trigger -> Action chain + */ +class TriggerActionChainTest : public ::testing::Test +{ +protected: + struct Trigger + { + std::string name; + std::function isActive; + std::vector boundActions; + float priority; + }; + + struct Action + { + std::string name; + std::function isPossible; + std::function execute; + float basePriority; + }; + + struct GameState + { + float healthPercent; + float manaPercent; + bool hasTarget; + bool inCombat; + float targetDistance; + float targetHealthPercent; + }; + + std::vector triggers_; + std::map actions_; + GameState state_; + + void SetUp() override + { + state_ = {100.0f, 100.0f, true, true, 10.0f, 100.0f}; + + // Define actions + actions_["heal_self"] = { + "heal_self", + [this]() { return state_.manaPercent >= 20.0f; }, + [this]() { state_.healthPercent += 30.0f; return true; }, + 80.0f + }; + + actions_["attack"] = { + "attack", + [this]() { return state_.hasTarget && state_.targetDistance <= 30.0f; }, + [this]() { state_.targetHealthPercent -= 10.0f; return true; }, + 50.0f + }; + + actions_["flee"] = { + "flee", + [this]() { return true; }, + [this]() { state_.targetDistance += 20.0f; return true; }, + 30.0f + }; + + // Define triggers + triggers_ = { + { + "critical_health", + [this]() { return state_.healthPercent < 20.0f; }, + {"heal_self", "flee"}, + 100.0f + }, + { + "low_health", + [this]() { return state_.healthPercent < 50.0f; }, + {"heal_self"}, + 80.0f + }, + { + "has_target", + [this]() { return state_.hasTarget && state_.inCombat; }, + {"attack"}, + 50.0f + }, + }; + } + + std::vector GetActiveTriggers() + { + std::vector active; + for (auto& trigger : triggers_) + { + if (trigger.isActive()) + active.push_back(&trigger); + } + + // Sort by priority + std::sort(active.begin(), active.end(), + [](Trigger* a, Trigger* b) { return a->priority > b->priority; }); + + return active; + } + + std::string ExecuteBestAction() + { + auto activeTriggers = GetActiveTriggers(); + + for (auto* trigger : activeTriggers) + { + for (auto const& actionName : trigger->boundActions) + { + auto it = actions_.find(actionName); + if (it != actions_.end() && it->second.isPossible()) + { + it->second.execute(); + return actionName; + } + } + } + + return ""; + } +}; + +TEST_F(TriggerActionChainTest, NoTriggerAtFullHealth) +{ + auto active = GetActiveTriggers(); + + // Only has_target trigger should be active + EXPECT_EQ(1u, active.size()); + EXPECT_EQ("has_target", active[0]->name); +} + +TEST_F(TriggerActionChainTest, LowHealthTriggerActivates) +{ + state_.healthPercent = 40.0f; + auto active = GetActiveTriggers(); + + EXPECT_EQ(2u, active.size()); + EXPECT_EQ("low_health", active[0]->name); // Higher priority +} + +TEST_F(TriggerActionChainTest, CriticalHealthTriggerHighestPriority) +{ + state_.healthPercent = 15.0f; + auto active = GetActiveTriggers(); + + EXPECT_EQ(3u, active.size()); + EXPECT_EQ("critical_health", active[0]->name); +} + +TEST_F(TriggerActionChainTest, ExecutesHealWhenLowHealth) +{ + state_.healthPercent = 40.0f; + std::string executed = ExecuteBestAction(); + + EXPECT_EQ("heal_self", executed); + EXPECT_FLOAT_EQ(70.0f, state_.healthPercent); // Healed +} + +TEST_F(TriggerActionChainTest, ExecutesAttackWhenHealthy) +{ + std::string executed = ExecuteBestAction(); + + EXPECT_EQ("attack", executed); + EXPECT_FLOAT_EQ(90.0f, state_.targetHealthPercent); // Damaged target +} + +TEST_F(TriggerActionChainTest, FallsBackToFleeWhenCantHeal) +{ + state_.healthPercent = 15.0f; + state_.manaPercent = 10.0f; // Not enough mana to heal + + std::string executed = ExecuteBestAction(); + + EXPECT_EQ("flee", executed); +} + +/** + * @brief Tests for Trigger -> Multiplier -> Action chain + */ +class TriggerMultiplierActionTest : public ::testing::Test +{ +protected: + struct Multiplier + { + std::string name; + std::function getValue; + }; + + struct ActionWithMultipliers + { + std::string name; + float basePriority; + std::vector multiplierNames; + std::function execute; + }; + + struct MultiplierContext + { + float healthPercent; + float manaPercent; + bool targetIsElite; + int aoeTargetCount; + }; + + std::map multipliers_; + std::vector actions_; + MultiplierContext context_; + + void SetUp() override + { + context_ = {100.0f, 100.0f, false, 1}; + + multipliers_["health_urgency"] = { + "health_urgency", + [this]() { + if (context_.healthPercent < 20.0f) return 3.0f; + if (context_.healthPercent < 50.0f) return 2.0f; + return 1.0f; + } + }; + + multipliers_["mana_conservation"] = { + "mana_conservation", + [this]() { + if (context_.manaPercent < 20.0f) return 0.5f; + if (context_.manaPercent < 50.0f) return 0.8f; + return 1.0f; + } + }; + + multipliers_["aoe_bonus"] = { + "aoe_bonus", + [this]() { + return 1.0f + (context_.aoeTargetCount - 1) * 0.5f; + } + }; + + multipliers_["elite_bonus"] = { + "elite_bonus", + [this]() { + return context_.targetIsElite ? 1.5f : 1.0f; + } + }; + + actions_ = { + {"heal", 50.0f, {"health_urgency", "mana_conservation"}, []() { return true; }}, + {"single_target_attack", 40.0f, {"elite_bonus"}, []() { return true; }}, + {"aoe_attack", 30.0f, {"aoe_bonus"}, []() { return true; }}, + }; + } + + float CalculateActionPriority(ActionWithMultipliers const& action) + { + float priority = action.basePriority; + + for (auto const& multName : action.multiplierNames) + { + auto it = multipliers_.find(multName); + if (it != multipliers_.end()) + { + priority *= it->second.getValue(); + } + } + + return priority; + } + + std::string SelectBestAction() + { + ActionWithMultipliers const* best = nullptr; + float bestPriority = -1.0f; + + for (auto const& action : actions_) + { + float priority = CalculateActionPriority(action); + if (priority > bestPriority) + { + best = &action; + bestPriority = priority; + } + } + + return best ? best->name : ""; + } +}; + +TEST_F(TriggerMultiplierActionTest, NormalPriority) +{ + // All multipliers are 1.0 + float healPriority = CalculateActionPriority(actions_[0]); + EXPECT_FLOAT_EQ(50.0f, healPriority); +} + +TEST_F(TriggerMultiplierActionTest, LowHealthBoostsPriority) +{ + context_.healthPercent = 30.0f; + + float healPriority = CalculateActionPriority(actions_[0]); + EXPECT_FLOAT_EQ(100.0f, healPriority); // 50 * 2.0 * 1.0 +} + +TEST_F(TriggerMultiplierActionTest, LowManaReducesPriority) +{ + context_.manaPercent = 30.0f; + + float healPriority = CalculateActionPriority(actions_[0]); + EXPECT_FLOAT_EQ(40.0f, healPriority); // 50 * 1.0 * 0.8 +} + +TEST_F(TriggerMultiplierActionTest, CombinedMultipliers) +{ + context_.healthPercent = 30.0f; // 2.0x + context_.manaPercent = 30.0f; // 0.8x + + float healPriority = CalculateActionPriority(actions_[0]); + EXPECT_FLOAT_EQ(80.0f, healPriority); // 50 * 2.0 * 0.8 +} + +TEST_F(TriggerMultiplierActionTest, AoeBonusWithMultipleTargets) +{ + context_.aoeTargetCount = 5; + + float aoePriority = CalculateActionPriority(actions_[2]); + EXPECT_FLOAT_EQ(90.0f, aoePriority); // 30 * (1.0 + 4*0.5) = 30 * 3.0 +} + +TEST_F(TriggerMultiplierActionTest, EliteBonusApplied) +{ + context_.targetIsElite = true; + + float stPriority = CalculateActionPriority(actions_[1]); + EXPECT_FLOAT_EQ(60.0f, stPriority); // 40 * 1.5 +} + +TEST_F(TriggerMultiplierActionTest, SelectsAoeWithManyTargets) +{ + context_.aoeTargetCount = 5; + + std::string best = SelectBestAction(); + EXPECT_EQ("aoe_attack", best); +} + +TEST_F(TriggerMultiplierActionTest, SelectsHealWhenLowHealth) +{ + context_.healthPercent = 25.0f; + + std::string best = SelectBestAction(); + EXPECT_EQ("heal", best); +} + +/** + * @brief Tests for strategy stacking behavior + */ +class StrategyStackingTest : public ::testing::Test +{ +protected: + struct Strategy + { + std::string name; + int priority; + std::vector triggers; + std::vector actions; + std::map actionMultipliers; + }; + + std::vector activeStrategies_; + + void SetUp() override + { + activeStrategies_ = { + { + "combat", + 100, + {"enemy_in_range", "low_health"}, + {"attack", "heal"}, + {{"attack", 1.0f}, {"heal", 1.0f}} + }, + { + "tank", + 90, + {"enemy_targeting_ally"}, + {"taunt", "shield_block"}, + {{"attack", 0.8f}, {"taunt", 2.0f}} + }, + { + "defensive", + 80, + {}, + {"heal", "shield"}, + {{"heal", 1.5f}, {"attack", 0.5f}} + }, + }; + } + + std::vector GetAllTriggers() + { + std::set triggerSet; + for (auto const& strategy : activeStrategies_) + { + for (auto const& trigger : strategy.triggers) + { + triggerSet.insert(trigger); + } + } + return std::vector(triggerSet.begin(), triggerSet.end()); + } + + std::vector GetAllActions() + { + std::set actionSet; + for (auto const& strategy : activeStrategies_) + { + for (auto const& action : strategy.actions) + { + actionSet.insert(action); + } + } + return std::vector(actionSet.begin(), actionSet.end()); + } + + float GetCombinedMultiplier(std::string const& actionName) + { + float combined = 1.0f; + for (auto const& strategy : activeStrategies_) + { + auto it = strategy.actionMultipliers.find(actionName); + if (it != strategy.actionMultipliers.end()) + { + combined *= it->second; + } + } + return combined; + } + + bool HasConflict(std::string const& action1, std::string const& action2) + { + // Check if actions are mutually exclusive + // Simplified: attack and heal can't happen same GCD + if ((action1 == "attack" && action2 == "heal") || + (action1 == "heal" && action2 == "attack")) + return true; + return false; + } +}; + +TEST_F(StrategyStackingTest, CombinesTriggers) +{ + auto triggers = GetAllTriggers(); + + EXPECT_EQ(3u, triggers.size()); +} + +TEST_F(StrategyStackingTest, CombinesActions) +{ + auto actions = GetAllActions(); + + // attack, heal, taunt, shield_block, shield + EXPECT_EQ(5u, actions.size()); +} + +TEST_F(StrategyStackingTest, MultiplierStacking) +{ + // Attack: combat(1.0) * tank(0.8) * defensive(0.5) = 0.4 + float attackMult = GetCombinedMultiplier("attack"); + EXPECT_FLOAT_EQ(0.4f, attackMult); + + // Heal: combat(1.0) * defensive(1.5) = 1.5 + float healMult = GetCombinedMultiplier("heal"); + EXPECT_FLOAT_EQ(1.5f, healMult); + + // Taunt: tank(2.0) = 2.0 + float tauntMult = GetCombinedMultiplier("taunt"); + EXPECT_FLOAT_EQ(2.0f, tauntMult); +} + +TEST_F(StrategyStackingTest, ActionConflicts) +{ + EXPECT_TRUE(HasConflict("attack", "heal")); + EXPECT_FALSE(HasConflict("attack", "taunt")); + EXPECT_FALSE(HasConflict("heal", "shield")); +} + +/** + * @brief Tests for complete combat simulation + */ +class CombatSimulationTest : public ::testing::Test +{ +protected: + struct CombatState + { + float botHealth; + float botMana; + float targetHealth; + int tickCount; + std::vector actionLog; + }; + + CombatState state_; + + void SetUp() override + { + state_ = {100.0f, 100.0f, 100.0f, 0, {}}; + } + + std::string DetermineAction() + { + // Priority: Critical heal > Normal heal > Attack + if (state_.botHealth < 20.0f && state_.botMana >= 30.0f) + return "emergency_heal"; + if (state_.botHealth < 50.0f && state_.botMana >= 20.0f) + return "heal"; + if (state_.targetHealth > 0.0f && state_.botMana >= 10.0f) + return "attack"; + return "wand"; // No mana, use wand + } + + void ExecuteAction(std::string const& action) + { + state_.actionLog.push_back(action); + + if (action == "emergency_heal") + { + state_.botHealth = std::min(100.0f, state_.botHealth + 40.0f); + state_.botMana -= 30.0f; + } + else if (action == "heal") + { + state_.botHealth = std::min(100.0f, state_.botHealth + 25.0f); + state_.botMana -= 20.0f; + } + else if (action == "attack") + { + state_.targetHealth -= 15.0f; + state_.botMana -= 10.0f; + } + else if (action == "wand") + { + state_.targetHealth -= 5.0f; + } + } + + void SimulateTick() + { + state_.tickCount++; + + // Bot takes damage + state_.botHealth -= 8.0f; + + // Regenerate some mana + state_.botMana = std::min(100.0f, state_.botMana + 2.0f); + + // Execute action + std::string action = DetermineAction(); + ExecuteAction(action); + } + + bool SimulateCombat(int maxTicks) + { + while (state_.tickCount < maxTicks) + { + SimulateTick(); + + // Check end conditions + if (state_.botHealth <= 0.0f) + return false; // Bot died + if (state_.targetHealth <= 0.0f) + return true; // Target died + } + + return state_.targetHealth <= 0.0f; + } +}; + +TEST_F(CombatSimulationTest, BotSurvivesCombat) +{ + bool victory = SimulateCombat(20); + + EXPECT_TRUE(victory); + EXPECT_GT(state_.botHealth, 0.0f); +} + +TEST_F(CombatSimulationTest, HealsWhenLow) +{ + SimulateCombat(20); + + // Should have healed at some point + bool healed = false; + for (auto const& action : state_.actionLog) + { + if (action == "heal" || action == "emergency_heal") + { + healed = true; + break; + } + } + + EXPECT_TRUE(healed); +} + +TEST_F(CombatSimulationTest, UsesWandWhenOom) +{ + state_.botMana = 5.0f; + + std::string action = DetermineAction(); + EXPECT_EQ("wand", action); +} + diff --git a/test/unit/Ai/Movement/PositioningTest.cpp b/test/unit/Ai/Movement/PositioningTest.cpp new file mode 100644 index 0000000000..3a248346ae --- /dev/null +++ b/test/unit/Ai/Movement/PositioningTest.cpp @@ -0,0 +1,518 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include +#include +#include + +/** + * @brief Tests for positioning and movement logic + */ + +/** + * @brief Tests for formation positioning + */ +class FormationPositioningTest : public ::testing::Test +{ +protected: + struct Position + { + float x, y, z; + float orientation; + }; + + struct FormationSlot + { + float offsetX; // Relative to leader, in leader's local space + float offsetY; + int priority; // Lower = closer to leader + }; + + Position leaderPos_; + std::vector formationSlots_; + + void SetUp() override + { + leaderPos_ = {100.0f, 100.0f, 0.0f, 0.0f}; + + // V-formation behind leader + formationSlots_ = { + {-2.0f, -2.0f, 1}, // Back-left + {2.0f, -2.0f, 2}, // Back-right + {-4.0f, -4.0f, 3}, // Far back-left + {4.0f, -4.0f, 4}, // Far back-right + }; + } + + float Distance(Position const& a, Position const& b) + { + float dx = a.x - b.x; + float dy = a.y - b.y; + return std::sqrt(dx * dx + dy * dy); + } + + Position CalculateWorldPosition(FormationSlot const& slot, Position const& leader) + { + // Rotate offset by leader's orientation + float cosO = std::cos(leader.orientation); + float sinO = std::sin(leader.orientation); + + float worldOffsetX = slot.offsetX * cosO - slot.offsetY * sinO; + float worldOffsetY = slot.offsetX * sinO + slot.offsetY * cosO; + + return { + leader.x + worldOffsetX, + leader.y + worldOffsetY, + leader.z, + leader.orientation + }; + } + + bool IsInFormation(Position const& current, FormationSlot const& slot, float tolerance = 1.0f) + { + Position target = CalculateWorldPosition(slot, leaderPos_); + return Distance(current, target) <= tolerance; + } +}; + +TEST_F(FormationPositioningTest, CalculatesWorldPosition) +{ + // Leader facing east (orientation = 0) + Position pos = CalculateWorldPosition(formationSlots_[0], leaderPos_); + + EXPECT_NEAR(98.0f, pos.x, 0.01f); // -2 X offset + EXPECT_NEAR(98.0f, pos.y, 0.01f); // -2 Y offset (behind) +} + +TEST_F(FormationPositioningTest, RotatesWithLeader) +{ + leaderPos_.orientation = M_PI / 2.0f; // Facing north + + Position pos = CalculateWorldPosition(formationSlots_[0], leaderPos_); + + // When facing north, local (-2, -2) rotates to world (2, -2) + EXPECT_NEAR(102.0f, pos.x, 0.01f); + EXPECT_NEAR(98.0f, pos.y, 0.01f); +} + +TEST_F(FormationPositioningTest, InFormationCheck) +{ + Position target = CalculateWorldPosition(formationSlots_[0], leaderPos_); + Position current = target; + + EXPECT_TRUE(IsInFormation(current, formationSlots_[0])); + + current.x += 2.0f; // Move out of tolerance + EXPECT_FALSE(IsInFormation(current, formationSlots_[0])); +} + +/** + * @brief Tests for combat positioning + */ +class CombatPositioningTest : public ::testing::Test +{ +protected: + struct Position + { + float x, y; + }; + + struct CombatContext + { + Position bossPos; + float bossOrientation; + Position tankPos; + bool isMelee; + bool isRanged; + float optimalRange; + }; + + CombatContext context_; + + void SetUp() override + { + context_ = { + {100.0f, 100.0f}, // Boss at center + 0.0f, // Boss facing east + {105.0f, 100.0f}, // Tank in front (east of boss) + true, // Is melee + false, + 5.0f // Optimal range + }; + } + + float Distance(Position const& a, Position const& b) + { + float dx = a.x - b.x; + float dy = a.y - b.y; + return std::sqrt(dx * dx + dy * dy); + } + + float AngleFromBoss(Position const& pos) + { + float dx = pos.x - context_.bossPos.x; + float dy = pos.y - context_.bossPos.y; + return std::atan2(dy, dx) - context_.bossOrientation; + } + + bool IsBehindBoss(Position const& pos) + { + float angle = AngleFromBoss(pos); + // Behind is roughly 120-240 degrees from front + float absAngle = std::abs(angle); + return absAngle > 2.0f && absAngle < 4.2f; // ~115-240 degrees + } + + bool IsInFront(Position const& pos) + { + float angle = std::abs(AngleFromBoss(pos)); + return angle < 1.0f; // ~60 degrees + } + + Position CalculateMeleePosition(bool preferBehind) + { + float angle; + if (preferBehind) + { + angle = context_.bossOrientation + M_PI; // Behind + } + else + { + angle = context_.bossOrientation + M_PI / 2.0f; // Side + } + + return { + context_.bossPos.x + std::cos(angle) * context_.optimalRange, + context_.bossPos.y + std::sin(angle) * context_.optimalRange + }; + } + + Position CalculateRangedPosition() + { + // Max range, spread from others + float angle = context_.bossOrientation + M_PI * 0.75f; // Back-side + return { + context_.bossPos.x + std::cos(angle) * context_.optimalRange, + context_.bossPos.y + std::sin(angle) * context_.optimalRange + }; + } +}; + +TEST_F(CombatPositioningTest, TankIsInFront) +{ + EXPECT_TRUE(IsInFront(context_.tankPos)); + EXPECT_FALSE(IsBehindBoss(context_.tankPos)); +} + +TEST_F(CombatPositioningTest, CalculateBehindPosition) +{ + Position behind = CalculateMeleePosition(true); + EXPECT_TRUE(IsBehindBoss(behind)); +} + +TEST_F(CombatPositioningTest, MeleeOptimalRange) +{ + Position pos = CalculateMeleePosition(true); + float dist = Distance(pos, context_.bossPos); + EXPECT_NEAR(context_.optimalRange, dist, 0.01f); +} + +/** + * @brief Tests for AoE avoidance + */ +class AoeAvoidanceTest : public ::testing::Test +{ +protected: + struct Position + { + float x, y; + }; + + struct AoeZone + { + Position center; + float radius; + uint32 durationMs; + float damagePerSecond; + }; + + std::vector aoeZones_; + + void SetUp() override + { + aoeZones_ = { + {{100.0f, 100.0f}, 10.0f, 5000, 1000.0f}, + {{120.0f, 100.0f}, 8.0f, 3000, 2000.0f}, + }; + } + + float Distance(Position const& a, Position const& b) + { + float dx = a.x - b.x; + float dy = a.y - b.y; + return std::sqrt(dx * dx + dy * dy); + } + + bool IsInAoe(Position const& pos) + { + for (auto const& zone : aoeZones_) + { + if (Distance(pos, zone.center) <= zone.radius) + return true; + } + return false; + } + + Position FindSafePosition(Position const& current, float moveSpeed, uint32 reactionTime) + { + if (!IsInAoe(current)) + return current; + + // Find direction away from nearest AoE + float nearestDist = 9999.0f; + Position nearestCenter{0, 0}; + + for (auto const& zone : aoeZones_) + { + float dist = Distance(current, zone.center); + if (dist < nearestDist) + { + nearestDist = dist; + nearestCenter = zone.center; + } + } + + // Move directly away + float dx = current.x - nearestCenter.x; + float dy = current.y - nearestCenter.y; + float len = std::sqrt(dx * dx + dy * dy); + if (len < 0.01f) + { + dx = 1.0f; + len = 1.0f; + } + + float moveDistance = moveSpeed * (reactionTime / 1000.0f); + + return { + current.x + (dx / len) * moveDistance, + current.y + (dy / len) * moveDistance + }; + } + + bool WillExitAoeInTime(Position const& current, float moveSpeed, AoeZone const& zone) + { + float distToEdge = zone.radius - Distance(current, zone.center); + if (distToEdge <= 0) + return true; // Already out + + float timeToExit = distToEdge / moveSpeed; + return timeToExit * 1000.0f < zone.durationMs; + } +}; + +TEST_F(AoeAvoidanceTest, DetectsInAoe) +{ + EXPECT_TRUE(IsInAoe({100.0f, 100.0f})); + EXPECT_TRUE(IsInAoe({105.0f, 100.0f})); + EXPECT_FALSE(IsInAoe({150.0f, 150.0f})); +} + +TEST_F(AoeAvoidanceTest, FindsSafePosition) +{ + // Start at edge of first AoE, away from second AoE + Position unsafe{95.0f, 100.0f}; // 5 yards from center, need to move 5+ yards to escape + Position safe = FindSafePosition(unsafe, 7.0f, 1000); // 7 yards/sec, 1000ms = 7 yards movement + + EXPECT_FALSE(IsInAoe(safe)); +} + +TEST_F(AoeAvoidanceTest, AlreadySafeReturnsCurrentPosition) +{ + Position safe{150.0f, 150.0f}; + Position result = FindSafePosition(safe, 7.0f, 500); + + EXPECT_FLOAT_EQ(safe.x, result.x); + EXPECT_FLOAT_EQ(safe.y, result.y); +} + +TEST_F(AoeAvoidanceTest, CanExitInTime) +{ + Position nearEdge{109.0f, 100.0f}; // 1 yard from edge + EXPECT_TRUE(WillExitAoeInTime(nearEdge, 7.0f, aoeZones_[0])); + + Position center{100.0f, 100.0f}; // 10 yards from edge + // At 7 yards/sec, 10 yards takes 1.43 seconds, which is under 5s duration + EXPECT_TRUE(WillExitAoeInTime(center, 7.0f, aoeZones_[0])); + + // Test case where you can't make it: slow movement, short duration + // aoeZones_[1] has 3000ms duration. At 1 yard/sec, 8 yards takes 8 seconds > 3 seconds + Position inSecondAoe{120.0f, 100.0f}; // Center of second AoE (8 yard radius) + EXPECT_FALSE(WillExitAoeInTime(inSecondAoe, 1.0f, aoeZones_[1])); +} + +/** + * @brief Tests for spread/stack mechanics + */ +class SpreadStackTest : public ::testing::Test +{ +protected: + struct Position + { + float x, y; + }; + + struct GroupMember + { + uint32 guid; + Position pos; + }; + + std::vector group_; + float spreadDistance_; + float stackDistance_; + + void SetUp() override + { + group_ = { + {1, {100.0f, 100.0f}}, + {2, {105.0f, 100.0f}}, + {3, {100.0f, 105.0f}}, + {4, {110.0f, 110.0f}}, + }; + spreadDistance_ = 8.0f; + stackDistance_ = 3.0f; + } + + float Distance(Position const& a, Position const& b) + { + float dx = a.x - b.x; + float dy = a.y - b.y; + return std::sqrt(dx * dx + dy * dy); + } + + bool IsSpreadCorrectly(uint32 guid) + { + Position myPos{0, 0}; + for (auto const& m : group_) + { + if (m.guid == guid) + { + myPos = m.pos; + break; + } + } + + for (auto const& m : group_) + { + if (m.guid == guid) + continue; + + if (Distance(myPos, m.pos) < spreadDistance_) + return false; + } + return true; + } + + bool IsStackedCorrectly(uint32 guid, Position const& stackPoint) + { + for (auto const& m : group_) + { + if (m.guid == guid) + return Distance(m.pos, stackPoint) <= stackDistance_; + } + return false; + } + + Position CalculateSpreadPosition(uint32 guid, Position const& center) + { + // Find an angle that puts us away from others + int memberIndex = 0; + for (size_t i = 0; i < group_.size(); i++) + { + if (group_[i].guid == guid) + { + memberIndex = static_cast(i); + break; + } + } + + float angle = (2.0f * M_PI * memberIndex) / group_.size(); + return { + center.x + std::cos(angle) * spreadDistance_, + center.y + std::sin(angle) * spreadDistance_ + }; + } + + Position CalculateGroupCenter() + { + float sumX = 0, sumY = 0; + for (auto const& m : group_) + { + sumX += m.pos.x; + sumY += m.pos.y; + } + return {sumX / group_.size(), sumY / group_.size()}; + } +}; + +TEST_F(SpreadStackTest, DetectsNotSpread) +{ + // Members 1 and 2 are only 5 yards apart + EXPECT_FALSE(IsSpreadCorrectly(1)); + EXPECT_FALSE(IsSpreadCorrectly(2)); +} + +TEST_F(SpreadStackTest, DetectsProperlySpread) +{ + // Move member 4 far away + group_[3].pos = {150.0f, 150.0f}; + EXPECT_TRUE(IsSpreadCorrectly(4)); +} + +TEST_F(SpreadStackTest, StackCheck) +{ + Position stackPoint{102.0f, 102.0f}; + + EXPECT_TRUE(IsStackedCorrectly(1, stackPoint)); // 2.8 yards away + EXPECT_FALSE(IsStackedCorrectly(4, stackPoint)); // Too far +} + +TEST_F(SpreadStackTest, CalculatesSpreadPositions) +{ + Position center = CalculateGroupCenter(); + + // All spread positions should be spreadDistance from center + for (auto const& m : group_) + { + Position spreadPos = CalculateSpreadPosition(m.guid, center); + float dist = Distance(spreadPos, center); + EXPECT_NEAR(spreadDistance_, dist, 0.01f); + } +} + +TEST_F(SpreadStackTest, SpreadPositionsAreSeparated) +{ + Position center = CalculateGroupCenter(); + + std::vector spreadPositions; + for (auto const& m : group_) + { + spreadPositions.push_back(CalculateSpreadPosition(m.guid, center)); + } + + // Check all pairs are adequately separated + for (size_t i = 0; i < spreadPositions.size(); i++) + { + for (size_t j = i + 1; j < spreadPositions.size(); j++) + { + float dist = Distance(spreadPositions[i], spreadPositions[j]); + EXPECT_GT(dist, spreadDistance_ * 0.5f); // At least half spread distance apart + } + } +} + diff --git a/test/unit/Ai/Pet/PetManagementTest.cpp b/test/unit/Ai/Pet/PetManagementTest.cpp new file mode 100644 index 0000000000..95d9338459 --- /dev/null +++ b/test/unit/Ai/Pet/PetManagementTest.cpp @@ -0,0 +1,478 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include +#include +#include + +/** + * @brief Tests for pet management logic + */ + +/** + * @brief Tests for pet stance management + */ +class PetStanceTest : public ::testing::Test +{ +protected: + enum class PetStance + { + Passive, + Defensive, + Aggressive + }; + + struct PetContext + { + bool ownerInCombat; + bool petHasTarget; + float petHealthPercent; + float distanceToOwner; + bool isPulling; // Owner is pulling mobs + }; + + PetContext context_; + PetStance currentStance_; + + void SetUp() override + { + context_ = {false, false, 100.0f, 5.0f, false}; + currentStance_ = PetStance::Defensive; + } + + PetStance DetermineOptimalStance() + { + // During pulls, be passive to not break CC + if (context_.isPulling) + return PetStance::Passive; + + // Low health pet should be passive + if (context_.petHealthPercent < 20.0f) + return PetStance::Passive; + + // In combat, be defensive + if (context_.ownerInCombat) + return PetStance::Defensive; + + // Out of combat, stay passive + return PetStance::Passive; + } + + bool ShouldRecallPet() + { + // Recall if too far + if (context_.distanceToOwner > 40.0f) + return true; + + // Recall if pet health is critical + if (context_.petHealthPercent < 15.0f) + return true; + + return false; + } +}; + +TEST_F(PetStanceTest, PassiveDuringPull) +{ + context_.isPulling = true; + EXPECT_EQ(PetStance::Passive, DetermineOptimalStance()); +} + +TEST_F(PetStanceTest, PassiveWhenLowHealth) +{ + context_.petHealthPercent = 15.0f; + EXPECT_EQ(PetStance::Passive, DetermineOptimalStance()); +} + +TEST_F(PetStanceTest, DefensiveInCombat) +{ + context_.ownerInCombat = true; + EXPECT_EQ(PetStance::Defensive, DetermineOptimalStance()); +} + +TEST_F(PetStanceTest, PassiveOutOfCombat) +{ + EXPECT_EQ(PetStance::Passive, DetermineOptimalStance()); +} + +TEST_F(PetStanceTest, RecallWhenFar) +{ + context_.distanceToOwner = 50.0f; + EXPECT_TRUE(ShouldRecallPet()); +} + +TEST_F(PetStanceTest, RecallWhenCriticalHealth) +{ + context_.petHealthPercent = 10.0f; + EXPECT_TRUE(ShouldRecallPet()); +} + +/** + * @brief Tests for pet ability usage + */ +class PetAbilityTest : public ::testing::Test +{ +protected: + struct PetAbility + { + uint32 id; + std::string name; + std::string type; // "damage", "utility", "defensive" + uint32 cooldownMs; + uint32 cooldownRemaining; + bool autocast; + }; + + struct PetAbilityContext + { + bool inCombat; + bool petHasTarget; + float petFocusPercent; + std::vector abilities; + }; + + PetAbilityContext context_; + + void SetUp() override + { + context_ = { + true, true, 100.0f, + { + {1, "Bite", "damage", 0, 0, true}, + {2, "Claw", "damage", 0, 0, true}, + {3, "Growl", "utility", 5000, 0, true}, // Taunt + {4, "Cower", "defensive", 10000, 0, false}, + {5, "Dash", "utility", 30000, 0, false}, + } + }; + } + + std::vector GetAutocastAbilities() + { + std::vector result; + for (auto const& ability : context_.abilities) + { + if (ability.autocast) + result.push_back(ability.id); + } + return result; + } + + bool ShouldUseAbility(uint32 abilityId) + { + for (auto const& ability : context_.abilities) + { + if (ability.id != abilityId) + continue; + + // Check cooldown + if (ability.cooldownRemaining > 0) + return false; + + // Must be in combat with target for most abilities + if (ability.type != "defensive" && !context_.petHasTarget) + return false; + + return true; + } + return false; + } + + bool ShouldToggleGrowl(bool tankPetIsActive) + { + // Growl should only be on if pet is tank + for (auto& ability : context_.abilities) + { + if (ability.name == "Growl") + return ability.autocast != tankPetIsActive; + } + return false; + } +}; + +TEST_F(PetAbilityTest, AutocastAbilitiesListed) +{ + auto autocast = GetAutocastAbilities(); + EXPECT_EQ(3u, autocast.size()); // Bite, Claw, Growl +} + +TEST_F(PetAbilityTest, CanUseOffCooldown) +{ + EXPECT_TRUE(ShouldUseAbility(1)); // Bite ready +} + +TEST_F(PetAbilityTest, CannotUseOnCooldown) +{ + context_.abilities[0].cooldownRemaining = 5000; + EXPECT_FALSE(ShouldUseAbility(1)); +} + +TEST_F(PetAbilityTest, CannotUseDamageWithoutTarget) +{ + context_.petHasTarget = false; + EXPECT_FALSE(ShouldUseAbility(1)); +} + +TEST_F(PetAbilityTest, CanUseDefensiveWithoutTarget) +{ + context_.petHasTarget = false; + EXPECT_TRUE(ShouldUseAbility(4)); // Cower is defensive +} + +TEST_F(PetAbilityTest, ToggleGrowlForTankPet) +{ + // Growl is currently on (autocast = true) + // If pet is tank, it should stay on + EXPECT_FALSE(ShouldToggleGrowl(true)); + + // If pet is not tank, Growl should be toggled off + EXPECT_TRUE(ShouldToggleGrowl(false)); +} + +/** + * @brief Tests for pet positioning + */ +class PetPositioningTest : public ::testing::Test +{ +protected: + struct Position + { + float x, y; + }; + + struct PetPositionContext + { + Position ownerPos; + Position targetPos; + float ownerOrientation; + bool isMeleePet; + bool isRangedPet; + float attackRange; + }; + + PetPositionContext context_; + + void SetUp() override + { + context_ = { + {100.0f, 100.0f}, + {120.0f, 100.0f}, + 0.0f, // Facing east + true, + false, + 5.0f + }; + } + + float Distance(Position const& a, Position const& b) + { + float dx = a.x - b.x; + float dy = a.y - b.y; + return std::sqrt(dx * dx + dy * dy); + } + + Position CalculateMeleePetPosition() + { + // Position behind target + float angle = std::atan2( + context_.targetPos.y - context_.ownerPos.y, + context_.targetPos.x - context_.ownerPos.x + ); + + float behindAngle = angle + M_PI; // Opposite side + + return { + context_.targetPos.x + std::cos(behindAngle) * context_.attackRange, + context_.targetPos.y + std::sin(behindAngle) * context_.attackRange + }; + } + + Position CalculateRangedPetPosition() + { + // Position at max range, to the side + float angle = std::atan2( + context_.targetPos.y - context_.ownerPos.y, + context_.targetPos.x - context_.ownerPos.x + ); + + float sideAngle = angle + M_PI / 2.0f; // 90 degrees to side + + return { + context_.targetPos.x + std::cos(sideAngle) * context_.attackRange, + context_.targetPos.y + std::sin(sideAngle) * context_.attackRange + }; + } + + Position CalculateFollowPosition() + { + // Behind owner at a set distance + float behindAngle = context_.ownerOrientation + M_PI; + return { + context_.ownerPos.x + std::cos(behindAngle) * 3.0f, + context_.ownerPos.y + std::sin(behindAngle) * 3.0f + }; + } +}; + +TEST_F(PetPositioningTest, MeleePetBehindTarget) +{ + Position pos = CalculateMeleePetPosition(); + + // Pet positions opposite to owner-target direction + // Owner at 100, target at 120, so pet goes to target - attackRange = 115 + EXPECT_LT(pos.x, context_.targetPos.x); + EXPECT_NEAR(context_.targetPos.y, pos.y, 0.1f); +} + +TEST_F(PetPositioningTest, MeleePetAtMeleeRange) +{ + Position pos = CalculateMeleePetPosition(); + float dist = Distance(pos, context_.targetPos); + EXPECT_NEAR(context_.attackRange, dist, 0.01f); +} + +TEST_F(PetPositioningTest, RangedPetToSide) +{ + context_.isRangedPet = true; + context_.isMeleePet = false; + + Position pos = CalculateRangedPetPosition(); + + // Should be to the side (y different from target) + EXPECT_NE(context_.targetPos.y, pos.y); +} + +TEST_F(PetPositioningTest, FollowPositionBehindOwner) +{ + Position pos = CalculateFollowPosition(); + + // Should be behind owner (opposite of orientation) + // Owner facing east (0), so behind is west + EXPECT_LT(pos.x, context_.ownerPos.x); +} + +/** + * @brief Tests for pet health management + */ +class PetHealthManagementTest : public ::testing::Test +{ +protected: + struct PetHealthContext + { + float petHealthPercent; + float petMaxHealth; + bool ownerCanHealPet; + bool hasMendPet; + uint32 mendPetCooldown; + bool petInCombat; + }; + + PetHealthContext context_; + + void SetUp() override + { + context_ = { + 100.0f, 10000.0f, true, true, 0, true + }; + } + + bool ShouldHealPet() + { + if (!context_.ownerCanHealPet) + return false; + if (context_.mendPetCooldown > 0) + return false; + return context_.petHealthPercent < 50.0f; + } + + bool ShouldDismissPet() + { + // Dismiss if pet is about to die and we can't save it + if (context_.petHealthPercent < 10.0f) + { + if (context_.mendPetCooldown > 5000) + return true; + if (!context_.ownerCanHealPet) + return true; + } + return false; + } + + bool ShouldRevivePet() + { + // Pet is dead (0% health) + if (context_.petHealthPercent <= 0.0f) + return !context_.petInCombat; // Revive out of combat + return false; + } + + float CalculateHealPriority() + { + // Priority based on health deficit + float deficit = 100.0f - context_.petHealthPercent; + + // Pet healing is lower priority than player healing + return deficit * 0.5f; + } +}; + +TEST_F(PetHealthManagementTest, HealBelowThreshold) +{ + context_.petHealthPercent = 40.0f; + EXPECT_TRUE(ShouldHealPet()); +} + +TEST_F(PetHealthManagementTest, DontHealAboveThreshold) +{ + context_.petHealthPercent = 60.0f; + EXPECT_FALSE(ShouldHealPet()); +} + +TEST_F(PetHealthManagementTest, DontHealOnCooldown) +{ + context_.petHealthPercent = 30.0f; + context_.mendPetCooldown = 5000; + EXPECT_FALSE(ShouldHealPet()); +} + +TEST_F(PetHealthManagementTest, DismissWhenDying) +{ + context_.petHealthPercent = 5.0f; + context_.mendPetCooldown = 10000; + EXPECT_TRUE(ShouldDismissPet()); +} + +TEST_F(PetHealthManagementTest, DontDismissWhenCanHeal) +{ + context_.petHealthPercent = 5.0f; + context_.mendPetCooldown = 0; + EXPECT_FALSE(ShouldDismissPet()); +} + +TEST_F(PetHealthManagementTest, ReviveOutOfCombat) +{ + context_.petHealthPercent = 0.0f; + context_.petInCombat = false; + EXPECT_TRUE(ShouldRevivePet()); +} + +TEST_F(PetHealthManagementTest, DontReviveInCombat) +{ + context_.petHealthPercent = 0.0f; + context_.petInCombat = true; + EXPECT_FALSE(ShouldRevivePet()); +} + +TEST_F(PetHealthManagementTest, HealPriorityLowerThanPlayer) +{ + context_.petHealthPercent = 40.0f; // 60% deficit + float priority = CalculateHealPriority(); + + // Pet priority should be half of deficit (30) + EXPECT_FLOAT_EQ(30.0f, priority); +} + diff --git a/test/unit/Ai/Resource/ResourceManagementTest.cpp b/test/unit/Ai/Resource/ResourceManagementTest.cpp new file mode 100644 index 0000000000..1e17c46d6e --- /dev/null +++ b/test/unit/Ai/Resource/ResourceManagementTest.cpp @@ -0,0 +1,516 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include +#include +#include +#include + +/** + * @brief Tests for resource management logic + */ + +/** + * @brief Tests for potion usage + */ +class PotionUsageTest : public ::testing::Test +{ +protected: + struct Potion + { + uint32 itemId; + std::string type; // "health", "mana", "both" + uint32 restoreAmount; + uint32 cooldownMs; + }; + + struct PotionContext + { + float healthPercent; + float manaPercent; + bool inCombat; + uint32 potionCooldownRemaining; + std::vector availablePotions; + }; + + PotionContext context_; + + void SetUp() override + { + context_ = { + 100.0f, 100.0f, true, 0, + { + {1, "health", 2000, 60000}, + {2, "mana", 3000, 60000}, + {3, "both", 1500, 60000}, // Rejuvenation potion + } + }; + } + + bool ShouldUseHealthPotion() + { + if (context_.potionCooldownRemaining > 0) + return false; + if (!context_.inCombat) + return false; + return context_.healthPercent < 35.0f; + } + + bool ShouldUseManaPotion() + { + if (context_.potionCooldownRemaining > 0) + return false; + if (!context_.inCombat) + return false; + return context_.manaPercent < 20.0f; + } + + uint32 SelectBestPotion() + { + if (context_.potionCooldownRemaining > 0) + return 0; + + bool needHealth = context_.healthPercent < 35.0f; + bool needMana = context_.manaPercent < 20.0f; + + if (needHealth && needMana) + { + // Look for combo potion first + for (auto const& potion : context_.availablePotions) + { + if (potion.type == "both") + return potion.itemId; + } + } + + if (needHealth) + { + for (auto const& potion : context_.availablePotions) + { + if (potion.type == "health") + return potion.itemId; + } + } + + if (needMana) + { + for (auto const& potion : context_.availablePotions) + { + if (potion.type == "mana") + return potion.itemId; + } + } + + return 0; + } +}; + +TEST_F(PotionUsageTest, NoNeedAtFullHealth) +{ + EXPECT_FALSE(ShouldUseHealthPotion()); +} + +TEST_F(PotionUsageTest, UseHealthPotionWhenLow) +{ + context_.healthPercent = 25.0f; + EXPECT_TRUE(ShouldUseHealthPotion()); +} + +TEST_F(PotionUsageTest, NoPotionOutOfCombat) +{ + context_.healthPercent = 25.0f; + context_.inCombat = false; + EXPECT_FALSE(ShouldUseHealthPotion()); +} + +TEST_F(PotionUsageTest, NoPotionOnCooldown) +{ + context_.healthPercent = 25.0f; + context_.potionCooldownRemaining = 30000; + EXPECT_FALSE(ShouldUseHealthPotion()); +} + +TEST_F(PotionUsageTest, SelectsHealthPotion) +{ + context_.healthPercent = 25.0f; + EXPECT_EQ(1u, SelectBestPotion()); +} + +TEST_F(PotionUsageTest, SelectsManaPotion) +{ + context_.manaPercent = 15.0f; + EXPECT_EQ(2u, SelectBestPotion()); +} + +TEST_F(PotionUsageTest, SelectsComboPotionWhenBothNeeded) +{ + context_.healthPercent = 25.0f; + context_.manaPercent = 15.0f; + EXPECT_EQ(3u, SelectBestPotion()); +} + +/** + * @brief Tests for healthstone/mana gem usage + */ +class SpecialItemUsageTest : public ::testing::Test +{ +protected: + struct SpecialItem + { + uint32 itemId; + std::string type; + uint32 restoreAmount; + uint32 cooldownMs; + uint32 cooldownRemaining; + }; + + struct ItemContext + { + float healthPercent; + float manaPercent; + bool inCombat; + uint32 potionCooldownRemaining; + std::vector items; + }; + + ItemContext context_; + + void SetUp() override + { + context_ = { + 100.0f, 100.0f, true, 0, + { + {1, "healthstone", 4000, 120000, 0}, + {2, "mana_gem", 3000, 120000, 0}, + } + }; + } + + bool ShouldUseHealthstone() + { + for (auto const& item : context_.items) + { + if (item.type == "healthstone" && item.cooldownRemaining == 0) + { + // Use at lower threshold than potion (save for emergencies) + return context_.healthPercent < 25.0f; + } + } + return false; + } + + bool ShouldUseManaGem() + { + for (auto const& item : context_.items) + { + if (item.type == "mana_gem" && item.cooldownRemaining == 0) + { + return context_.manaPercent < 15.0f; + } + } + return false; + } + + bool PrefersHealthstoneOverPotion() + { + // Healthstone doesn't share potion cooldown, so prefer it + // to save potion for when we need mana too + for (auto const& item : context_.items) + { + if (item.type == "healthstone" && item.cooldownRemaining == 0) + return true; + } + return false; + } +}; + +TEST_F(SpecialItemUsageTest, UseHealthstoneWhenCritical) +{ + context_.healthPercent = 20.0f; + EXPECT_TRUE(ShouldUseHealthstone()); +} + +TEST_F(SpecialItemUsageTest, DontUseHealthstoneWhenNotCritical) +{ + context_.healthPercent = 30.0f; // Above 25% threshold + EXPECT_FALSE(ShouldUseHealthstone()); +} + +TEST_F(SpecialItemUsageTest, UseManaGemWhenLow) +{ + context_.manaPercent = 10.0f; + EXPECT_TRUE(ShouldUseManaGem()); +} + +TEST_F(SpecialItemUsageTest, PreferHealthstoneOverPotion) +{ + EXPECT_TRUE(PrefersHealthstoneOverPotion()); +} + +/** + * @brief Tests for mana conservation + */ +class ManaConservationTest : public ::testing::Test +{ +protected: + struct Spell + { + uint32 id; + std::string name; + uint32 manaCost; + float efficiency; // Effect per mana + bool isExpensive; + }; + + struct ConservationContext + { + float manaPercent; + bool inCombat; + float combatTimeRemaining; // Estimated seconds + std::vector spells; + }; + + ConservationContext context_; + + void SetUp() override + { + context_ = { + 100.0f, true, 60.0f, + { + {1, "Flash Heal", 400, 2.0f, true}, + {2, "Heal", 300, 3.0f, false}, + {3, "Greater Heal", 700, 2.5f, true}, + } + }; + } + + float GetManaConservationMultiplier() + { + if (context_.manaPercent > 80.0f) + return 1.0f; // No conservation needed + if (context_.manaPercent > 50.0f) + return 1.2f; // Slight preference for efficient spells + if (context_.manaPercent > 30.0f) + return 1.5f; // Strong preference + return 2.0f; // Critical - heavily favor efficient spells + } + + float CalculateSpellScore(Spell const& spell) + { + float baseScore = spell.efficiency * 100.0f; + + // Apply conservation multiplier + float conservationMult = GetManaConservationMultiplier(); + if (spell.isExpensive) + baseScore /= conservationMult; + + return baseScore; + } + + uint32 SelectMostEfficientSpell() + { + Spell const* best = nullptr; + float bestScore = -1.0f; + + for (auto const& spell : context_.spells) + { + float score = CalculateSpellScore(spell); + if (score > bestScore) + { + best = &spell; + bestScore = score; + } + } + + return best ? best->id : 0; + } + + bool ShouldWandInsteadOfCast() + { + // Wand when mana is very low and not in emergency + return context_.manaPercent < 10.0f; + } +}; + +TEST_F(ManaConservationTest, NoConservationAtHighMana) +{ + EXPECT_FLOAT_EQ(1.0f, GetManaConservationMultiplier()); +} + +TEST_F(ManaConservationTest, ModerateConservationAtMediumMana) +{ + context_.manaPercent = 60.0f; + EXPECT_FLOAT_EQ(1.2f, GetManaConservationMultiplier()); +} + +TEST_F(ManaConservationTest, StrongConservationAtLowMana) +{ + context_.manaPercent = 40.0f; + EXPECT_FLOAT_EQ(1.5f, GetManaConservationMultiplier()); +} + +TEST_F(ManaConservationTest, CriticalConservationAtVeryLowMana) +{ + context_.manaPercent = 20.0f; + EXPECT_FLOAT_EQ(2.0f, GetManaConservationMultiplier()); +} + +TEST_F(ManaConservationTest, SelectsEfficientSpellWhenConserving) +{ + context_.manaPercent = 30.0f; + uint32 selected = SelectMostEfficientSpell(); + EXPECT_EQ(2u, selected); // Heal is most efficient +} + +TEST_F(ManaConservationTest, WandAtCriticalMana) +{ + context_.manaPercent = 5.0f; + EXPECT_TRUE(ShouldWandInsteadOfCast()); +} + +/** + * @brief Tests for food/drink usage + */ +class FoodDrinkUsageTest : public ::testing::Test +{ +protected: + struct Consumable + { + uint32 itemId; + std::string type; // "food", "drink", "both" + uint32 restorePerTick; + uint32 totalRestore; + }; + + struct RestContext + { + float healthPercent; + float manaPercent; + bool inCombat; + bool isSitting; + bool isEating; + bool isDrinking; + std::vector available; + }; + + RestContext context_; + + void SetUp() override + { + context_ = { + 50.0f, 50.0f, false, false, false, false, + { + {1, "food", 250, 7500}, + {2, "drink", 300, 9000}, + {3, "both", 200, 6000}, + } + }; + } + + bool ShouldEat() + { + if (context_.inCombat) + return false; + if (context_.isEating) + return false; + return context_.healthPercent < 80.0f; + } + + bool ShouldDrink() + { + if (context_.inCombat) + return false; + if (context_.isDrinking) + return false; + return context_.manaPercent < 80.0f; + } + + bool ShouldUseMageTable() + { + // Use mage table food/drink if available and need both + bool needHealth = context_.healthPercent < 80.0f; + bool needMana = context_.manaPercent < 80.0f; + + if (needHealth && needMana) + { + for (auto const& item : context_.available) + { + if (item.type == "both") + return true; + } + } + return false; + } + + bool IsReadyToContinue() + { + return context_.healthPercent >= 80.0f && context_.manaPercent >= 80.0f; + } + + float EstimateTimeToFull(float currentPercent, uint32 restorePerTick, float maxValue) + { + float deficit = (100.0f - currentPercent) / 100.0f * maxValue; + float ticksNeeded = deficit / restorePerTick; + return ticksNeeded * 2.0f; // Assume 2 second ticks + } +}; + +TEST_F(FoodDrinkUsageTest, EatWhenHealthLow) +{ + EXPECT_TRUE(ShouldEat()); +} + +TEST_F(FoodDrinkUsageTest, DontEatInCombat) +{ + context_.inCombat = true; + EXPECT_FALSE(ShouldEat()); +} + +TEST_F(FoodDrinkUsageTest, DontEatWhenAlreadyEating) +{ + context_.isEating = true; + EXPECT_FALSE(ShouldEat()); +} + +TEST_F(FoodDrinkUsageTest, DontEatWhenHealthy) +{ + context_.healthPercent = 90.0f; + EXPECT_FALSE(ShouldEat()); +} + +TEST_F(FoodDrinkUsageTest, DrinkWhenManaLow) +{ + EXPECT_TRUE(ShouldDrink()); +} + +TEST_F(FoodDrinkUsageTest, UseMageTableWhenBothNeeded) +{ + EXPECT_TRUE(ShouldUseMageTable()); +} + +TEST_F(FoodDrinkUsageTest, DontUseMageTableWhenOnlyOneNeeded) +{ + context_.healthPercent = 90.0f; + EXPECT_FALSE(ShouldUseMageTable()); +} + +TEST_F(FoodDrinkUsageTest, ReadyToContinue) +{ + EXPECT_FALSE(IsReadyToContinue()); + + context_.healthPercent = 90.0f; + context_.manaPercent = 85.0f; + EXPECT_TRUE(IsReadyToContinue()); +} + +TEST_F(FoodDrinkUsageTest, EstimatesTimeToFull) +{ + float time = EstimateTimeToFull(50.0f, 250, 10000.0f); + // 50% of 10000 = 5000 deficit, 5000/250 = 20 ticks, 20*2 = 40 seconds + EXPECT_NEAR(40.0f, time, 0.1f); +} + diff --git a/test/unit/Ai/Trigger/TriggerLogicTest.cpp b/test/unit/Ai/Trigger/TriggerLogicTest.cpp new file mode 100644 index 0000000000..604b03292c --- /dev/null +++ b/test/unit/Ai/Trigger/TriggerLogicTest.cpp @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include + +/** + * @brief Tests for trigger logic patterns + * + * These tests validate the core logic patterns used by triggers + * without requiring actual game objects. + */ + +/** + * @brief Simulates basic trigger activation logic + */ +class TriggerLogicTest : public ::testing::Test +{ +protected: + /** + * @brief Check if a threshold trigger should activate + * @param currentValue Current value to check + * @param threshold Threshold to check against + * @return true if current value is at or below threshold + */ + bool IsThresholdTriggered(float currentValue, float threshold) + { + return currentValue <= threshold; + } + + /** + * @brief Check if value is in range (ValueInRangeTrigger pattern) + * @param value Current value + * @param minValue Minimum value (inclusive) + * @param maxValue Maximum value (exclusive) + * @return true if value is in range [minValue, maxValue) + */ + bool IsInRange(float value, float minValue, float maxValue) + { + return value >= minValue && value < maxValue; + } + + /** + * @brief Check if cooldown has elapsed + * @param lastActivation Time of last activation + * @param currentTime Current time + * @param cooldown Cooldown duration + * @return true if cooldown has elapsed + */ + bool IsCooldownElapsed(uint32 lastActivation, uint32 currentTime, uint32 cooldown) + { + if (lastActivation == 0) + return true; // Never activated before + return (currentTime - lastActivation) >= cooldown; + } +}; + +TEST_F(TriggerLogicTest, ThresholdTriggerActivation) +{ + float const criticalThreshold = 20.0f; + + // Should trigger when at or below threshold + EXPECT_TRUE(IsThresholdTriggered(5.0f, criticalThreshold)); + EXPECT_TRUE(IsThresholdTriggered(15.0f, criticalThreshold)); + EXPECT_TRUE(IsThresholdTriggered(20.0f, criticalThreshold)); + + // Should not trigger when above threshold + EXPECT_FALSE(IsThresholdTriggered(21.0f, criticalThreshold)); + EXPECT_FALSE(IsThresholdTriggered(50.0f, criticalThreshold)); + EXPECT_FALSE(IsThresholdTriggered(100.0f, criticalThreshold)); +} + +TEST_F(TriggerLogicTest, RangeTriggerActivation) +{ + // Range [20, 45) + float const minValue = 20.0f; + float const maxValue = 45.0f; + + // In range + EXPECT_TRUE(IsInRange(20.0f, minValue, maxValue)); // At min (inclusive) + EXPECT_TRUE(IsInRange(30.0f, minValue, maxValue)); // Middle + EXPECT_TRUE(IsInRange(44.9f, minValue, maxValue)); // Just below max + + // Out of range + EXPECT_FALSE(IsInRange(19.9f, minValue, maxValue)); // Below min + EXPECT_FALSE(IsInRange(45.0f, minValue, maxValue)); // At max (exclusive) + EXPECT_FALSE(IsInRange(50.0f, minValue, maxValue)); // Above max +} + +TEST_F(TriggerLogicTest, CooldownCheck) +{ + uint32 const cooldown = 5000; // 5 seconds + + // Never activated - should be ready + EXPECT_TRUE(IsCooldownElapsed(0, 10000, cooldown)); + + // Just activated - should not be ready + EXPECT_FALSE(IsCooldownElapsed(10000, 10000, cooldown)); + + // Not enough time passed + EXPECT_FALSE(IsCooldownElapsed(10000, 13000, cooldown)); + + // Exactly at cooldown - should be ready + EXPECT_TRUE(IsCooldownElapsed(10000, 15000, cooldown)); + + // Past cooldown - should be ready + EXPECT_TRUE(IsCooldownElapsed(10000, 20000, cooldown)); +} + +/** + * @brief Tests for multi-condition trigger logic + */ +class MultiConditionTriggerTest : public ::testing::Test +{ +protected: + struct TriggerConditions + { + bool hasTarget; + bool inCombat; + bool hasEnoughMana; + float healthPercent; + float distanceToTarget; + }; + + /** + * @brief Example: Heal spell trigger logic + */ + bool ShouldCastHeal(TriggerConditions const& cond, float healThreshold, float maxRange) + { + return cond.hasTarget && + cond.hasEnoughMana && + cond.healthPercent <= healThreshold && + cond.distanceToTarget <= maxRange; + } + + /** + * @brief Example: Attack spell trigger logic + */ + bool ShouldCastAttack(TriggerConditions const& cond, float maxRange) + { + return cond.hasTarget && + cond.inCombat && + cond.hasEnoughMana && + cond.distanceToTarget <= maxRange; + } +}; + +TEST_F(MultiConditionTriggerTest, HealTriggerConditions) +{ + float const healThreshold = 45.0f; + float const maxRange = 40.0f; + + // All conditions met + TriggerConditions goodConditions{true, true, true, 30.0f, 20.0f}; + EXPECT_TRUE(ShouldCastHeal(goodConditions, healThreshold, maxRange)); + + // No target + TriggerConditions noTarget{false, true, true, 30.0f, 20.0f}; + EXPECT_FALSE(ShouldCastHeal(noTarget, healThreshold, maxRange)); + + // Not enough mana + TriggerConditions noMana{true, true, false, 30.0f, 20.0f}; + EXPECT_FALSE(ShouldCastHeal(noMana, healThreshold, maxRange)); + + // Health above threshold + TriggerConditions healthyTarget{true, true, true, 80.0f, 20.0f}; + EXPECT_FALSE(ShouldCastHeal(healthyTarget, healThreshold, maxRange)); + + // Out of range + TriggerConditions outOfRange{true, true, true, 30.0f, 50.0f}; + EXPECT_FALSE(ShouldCastHeal(outOfRange, healThreshold, maxRange)); +} + +TEST_F(MultiConditionTriggerTest, AttackTriggerConditions) +{ + float const maxRange = 30.0f; + + // All conditions met + TriggerConditions combatReady{true, true, true, 100.0f, 20.0f}; + EXPECT_TRUE(ShouldCastAttack(combatReady, maxRange)); + + // Not in combat + TriggerConditions noCombat{true, false, true, 100.0f, 20.0f}; + EXPECT_FALSE(ShouldCastAttack(noCombat, maxRange)); + + // Out of range + TriggerConditions tooFar{true, true, true, 100.0f, 40.0f}; + EXPECT_FALSE(ShouldCastAttack(tooFar, maxRange)); +} + +/** + * @brief Tests for priority-based trigger selection + */ +class TriggerPriorityTest : public ::testing::Test +{ +protected: + struct TriggerEntry + { + std::string name; + float priority; + bool isActive; + }; + + /** + * @brief Find highest priority active trigger + */ + std::string FindHighestPriorityTrigger(std::vector const& triggers) + { + TriggerEntry const* best = nullptr; + + for (auto const& trigger : triggers) + { + if (!trigger.isActive) + continue; + + if (!best || trigger.priority > best->priority) + best = &trigger; + } + + return best ? best->name : ""; + } +}; + +TEST_F(TriggerPriorityTest, SelectsHighestPriority) +{ + std::vector triggers = { + {"low_priority", 10.0f, true}, + {"high_priority", 100.0f, true}, + {"medium_priority", 50.0f, true}, + }; + + EXPECT_EQ("high_priority", FindHighestPriorityTrigger(triggers)); +} + +TEST_F(TriggerPriorityTest, SkipsInactiveTriggers) +{ + std::vector triggers = { + {"low_priority", 10.0f, true}, + {"high_priority", 100.0f, false}, // Inactive + {"medium_priority", 50.0f, true}, + }; + + EXPECT_EQ("medium_priority", FindHighestPriorityTrigger(triggers)); +} + +TEST_F(TriggerPriorityTest, ReturnsEmptyWhenNoActive) +{ + std::vector triggers = { + {"trigger1", 10.0f, false}, + {"trigger2", 100.0f, false}, + }; + + EXPECT_EQ("", FindHighestPriorityTrigger(triggers)); +} + diff --git a/test/unit/Ai/Value/ValueLogicTest.cpp b/test/unit/Ai/Value/ValueLogicTest.cpp new file mode 100644 index 0000000000..4f09d293b7 --- /dev/null +++ b/test/unit/Ai/Value/ValueLogicTest.cpp @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include + +/** + * @brief Tests for value calculation logic patterns + * + * These tests validate the core calculation patterns used by values + * without requiring actual game objects. + */ + +/** + * @brief Tests for distance calculations + */ +class DistanceValueTest : public ::testing::Test +{ +protected: + struct Position + { + float x, y, z; + }; + + /** + * @brief Calculate 2D distance between positions + */ + float Calculate2DDistance(Position const& a, Position const& b) + { + float dx = a.x - b.x; + float dy = a.y - b.y; + return std::sqrt(dx * dx + dy * dy); + } + + /** + * @brief Calculate 3D distance between positions + */ + float Calculate3DDistance(Position const& a, Position const& b) + { + float dx = a.x - b.x; + float dy = a.y - b.y; + float dz = a.z - b.z; + return std::sqrt(dx * dx + dy * dy + dz * dz); + } + + /** + * @brief Check if within range + */ + bool IsWithinRange(Position const& a, Position const& b, float range) + { + return Calculate2DDistance(a, b) <= range; + } +}; + +TEST_F(DistanceValueTest, Calculate2DDistanceBasic) +{ + Position origin{0.0f, 0.0f, 0.0f}; + Position point{3.0f, 4.0f, 0.0f}; + + // 3-4-5 triangle + EXPECT_FLOAT_EQ(5.0f, Calculate2DDistance(origin, point)); +} + +TEST_F(DistanceValueTest, Calculate2DDistanceIgnoresZ) +{ + Position a{0.0f, 0.0f, 0.0f}; + Position b{3.0f, 4.0f, 100.0f}; // Large Z difference + + // Should still be 5.0 (ignoring Z) + EXPECT_FLOAT_EQ(5.0f, Calculate2DDistance(a, b)); +} + +TEST_F(DistanceValueTest, Calculate3DDistance) +{ + Position origin{0.0f, 0.0f, 0.0f}; + Position point{2.0f, 2.0f, 1.0f}; + + // sqrt(4 + 4 + 1) = sqrt(9) = 3 + EXPECT_FLOAT_EQ(3.0f, Calculate3DDistance(origin, point)); +} + +TEST_F(DistanceValueTest, SamePositionIsZero) +{ + Position pos{10.0f, 20.0f, 30.0f}; + + EXPECT_FLOAT_EQ(0.0f, Calculate2DDistance(pos, pos)); + EXPECT_FLOAT_EQ(0.0f, Calculate3DDistance(pos, pos)); +} + +TEST_F(DistanceValueTest, RangeCheck) +{ + Position a{0.0f, 0.0f, 0.0f}; + Position b{3.0f, 4.0f, 0.0f}; // Distance = 5 + + EXPECT_TRUE(IsWithinRange(a, b, 5.0f)); // Exactly at range + EXPECT_TRUE(IsWithinRange(a, b, 10.0f)); // Within range + EXPECT_FALSE(IsWithinRange(a, b, 4.0f)); // Out of range +} + +/** + * @brief Tests for percentage calculations + */ +class PercentageValueTest : public ::testing::Test +{ +protected: + /** + * @brief Calculate percentage + */ + float CalculatePercentage(float current, float maximum) + { + if (maximum <= 0.0f) + return 0.0f; + return (current / maximum) * 100.0f; + } + + /** + * @brief Calculate percentage with clamping + */ + float CalculatePercentageClamped(float current, float maximum) + { + float pct = CalculatePercentage(current, maximum); + if (pct < 0.0f) return 0.0f; + if (pct > 100.0f) return 100.0f; + return pct; + } +}; + +TEST_F(PercentageValueTest, BasicPercentage) +{ + EXPECT_FLOAT_EQ(50.0f, CalculatePercentage(50.0f, 100.0f)); + EXPECT_FLOAT_EQ(25.0f, CalculatePercentage(25.0f, 100.0f)); + EXPECT_FLOAT_EQ(100.0f, CalculatePercentage(100.0f, 100.0f)); + EXPECT_FLOAT_EQ(0.0f, CalculatePercentage(0.0f, 100.0f)); +} + +TEST_F(PercentageValueTest, DifferentMaximums) +{ + EXPECT_FLOAT_EQ(50.0f, CalculatePercentage(500.0f, 1000.0f)); + EXPECT_FLOAT_EQ(50.0f, CalculatePercentage(5000.0f, 10000.0f)); + EXPECT_FLOAT_EQ(20.0f, CalculatePercentage(1000.0f, 5000.0f)); +} + +TEST_F(PercentageValueTest, ZeroMaximumReturnsZero) +{ + EXPECT_FLOAT_EQ(0.0f, CalculatePercentage(50.0f, 0.0f)); +} + +TEST_F(PercentageValueTest, ClampedPercentage) +{ + EXPECT_FLOAT_EQ(0.0f, CalculatePercentageClamped(-50.0f, 100.0f)); + EXPECT_FLOAT_EQ(100.0f, CalculatePercentageClamped(150.0f, 100.0f)); + EXPECT_FLOAT_EQ(50.0f, CalculatePercentageClamped(50.0f, 100.0f)); +} + +/** + * @brief Tests for threat calculations + */ +class ThreatValueTest : public ::testing::Test +{ +protected: + struct ThreatEntry + { + uint32 targetGuid; + float threatAmount; + }; + + /** + * @brief Find highest threat target + */ + uint32 FindHighestThreat(std::vector const& threats) + { + if (threats.empty()) + return 0; + + auto maxIt = std::max_element(threats.begin(), threats.end(), + [](ThreatEntry const& a, ThreatEntry const& b) { + return a.threatAmount < b.threatAmount; + }); + + return maxIt->targetGuid; + } + + /** + * @brief Calculate threat percentage vs tank + */ + float CalculateThreatPercentage(float myThreat, float tankThreat) + { + if (tankThreat <= 0.0f) + return 100.0f; // If tank has no threat, we're at max + + return (myThreat / tankThreat) * 100.0f; + } + + /** + * @brief Check if pulling aggro (simplified) + */ + bool WillPullAggro(float myThreat, float tankThreat, bool melee) + { + float threshold = melee ? 110.0f : 130.0f; // Melee: 110%, Ranged: 130% + return CalculateThreatPercentage(myThreat, tankThreat) >= threshold; + } +}; + +TEST_F(ThreatValueTest, FindHighestThreat) +{ + std::vector threats = { + {1, 1000.0f}, + {2, 5000.0f}, + {3, 3000.0f}, + }; + + EXPECT_EQ(2u, FindHighestThreat(threats)); +} + +TEST_F(ThreatValueTest, EmptyThreatList) +{ + std::vector threats; + EXPECT_EQ(0u, FindHighestThreat(threats)); +} + +TEST_F(ThreatValueTest, ThreatPercentageCalculation) +{ + EXPECT_FLOAT_EQ(50.0f, CalculateThreatPercentage(5000.0f, 10000.0f)); + EXPECT_FLOAT_EQ(100.0f, CalculateThreatPercentage(10000.0f, 10000.0f)); + EXPECT_FLOAT_EQ(120.0f, CalculateThreatPercentage(12000.0f, 10000.0f)); +} + +TEST_F(ThreatValueTest, AggroPullCalculation) +{ + float tankThreat = 10000.0f; + + // Melee threshold: 110% + EXPECT_FALSE(WillPullAggro(10000.0f, tankThreat, true)); // 100% + EXPECT_FALSE(WillPullAggro(10900.0f, tankThreat, true)); // 109% + EXPECT_TRUE(WillPullAggro(11000.0f, tankThreat, true)); // 110% + + // Ranged threshold: 130% + EXPECT_FALSE(WillPullAggro(12000.0f, tankThreat, false)); // 120% + EXPECT_FALSE(WillPullAggro(12900.0f, tankThreat, false)); // 129% + EXPECT_TRUE(WillPullAggro(13000.0f, tankThreat, false)); // 130% +} + +/** + * @brief Tests for item value calculations + */ +class ItemValueTest : public ::testing::Test +{ +protected: + struct ItemStats + { + uint32 itemLevel; + uint32 stamina; + uint32 intellect; + uint32 strength; + uint32 agility; + uint32 spellPower; + uint32 attackPower; + }; + + /** + * @brief Calculate caster item value (simplified) + */ + float CalculateCasterValue(ItemStats const& item) + { + return static_cast(item.intellect * 2 + item.stamina + item.spellPower * 3); + } + + /** + * @brief Calculate melee DPS item value (simplified) + */ + float CalculateMeleeValue(ItemStats const& item) + { + return static_cast(item.strength * 2 + item.agility * 2 + item.stamina + item.attackPower); + } + + /** + * @brief Compare items for a role + */ + bool IsUpgrade(ItemStats const& newItem, ItemStats const& currentItem, bool isCaster) + { + if (isCaster) + return CalculateCasterValue(newItem) > CalculateCasterValue(currentItem); + else + return CalculateMeleeValue(newItem) > CalculateMeleeValue(currentItem); + } +}; + +TEST_F(ItemValueTest, CasterValueCalculation) +{ + ItemStats casterItem{200, 50, 100, 0, 0, 200, 0}; + // 100*2 + 50 + 200*3 = 200 + 50 + 600 = 850 + EXPECT_FLOAT_EQ(850.0f, CalculateCasterValue(casterItem)); +} + +TEST_F(ItemValueTest, MeleeValueCalculation) +{ + ItemStats meleeItem{200, 50, 0, 100, 80, 0, 200}; + // 100*2 + 80*2 + 50 + 200 = 200 + 160 + 50 + 200 = 610 + EXPECT_FLOAT_EQ(610.0f, CalculateMeleeValue(meleeItem)); +} + +TEST_F(ItemValueTest, UpgradeComparison) +{ + ItemStats currentCaster{180, 40, 80, 0, 0, 150, 0}; + ItemStats betterCaster{200, 50, 100, 0, 0, 200, 0}; + ItemStats worseCaster{160, 30, 60, 0, 0, 100, 0}; + + EXPECT_TRUE(IsUpgrade(betterCaster, currentCaster, true)); + EXPECT_FALSE(IsUpgrade(worseCaster, currentCaster, true)); +} + +/** + * @brief Tests for mana efficiency calculations + */ +class ManaEfficiencyTest : public ::testing::Test +{ +protected: + /** + * @brief Calculate mana efficiency (healing per mana) + */ + float CalculateHealingPerMana(float healAmount, uint32 manaCost) + { + if (manaCost == 0) + return 0.0f; + return healAmount / static_cast(manaCost); + } + + /** + * @brief Calculate if spell is mana efficient enough + */ + bool IsManaEfficient(float healAmount, uint32 manaCost, float minEfficiency) + { + return CalculateHealingPerMana(healAmount, manaCost) >= minEfficiency; + } +}; + +TEST_F(ManaEfficiencyTest, BasicEfficiency) +{ + EXPECT_FLOAT_EQ(2.0f, CalculateHealingPerMana(1000.0f, 500)); + EXPECT_FLOAT_EQ(5.0f, CalculateHealingPerMana(2500.0f, 500)); + EXPECT_FLOAT_EQ(0.5f, CalculateHealingPerMana(250.0f, 500)); +} + +TEST_F(ManaEfficiencyTest, ZeroCostReturnsZero) +{ + EXPECT_FLOAT_EQ(0.0f, CalculateHealingPerMana(1000.0f, 0)); +} + +TEST_F(ManaEfficiencyTest, EfficiencyThreshold) +{ + float minEfficiency = 2.0f; + + EXPECT_TRUE(IsManaEfficient(1000.0f, 500, minEfficiency)); // 2.0 + EXPECT_TRUE(IsManaEfficient(1500.0f, 500, minEfficiency)); // 3.0 + EXPECT_FALSE(IsManaEfficient(800.0f, 500, minEfficiency)); // 1.6 +} + diff --git a/test/unit/Bot/Core/ManagerRegistryTest.cpp b/test/unit/Bot/Core/ManagerRegistryTest.cpp new file mode 100644 index 0000000000..6ad743b7e3 --- /dev/null +++ b/test/unit/Bot/Core/ManagerRegistryTest.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include "mocks/MockManagers.h" +#include "Bot/Core/ManagerRegistry.h" + +using ::testing::Return; +using ::testing::_; + +/** + * @brief Tests for the ManagerRegistry + * + * These tests demonstrate how to use the manager registry + * with mock implementations for testing. + */ +class ManagerRegistryTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // Create a fresh registry for each test + registry_ = std::make_unique(); + } + + void TearDown() override + { + registry_.reset(); + } + + std::unique_ptr registry_; +}; + +TEST_F(ManagerRegistryTest, InitiallyEmpty) +{ + EXPECT_FALSE(registry_->HasTravelManager()); + EXPECT_FALSE(registry_->HasRandomBotManager()); + EXPECT_FALSE(registry_->HasBotRepository()); + EXPECT_FALSE(registry_->IsInitialized()); +} + +TEST_F(ManagerRegistryTest, CanRegisterTravelManager) +{ + auto mockTravel = std::make_shared(); + registry_->SetTravelManager(mockTravel); + + EXPECT_TRUE(registry_->HasTravelManager()); + EXPECT_FALSE(registry_->IsInitialized()); // Still missing other managers +} + +TEST_F(ManagerRegistryTest, CanRegisterRandomBotManager) +{ + auto mockRandomBot = std::make_shared(); + registry_->SetRandomBotManager(mockRandomBot); + + EXPECT_TRUE(registry_->HasRandomBotManager()); + EXPECT_FALSE(registry_->IsInitialized()); +} + +TEST_F(ManagerRegistryTest, CanRegisterBotRepository) +{ + auto mockRepo = std::make_shared(); + registry_->SetBotRepository(mockRepo); + + EXPECT_TRUE(registry_->HasBotRepository()); + EXPECT_FALSE(registry_->IsInitialized()); +} + +TEST_F(ManagerRegistryTest, IsInitializedWhenAllManagersSet) +{ + auto mockTravel = std::make_shared(); + auto mockRandomBot = std::make_shared(); + auto mockRepo = std::make_shared(); + + registry_->SetTravelManager(mockTravel); + registry_->SetRandomBotManager(mockRandomBot); + registry_->SetBotRepository(mockRepo); + + EXPECT_TRUE(registry_->IsInitialized()); +} + +TEST_F(ManagerRegistryTest, CanAccessTravelManagerThroughInterface) +{ + auto mockTravel = std::make_shared(); + registry_->SetTravelManager(mockTravel); + + EXPECT_CALL(*mockTravel, RandomTeleportForLevel(_)).Times(1); + + registry_->GetTravelManager().RandomTeleportForLevel(nullptr); +} + +TEST_F(ManagerRegistryTest, CanAccessRandomBotManagerThroughInterface) +{ + auto mockRandomBot = std::make_shared(); + registry_->SetRandomBotManager(mockRandomBot); + + EXPECT_CALL(*mockRandomBot, GetActiveBotCount()).WillOnce(Return(50u)); + + EXPECT_EQ(50u, registry_->GetRandomBotManager().GetActiveBotCount()); +} + +TEST_F(ManagerRegistryTest, CanAccessBotRepositoryThroughInterface) +{ + auto mockRepo = std::make_shared(); + registry_->SetBotRepository(mockRepo); + + EXPECT_CALL(*mockRepo, HasSavedData(123)).WillOnce(Return(true)); + + EXPECT_TRUE(registry_->GetBotRepository().HasSavedData(123)); +} + +TEST_F(ManagerRegistryTest, TemplateAccessors) +{ + auto mockTravel = std::make_shared(); + auto mockRandomBot = std::make_shared(); + auto mockRepo = std::make_shared(); + + registry_->Register(mockTravel); + registry_->Register(mockRandomBot); + registry_->Register(mockRepo); + + EXPECT_TRUE(registry_->IsInitialized()); + + // Access through template + EXPECT_CALL(*mockRandomBot, GetMaxAllowedBotCount()).WillOnce(Return(100u)); + EXPECT_EQ(100u, registry_->Get().GetMaxAllowedBotCount()); +} + +/** + * @brief Integration-style test demonstrating mock manager usage + */ +class ManagerIntegrationTest : public ::testing::Test +{ +protected: + void SetUp() override + { + mockTravel_ = std::make_shared(); + mockRandomBot_ = std::make_shared(); + mockRepo_ = std::make_shared(); + + registry_.SetTravelManager(mockTravel_); + registry_.SetRandomBotManager(mockRandomBot_); + registry_.SetBotRepository(mockRepo_); + } + + ManagerRegistry registry_; + std::shared_ptr mockTravel_; + std::shared_ptr mockRandomBot_; + std::shared_ptr mockRepo_; +}; + +TEST_F(ManagerIntegrationTest, SimulateBotTeleportFlow) +{ + // Simulate: Check if bot is random, then teleport + EXPECT_CALL(*mockRandomBot_, IsRandomBot(testing::A())).WillOnce(Return(true)); + EXPECT_CALL(*mockTravel_, RandomTeleportForLevel(_)).Times(1); + + // Simulated flow + if (registry_.GetRandomBotManager().IsRandomBot(static_cast(nullptr))) + { + registry_.GetTravelManager().RandomTeleportForLevel(nullptr); + } +} + +TEST_F(ManagerIntegrationTest, SimulateBotPersistence) +{ + // Simulate: Save bot data if bot exists + EXPECT_CALL(*mockRandomBot_, GetActiveBotCount()).WillOnce(Return(1u)); + EXPECT_CALL(*mockRepo_, Save(_)).Times(1); + + // Simulated flow + if (registry_.GetRandomBotManager().GetActiveBotCount() > 0) + { + registry_.GetBotRepository().Save(nullptr); + } +} + +TEST_F(ManagerIntegrationTest, SimulateBotActivityScaling) +{ + EXPECT_CALL(*mockRandomBot_, GetActivityPercentage()).WillOnce(Return(75.0f)); + EXPECT_CALL(*mockRandomBot_, GetMaxAllowedBotCount()).WillOnce(Return(100u)); + + float activity = registry_.GetRandomBotManager().GetActivityPercentage(); + uint32 maxBots = registry_.GetRandomBotManager().GetMaxAllowedBotCount(); + + // Calculated expected active bots + uint32 expectedActive = static_cast(maxBots * (activity / 100.0f)); + EXPECT_EQ(75u, expectedActive); +} + +/** + * @brief Tests demonstrating the mock injection pattern for testing code + * that depends on ManagerRegistry + */ +TEST_F(ManagerRegistryTest, CanInjectMockRandomBotManager) +{ + // This test demonstrates the pattern from the migration plan + auto mockMgr = std::make_shared(); + registry_->SetRandomBotManager(mockMgr); + + EXPECT_CALL(*mockMgr, IsRandomBot(testing::A())).WillOnce(Return(true)); + + EXPECT_TRUE(registry_->GetRandomBotManager().IsRandomBot(nullptr)); +} + +TEST_F(ManagerRegistryTest, CanInjectMockTravelManager) +{ + auto mockMgr = std::make_shared(); + registry_->SetTravelManager(mockMgr); + + EXPECT_CALL(*mockMgr, ClearDestination(_)).Times(1); + + registry_->GetTravelManager().ClearDestination(nullptr); +} + +TEST_F(ManagerRegistryTest, CanInjectMockBotRepository) +{ + auto mockRepo = std::make_shared(); + registry_->SetBotRepository(mockRepo); + + EXPECT_CALL(*mockRepo, Save(_)).Times(1); + + registry_->GetBotRepository().Save(nullptr); +} + +TEST_F(ManagerIntegrationTest, SimulateRandomBotValueStorage) +{ + // Test Get/SetValue which is commonly used + EXPECT_CALL(*mockRandomBot_, GetValue(testing::A(), "testKey")) + .WillOnce(Return(42u)); + EXPECT_CALL(*mockRandomBot_, SetValue(testing::A(), "testKey", 100u, "")) + .Times(1); + + uint32 value = registry_.GetRandomBotManager().GetValue(nullptr, "testKey"); + EXPECT_EQ(42u, value); + + registry_.GetRandomBotManager().SetValue(nullptr, "testKey", 100u); +} + +TEST_F(ManagerIntegrationTest, SimulateTradeMultipliers) +{ + // Test GetBuyMultiplier/GetSellMultiplier commonly used in trade actions + EXPECT_CALL(*mockRandomBot_, GetBuyMultiplier(_)).WillOnce(Return(0.95)); + EXPECT_CALL(*mockRandomBot_, GetSellMultiplier(_)).WillOnce(Return(1.05)); + + double buyMult = registry_.GetRandomBotManager().GetBuyMultiplier(nullptr); + double sellMult = registry_.GetRandomBotManager().GetSellMultiplier(nullptr); + + EXPECT_DOUBLE_EQ(0.95, buyMult); + EXPECT_DOUBLE_EQ(1.05, sellMult); +} diff --git a/test/unit/Bot/Service/ChatServiceTest.cpp b/test/unit/Bot/Service/ChatServiceTest.cpp new file mode 100644 index 0000000000..cda4698585 --- /dev/null +++ b/test/unit/Bot/Service/ChatServiceTest.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include "mocks/MockBotServices.h" + +using ::testing::Return; +using ::testing::_; + +/** + * @brief Tests for BotChatService + * + * These tests verify the chat service functionality + * for bot communication operations. + */ +class ChatServiceTest : public ::testing::Test +{ +protected: + MockChatService mockChatService; +}; + +TEST_F(ChatServiceTest, CanMockTellMaster) +{ + EXPECT_CALL(mockChatService, TellMaster(std::string("Hello, master!"), _)) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockChatService.TellMaster("Hello, master!")); +} + +TEST_F(ChatServiceTest, CanMockTellMasterNoFacing) +{ + EXPECT_CALL(mockChatService, TellMasterNoFacing(std::string("Status update"), _)) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockChatService.TellMasterNoFacing("Status update")); +} + +TEST_F(ChatServiceTest, CanMockTellError) +{ + EXPECT_CALL(mockChatService, TellError(std::string("Error occurred"), _)) + .WillOnce(Return(false)); + + EXPECT_FALSE(mockChatService.TellError("Error occurred")); +} + +TEST_F(ChatServiceTest, CanMockSayToGuild) +{ + EXPECT_CALL(mockChatService, SayToGuild(std::string("Guild message"))) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockChatService.SayToGuild("Guild message")); +} + +TEST_F(ChatServiceTest, CanMockSayToWorld) +{ + EXPECT_CALL(mockChatService, SayToWorld(std::string("World message"))) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockChatService.SayToWorld("World message")); +} + +TEST_F(ChatServiceTest, CanMockSayToChannel) +{ + EXPECT_CALL(mockChatService, SayToChannel(std::string("Channel message"), 1)) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockChatService.SayToChannel("Channel message", 1)); +} + +TEST_F(ChatServiceTest, CanMockSayToParty) +{ + EXPECT_CALL(mockChatService, SayToParty(std::string("Party message"))) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockChatService.SayToParty("Party message")); +} + +TEST_F(ChatServiceTest, CanMockSayToRaid) +{ + EXPECT_CALL(mockChatService, SayToRaid(std::string("Raid message"))) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockChatService.SayToRaid("Raid message")); +} + +TEST_F(ChatServiceTest, CanMockSay) +{ + EXPECT_CALL(mockChatService, Say(std::string("Hello"))) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockChatService.Say("Hello")); +} + +TEST_F(ChatServiceTest, CanMockYell) +{ + EXPECT_CALL(mockChatService, Yell(std::string("Help!"))) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockChatService.Yell("Help!")); +} + +TEST_F(ChatServiceTest, CanMockWhisper) +{ + EXPECT_CALL(mockChatService, Whisper(std::string("Secret message"), std::string("PlayerName"))) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockChatService.Whisper("Secret message", "PlayerName")); +} + +TEST_F(ChatServiceTest, CanMockPlaySound) +{ + EXPECT_CALL(mockChatService, PlaySound(12345)) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockChatService.PlaySound(12345)); +} + +TEST_F(ChatServiceTest, CanMockPlayEmote) +{ + EXPECT_CALL(mockChatService, PlayEmote(67890)) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockChatService.PlayEmote(67890)); +} + +/** + * @brief Tests for chat behavior patterns + */ +class ChatBehaviorPatternTest : public ::testing::Test +{ +protected: + MockChatService mockChatService; + + bool NotifyMasterAboutCombat(MockChatService& chatService, bool inCombat) + { + if (inCombat) + { + return chatService.TellMasterNoFacing("Entering combat!"); + } + return chatService.TellMasterNoFacing("Combat ended."); + } + + bool AnnounceToParty(MockChatService& chatService, std::string const& message) + { + return chatService.SayToParty(message); + } +}; + +TEST_F(ChatBehaviorPatternTest, NotifyMasterEnteringCombat) +{ + EXPECT_CALL(mockChatService, TellMasterNoFacing(std::string("Entering combat!"), _)) + .WillOnce(Return(true)); + + EXPECT_TRUE(NotifyMasterAboutCombat(mockChatService, true)); +} + +TEST_F(ChatBehaviorPatternTest, NotifyMasterCombatEnded) +{ + EXPECT_CALL(mockChatService, TellMasterNoFacing(std::string("Combat ended."), _)) + .WillOnce(Return(true)); + + EXPECT_TRUE(NotifyMasterAboutCombat(mockChatService, false)); +} + +TEST_F(ChatBehaviorPatternTest, PartyAnnouncement) +{ + EXPECT_CALL(mockChatService, SayToParty(std::string("Ready to go!"))) + .WillOnce(Return(true)); + + EXPECT_TRUE(AnnounceToParty(mockChatService, "Ready to go!")); +} diff --git a/test/unit/Bot/Service/ConfigProviderTest.cpp b/test/unit/Bot/Service/ConfigProviderTest.cpp new file mode 100644 index 0000000000..c2462e680f --- /dev/null +++ b/test/unit/Bot/Service/ConfigProviderTest.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include "mocks/MockPlayerbotAIConfig.h" + +/** + * @brief Tests for the MockConfigProvider + * + * These tests demonstrate how to use the mock configuration provider + * for testing bot components that depend on configuration values. + */ +class ConfigProviderTest : public ::testing::Test +{ +protected: + void SetUp() override + { + mockConfig.SetupDefaults(); + } + + MockConfigProvider mockConfig; +}; + +TEST_F(ConfigProviderTest, DefaultDistanceValues) +{ + // Verify default distance values are set correctly + EXPECT_FLOAT_EQ(75.0f, mockConfig.GetSightDistance()); + EXPECT_FLOAT_EQ(26.0f, mockConfig.GetSpellDistance()); + EXPECT_FLOAT_EQ(150.0f, mockConfig.GetReactDistance()); + EXPECT_FLOAT_EQ(1.5f, mockConfig.GetMeleeDistance()); + EXPECT_FLOAT_EQ(38.0f, mockConfig.GetHealDistance()); +} + +TEST_F(ConfigProviderTest, DefaultHealthThresholds) +{ + // Verify default health thresholds + EXPECT_EQ(20u, mockConfig.GetCriticalHealth()); + EXPECT_EQ(45u, mockConfig.GetLowHealth()); + EXPECT_EQ(65u, mockConfig.GetMediumHealth()); + EXPECT_EQ(85u, mockConfig.GetAlmostFullHealth()); +} + +TEST_F(ConfigProviderTest, DefaultManaThresholds) +{ + // Verify default mana thresholds + EXPECT_EQ(15u, mockConfig.GetLowMana()); + EXPECT_EQ(40u, mockConfig.GetMediumMana()); + EXPECT_EQ(80u, mockConfig.GetHighMana()); +} + +TEST_F(ConfigProviderTest, DefaultTimingValues) +{ + // Verify default timing values + EXPECT_EQ(1500u, mockConfig.GetGlobalCooldown()); + EXPECT_EQ(100u, mockConfig.GetReactDelay()); + EXPECT_EQ(5000u, mockConfig.GetMaxWaitForMove()); +} + +TEST_F(ConfigProviderTest, DefaultFeatureFlags) +{ + // Verify default feature flags + EXPECT_TRUE(mockConfig.IsEnabled()); + EXPECT_TRUE(mockConfig.IsFleeingEnabled()); + EXPECT_TRUE(mockConfig.IsAutoAvoidAoe()); + EXPECT_FALSE(mockConfig.IsDynamicReactDelay()); +} + +TEST_F(ConfigProviderTest, CanOverrideWithExpectCall) +{ + using ::testing::Return; + + // Override specific values for a test + EXPECT_CALL(mockConfig, GetCriticalHealth()).WillOnce(Return(25u)); + EXPECT_CALL(mockConfig, GetLowHealth()).WillOnce(Return(50u)); + + EXPECT_EQ(25u, mockConfig.GetCriticalHealth()); + EXPECT_EQ(50u, mockConfig.GetLowHealth()); +} + +TEST_F(ConfigProviderTest, CanMockLogging) +{ + using ::testing::Return; + using ::testing::_; + + // Default returns false for HasLog + EXPECT_FALSE(mockConfig.HasLog("test.log")); + + // Can override for specific files + EXPECT_CALL(mockConfig, HasLog("debug.log")).WillOnce(Return(true)); + EXPECT_TRUE(mockConfig.HasLog("debug.log")); +} diff --git a/test/unit/Bot/Service/ItemServiceTest.cpp b/test/unit/Bot/Service/ItemServiceTest.cpp new file mode 100644 index 0000000000..93d5b2ef82 --- /dev/null +++ b/test/unit/Bot/Service/ItemServiceTest.cpp @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include "mocks/MockBotServices.h" + +using ::testing::Return; +using ::testing::_; + +/** + * @brief Tests for BotItemService + * + * These tests verify the item service functionality + * for bot inventory and item management operations. + */ +class ItemServiceTest : public ::testing::Test +{ +protected: + MockItemService mockItemService; +}; + +TEST_F(ItemServiceTest, CanMockFindPoison) +{ + EXPECT_CALL(mockItemService, FindPoison()) + .WillOnce(Return(nullptr)); + + EXPECT_EQ(nullptr, mockItemService.FindPoison()); +} + +TEST_F(ItemServiceTest, CanMockFindAmmo) +{ + EXPECT_CALL(mockItemService, FindAmmo()) + .WillOnce(Return(nullptr)); + + EXPECT_EQ(nullptr, mockItemService.FindAmmo()); +} + +TEST_F(ItemServiceTest, CanMockFindBandage) +{ + EXPECT_CALL(mockItemService, FindBandage()) + .WillOnce(Return(nullptr)); + + EXPECT_EQ(nullptr, mockItemService.FindBandage()); +} + +TEST_F(ItemServiceTest, CanMockFindOpenableItem) +{ + EXPECT_CALL(mockItemService, FindOpenableItem()) + .WillOnce(Return(nullptr)); + + EXPECT_EQ(nullptr, mockItemService.FindOpenableItem()); +} + +TEST_F(ItemServiceTest, CanMockFindLockedItem) +{ + EXPECT_CALL(mockItemService, FindLockedItem()) + .WillOnce(Return(nullptr)); + + EXPECT_EQ(nullptr, mockItemService.FindLockedItem()); +} + +TEST_F(ItemServiceTest, CanMockFindConsumable) +{ + EXPECT_CALL(mockItemService, FindConsumable(12345)) + .WillOnce(Return(nullptr)); + + EXPECT_EQ(nullptr, mockItemService.FindConsumable(12345)); +} + +TEST_F(ItemServiceTest, CanMockFindStoneFor) +{ + EXPECT_CALL(mockItemService, FindStoneFor(_)) + .WillOnce(Return(nullptr)); + + EXPECT_EQ(nullptr, mockItemService.FindStoneFor(nullptr)); +} + +TEST_F(ItemServiceTest, CanMockFindOilFor) +{ + EXPECT_CALL(mockItemService, FindOilFor(_)) + .WillOnce(Return(nullptr)); + + EXPECT_EQ(nullptr, mockItemService.FindOilFor(nullptr)); +} + +TEST_F(ItemServiceTest, CanMockGetInventoryAndEquippedItems) +{ + std::vector emptyItems; + EXPECT_CALL(mockItemService, GetInventoryAndEquippedItems()) + .WillOnce(Return(emptyItems)); + + auto result = mockItemService.GetInventoryAndEquippedItems(); + EXPECT_TRUE(result.empty()); +} + +TEST_F(ItemServiceTest, CanMockGetInventoryItems) +{ + std::vector emptyItems; + EXPECT_CALL(mockItemService, GetInventoryItems()) + .WillOnce(Return(emptyItems)); + + auto result = mockItemService.GetInventoryItems(); + EXPECT_TRUE(result.empty()); +} + +TEST_F(ItemServiceTest, CanMockGetInventoryItemsCountWithId) +{ + EXPECT_CALL(mockItemService, GetInventoryItemsCountWithId(12345)) + .WillOnce(Return(5)); + + EXPECT_EQ(5u, mockItemService.GetInventoryItemsCountWithId(12345)); +} + +TEST_F(ItemServiceTest, CanMockHasItemInInventory) +{ + EXPECT_CALL(mockItemService, HasItemInInventory(12345)) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockItemService.HasItemInInventory(12345)); +} + +TEST_F(ItemServiceTest, CanMockGetEquipGearScore) +{ + EXPECT_CALL(mockItemService, GetEquipGearScore(_)) + .WillOnce(Return(200)); + + EXPECT_EQ(200u, mockItemService.GetEquipGearScore(nullptr)); +} + +TEST_F(ItemServiceTest, CanMockGetCurrentQuestsRequiringItemId) +{ + std::vector> emptyQuests; + EXPECT_CALL(mockItemService, GetCurrentQuestsRequiringItemId(12345)) + .WillOnce(Return(emptyQuests)); + + auto result = mockItemService.GetCurrentQuestsRequiringItemId(12345); + EXPECT_TRUE(result.empty()); +} + +/** + * @brief Tests for item behavior patterns + */ +class ItemBehaviorPatternTest : public ::testing::Test +{ +protected: + MockItemService mockItemService; + + bool HasWeaponEnhancement(MockItemService& itemService, Item* weapon) + { + return itemService.FindStoneFor(weapon) != nullptr || + itemService.FindOilFor(weapon) != nullptr; + } + + bool HasSufficientAmmo(MockItemService& itemService, uint32 minCount) + { + if (Item* ammo = itemService.FindAmmo()) + { + return itemService.GetInventoryItemsCountWithId(ammo->GetEntry()) >= minCount; + } + return false; + } + + bool NeedsHealing(MockItemService& itemService) + { + return itemService.FindBandage() != nullptr; + } +}; + +TEST_F(ItemBehaviorPatternTest, CheckNoWeaponEnhancement) +{ + EXPECT_CALL(mockItemService, FindStoneFor(_)) + .WillOnce(Return(nullptr)); + EXPECT_CALL(mockItemService, FindOilFor(_)) + .WillOnce(Return(nullptr)); + + EXPECT_FALSE(HasWeaponEnhancement(mockItemService, nullptr)); +} + +TEST_F(ItemBehaviorPatternTest, CheckNoAmmo) +{ + EXPECT_CALL(mockItemService, FindAmmo()) + .WillOnce(Return(nullptr)); + + EXPECT_FALSE(HasSufficientAmmo(mockItemService, 100)); +} + +TEST_F(ItemBehaviorPatternTest, CheckHasBandage) +{ + EXPECT_CALL(mockItemService, FindBandage()) + .WillOnce(Return(nullptr)); + + EXPECT_FALSE(NeedsHealing(mockItemService)); +} + +/** + * @brief Tests for inventory query patterns + */ +class ItemInventoryQueryTest : public ::testing::Test +{ +protected: + MockItemService mockItemService; +}; + +TEST_F(ItemInventoryQueryTest, CanCheckItemExists) +{ + EXPECT_CALL(mockItemService, HasItemInInventory(1234)) + .WillOnce(Return(true)); + EXPECT_CALL(mockItemService, HasItemInInventory(5678)) + .WillOnce(Return(false)); + + EXPECT_TRUE(mockItemService.HasItemInInventory(1234)); + EXPECT_FALSE(mockItemService.HasItemInInventory(5678)); +} + +TEST_F(ItemInventoryQueryTest, CanCountItems) +{ + EXPECT_CALL(mockItemService, GetInventoryItemsCountWithId(1234)) + .WillOnce(Return(20)); + EXPECT_CALL(mockItemService, GetInventoryItemsCountWithId(5678)) + .WillOnce(Return(0)); + + EXPECT_EQ(20u, mockItemService.GetInventoryItemsCountWithId(1234)); + EXPECT_EQ(0u, mockItemService.GetInventoryItemsCountWithId(5678)); +} + +TEST_F(ItemInventoryQueryTest, CanGetGearScore) +{ + EXPECT_CALL(mockItemService, GetEquipGearScore(nullptr)) + .WillOnce(Return(150)); + + EXPECT_EQ(150u, mockItemService.GetEquipGearScore(nullptr)); +} diff --git a/test/unit/Bot/Service/RoleServiceTest.cpp b/test/unit/Bot/Service/RoleServiceTest.cpp new file mode 100644 index 0000000000..47a6ef2ffd --- /dev/null +++ b/test/unit/Bot/Service/RoleServiceTest.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include "mocks/MockBotServices.h" + +using ::testing::Return; +using ::testing::_; + +/** + * @brief Tests for the MockRoleService + * + * These tests demonstrate how to use the mock role service + * for testing bot components that depend on role detection. + */ +class RoleServiceTest : public ::testing::Test +{ +protected: + MockRoleService mockRoleService; +}; + +TEST_F(RoleServiceTest, CanMockTankRole) +{ + // Set up mock to return true for IsTank + EXPECT_CALL(mockRoleService, IsTank(_, false)).WillOnce(Return(true)); + EXPECT_CALL(mockRoleService, IsHeal(_, false)).WillOnce(Return(false)); + EXPECT_CALL(mockRoleService, IsDps(_, false)).WillOnce(Return(false)); + + EXPECT_TRUE(mockRoleService.IsTank(nullptr, false)); + EXPECT_FALSE(mockRoleService.IsHeal(nullptr, false)); + EXPECT_FALSE(mockRoleService.IsDps(nullptr, false)); +} + +TEST_F(RoleServiceTest, CanMockHealerRole) +{ + EXPECT_CALL(mockRoleService, IsTank(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(mockRoleService, IsHeal(_, _)).WillRepeatedly(Return(true)); + EXPECT_CALL(mockRoleService, IsDps(_, _)).WillRepeatedly(Return(false)); + + EXPECT_FALSE(mockRoleService.IsTank(nullptr, false)); + EXPECT_TRUE(mockRoleService.IsHeal(nullptr, false)); + EXPECT_FALSE(mockRoleService.IsDps(nullptr, false)); +} + +TEST_F(RoleServiceTest, CanMockDpsRole) +{ + EXPECT_CALL(mockRoleService, IsTank(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(mockRoleService, IsHeal(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(mockRoleService, IsDps(_, _)).WillRepeatedly(Return(true)); + + EXPECT_TRUE(mockRoleService.IsDps(nullptr, false)); +} + +TEST_F(RoleServiceTest, CanMockCombatStyle) +{ + // Ranged caster + EXPECT_CALL(mockRoleService, IsRanged(_, _)).WillOnce(Return(true)); + EXPECT_CALL(mockRoleService, IsMelee(_, _)).WillOnce(Return(false)); + EXPECT_CALL(mockRoleService, IsCaster(_, _)).WillOnce(Return(true)); + + EXPECT_TRUE(mockRoleService.IsRanged(nullptr, false)); + EXPECT_FALSE(mockRoleService.IsMelee(nullptr, false)); + EXPECT_TRUE(mockRoleService.IsCaster(nullptr, false)); +} + +TEST_F(RoleServiceTest, CanMockTankHierarchy) +{ + // Main tank + EXPECT_CALL(mockRoleService, IsMainTank(_)).WillOnce(Return(true)); + EXPECT_CALL(mockRoleService, IsAssistTank(_)).WillOnce(Return(false)); + EXPECT_CALL(mockRoleService, IsBotMainTank(_)).WillOnce(Return(true)); + + EXPECT_TRUE(mockRoleService.IsMainTank(nullptr)); + EXPECT_FALSE(mockRoleService.IsAssistTank(nullptr)); + EXPECT_TRUE(mockRoleService.IsBotMainTank(nullptr)); +} + +TEST_F(RoleServiceTest, CanMockGroupQueries) +{ + EXPECT_CALL(mockRoleService, GetGroupTankNum(_)).WillOnce(Return(2)); + EXPECT_CALL(mockRoleService, GetAssistTankIndex(_)).WillOnce(Return(1)); + EXPECT_CALL(mockRoleService, GetGroupSlotIndex(_)).WillOnce(Return(3)); + + EXPECT_EQ(2u, mockRoleService.GetGroupTankNum(nullptr)); + EXPECT_EQ(1, mockRoleService.GetAssistTankIndex(nullptr)); + EXPECT_EQ(3, mockRoleService.GetGroupSlotIndex(nullptr)); +} + +/** + * @brief Tests for role-based behavior patterns + */ +class RoleBehaviorPatternTest : public ::testing::Test +{ +protected: + MockRoleService mockRoleService; + + /** + * @brief Determine if a player should use melee attacks + */ + bool ShouldUseMeleeAttacks(MockRoleService& roleService, void* player) + { + return roleService.IsMelee(static_cast(player), false); + } + + /** + * @brief Determine if a player should stay at range + */ + bool ShouldStayAtRange(MockRoleService& roleService, void* player) + { + return roleService.IsRanged(static_cast(player), false); + } + + /** + * @brief Determine if a player should heal others + */ + bool ShouldHealOthers(MockRoleService& roleService, void* player) + { + return roleService.IsHeal(static_cast(player), false); + } + + /** + * @brief Determine if a player should taunt + */ + bool ShouldTaunt(MockRoleService& roleService, void* player) + { + auto* p = static_cast(player); + return roleService.IsTank(p, false) && roleService.IsMainTank(p); + } +}; + +TEST_F(RoleBehaviorPatternTest, MeleeWarriorBehavior) +{ + EXPECT_CALL(mockRoleService, IsMelee(_, _)).WillRepeatedly(Return(true)); + EXPECT_CALL(mockRoleService, IsRanged(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(mockRoleService, IsTank(_, _)).WillRepeatedly(Return(true)); + EXPECT_CALL(mockRoleService, IsMainTank(_)).WillRepeatedly(Return(true)); + + EXPECT_TRUE(ShouldUseMeleeAttacks(mockRoleService, nullptr)); + EXPECT_FALSE(ShouldStayAtRange(mockRoleService, nullptr)); + EXPECT_TRUE(ShouldTaunt(mockRoleService, nullptr)); +} + +TEST_F(RoleBehaviorPatternTest, RangedHealerBehavior) +{ + EXPECT_CALL(mockRoleService, IsMelee(_, _)).WillRepeatedly(Return(false)); + EXPECT_CALL(mockRoleService, IsRanged(_, _)).WillRepeatedly(Return(true)); + EXPECT_CALL(mockRoleService, IsHeal(_, _)).WillRepeatedly(Return(true)); + + EXPECT_FALSE(ShouldUseMeleeAttacks(mockRoleService, nullptr)); + EXPECT_TRUE(ShouldStayAtRange(mockRoleService, nullptr)); + EXPECT_TRUE(ShouldHealOthers(mockRoleService, nullptr)); +} + +TEST_F(RoleBehaviorPatternTest, OffTankBehavior) +{ + EXPECT_CALL(mockRoleService, IsTank(_, _)).WillRepeatedly(Return(true)); + EXPECT_CALL(mockRoleService, IsMainTank(_)).WillRepeatedly(Return(false)); + + // Off-tank should not taunt (only main tank taunts) + EXPECT_FALSE(ShouldTaunt(mockRoleService, nullptr)); +} + +/** + * @brief Tests for role assistant index functions + * + * These test the IsAssistHealOfIndex and IsAssistRangedDpsOfIndex methods + * which use a two-pass algorithm: assistants first, then non-assistants. + */ +class RoleAssistantIndexTest : public ::testing::Test +{ +protected: + MockRoleService mockRoleService; +}; + +TEST_F(RoleAssistantIndexTest, CanMockAssistHealOfIndex) +{ + // Test that we can configure the mock for IsAssistHealOfIndex + EXPECT_CALL(mockRoleService, IsAssistHealOfIndex(_, 0, false)).WillOnce(Return(true)); + EXPECT_CALL(mockRoleService, IsAssistHealOfIndex(_, 1, false)).WillOnce(Return(false)); + + EXPECT_TRUE(mockRoleService.IsAssistHealOfIndex(nullptr, 0, false)); + EXPECT_FALSE(mockRoleService.IsAssistHealOfIndex(nullptr, 1, false)); +} + +TEST_F(RoleAssistantIndexTest, CanMockAssistHealOfIndexWithDeadPlayerFilter) +{ + // Test ignoreDeadPlayers parameter + EXPECT_CALL(mockRoleService, IsAssistHealOfIndex(_, 0, true)).WillOnce(Return(true)); + EXPECT_CALL(mockRoleService, IsAssistHealOfIndex(_, 0, false)).WillOnce(Return(false)); + + // With ignoreDeadPlayers=true, might return different result + EXPECT_TRUE(mockRoleService.IsAssistHealOfIndex(nullptr, 0, true)); + EXPECT_FALSE(mockRoleService.IsAssistHealOfIndex(nullptr, 0, false)); +} + +TEST_F(RoleAssistantIndexTest, CanMockAssistRangedDpsOfIndex) +{ + // Test that we can configure the mock for IsAssistRangedDpsOfIndex + EXPECT_CALL(mockRoleService, IsAssistRangedDpsOfIndex(_, 0, false)).WillOnce(Return(true)); + EXPECT_CALL(mockRoleService, IsAssistRangedDpsOfIndex(_, 1, false)).WillOnce(Return(false)); + + EXPECT_TRUE(mockRoleService.IsAssistRangedDpsOfIndex(nullptr, 0, false)); + EXPECT_FALSE(mockRoleService.IsAssistRangedDpsOfIndex(nullptr, 1, false)); +} + +TEST_F(RoleAssistantIndexTest, CanMockAssistRangedDpsOfIndexWithDeadPlayerFilter) +{ + // Test ignoreDeadPlayers parameter + EXPECT_CALL(mockRoleService, IsAssistRangedDpsOfIndex(_, 0, true)).WillOnce(Return(true)); + EXPECT_CALL(mockRoleService, IsAssistRangedDpsOfIndex(_, 0, false)).WillOnce(Return(false)); + + // With ignoreDeadPlayers=true, might return different result + EXPECT_TRUE(mockRoleService.IsAssistRangedDpsOfIndex(nullptr, 0, true)); + EXPECT_FALSE(mockRoleService.IsAssistRangedDpsOfIndex(nullptr, 0, false)); +} diff --git a/test/unit/Bot/Service/SpellServiceTest.cpp b/test/unit/Bot/Service/SpellServiceTest.cpp new file mode 100644 index 0000000000..6018dceff3 --- /dev/null +++ b/test/unit/Bot/Service/SpellServiceTest.cpp @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include +#include "mocks/MockBotServices.h" + +using ::testing::Return; +using ::testing::_; + +/** + * @brief Tests for BotSpellService + * + * These tests verify the spell service functionality + * for bot spell casting and aura management operations. + */ +class SpellServiceTest : public ::testing::Test +{ +protected: + MockSpellService mockSpellService; +}; + +TEST_F(SpellServiceTest, CanMockCanCastSpellByName) +{ + EXPECT_CALL(mockSpellService, CanCastSpell(std::string("Fireball"), _, _)) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockSpellService.CanCastSpell("Fireball", nullptr, nullptr)); +} + +TEST_F(SpellServiceTest, CanMockCastSpellByName) +{ + EXPECT_CALL(mockSpellService, CastSpell(std::string("Fireball"), _, _)) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockSpellService.CastSpell("Fireball", nullptr, nullptr)); +} + +TEST_F(SpellServiceTest, CanMockCanCastSpellById) +{ + EXPECT_CALL(mockSpellService, CanCastSpell(uint32(12345), (Unit*)nullptr, _, _, _)) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockSpellService.CanCastSpell(uint32(12345), (Unit*)nullptr, true, nullptr, nullptr)); +} + +TEST_F(SpellServiceTest, CanMockCastSpellById) +{ + EXPECT_CALL(mockSpellService, CastSpell(uint32(12345), (Unit*)nullptr, _)) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockSpellService.CastSpell(uint32(12345), (Unit*)nullptr, nullptr)); +} + +TEST_F(SpellServiceTest, CanMockHasAuraByName) +{ + EXPECT_CALL(mockSpellService, HasAura(std::string("Power Word: Fortitude"), _, _, _, _, _)) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockSpellService.HasAura("Power Word: Fortitude", nullptr)); +} + +TEST_F(SpellServiceTest, CanMockHasAuraById) +{ + EXPECT_CALL(mockSpellService, HasAura(uint32(12345), _)) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockSpellService.HasAura(uint32(12345), nullptr)); +} + +TEST_F(SpellServiceTest, CanMockGetAura) +{ + EXPECT_CALL(mockSpellService, GetAura(std::string("Renew"), _, _, _, _)) + .WillOnce(Return(nullptr)); + + EXPECT_EQ(nullptr, mockSpellService.GetAura("Renew", nullptr)); +} + +TEST_F(SpellServiceTest, CanMockHasAuraToDispel) +{ + EXPECT_CALL(mockSpellService, HasAuraToDispel(_, 1)) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockSpellService.HasAuraToDispel(nullptr, 1)); +} + +TEST_F(SpellServiceTest, CanMockCanDispel) +{ + EXPECT_CALL(mockSpellService, CanDispel(_, 1)) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockSpellService.CanDispel(nullptr, 1)); +} + +TEST_F(SpellServiceTest, CanMockIsInterruptableSpellCasting) +{ + EXPECT_CALL(mockSpellService, IsInterruptableSpellCasting(_, std::string("Kick"))) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockSpellService.IsInterruptableSpellCasting(nullptr, "Kick")); +} + +TEST_F(SpellServiceTest, CanMockCalculateGlobalCooldown) +{ + EXPECT_CALL(mockSpellService, CalculateGlobalCooldown(12345)) + .WillOnce(Return(1500)); + + EXPECT_EQ(1500, mockSpellService.CalculateGlobalCooldown(12345)); +} + +TEST_F(SpellServiceTest, CanMockIsInVehicle) +{ + EXPECT_CALL(mockSpellService, IsInVehicle(true, false, false, false, false)) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockSpellService.IsInVehicle(true, false, false, false, false)); +} + +/** + * @brief Tests for spell behavior patterns + */ +class SpellBehaviorPatternTest : public ::testing::Test +{ +protected: + MockSpellService mockSpellService; + + bool CanCastHeal(MockSpellService& spellService, Unit* target, std::string const& healSpell) + { + return spellService.CanCastSpell(healSpell, target); + } + + bool TryCastHeal(MockSpellService& spellService, Unit* target, std::string const& healSpell) + { + if (CanCastHeal(spellService, target, healSpell)) + { + return spellService.CastSpell(healSpell, target); + } + return false; + } + + bool ShouldDispel(MockSpellService& spellService, Unit* target, uint32 dispelType) + { + return spellService.HasAuraToDispel(target, dispelType); + } + + bool ShouldInterrupt(MockSpellService& spellService, Unit* target, std::string const& interruptSpell) + { + return spellService.IsInterruptableSpellCasting(target, interruptSpell); + } +}; + +TEST_F(SpellBehaviorPatternTest, CanCheckHealability) +{ + EXPECT_CALL(mockSpellService, CanCastSpell(std::string("Flash Heal"), _, _)) + .WillOnce(Return(true)); + + EXPECT_TRUE(CanCastHeal(mockSpellService, nullptr, "Flash Heal")); +} + +TEST_F(SpellBehaviorPatternTest, TryCastHealSuccess) +{ + EXPECT_CALL(mockSpellService, CanCastSpell(std::string("Flash Heal"), _, _)) + .WillOnce(Return(true)); + EXPECT_CALL(mockSpellService, CastSpell(std::string("Flash Heal"), _, _)) + .WillOnce(Return(true)); + + EXPECT_TRUE(TryCastHeal(mockSpellService, nullptr, "Flash Heal")); +} + +TEST_F(SpellBehaviorPatternTest, TryCastHealFailCannotCast) +{ + EXPECT_CALL(mockSpellService, CanCastSpell(std::string("Flash Heal"), _, _)) + .WillOnce(Return(false)); + + EXPECT_FALSE(TryCastHeal(mockSpellService, nullptr, "Flash Heal")); +} + +TEST_F(SpellBehaviorPatternTest, CheckDispelNeed) +{ + EXPECT_CALL(mockSpellService, HasAuraToDispel(_, 1)) + .WillOnce(Return(true)); + + EXPECT_TRUE(ShouldDispel(mockSpellService, nullptr, 1)); +} + +TEST_F(SpellBehaviorPatternTest, CheckInterruptNeed) +{ + EXPECT_CALL(mockSpellService, IsInterruptableSpellCasting(_, std::string("Kick"))) + .WillOnce(Return(true)); + + EXPECT_TRUE(ShouldInterrupt(mockSpellService, nullptr, "Kick")); +} + +/** + * @brief Tests for aura management patterns + */ +class SpellAuraManagementTest : public ::testing::Test +{ +protected: + MockSpellService mockSpellService; + + bool HasBuff(MockSpellService& spellService, Unit* unit, std::string const& buffName) + { + return spellService.HasAura(buffName, unit); + } + + bool NeedsBuffRefresh(MockSpellService& spellService, Unit* unit, std::string const& buffName) + { + return !spellService.HasAura(buffName, unit); + } +}; + +TEST_F(SpellAuraManagementTest, CheckHasBuff) +{ + EXPECT_CALL(mockSpellService, HasAura(std::string("Power Word: Fortitude"), _, _, _, _, _)) + .WillOnce(Return(true)); + + EXPECT_TRUE(HasBuff(mockSpellService, nullptr, "Power Word: Fortitude")); +} + +TEST_F(SpellAuraManagementTest, CheckNeedsBuffRefresh) +{ + EXPECT_CALL(mockSpellService, HasAura(std::string("Power Word: Fortitude"), _, _, _, _, _)) + .WillOnce(Return(false)); + + EXPECT_TRUE(NeedsBuffRefresh(mockSpellService, nullptr, "Power Word: Fortitude")); +} + +TEST_F(SpellAuraManagementTest, CheckNoBuffRefreshNeeded) +{ + EXPECT_CALL(mockSpellService, HasAura(std::string("Power Word: Fortitude"), _, _, _, _, _)) + .WillOnce(Return(true)); + + EXPECT_FALSE(NeedsBuffRefresh(mockSpellService, nullptr, "Power Word: Fortitude")); +} + +/** + * @brief Tests for vehicle spell patterns + */ +class SpellVehicleTest : public ::testing::Test +{ +protected: + MockSpellService mockSpellService; +}; + +TEST_F(SpellVehicleTest, CanCheckInVehicleWithControl) +{ + EXPECT_CALL(mockSpellService, IsInVehicle(true, false, false, false, false)) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockSpellService.IsInVehicle(true, false, false, false, false)); +} + +TEST_F(SpellVehicleTest, CanCheckInVehicleCanCast) +{ + EXPECT_CALL(mockSpellService, IsInVehicle(false, true, false, false, false)) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockSpellService.IsInVehicle(false, true, false, false, false)); +} + +TEST_F(SpellVehicleTest, CanMockCanCastVehicleSpell) +{ + EXPECT_CALL(mockSpellService, CanCastVehicleSpell(12345, _)) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockSpellService.CanCastVehicleSpell(12345, nullptr)); +} + +TEST_F(SpellVehicleTest, CanMockCastVehicleSpell) +{ + EXPECT_CALL(mockSpellService, CastVehicleSpell(uint32(12345), (Unit*)nullptr)) + .WillOnce(Return(true)); + + EXPECT_TRUE(mockSpellService.CastVehicleSpell(uint32(12345), (Unit*)nullptr)); +} diff --git a/test/unit/HealthThresholdTest.cpp b/test/unit/HealthThresholdTest.cpp new file mode 100644 index 0000000000..1dcceeeff8 --- /dev/null +++ b/test/unit/HealthThresholdTest.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include +#include "mocks/MockPlayerbotAIConfig.h" + +/** + * @brief Tests for health threshold logic + * + * These tests validate the logic used by health triggers + * without requiring actual game objects. + */ +class HealthThresholdTest : public ::testing::Test +{ +protected: + void SetUp() override + { + mockConfig.SetupDefaults(); + } + + MockConfigProvider mockConfig; + + /** + * @brief Check if health percentage is in critical range + * @param healthPercent Current health as percentage (0-100) + * @param threshold Critical health threshold + * @return true if health is at or below critical threshold + */ + bool IsCriticalHealth(float healthPercent, uint32 threshold) + { + return healthPercent <= static_cast(threshold); + } + + /** + * @brief Check if health percentage is in a range + * @param healthPercent Current health as percentage (0-100) + * @param minValue Minimum value (inclusive) + * @param maxValue Maximum value (exclusive) + * @return true if health is in range [minValue, maxValue) + */ + bool IsHealthInRange(float healthPercent, float minValue, float maxValue) + { + return healthPercent >= minValue && healthPercent < maxValue; + } +}; + +TEST_F(HealthThresholdTest, CriticalHealthDetection) +{ + uint32 criticalThreshold = mockConfig.GetCriticalHealth(); // Default: 20 + + // At or below critical + EXPECT_TRUE(IsCriticalHealth(5.0f, criticalThreshold)); + EXPECT_TRUE(IsCriticalHealth(15.0f, criticalThreshold)); + EXPECT_TRUE(IsCriticalHealth(20.0f, criticalThreshold)); + + // Above critical + EXPECT_FALSE(IsCriticalHealth(21.0f, criticalThreshold)); + EXPECT_FALSE(IsCriticalHealth(50.0f, criticalThreshold)); + EXPECT_FALSE(IsCriticalHealth(100.0f, criticalThreshold)); +} + +TEST_F(HealthThresholdTest, LowHealthDetection) +{ + uint32 lowThreshold = mockConfig.GetLowHealth(); // Default: 45 + + EXPECT_TRUE(IsCriticalHealth(30.0f, lowThreshold)); + EXPECT_TRUE(IsCriticalHealth(45.0f, lowThreshold)); + EXPECT_FALSE(IsCriticalHealth(46.0f, lowThreshold)); +} + +TEST_F(HealthThresholdTest, HealthRangeDetection) +{ + // Test "medium health" range (lowHealth to mediumHealth) + uint32 lowHealth = mockConfig.GetLowHealth(); // 45 + uint32 mediumHealth = mockConfig.GetMediumHealth(); // 65 + + // In range + EXPECT_TRUE(IsHealthInRange(50.0f, lowHealth, mediumHealth)); + EXPECT_TRUE(IsHealthInRange(60.0f, lowHealth, mediumHealth)); + + // Below range + EXPECT_FALSE(IsHealthInRange(30.0f, lowHealth, mediumHealth)); + EXPECT_FALSE(IsHealthInRange(44.0f, lowHealth, mediumHealth)); + + // Above range + EXPECT_FALSE(IsHealthInRange(65.0f, lowHealth, mediumHealth)); + EXPECT_FALSE(IsHealthInRange(80.0f, lowHealth, mediumHealth)); + + // At boundaries + EXPECT_TRUE(IsHealthInRange(45.0f, lowHealth, mediumHealth)); // Min inclusive + EXPECT_FALSE(IsHealthInRange(65.0f, lowHealth, mediumHealth)); // Max exclusive +} + +TEST_F(HealthThresholdTest, AlmostFullHealthRange) +{ + uint32 mediumHealth = mockConfig.GetMediumHealth(); // 65 + uint32 almostFullHealth = mockConfig.GetAlmostFullHealth(); // 85 + + // In "almost full" range + EXPECT_TRUE(IsHealthInRange(70.0f, mediumHealth, almostFullHealth)); + EXPECT_TRUE(IsHealthInRange(80.0f, mediumHealth, almostFullHealth)); + + // Below range + EXPECT_FALSE(IsHealthInRange(60.0f, mediumHealth, almostFullHealth)); + + // Above range (full health) + EXPECT_FALSE(IsHealthInRange(90.0f, mediumHealth, almostFullHealth)); + EXPECT_FALSE(IsHealthInRange(100.0f, mediumHealth, almostFullHealth)); +} + +TEST_F(HealthThresholdTest, CustomThresholdsWithMock) +{ + using ::testing::Return; + + // Simulate a custom configuration with different thresholds + EXPECT_CALL(mockConfig, GetCriticalHealth()).WillRepeatedly(Return(15u)); + EXPECT_CALL(mockConfig, GetLowHealth()).WillRepeatedly(Return(35u)); + EXPECT_CALL(mockConfig, GetMediumHealth()).WillRepeatedly(Return(55u)); + EXPECT_CALL(mockConfig, GetAlmostFullHealth()).WillRepeatedly(Return(75u)); + + // Verify the custom thresholds + EXPECT_EQ(15u, mockConfig.GetCriticalHealth()); + EXPECT_EQ(35u, mockConfig.GetLowHealth()); + EXPECT_EQ(55u, mockConfig.GetMediumHealth()); + EXPECT_EQ(75u, mockConfig.GetAlmostFullHealth()); + + // Test with custom thresholds + EXPECT_TRUE(IsCriticalHealth(15.0f, mockConfig.GetCriticalHealth())); + EXPECT_FALSE(IsCriticalHealth(16.0f, mockConfig.GetCriticalHealth())); +} + +/** + * @brief Simulates the ValueInRangeTrigger logic + */ +class ValueInRangeTest : public ::testing::Test +{ +protected: + /** + * @brief Check if value is in range (mirrors ValueInRangeTrigger::IsActive) + * + * From HealthTriggers.h: + * bool IsActive() override { + * float value = GetValue(); + * return value < maxValue && value >= minValue; + * } + */ + bool IsValueInRange(float value, float minValue, float maxValue) + { + return value < maxValue && value >= minValue; + } +}; + +TEST_F(ValueInRangeTest, BasicRangeCheck) +{ + // Range [20, 45) + EXPECT_FALSE(IsValueInRange(19.0f, 20.0f, 45.0f)); // Below min + EXPECT_TRUE(IsValueInRange(20.0f, 20.0f, 45.0f)); // At min (inclusive) + EXPECT_TRUE(IsValueInRange(30.0f, 20.0f, 45.0f)); // In range + EXPECT_TRUE(IsValueInRange(44.9f, 20.0f, 45.0f)); // Just below max + EXPECT_FALSE(IsValueInRange(45.0f, 20.0f, 45.0f)); // At max (exclusive) + EXPECT_FALSE(IsValueInRange(50.0f, 20.0f, 45.0f)); // Above max +} + +TEST_F(ValueInRangeTest, EdgeCases) +{ + // Empty range + EXPECT_FALSE(IsValueInRange(10.0f, 10.0f, 10.0f)); + + // Inverted range (min > max should always be false) + EXPECT_FALSE(IsValueInRange(25.0f, 50.0f, 20.0f)); + + // Zero-based range + EXPECT_TRUE(IsValueInRange(0.0f, 0.0f, 100.0f)); + EXPECT_TRUE(IsValueInRange(50.0f, 0.0f, 100.0f)); + EXPECT_FALSE(IsValueInRange(100.0f, 0.0f, 100.0f)); +}