Skip to content

Commit 4038609

Browse files
authored
Natural Hunterbot strategies (#288)
1 parent 631b46b commit 4038609

8 files changed

Lines changed: 176 additions & 8 deletions

File tree

src/modules/Bots/playerbot/PlayerbotFactory.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ class PlayerbotFactory : public InventoryAction
4646
*/
4747
void Refresh();
4848

49+
/**
50+
* @brief Initializes the pet for the player bot.
51+
*/
52+
void InitPet();
53+
4954
private:
5055
/**
5156
* @brief Randomizes the player bot with an option for incremental changes.
@@ -148,11 +153,6 @@ class PlayerbotFactory : public InventoryAction
148153
*/
149154
void InitQuests();
150155

151-
/**
152-
* @brief Initializes the pet for the player bot.
153-
*/
154-
void InitPet();
155-
156156
/**
157157
* @brief Clears the inventory of the player bot.
158158
*/

src/modules/Bots/playerbot/strategy/hunter/GenericHunterNonCombatStrategy.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class GenericHunterNonCombatStrategyActionNodeFactory : public NamedObjectFactor
1313
creators["rapid fire"] = &rapid_fire;
1414
creators["boost"] = &rapid_fire;
1515
creators["aspect of the pack"] = &aspect_of_the_pack;
16+
creators["aspect of the viper"] = &aspect_of_the_viper;
1617
}
1718
private:
1819
static ActionNode* rapid_fire(PlayerbotAI* ai)
@@ -29,6 +30,14 @@ class GenericHunterNonCombatStrategyActionNodeFactory : public NamedObjectFactor
2930
/*A*/ NextAction::array(0, new NextAction("aspect of the cheetah"), NULL),
3031
/*C*/ NULL);
3132
}
33+
// aspect of the viper doesn't exist in Vanilla 1.12 - fall back to drinking
34+
static ActionNode* aspect_of_the_viper(PlayerbotAI* ai)
35+
{
36+
return new ActionNode ("aspect of the viper",
37+
/*P*/ NULL,
38+
/*A*/ NextAction::array(0, new NextAction("drink"), NULL),
39+
/*C*/ NULL);
40+
}
3241
};
3342

3443
GenericHunterNonCombatStrategy::GenericHunterNonCombatStrategy(PlayerbotAI* ai) : NonCombatStrategy(ai)

src/modules/Bots/playerbot/strategy/hunter/GenericHunterStrategy.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,15 @@ void GenericHunterStrategy::InitTriggers(std::list<TriggerNode*> &triggers)
5050

5151
triggers.push_back(new TriggerNode(
5252
"enemy too close for spell",
53-
NextAction::array(0, new NextAction("wing clip", 50.0f), new NextAction("flee",49.0f), new NextAction("concussive shot", 48.0f), NULL)));
53+
NextAction::array(0,
54+
new NextAction("intimidation", 52.0f),
55+
new NextAction("wing clip", 51.0f),
56+
new NextAction("hunter ensure ranged position", 50.0f),
57+
new NextAction("mongoose bite", 49.5f),
58+
new NextAction("disengage", 49.0f),
59+
new NextAction("hunter melee", 48.5f),
60+
new NextAction("flee", 48.0f),
61+
NULL)));
5462

