From 10799e689c4290cb738bfff52fb8117d4e2d1a36 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Fri, 12 Apr 2024 00:32:07 +0800 Subject: [PATCH 1/9] Avoid aoe base --- src/strategy/actions/ActionContext.h | 2 ++ src/strategy/actions/MovementActions.cpp | 10 ++++++++++ src/strategy/actions/MovementActions.h | 11 ++++++++++- src/strategy/generic/CombatStrategy.cpp | 16 ++++++++++++---- src/strategy/generic/CombatStrategy.h | 1 + src/strategy/triggers/TriggerContext.h | 1 - src/strategy/values/ExpectedLifetimeValue.h | 10 ++++++++++ src/strategy/values/ValueContext.h | 7 ++++--- 8 files changed, 49 insertions(+), 9 deletions(-) diff --git a/src/strategy/actions/ActionContext.h b/src/strategy/actions/ActionContext.h index 0b5568ef..6cdc68b1 100644 --- a/src/strategy/actions/ActionContext.h +++ b/src/strategy/actions/ActionContext.h @@ -88,6 +88,7 @@ class ActionContext : public NamedObjectContext creators["reach party member to resurrect"] = &ActionContext::reach_party_member_to_resurrect; creators["flee"] = &ActionContext::flee; creators["flee with pet"] = &ActionContext::flee_with_pet; + creators["avoid aoe"] = &ActionContext::avoid_aoe; creators["gift of the naaru"] = &ActionContext::gift_of_the_naaru; creators["shoot"] = &ActionContext::shoot; creators["lifeblood"] = &ActionContext::lifeblood; @@ -263,6 +264,7 @@ class ActionContext : public NamedObjectContext static Action* reach_party_member_to_resurrect(PlayerbotAI* botAI) { return new ReachPartyMemberToResurrectAction(botAI); } static Action* flee(PlayerbotAI* botAI) { return new FleeAction(botAI); } static Action* flee_with_pet(PlayerbotAI* botAI) { return new FleeWithPetAction(botAI); } + static Action* avoid_aoe(PlayerbotAI* botAI) { return new AvoidAoeAction(botAI); } static Action* gift_of_the_naaru(PlayerbotAI* botAI) { return new CastGiftOfTheNaaruAction(botAI); } static Action* lifeblood(PlayerbotAI* botAI) { return new CastLifeBloodAction(botAI); } static Action* arcane_torrent(PlayerbotAI* botAI) { return new CastArcaneTorrentAction(botAI); } diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index faf6a328..d4419670 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -1482,6 +1482,16 @@ bool FleeWithPetAction::Execute(Event event) return Flee(AI_VALUE(Unit*, "current target")); } +bool AvoidAoeAction::isUseful() +{ + return false; +} + +bool AvoidAoeAction::Execute(Event event) +{ + return false; +} + bool RunAwayAction::Execute(Event event) { return Flee(AI_VALUE(Unit*, "master target")); diff --git a/src/strategy/actions/MovementActions.h b/src/strategy/actions/MovementActions.h index 2a605cb1..4865d7e1 100644 --- a/src/strategy/actions/MovementActions.h +++ b/src/strategy/actions/MovementActions.h @@ -66,11 +66,20 @@ class FleeWithPetAction : public MovementAction bool Execute(Event event) override; }; +class AvoidAoeAction : public MovementAction +{ + public: + AvoidAoeAction(PlayerbotAI* botAI) : MovementAction(botAI, "avoid aoe") { } + + bool isUseful() override; + bool Execute(Event event) override; +}; + class RunAwayAction : public MovementAction { public: RunAwayAction(PlayerbotAI* botAI) : MovementAction(botAI, "runaway") { } - + bool Execute(Event event) override; }; diff --git a/src/strategy/generic/CombatStrategy.cpp b/src/strategy/generic/CombatStrategy.cpp index 483fbe00..3b72af7f 100644 --- a/src/strategy/generic/CombatStrategy.cpp +++ b/src/strategy/generic/CombatStrategy.cpp @@ -4,6 +4,7 @@ #include "CombatStrategy.h" #include "Playerbots.h" +#include "Strategy.h" void CombatStrategy::InitTriggers(std::vector &triggers) { @@ -62,15 +63,22 @@ float AvoidAoeStrategyMultiplier::GetValue(Action* action) return 1.0f; } +NextAction** AvoidAoeStrategy::getDefaultActions() +{ + return NextAction::array(0, + new NextAction("avoid aoe", ACTION_EMERGENCY), + nullptr); +} + void AvoidAoeStrategy::InitTriggers(std::vector& triggers) { - triggers.push_back(new TriggerNode( - "has area debuff", - NextAction::array(0, new NextAction("flee", ACTION_EMERGENCY + 5), NULL))); + // triggers.push_back(new TriggerNode( + // "has area debuff", + // NextAction::array(0, new NextAction("flee", ACTION_EMERGENCY + 5), NULL))); } void AvoidAoeStrategy::InitMultipliers(std::vector& multipliers) { - multipliers.push_back(new AvoidAoeStrategyMultiplier(botAI)); + // multipliers.push_back(new AvoidAoeStrategyMultiplier(botAI)); } \ No newline at end of file diff --git a/src/strategy/generic/CombatStrategy.h b/src/strategy/generic/CombatStrategy.h index 0ac9e3f3..33813615 100644 --- a/src/strategy/generic/CombatStrategy.h +++ b/src/strategy/generic/CombatStrategy.h @@ -23,6 +23,7 @@ class AvoidAoeStrategy : public Strategy public: explicit AvoidAoeStrategy(PlayerbotAI* ai); const std::string getName() override { return "avoid aoe"; } + NextAction** getDefaultActions() override; void InitMultipliers(std::vector& multipliers) override; void InitTriggers(std::vector& triggers) override; }; diff --git a/src/strategy/triggers/TriggerContext.h b/src/strategy/triggers/TriggerContext.h index 1abed160..92fb96a2 100644 --- a/src/strategy/triggers/TriggerContext.h +++ b/src/strategy/triggers/TriggerContext.h @@ -80,7 +80,6 @@ class TriggerContext : public NamedObjectContext creators["has area debuff"] = &TriggerContext::HasAreaDebuff; - creators["enemy out of melee"] = &TriggerContext::EnemyOutOfMelee; creators["enemy out of spell"] = &TriggerContext::EnemyOutOfSpell; creators["enemy too close for spell"] = &TriggerContext::enemy_too_close_for_spell; diff --git a/src/strategy/values/ExpectedLifetimeValue.h b/src/strategy/values/ExpectedLifetimeValue.h index e9f432f2..65780bed 100644 --- a/src/strategy/values/ExpectedLifetimeValue.h +++ b/src/strategy/values/ExpectedLifetimeValue.h @@ -33,4 +33,14 @@ class ExpectedGroupDpsValue : public FloatCalculatedValue public: float Calculate() override; }; + +class AreaDebuffValue : public CalculatedValue +{ + public: + AreaDebuffValue(PlayerbotAI* botAI) : + CalculatedValue(botAI, "area debuff", 20 * 1000) { } + + Aura* Calculate() override; +}; + #endif diff --git a/src/strategy/values/ValueContext.h b/src/strategy/values/ValueContext.h index 38f37840..c90dbd49 100644 --- a/src/strategy/values/ValueContext.h +++ b/src/strategy/values/ValueContext.h @@ -295,8 +295,9 @@ class ValueContext : public NamedObjectContext creators["boss target"] = &ValueContext::boss_target; creators["nearest triggers"] = &ValueContext::nearest_triggers; creators["neglect threat"] = &ValueContext::neglect_threat; - creators["expected lifetime"] = &ValueContext::expected_lifetime; - creators["expected group dps"] = &ValueContext::expected_group_dps; + creators["expected lifetime"] = &ValueContext::expected_lifetime; + creators["expected group dps"] = &ValueContext::expected_group_dps; + creators["area debuff"] = &ValueContext::area_debuff; } private: @@ -497,7 +498,7 @@ class ValueContext : public NamedObjectContext static UntypedValue* neglect_threat(PlayerbotAI* ai) { return new NeglectThreatResetValue(ai); } static UntypedValue* expected_lifetime(PlayerbotAI* ai) { return new ExpectedLifetimeValue(ai); } static UntypedValue* expected_group_dps(PlayerbotAI* ai) { return new ExpectedGroupDpsValue(ai); } - + static UntypedValue* area_debuff(PlayerbotAI* ai) { return new AreaDebuffValue(ai); } }; #endif From df1b280b14b8d51c1364bddb0fc844539778c3f4 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 14 Apr 2024 00:00:41 +0800 Subject: [PATCH 2/9] Avoie aoe strategy --- src/strategy/actions/MovementActions.cpp | 85 ++++++++++++++++---- src/strategy/actions/TellLosAction.cpp | 8 +- src/strategy/values/AoeValues.cpp | 23 ++++++ src/strategy/values/AoeValues.h | 9 +++ src/strategy/values/ExpectedLifetimeValue.h | 9 --- src/strategy/values/PossibleTargetsValue.cpp | 15 ++++ src/strategy/values/PossibleTargetsValue.h | 10 +++ src/strategy/values/ValueContext.h | 2 + 8 files changed, 135 insertions(+), 26 deletions(-) diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index d4419670..d701b293 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -155,22 +155,25 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, !bot->IsFlying() && !bot->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING) && !bot->IsInWater(); if (!generatePath) { float distance = bot->GetExactDist(x, y, z); - WaitForReach(distance); - - if (bot->IsSitState()) - bot->SetStandState(UNIT_STAND_STATE_STAND); - - if (bot->IsNonMeleeSpellCast(true)) + if (distance > sPlayerbotAIConfig->contactDistance) { - bot->CastStop(); - botAI->InterruptSpell(); + WaitForReach(distance); + + if (bot->IsSitState()) + bot->SetStandState(UNIT_STAND_STATE_STAND); + + if (bot->IsNonMeleeSpellCast(true)) + { + bot->CastStop(); + botAI->InterruptSpell(); + } + MotionMaster &mm = *bot->GetMotionMaster(); + + mm.Clear(); + mm.MovePoint(mapId, x, y, z, generatePath); + AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation()); + return true; } - MotionMaster &mm = *bot->GetMotionMaster(); - - mm.Clear(); - mm.MovePoint(mapId, x, y, z, generatePath); - AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation()); - return true; } else { float modifiedZ; Movement::PointsArray path = SearchForBestPath(x, y, z, modifiedZ, sPlayerbotAIConfig->maxMovementSearchTime); @@ -197,7 +200,6 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation()); return true; } - } return false; @@ -1484,11 +1486,62 @@ bool FleeWithPetAction::Execute(Event event) bool AvoidAoeAction::isUseful() { - return false; + return AI_VALUE(Aura*, "area debuff"); } bool AvoidAoeAction::Execute(Event event) { + // Case #1: Aura with dynamic object + Aura* aura = AI_VALUE(Aura*, "area debuff"); + if (!aura) { + return false; + } + if (!aura->GetSpellInfo()) { + return false; + } + if (!bot->HasAura(aura->GetSpellInfo()->Id)) { + return false; + } + DynamicObject* dynOwner = aura->GetDynobjOwner(); + if (!dynOwner || !dynOwner->IsInWorld()) { + return false; + } + float radius = dynOwner->GetRadius(); + if (bot->GetExactDist(dynOwner) > radius) { + return false; + } + Unit* currentTarget = AI_VALUE(Unit*, "current target"); + std::vector possibleAngles; + if (currentTarget) { + float angleLeft = bot->GetAngle(currentTarget) + M_PI / 2; + float angleRight = bot->GetAngle(currentTarget) - M_PI / 2; + possibleAngles.push_back(angleLeft); + possibleAngles.push_back(angleRight); + } else { + float angleTo = bot->GetAngle(dynOwner) - M_PI; + possibleAngles.push_back(angleTo); + } + float farestDis = 0.0f; + Position bestPos; + // float disToDyn = bot->GetExactDist(dynOwner); + // float maxDisToGo = radius > disToDyn ? std::sqrt(radius * radius - disToDyn * disToDyn) + 0.5f : 0.5f; + for (float &angle : possibleAngles) { + float fleeDis = sPlayerbotAIConfig->fleeDistance; + Position pos{bot->GetPositionX() + cos(angle) * fleeDis, + bot->GetPositionY() + sin(angle) * fleeDis, + bot->GetPositionZ()}; + // todo(Yunfan): check carefully + if (dynOwner->GetExactDist(pos) > farestDis) { + farestDis = dynOwner->GetExactDist(pos); + bestPos = pos; + } + } + if (farestDis > 0.0f) { + std::ostringstream out; + out << "I'm avoiding aoe spell [" << aura->GetSpellInfo()->SpellName[0] << "]..."; + bot->Say(out.str(), LANG_UNIVERSAL); + return MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, false, true); + } return false; } diff --git a/src/strategy/actions/TellLosAction.cpp b/src/strategy/actions/TellLosAction.cpp index 6d7df103..234bebcc 100644 --- a/src/strategy/actions/TellLosAction.cpp +++ b/src/strategy/actions/TellLosAction.cpp @@ -37,6 +37,11 @@ bool TellLosAction::Execute(Event event) ListUnits("--- Friendly players ---", *context->GetValue("nearest friendly players")); } + if (param.empty() || param == "triggers") + { + ListUnits("--- Triggers ---", *context->GetValue("possible triggers")); + } + return true; } @@ -46,8 +51,9 @@ void TellLosAction::ListUnits(std::string const title, GuidVector units) for (ObjectGuid const guid : units) { - if (Unit* unit = botAI->GetUnit(guid)) + if (Unit* unit = botAI->GetUnit(guid)) { botAI->TellMaster(unit->GetName()); + } } } diff --git a/src/strategy/values/AoeValues.cpp b/src/strategy/values/AoeValues.cpp index 67f7d1e0..3d93640c 100644 --- a/src/strategy/values/AoeValues.cpp +++ b/src/strategy/values/AoeValues.cpp @@ -116,4 +116,27 @@ bool HasAreaDebuffValue::Calculate() } return false; +} + +Aura* AreaDebuffValue::Calculate() +{ + Unit::AuraApplicationMap& map = bot->GetAppliedAuras(); + for (Unit::AuraApplicationMap::iterator i = map.begin(); i != map.end(); ++i) + { + Aura *aura = i->second->GetBase(); + if (!aura) + continue; + + AuraObjectType type = aura->GetType(); + // bool is_area = aura->IsArea(); + bool isPositive = aura->GetSpellInfo()->IsPositive(); + if (type == DYNOBJ_AURA_TYPE && !isPositive) { + DynamicObject* dynOwner = aura->GetDynobjOwner(); + if (!dynOwner) { + continue; + } + return aura; + } + } + return nullptr; } \ No newline at end of file diff --git a/src/strategy/values/AoeValues.h b/src/strategy/values/AoeValues.h index 04ca3f37..a6cde02a 100644 --- a/src/strategy/values/AoeValues.h +++ b/src/strategy/values/AoeValues.h @@ -41,4 +41,13 @@ class HasAreaDebuffValue : public BoolCalculatedValue, public Qualified virtual bool Calculate(); }; +class AreaDebuffValue : public CalculatedValue +{ + public: + AreaDebuffValue(PlayerbotAI* botAI) : + CalculatedValue(botAI, "area debuff", 1 * 1000) { } + + Aura* Calculate() override; +}; + #endif diff --git a/src/strategy/values/ExpectedLifetimeValue.h b/src/strategy/values/ExpectedLifetimeValue.h index 65780bed..8b5e994d 100644 --- a/src/strategy/values/ExpectedLifetimeValue.h +++ b/src/strategy/values/ExpectedLifetimeValue.h @@ -34,13 +34,4 @@ class ExpectedGroupDpsValue : public FloatCalculatedValue float Calculate() override; }; -class AreaDebuffValue : public CalculatedValue -{ - public: - AreaDebuffValue(PlayerbotAI* botAI) : - CalculatedValue(botAI, "area debuff", 20 * 1000) { } - - Aura* Calculate() override; -}; - #endif diff --git a/src/strategy/values/PossibleTargetsValue.cpp b/src/strategy/values/PossibleTargetsValue.cpp index 50e29788..729c0ae0 100644 --- a/src/strategy/values/PossibleTargetsValue.cpp +++ b/src/strategy/values/PossibleTargetsValue.cpp @@ -8,6 +8,7 @@ #include "GridNotifiers.h" #include "GridNotifiersImpl.h" #include "Playerbots.h" +#include "Unit.h" void PossibleTargetsValue::FindUnits(std::list& targets) { @@ -20,3 +21,17 @@ bool PossibleTargetsValue::AcceptUnit(Unit* unit) { return AttackersValue::IsPossibleTarget(unit, bot, range); } + +void PossibleTriggersValue::FindUnits(std::list& targets) +{ + Acore::AnyUnfriendlyUnitInObjectRangeCheck u_check(bot, bot, range); + Acore::UnitListSearcher searcher(bot, targets, u_check); + Cell::VisitAllObjects(bot, searcher, range); +} + +bool PossibleTriggersValue::AcceptUnit(Unit* unit) +{ + return unit->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE) && unit->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE); + return true; // AttackersValue::IsPossibleTarget(unit, bot, range); +} + diff --git a/src/strategy/values/PossibleTargetsValue.h b/src/strategy/values/PossibleTargetsValue.h index e52b653a..81cd2bd3 100644 --- a/src/strategy/values/PossibleTargetsValue.h +++ b/src/strategy/values/PossibleTargetsValue.h @@ -27,4 +27,14 @@ class AllTargetsValue : public PossibleTargetsValue AllTargetsValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance) : PossibleTargetsValue(botAI, "all targets", range, true) { } }; +class PossibleTriggersValue : public NearestUnitsValue +{ + public: + PossibleTriggersValue(PlayerbotAI* botAI, std::string const name = "possible targets", float range = sPlayerbotAIConfig->sightDistance, bool ignoreLos = true): + NearestUnitsValue(botAI, name, range, ignoreLos) { } + + protected: + void FindUnits(std::list& targets) override; + bool AcceptUnit(Unit* unit) override; +}; #endif diff --git a/src/strategy/values/ValueContext.h b/src/strategy/values/ValueContext.h index c90dbd49..9cba19d4 100644 --- a/src/strategy/values/ValueContext.h +++ b/src/strategy/values/ValueContext.h @@ -110,6 +110,7 @@ class ValueContext : public NamedObjectContext creators["nearest enemy players"] = &ValueContext::nearest_enemy_players; creators["possible targets"] = &ValueContext::possible_targets; creators["possible targets no los"] = &ValueContext::possible_targets_no_los; + creators["possible triggers"] = &ValueContext::possible_triggers; creators["possible adds"] = &ValueContext::possible_adds; creators["all targets"] = &ValueContext::all_targets; creators["possible rpg targets"] = &ValueContext::possible_rpg_targets; @@ -377,6 +378,7 @@ class ValueContext : public NamedObjectContext static UntypedValue* nearest_corpses(PlayerbotAI* botAI) { return new NearestCorpsesValue(botAI); } static UntypedValue* possible_rpg_targets(PlayerbotAI* botAI) { return new PossibleRpgTargetsValue(botAI); } static UntypedValue* possible_targets(PlayerbotAI* botAI) { return new PossibleTargetsValue(botAI); } + static UntypedValue* possible_triggers(PlayerbotAI* botAI) { return new PossibleTriggersValue(botAI); } static UntypedValue* possible_targets_no_los(PlayerbotAI* botAI) { return new PossibleTargetsValue(botAI, "possible targets", sPlayerbotAIConfig->sightDistance, true); } static UntypedValue* possible_adds(PlayerbotAI* botAI) { return new PossibleAddsValue(botAI); } static UntypedValue* all_targets(PlayerbotAI* botAI) { return new AllTargetsValue(botAI); } From 0499e5da46edb305945a6840e95e65f6396da9fd Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Sun, 14 Apr 2024 13:03:18 +0800 Subject: [PATCH 3/9] Auto avoid aoe config --- conf/playerbots.conf.dist | 3 +++ src/AiFactory.cpp | 7 +++++- src/PlayerbotAIConfig.cpp | 1 + src/PlayerbotAIConfig.h | 3 ++- src/strategy/actions/MovementActions.cpp | 24 +++++++++++++------ src/strategy/actions/MovementActions.h | 3 +++ src/strategy/values/AoeValues.h | 1 + src/strategy/values/NearestGameObjects.cpp | 28 ++++++++++++++++++++++ src/strategy/values/NearestGameObjects.h | 13 ++++++++++ src/strategy/values/ValueContext.h | 2 ++ 10 files changed, 76 insertions(+), 9 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index e883c56e..c3afc2a3 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -322,6 +322,9 @@ AiPlayerbot.AutoSaveMana = 1 # Default: 60 (60%) AiPlayerbot.SaveManaThreshold = 60 +# Enable auto avoid aoe (experimental) +# Default: 0 (disable) +AiPlayerbot.AutoAvoidAoe = 0 # Random bot default strategies (applied after defaults) AiPlayerbot.RandomBotCombatStrategies = "+dps,+dps assist,-threat" diff --git a/src/AiFactory.cpp b/src/AiFactory.cpp index 12beba33..bd903b5c 100644 --- a/src/AiFactory.cpp +++ b/src/AiFactory.cpp @@ -266,9 +266,14 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa { engine->addStrategies("racials", "chat", "default", "cast time", "duel", "boost", nullptr); } - if (sPlayerbotAIConfig->autoSaveMana) { + if (sPlayerbotAIConfig->autoSaveMana) + { engine->addStrategy("auto save mana"); } + if (sPlayerbotAIConfig->autoAvoidAoe && facade->HasRealPlayerMaster()) + { + engine->addStrategy("avoid aoe"); + } switch (player->getClass()) { case CLASS_PRIEST: diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 8ddc3e0d..f56592e9 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -91,6 +91,7 @@ bool PlayerbotAIConfig::Initialize() mediumMana = sConfigMgr->GetOption("AiPlayerbot.MediumMana", 40); autoSaveMana = sConfigMgr->GetOption("AiPlayerbot.AutoSaveMana", true); saveManaThreshold = sConfigMgr->GetOption("AiPlayerbot.SaveManaThreshold", 60); + autoAvoidAoe = sConfigMgr->GetOption("AiPlayerbot.AutoAvoidAoe", false); randomGearLoweringChance = sConfigMgr->GetOption("AiPlayerbot.RandomGearLoweringChance", 0.15f); randomBotMaxLevelChance = sConfigMgr->GetOption("AiPlayerbot.RandomBotMaxLevelChance", 0.15f); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 22930b86..46740aa0 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -64,7 +64,8 @@ class PlayerbotAIConfig uint32 lowMana, mediumMana; bool autoSaveMana; uint32 saveManaThreshold; - + bool autoAvoidAoe; + uint32 openGoSpell; bool randomBotAutologin; bool botAutologin; diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index d701b293..185b7848 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -1492,6 +1492,16 @@ bool AvoidAoeAction::isUseful() bool AvoidAoeAction::Execute(Event event) { // Case #1: Aura with dynamic object + if (AvoidAuraWithDynamicObj()) { + return true; + } + // Case #2: Trap game object with spell + // Case #3: Trigger npc + return false; +} + +bool AvoidAoeAction::AvoidAuraWithDynamicObj() +{ Aura* aura = AI_VALUE(Aura*, "area debuff"); if (!aura) { return false; @@ -1523,24 +1533,24 @@ bool AvoidAoeAction::Execute(Event event) } float farestDis = 0.0f; Position bestPos; - // float disToDyn = bot->GetExactDist(dynOwner); - // float maxDisToGo = radius > disToDyn ? std::sqrt(radius * radius - disToDyn * disToDyn) + 0.5f : 0.5f; for (float &angle : possibleAngles) { float fleeDis = sPlayerbotAIConfig->fleeDistance; Position pos{bot->GetPositionX() + cos(angle) * fleeDis, bot->GetPositionY() + sin(angle) * fleeDis, bot->GetPositionZ()}; - // todo(Yunfan): check carefully + // todo (Yunfan): check carefully if (dynOwner->GetExactDist(pos) > farestDis) { farestDis = dynOwner->GetExactDist(pos); bestPos = pos; } } if (farestDis > 0.0f) { - std::ostringstream out; - out << "I'm avoiding aoe spell [" << aura->GetSpellInfo()->SpellName[0] << "]..."; - bot->Say(out.str(), LANG_UNIVERSAL); - return MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, false, true); + if (MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, false, true)) { + std::ostringstream out; + out << "I'm avoiding aoe spell [" << aura->GetSpellInfo()->SpellName[0] << "]..."; + bot->Say(out.str(), LANG_UNIVERSAL); + return true; + } } return false; } diff --git a/src/strategy/actions/MovementActions.h b/src/strategy/actions/MovementActions.h index 4865d7e1..58507e0c 100644 --- a/src/strategy/actions/MovementActions.h +++ b/src/strategy/actions/MovementActions.h @@ -73,6 +73,9 @@ class AvoidAoeAction : public MovementAction bool isUseful() override; bool Execute(Event event) override; + + protected: + bool AvoidAuraWithDynamicObj(); }; class RunAwayAction : public MovementAction diff --git a/src/strategy/values/AoeValues.h b/src/strategy/values/AoeValues.h index a6cde02a..45fedf87 100644 --- a/src/strategy/values/AoeValues.h +++ b/src/strategy/values/AoeValues.h @@ -5,6 +5,7 @@ #ifndef _PLAYERBOT_AOEVALUES_H #define _PLAYERBOT_AOEVALUES_H +#include "GameObject.h" #include "Object.h" #include "Value.h" #include "AiObjectContext.h" diff --git a/src/strategy/values/NearestGameObjects.cpp b/src/strategy/values/NearestGameObjects.cpp index 842b6a63..3380d4a2 100644 --- a/src/strategy/values/NearestGameObjects.cpp +++ b/src/strategy/values/NearestGameObjects.cpp @@ -7,6 +7,7 @@ #include "GridNotifiers.h" #include "GridNotifiersImpl.h" #include "Playerbots.h" +#include "SharedDefines.h" class AnyGameObjectInObjectRangeCheck { @@ -42,3 +43,30 @@ GuidVector NearestGameObjects::Calculate() return result; } + +GuidVector NearestTrapWithDamageValue::Calculate() +{ + std::list targets; + AnyGameObjectInObjectRangeCheck u_check(bot, range); + Acore::GameObjectListSearcher searcher(bot, targets, u_check); + Cell::VisitAllObjects(bot, searcher, range); + + GuidVector result; + for (GameObject* go : targets) + { + if (go->GetGoType() != GAMEOBJECT_TYPE_TRAP) + { + continue; + } + uint32 spellId = go->GetSpellId(); + if (!spellId) + { + continue; + } + // if (ignoreLos || bot->IsWithinLOSInMap(go)) + result.push_back(go->GetGUID()); + } + + return result; +} + diff --git a/src/strategy/values/NearestGameObjects.h b/src/strategy/values/NearestGameObjects.h index 73fec151..2321c728 100644 --- a/src/strategy/values/NearestGameObjects.h +++ b/src/strategy/values/NearestGameObjects.h @@ -24,4 +24,17 @@ class NearestGameObjects : public ObjectGuidListCalculatedValue bool ignoreLos; }; +class NearestTrapWithDamageValue : public ObjectGuidListCalculatedValue +{ + public: + NearestTrapWithDamageValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance) : + ObjectGuidListCalculatedValue(botAI, "nearest trap with damage", 1 * 1000), range(range) { } + + protected: + GuidVector Calculate() override; + + private: + float range; +}; + #endif diff --git a/src/strategy/values/ValueContext.h b/src/strategy/values/ValueContext.h index 9cba19d4..72e65185 100644 --- a/src/strategy/values/ValueContext.h +++ b/src/strategy/values/ValueContext.h @@ -299,6 +299,7 @@ class ValueContext : public NamedObjectContext creators["expected lifetime"] = &ValueContext::expected_lifetime; creators["expected group dps"] = &ValueContext::expected_group_dps; creators["area debuff"] = &ValueContext::area_debuff; + creators["nearest trap with damage"] = &ValueContext::nearest_trap_with_damange; } private: @@ -501,6 +502,7 @@ class ValueContext : public NamedObjectContext static UntypedValue* expected_lifetime(PlayerbotAI* ai) { return new ExpectedLifetimeValue(ai); } static UntypedValue* expected_group_dps(PlayerbotAI* ai) { return new ExpectedGroupDpsValue(ai); } static UntypedValue* area_debuff(PlayerbotAI* ai) { return new AreaDebuffValue(ai); } + static UntypedValue* nearest_trap_with_damange(PlayerbotAI* ai) { return new NearestTrapWithDamageValue(ai); } }; #endif From c5abc2ee1e0a2e2ee168117c74835cae97723494 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Mon, 15 Apr 2024 17:05:50 +0800 Subject: [PATCH 4/9] Warlock --- src/strategy/warlock/WarlockTriggers.cpp | 5 +++++ src/strategy/warlock/WarlockTriggers.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/strategy/warlock/WarlockTriggers.cpp b/src/strategy/warlock/WarlockTriggers.cpp index 8fa44758..1109ead5 100644 --- a/src/strategy/warlock/WarlockTriggers.cpp +++ b/src/strategy/warlock/WarlockTriggers.cpp @@ -42,3 +42,8 @@ bool UnstableAfflictionOnAttackerTrigger::IsActive() // !botAI->HasAura("unstable affliction", GetTarget(), false, true); } +bool DecimationTrigger::IsActive() +{ + Aura *aura = botAI->GetAura(getName(), GetTarget(), false, true); + return aura->GetDuration() > 3000; +} \ No newline at end of file diff --git a/src/strategy/warlock/WarlockTriggers.h b/src/strategy/warlock/WarlockTriggers.h index cdefd8c0..8578e43a 100644 --- a/src/strategy/warlock/WarlockTriggers.h +++ b/src/strategy/warlock/WarlockTriggers.h @@ -142,6 +142,7 @@ class DecimationTrigger : public HasAuraTrigger { public: DecimationTrigger(PlayerbotAI* ai) : HasAuraTrigger(ai, "decimation") {} + bool IsActive() override; }; class MoltenCoreTrigger : public HasAuraTrigger From d6a4fc3872e1f4e54d38eacb1490d984531f7392 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Mon, 15 Apr 2024 17:06:00 +0800 Subject: [PATCH 5/9] Distance check --- conf/playerbots.conf.dist | 1 - src/strategy/actions/MovementActions.cpp | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index c3afc2a3..4820efb3 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -313,7 +313,6 @@ AiPlayerbot.AlmostFullHealth = 85 AiPlayerbot.LowMana = 15 AiPlayerbot.MediumMana = 40 - # Enable healer bot save mana # Default: 1 (enable) AiPlayerbot.AutoSaveMana = 1 diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index 185b7848..d0c3b659 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -1517,7 +1517,7 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj() return false; } float radius = dynOwner->GetRadius(); - if (bot->GetExactDist(dynOwner) > radius) { + if (bot->GetDistance(dynOwner) > radius) { return false; } Unit* currentTarget = AI_VALUE(Unit*, "current target"); From 09ae42ea30dacba9326b1ab2bbe98b012450fa6f Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Mon, 15 Apr 2024 20:36:37 +0800 Subject: [PATCH 6/9] Fix warlock --- src/strategy/warlock/WarlockTriggers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strategy/warlock/WarlockTriggers.cpp b/src/strategy/warlock/WarlockTriggers.cpp index 1109ead5..733e7502 100644 --- a/src/strategy/warlock/WarlockTriggers.cpp +++ b/src/strategy/warlock/WarlockTriggers.cpp @@ -45,5 +45,5 @@ bool UnstableAfflictionOnAttackerTrigger::IsActive() bool DecimationTrigger::IsActive() { Aura *aura = botAI->GetAura(getName(), GetTarget(), false, true); - return aura->GetDuration() > 3000; + return aura && aura->GetDuration() > 3000; } \ No newline at end of file From 56881c3a4d73d4982d5c3bc61c55965d93c369b5 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Mon, 15 Apr 2024 20:36:44 +0800 Subject: [PATCH 7/9] Avoid aoe for game object --- src/strategy/actions/MovementActions.cpp | 94 +++++++++++++++++++--- src/strategy/actions/MovementActions.h | 2 + src/strategy/values/NearestGameObjects.cpp | 26 +++++- src/strategy/values/NearestGameObjects.h | 2 +- 4 files changed, 107 insertions(+), 17 deletions(-) diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index d0c3b659..48e6fd00 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -3,6 +3,7 @@ */ #include "MovementActions.h" +#include "GameObject.h" #include "Map.h" #include "MotionMaster.h" #include "MoveSplineInitArgs.h" @@ -13,6 +14,7 @@ #include "PlayerbotAIConfig.h" #include "Random.h" #include "SharedDefines.h" +#include "SpellInfo.h" #include "TargetedMovementGenerator.h" #include "Event.h" #include "LastMovementValue.h" @@ -1486,17 +1488,21 @@ bool FleeWithPetAction::Execute(Event event) bool AvoidAoeAction::isUseful() { - return AI_VALUE(Aura*, "area debuff"); + GuidVector traps = AI_VALUE(GuidVector, "nearest trap with damage"); + return AI_VALUE(Aura*, "area debuff") || !traps.empty(); } bool AvoidAoeAction::Execute(Event event) { - // Case #1: Aura with dynamic object + // Case #1: Aura with dynamic object (e.g. rain of fire) if (AvoidAuraWithDynamicObj()) { return true; } - // Case #2: Trap game object with spell - // Case #3: Trigger npc + // Case #2: Trap game object with spell (e.g. lava bomb) + if (AvoidGameObjectWithDamage()) { + return true; + } + // Case #3: Trigger npc (e.g. Lesser shadow fissure) return false; } @@ -1506,10 +1512,11 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj() if (!aura) { return false; } - if (!aura->GetSpellInfo()) { + const SpellInfo* spellInfo = aura->GetSpellInfo(); + if (!spellInfo) { return false; } - if (!bot->HasAura(aura->GetSpellInfo()->Id)) { + if (!bot->HasAura(spellInfo->Id)) { return false; } DynamicObject* dynOwner = aura->GetDynobjOwner(); @@ -1520,6 +1527,69 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj() if (bot->GetDistance(dynOwner) > radius) { return false; } + std::ostringstream name; + name << "[" << spellInfo->SpellName[0] << "](aura)"; + if (FleePostion(dynOwner->GetPosition(), radius, name.str())) { + return true; + } + return false; +} + +bool AvoidAoeAction::AvoidGameObjectWithDamage() +{ + GuidVector traps = AI_VALUE(GuidVector, "nearest trap with damage"); + if (traps.empty()) { + return false; + } + for (ObjectGuid &guid : traps) { + GameObject* go = botAI->GetGameObject(guid); + if (!go || !go->IsInWorld()) { + continue; + } + if (go->GetGoType() != GAMEOBJECT_TYPE_TRAP) + { + continue; + } + const GameObjectTemplate* goInfo = go->GetGOInfo(); + if (!goInfo) + { + continue; + } + uint32 spellId = goInfo->trap.spellId; + if (!spellId) + { + continue; + } + const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId); + if (spellInfo->IsPositive()) { + continue; + } + float radius = 5.0f; + for (int i = 0; i < MAX_SPELL_EFFECTS; i++) { + if (spellInfo->Effects[i].Effect == SPELL_EFFECT_APPLY_AURA) { + if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_PERIODIC_DAMAGE) { + radius = spellInfo->Effects[i].CalcRadius(); + break; + } + } else if (spellInfo->Effects[i].Effect == SPELL_EFFECT_SCHOOL_DAMAGE) { + break; + } + } + if (bot->GetDistance(go) > radius) { + return false; + } + std::ostringstream name; + name << "[" << spellInfo->SpellName[0] << "](object)"; + if (FleePostion(go->GetPosition(), radius, name.str())) { + return true; + } + + } + return false; +} + +bool AvoidAoeAction::FleePostion(Position pos, float radius, std::string name) +{ Unit* currentTarget = AI_VALUE(Unit*, "current target"); std::vector possibleAngles; if (currentTarget) { @@ -1528,26 +1598,26 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj() possibleAngles.push_back(angleLeft); possibleAngles.push_back(angleRight); } else { - float angleTo = bot->GetAngle(dynOwner) - M_PI; + float angleTo = bot->GetAngle(&pos) - M_PI; possibleAngles.push_back(angleTo); } float farestDis = 0.0f; Position bestPos; for (float &angle : possibleAngles) { float fleeDis = sPlayerbotAIConfig->fleeDistance; - Position pos{bot->GetPositionX() + cos(angle) * fleeDis, + Position fleePos{bot->GetPositionX() + cos(angle) * fleeDis, bot->GetPositionY() + sin(angle) * fleeDis, bot->GetPositionZ()}; // todo (Yunfan): check carefully - if (dynOwner->GetExactDist(pos) > farestDis) { - farestDis = dynOwner->GetExactDist(pos); - bestPos = pos; + if (pos.GetExactDist(fleePos) > farestDis) { + farestDis = pos.GetExactDist(fleePos); + bestPos = fleePos; } } if (farestDis > 0.0f) { if (MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, false, true)) { std::ostringstream out; - out << "I'm avoiding aoe spell [" << aura->GetSpellInfo()->SpellName[0] << "]..."; + out << "I'm avoiding aoe spell " << name << "..."; bot->Say(out.str(), LANG_UNIVERSAL); return true; } diff --git a/src/strategy/actions/MovementActions.h b/src/strategy/actions/MovementActions.h index 58507e0c..4596aeb3 100644 --- a/src/strategy/actions/MovementActions.h +++ b/src/strategy/actions/MovementActions.h @@ -76,6 +76,8 @@ class AvoidAoeAction : public MovementAction protected: bool AvoidAuraWithDynamicObj(); + bool AvoidGameObjectWithDamage(); + bool FleePostion(Position pos, float radius, std::string name); }; class RunAwayAction : public MovementAction diff --git a/src/strategy/values/NearestGameObjects.cpp b/src/strategy/values/NearestGameObjects.cpp index 3380d4a2..fe8f5c9e 100644 --- a/src/strategy/values/NearestGameObjects.cpp +++ b/src/strategy/values/NearestGameObjects.cpp @@ -8,6 +8,7 @@ #include "GridNotifiersImpl.h" #include "Playerbots.h" #include "SharedDefines.h" +#include "SpellMgr.h" class AnyGameObjectInObjectRangeCheck { @@ -58,15 +59,32 @@ GuidVector NearestTrapWithDamageValue::Calculate() { continue; } - uint32 spellId = go->GetSpellId(); + const GameObjectTemplate* goInfo = go->GetGOInfo(); + if (!goInfo) + { + continue; + } + uint32 spellId = goInfo->trap.spellId; if (!spellId) { continue; } - // if (ignoreLos || bot->IsWithinLOSInMap(go)) - result.push_back(go->GetGUID()); + const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId); + if (spellInfo->IsPositive()) { + continue; + } + for (int i = 0; i < MAX_SPELL_EFFECTS; i++) { + if (spellInfo->Effects[i].Effect == SPELL_EFFECT_APPLY_AURA) { + if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_PERIODIC_DAMAGE) { + result.push_back(go->GetGUID()); + break; + } + } else if (spellInfo->Effects[i].Effect == SPELL_EFFECT_SCHOOL_DAMAGE) { + result.push_back(go->GetGUID()); + break; + } + } } - return result; } diff --git a/src/strategy/values/NearestGameObjects.h b/src/strategy/values/NearestGameObjects.h index 2321c728..d0e7850f 100644 --- a/src/strategy/values/NearestGameObjects.h +++ b/src/strategy/values/NearestGameObjects.h @@ -27,7 +27,7 @@ class NearestGameObjects : public ObjectGuidListCalculatedValue class NearestTrapWithDamageValue : public ObjectGuidListCalculatedValue { public: - NearestTrapWithDamageValue(PlayerbotAI* botAI, float range = sPlayerbotAIConfig->sightDistance) : + NearestTrapWithDamageValue(PlayerbotAI* botAI, float range = 10.0f) : ObjectGuidListCalculatedValue(botAI, "nearest trap with damage", 1 * 1000), range(range) { } protected: From caa3eb6423cd8019a5f4a9093448d3dce6230d57 Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Mon, 15 Apr 2024 20:52:44 +0800 Subject: [PATCH 8/9] Fix avoid aoe output text --- src/strategy/actions/MovementActions.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index 48e6fd00..c5ac3e62 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -1528,7 +1528,7 @@ bool AvoidAoeAction::AvoidAuraWithDynamicObj() return false; } std::ostringstream name; - name << "[" << spellInfo->SpellName[0] << "](aura)"; + name << "[" << spellInfo->SpellName[0] << "] (aura)"; if (FleePostion(dynOwner->GetPosition(), radius, name.str())) { return true; } @@ -1579,7 +1579,7 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage() return false; } std::ostringstream name; - name << "[" << spellInfo->SpellName[0] << "](object)"; + name << "[" << spellInfo->SpellName[0] << "] (object)"; if (FleePostion(go->GetPosition(), radius, name.str())) { return true; } @@ -1617,7 +1617,7 @@ bool AvoidAoeAction::FleePostion(Position pos, float radius, std::string name) if (farestDis > 0.0f) { if (MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, false, true)) { std::ostringstream out; - out << "I'm avoiding aoe spell " << name << "..."; + out << "Avoiding spell " << name << "..."; bot->Say(out.str(), LANG_UNIVERSAL); return true; } From 47064cc0f46c4df149259e3c5e4b3805e64a5c1a Mon Sep 17 00:00:00 2001 From: Yunfan Li Date: Mon, 15 Apr 2024 22:22:10 +0800 Subject: [PATCH 9/9] [Avoid aoe] Fix friendly game object --- src/strategy/actions/MovementActions.cpp | 23 +++++++++++----------- src/strategy/values/NearestGameObjects.cpp | 5 +++++ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index c5ac3e62..cd5ff65f 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -170,7 +170,6 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, botAI->InterruptSpell(); } MotionMaster &mm = *bot->GetMotionMaster(); - mm.Clear(); mm.MovePoint(mapId, x, y, z, generatePath); AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation()); @@ -1564,17 +1563,17 @@ bool AvoidAoeAction::AvoidGameObjectWithDamage() if (spellInfo->IsPositive()) { continue; } - float radius = 5.0f; - for (int i = 0; i < MAX_SPELL_EFFECTS; i++) { - if (spellInfo->Effects[i].Effect == SPELL_EFFECT_APPLY_AURA) { - if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_PERIODIC_DAMAGE) { - radius = spellInfo->Effects[i].CalcRadius(); - break; - } - } else if (spellInfo->Effects[i].Effect == SPELL_EFFECT_SCHOOL_DAMAGE) { - break; - } - } + float radius = (float)goInfo->trap.diameter / 2; + // for (int i = 0; i < MAX_SPELL_EFFECTS; i++) { + // if (spellInfo->Effects[i].Effect == SPELL_EFFECT_APPLY_AURA) { + // if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_PERIODIC_DAMAGE) { + // radius = spellInfo->Effects[i].CalcRadius(); + // break; + // } + // } else if (spellInfo->Effects[i].Effect == SPELL_EFFECT_SCHOOL_DAMAGE) { + // break; + // } + // } if (bot->GetDistance(go) > radius) { return false; } diff --git a/src/strategy/values/NearestGameObjects.cpp b/src/strategy/values/NearestGameObjects.cpp index fe8f5c9e..d15d0da4 100644 --- a/src/strategy/values/NearestGameObjects.cpp +++ b/src/strategy/values/NearestGameObjects.cpp @@ -59,6 +59,11 @@ GuidVector NearestTrapWithDamageValue::Calculate() { continue; } + Unit* owner = go->GetOwner(); + if (owner && owner->IsFriendlyTo(bot)) + { + continue; + } const GameObjectTemplate* goInfo = go->GetGOInfo(); if (!goInfo) {