5563
triggers.push_back(new TriggerNode(
5664
"medium threat",
@@ -63,4 +71,9 @@ void GenericHunterStrategy::InitTriggers(std::list<TriggerNode*> &triggers)
6371
triggers.push_back(new TriggerNode(
6472
"rapid fire",
6573
NextAction::array(0, new NextAction("rapid fire", 55.0f), NULL)));
74+
75+
triggers.push_back(new TriggerNode(
76+
"bestial wrath",
77+
NextAction::array(0, new NextAction("bestial wrath", 55.0f), NULL)));
78+
6679
}

src/modules/Bots/playerbot/strategy/hunter/HunterActions.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "../../playerbot.h"
33
#include "../actions/GenericActions.h"
44
#include "HunterActions.h"
5+
#include "../../PlayerbotFactory.h"
56

67
using namespace ai;
78

@@ -24,3 +25,46 @@ Value<Unit*>* CastFreezingTrap::GetTargetValue()
2425
{
2526
return context->GetValue<Unit*>("cc target", "freezing trap");
2627
}
28+
29+
bool CastRevivePetAction::isPossible()
30+
{
31+
if (bot->GetPet())
32+
return CastBuffSpellAction::isPossible();
33+
PetDatabaseStatus status = Pet::GetStatusFromDB(bot);
34+
return status == PET_DB_DEAD || status == PET_DB_NO_PET;
35+
}
36+
37+
bool CastRevivePetAction::Execute(Event event)
38+
{
39+
if (!bot->GetPet() && Pet::GetStatusFromDB(bot) == PET_DB_NO_PET)
40+
{
41+
PlayerbotFactory factory(bot, bot->getLevel());
42+
factory.InitPet();
43+
return true;
44+
}
45+
return CastBuffSpellAction::Execute(event);
46+
}
47+
48+
bool CastIntimidationAction::isUseful()
49+
{
50+
return CastSpellAction::isUseful() && AI_VALUE(Unit*, "pet target") != NULL;
51+
}
52+
53+
bool HunterMeleeAction::isUseful()
54+
{
55+
// Only swing if enemy is already in our face AND targeting us.
56+
// Perhaps in the future a ranged/melee hunter strategy would be nice.
57+
Unit* target = AI_VALUE(Unit*, "current target");
58+
if (!target || !target->IsAlive()) return false;
59+
bool victim = target->getVictim() == bot;
60+
float dist = AI_VALUE2(float, "distance", "current target");
61+
return victim && dist <= ATTACK_DISTANCE;
62+
}
63+
64+
bool HunterMeleeAction::Execute(Event event)
65+
{
66+
Unit* target = AI_VALUE(Unit*, "current target");
67+
if (!target) return false;
68+
bot->Attack(target, true);
69+
return true;
70+
}

src/modules/Bots/playerbot/strategy/hunter/HunterActions.h

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ namespace ai
8787
{
8888
public:
8989
CastRevivePetAction(PlayerbotAI* ai) : CastBuffSpellAction(ai, "revive pet") {}
90+
virtual bool isPossible();
91+
virtual bool Execute(Event event);
9092
};
9193

9294
class CastTrueshotAuraAction : public CastBuffSpellAction
@@ -120,7 +122,8 @@ namespace ai
120122
CastWingClipAction(PlayerbotAI* ai) : CastMeleeSpellAction(ai, "wing clip") {}
121123
virtual bool isUseful()
122124
{
123-
return CastMeleeSpellAction::isUseful() && !ai->HasAura(spell, GetTarget());
125+
Unit* target = GetTarget();
126+
return target && target->IsAlive() && CastMeleeSpellAction::isUseful() && !ai->HasAura(spell, target);
124127
}
125128
};
126129

@@ -129,4 +132,66 @@ namespace ai
129132
public:
130133
CastSerpentStingOnAttackerAction(PlayerbotAI* ai) : CastDebuffSpellOnAttackerAction(ai, "serpent sting") {}
131134
};
135+
136+
BEGIN_MELEE_SPELL_ACTION(CastDisengageAction, "disengage")
137+
END_SPELL_ACTION()
138+
139+
BEGIN_MELEE_SPELL_ACTION(CastImmolationTrapAction, "immolation trap")
140+
END_SPELL_ACTION()
141+
142+
BEGIN_MELEE_SPELL_ACTION(CastFrostTrapAction, "frost trap")
143+
END_SPELL_ACTION()
144+
145+
BEGIN_MELEE_SPELL_ACTION(CastExplosiveTrapAction, "explosive trap")
146+
END_SPELL_ACTION()
147+
148+
BEGIN_RANGED_SPELL_ACTION(CastScatterShotAction, "scatter shot")
149+
END_SPELL_ACTION()
150+
151+
class CastBestialWrathAction : public CastAuraSpellAction
152+
{
153+
public:
154+
CastBestialWrathAction(PlayerbotAI* ai) : CastAuraSpellAction(ai, "bestial wrath") {}
155+
virtual string GetTargetName() { return "pet target"; }
156+
virtual bool isUseful() { return CastAuraSpellAction::isUseful() && AI_VALUE(Unit*, "pet target") != NULL; }
157+
};
158+
159+
class CastMongooseBiteAction : public CastMeleeSpellAction
160+
{
161+
public:
162+
CastMongooseBiteAction(PlayerbotAI* ai) : CastMeleeSpellAction(ai, "mongoose bite") {}
163+
virtual bool isPossible() { return bot->HasAuraState(AURA_STATE_DEFENSE) && CastMeleeSpellAction::isPossible(); }
164+
};
165+
166+
class CastIntimidationAction : public CastSpellAction
167+
{
168+
public:
169+
CastIntimidationAction(PlayerbotAI* ai) : CastSpellAction(ai, "intimidation") {}
170+
virtual bool isUseful();
171+
};
172+
173+
class HunterMeleeAction : public Action
174+
{
175+
public:
176+
HunterMeleeAction(PlayerbotAI* ai) : Action(ai, "hunter melee") {}
177+
virtual bool Execute(Event event);
178+
virtual bool isUseful();
179+
};
180+
181+
class HunterEnsureRangedPositionAction : public MovementAction
182+
{
183+
public:
184+
HunterEnsureRangedPositionAction(PlayerbotAI* ai) : MovementAction(ai, "hunter ensure ranged position") {}
185+
virtual bool Execute(Event event)
186+
{
187+
return MoveTo(AI_VALUE(Unit*, "current target"), sPlayerbotAIConfig.spellDistance);
188+
}
189+
virtual bool isUseful()
190+
{
191+
Unit* target = AI_VALUE(Unit*, "current target");
192+
if (!target || !target->IsAlive()) return false;
193+
return target->getVictim() != bot &&
194+
bot->GetDistance(target) < sPlayerbotAIConfig.spellDistance;
195+
}
196+
};
132197
}

src/modules/Bots/playerbot/strategy/hunter/HunterAiObjectContext.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ namespace ai
7676
creators["freezing trap"] = &TriggerFactoryInternal::freezing_trap;
7777
creators["aspect of the pack"] = &TriggerFactoryInternal::aspect_of_the_pack;
7878
creators["rapid fire"] = &TriggerFactoryInternal::rapid_fire;
79+
creators["bestial wrath"] = &TriggerFactoryInternal::bestial_wrath;
7980
creators["aspect of the hawk"] = &TriggerFactoryInternal::aspect_of_the_hawk;
8081
creators["aspect of the wild"] = &TriggerFactoryInternal::aspect_of_the_wild;
8182
creators["aspect of the viper"] = &TriggerFactoryInternal::aspect_of_the_viper;
@@ -95,6 +96,7 @@ namespace ai
9596
static Trigger* freezing_trap(PlayerbotAI* ai) { return new FreezingTrapTrigger(ai); }
9697
static Trigger* aspect_of_the_pack(PlayerbotAI* ai) { return new HunterAspectOfThePackTrigger(ai); }
9798
static Trigger* rapid_fire(PlayerbotAI* ai) { return new RapidFireTrigger(ai); }
99+
static Trigger* bestial_wrath(PlayerbotAI* ai) { return new BestialWrathTrigger(ai); }
98100
static Trigger* aspect_of_the_hawk(PlayerbotAI* ai) { return new HunterAspectOfTheHawkTrigger(ai); }
99101
static Trigger* aspect_of_the_wild(PlayerbotAI* ai) { return new HunterAspectOfTheWildTrigger(ai); }
100102
};
@@ -141,6 +143,16 @@ namespace ai
141143
creators["trueshot aura"] = &AiObjectContextInternal::trueshot_aura;
142144
creators["feign death"] = &AiObjectContextInternal::feign_death;
143145
creators["wing clip"] = &AiObjectContextInternal::wing_clip;
146+
creators["disengage"] = &AiObjectContextInternal::disengage;
147+
creators["immolation trap"] = &AiObjectContextInternal::immolation_trap;
148+
creators["frost trap"] = &AiObjectContextInternal::frost_trap;
149+
creators["explosive trap"] = &AiObjectContextInternal::explosive_trap;
150+
creators["scatter shot"] = &AiObjectContextInternal::scatter_shot;
151+
creators["bestial wrath"] = &AiObjectContextInternal::bestial_wrath;
152+
creators["mongoose bite"] = &AiObjectContextInternal::mongoose_bite;
153+
creators["intimidation"] = &AiObjectContextInternal::intimidation;
154+
creators["hunter melee"] = &AiObjectContextInternal::hunter_melee;
155+
creators["hunter ensure ranged position"] = &AiObjectContextInternal::hunter_ensure_ranged_position;
144156
}
145157

146158
private:
@@ -167,6 +179,16 @@ namespace ai
167179
static Action* rapid_fire(PlayerbotAI* ai) { return new CastRapidFireAction(ai); }
168180
static Action* aspect_of_the_hawk(PlayerbotAI* ai) { return new CastAspectOfTheHawkAction(ai); }
169181
static Action* aspect_of_the_wild(PlayerbotAI* ai) { return new CastAspectOfTheWildAction(ai); }
182+
static Action* disengage(PlayerbotAI* ai) { return new CastDisengageAction(ai); }
183+
static Action* immolation_trap(PlayerbotAI* ai) { return new CastImmolationTrapAction(ai); }
184+
static Action* frost_trap(PlayerbotAI* ai) { return new CastFrostTrapAction(ai); }
185+
static Action* explosive_trap(PlayerbotAI* ai) { return new CastExplosiveTrapAction(ai); }
186+
static Action* scatter_shot(PlayerbotAI* ai) { return new CastScatterShotAction(ai); }
187+
static Action* bestial_wrath(PlayerbotAI* ai) { return new CastBestialWrathAction(ai); }
188+
static Action* mongoose_bite(PlayerbotAI* ai) { return new CastMongooseBiteAction(ai); }
189+
static Action* intimidation(PlayerbotAI* ai) { return new CastIntimidationAction(ai); }
190+
static Action* hunter_melee(PlayerbotAI* ai) { return new HunterMeleeAction(ai); }
191+
static Action* hunter_ensure_ranged_position(PlayerbotAI* ai) { return new HunterEnsureRangedPositionAction(ai); }
170192
static Action* aspect_of_the_pack(PlayerbotAI* ai) { return new CastAspectOfThePackAction(ai); }
171193
static Action* aspect_of_the_cheetah(PlayerbotAI* ai) { return new CastAspectOfTheCheetahAction(ai); }
172194
static Action* wing_clip(PlayerbotAI* ai) { return new CastWingClipAction(ai); }

src/modules/Bots/playerbot/strategy/hunter/HunterTriggers.cpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,16 @@ bool HunterNoStingsActiveTrigger::IsActive()
1616

1717
bool HuntersPetDeadTrigger::IsActive()
1818
{
19+
if (AI_VALUE2(bool, "mounted", "self target"))
20+
return false;
21+
1922
Unit* pet = AI_VALUE(Unit*, "pet target");
20-
return pet && AI_VALUE2(bool, "dead", "pet target") && !AI_VALUE2(bool, "mounted", "self target");
23+
if (pet)
24+
return AI_VALUE2(bool, "dead", "pet target");
25+
26+
// Pet not in world — check DB to catch the common case where the corpse timer has already expired
27+
PetDatabaseStatus status = Pet::GetStatusFromDB(bot);
28+
return status == PET_DB_DEAD || status == PET_DB_NO_PET;
2129
}
2230

2331

src/modules/Bots/playerbot/strategy/hunter/HunterTriggers.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,12 @@ namespace ai
7272
RapidFireTrigger(PlayerbotAI* ai) : BoostTrigger(ai, "rapid fire") {}
7373
};
7474

75+
class BestialWrathTrigger : public BoostTrigger
76+
{
77+
public:
78+
BestialWrathTrigger(PlayerbotAI* ai) : BoostTrigger(ai, "bestial wrath") {}
79+
};
80+
7581
class TrueshotAuraTrigger : public BuffTrigger
7682
{
7783
public:
@@ -83,4 +89,5 @@ namespace ai
8389
public:
8490
SerpentStingOnAttackerTrigger(PlayerbotAI* ai) : DebuffOnAttackerTrigger(ai, "serpent sting") {}
8591
};
92+
8693
}

0 commit comments

Comments
 (0)