feat(Core/AI): CU_SAI - Custom Target Options (#2879)

NEW:
* SMART_TARGET_PLAYER_WITH_AURA (spellid, negation?, distMax, distMin) - if target.O is set it will resize the list of targets to target.o
* SMART_TARGET_RANDOM_POINT (range, amount (for summon creature), self (creature is middle else use xyz) (ONLY USED FOR SUMMON CREATURE OR MOVE/JUMP TO POS ACTIONS FOR NOW)

MODIFIED:
* SMART_ACTION_SUMMON_CREATURE now possible to spawn multiple creatures with SMART_TARGET_RANDOM_POINT
* SMART_ACTION_MOVE_TO_POS/JUMP_TO_POS now possible to move to a random point with SMART_TARGET_RANDOM_POINT
* SMART_TARGET_PLAYER_RANGE no longer targets GMs or dead targets and when target.o is >0 it will try all possible targets in max instead of min-maxing


Co-authored-by: Francesco Borzì <borzifrancesco@gmail.com>
This commit is contained in:
P-Kito
2020-04-18 16:59:52 +02:00
committed by GitHub
parent 63c86a9a14
commit 2514f8fc9a
7 changed files with 1033 additions and 677 deletions

View File

@@ -1547,6 +1547,28 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (!summoner)
break;
if (e.GetTargetType() == SMART_TARGET_RANDOM_POINT)
{
float range = (float)e.target.randomPoint.range;
Position randomPoint;
Position srcPos = { e.target.x, e.target.y, e.target.z, e.target.o };
for (uint32 i = 0; i < e.target.randomPoint.amount; i++)
{
if (e.target.randomPoint.self > 0)
me->GetRandomPoint(me->GetPosition(), range, randomPoint);
else
me->GetRandomPoint(srcPos, range, randomPoint);
if (Creature* summon = summoner->SummonCreature(e.action.summonCreature.creature, randomPoint, (TempSummonType)e.action.summonCreature.type, e.action.summonCreature.duration))
{
if (unit && e.action.summonCreature.attackInvoker)
summon->AI()->AttackStart(unit);
else if (me && e.action.summonCreature.attackScriptOwner)
summon->AI()->AttackStart(me);
}
}
break;
}
if (targets)
{
float x, y, z, o;
@@ -1896,6 +1918,28 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
WorldObject* target = NULL;
if (e.GetTargetType() == SMART_TARGET_RANDOM_POINT)
{
if (me)
{
float range = (float)e.target.randomPoint.range;
Position randomPoint;
Position srcPos = { e.target.x, e.target.y, e.target.z, e.target.o };
me->GetRandomPoint(srcPos, range, randomPoint);
me->GetMotionMaster()->MovePoint(
e.action.MoveToPos.pointId,
randomPoint.m_positionX,
randomPoint.m_positionY,
randomPoint.m_positionZ,
true,
true,
e.action.MoveToPos.controlled ? MOTION_SLOT_CONTROLLED : MOTION_SLOT_ACTIVE
);
}
break;
}
/*if (e.GetTargetType() == SMART_TARGET_CREATURE_RANGE || e.GetTargetType() == SMART_TARGET_CREATURE_GUID ||
e.GetTargetType() == SMART_TARGET_CREATURE_DISTANCE || e.GetTargetType() == SMART_TARGET_GAMEOBJECT_RANGE ||
e.GetTargetType() == SMART_TARGET_GAMEOBJECT_GUID || e.GetTargetType() == SMART_TARGET_GAMEOBJECT_DISTANCE ||
@@ -2456,6 +2500,20 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
case SMART_ACTION_JUMP_TO_POS:
{
if (e.GetTargetType() == SMART_TARGET_RANDOM_POINT)
{
if (me)
{
float range = (float)e.target.randomPoint.range;
Position randomPoint;
Position srcPos = { e.target.x, e.target.y, e.target.z, e.target.o };
me->GetRandomPoint(srcPos, range, randomPoint);
me->GetMotionMaster()->MoveJump(randomPoint, (float)e.action.jump.speedxy, (float)e.action.jump.speedz);
}
break;
}
ObjectList* targets = GetTargets(e, unit);
if (!targets)
break;
@@ -3614,17 +3672,23 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
}
case SMART_TARGET_PLAYER_RANGE:
{
uint32 count = 0;
// will always return a valid pointer, even if empty list
ObjectList* units = GetWorldObjectsInDist((float)e.target.playerRange.maxDist);
if (!units->empty() && GetBaseObject())
{
for (ObjectList::const_iterator itr = units->begin(); itr != units->end(); ++itr)
if (IsPlayer(*itr) && GetBaseObject()->IsInRange(*itr, (float)e.target.playerRange.minDist, (float)e.target.playerRange.maxDist))
{
if (IsPlayer(*itr) && GetBaseObject()->IsInRange(*itr, (float)e.target.playerRange.minDist, (float)e.target.playerRange.maxDist) && (*itr)->ToPlayer()->IsAlive() && !(*itr)->ToPlayer()->IsGameMaster())
l->push_back(*itr);
if (e.target.playerRange.maxCount && ++count >= e.target.playerRange.maxCount)
break;
}
// If Orientation is also set and we didnt find targets, try it with all the range
if (l->empty() && e.target.o > 0)
for (ObjectList::const_iterator itr = units->begin(); itr != units->end(); ++itr)
if (IsPlayer(*itr) && baseObject->IsInRange(*itr, 0.0f, float(e.target.playerRange.maxDist)) && (*itr)->ToPlayer()->IsAlive() && !(*itr)->ToPlayer()->IsGameMaster())
l->push_back(*itr);
if (e.target.playerRange.maxCount > 0)
acore::Containers::RandomResizeList(*l, e.target.playerRange.maxCount);
}
delete units;
break;
@@ -3732,6 +3796,64 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
break;
}
case SMART_TARGET_PLAYER_WITH_AURA:
{
// will always return a valid pointer, even if empty list
ObjectList* units = GetWorldObjectsInDist(e.target.z ? e.target.z : float(e.target.playerWithAura.distMax));
for (ObjectList::const_iterator itr = units->begin(); itr != units->end(); ++itr)
if (IsPlayer(*itr) && (*itr)->ToPlayer()->IsAlive() && !(*itr)->ToPlayer()->IsGameMaster())
if (GetBaseObject()->IsInRange(*itr, (float)e.target.playerWithAura.distMin, (float)e.target.playerWithAura.distMax))
if (bool(e.target.playerWithAura.negation) != (*itr)->ToPlayer()->HasAura(e.target.playerWithAura.spellId))
l->push_back(*itr);
if (e.target.o > 0)
acore::Containers::RandomResizeList(*l, e.target.o);
delete units;
break;
}
case SMART_TARGET_ROLE_SELECTION:
{
// will always return a valid pointer, even if empty list
ObjectList* units = GetWorldObjectsInDist(float(e.target.roleSelection.maxDist));
// 1 = Tanks, 2 = Healer, 4 = Damage
uint32 roleMask = e.target.roleSelection.roleMask;
for (ObjectList::const_iterator itr = units->begin(); itr != units->end(); ++itr)
if (Player* targetPlayer = (*itr)->ToPlayer())
if (targetPlayer->IsAlive() && !targetPlayer->IsGameMaster())
{
if (roleMask & SMART_TARGET_ROLE_FLAG_TANKS)
{
if (targetPlayer->HasTankSpec())
{
l->push_back(*itr);
continue;
}
}
if (roleMask & SMART_TARGET_ROLE_FLAG_HEALERS)
{
if (targetPlayer->HasHealSpec())
{
l->push_back(*itr);
continue;
}
}
if (roleMask & SMART_TARGET_ROLE_FLAG_DAMAGERS)
{
if (targetPlayer->HasCasterSpec() || targetPlayer->HasMeleeSpec())
{
l->push_back(*itr);
continue;
}
}
}
if (e.target.roleSelection.resize > 0)
acore::Containers::RandomResizeList(*l, e.target.roleSelection.resize);
delete units;
break;
}
case SMART_TARGET_NONE:
case SMART_TARGET_POSITION:
default:

View File

@@ -344,6 +344,9 @@ bool SmartAIMgr::IsTargetValid(SmartScriptHolder const& e)
case SMART_TARGET_CLOSEST_FRIENDLY:
case SMART_TARGET_STORED:
case SMART_TARGET_FARTHEST:
case SMART_TARGET_PLAYER_WITH_AURA:
case SMART_TARGET_RANDOM_POINT:
case SMART_TARGET_ROLE_SELECTION:
break;
default:
sLog->outErrorDb("SmartAIMgr: Not handled target_type(%u), Entry %d SourceType %u Event %u Action %u, skipped.", e.GetTargetType(), e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
@@ -396,7 +399,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
default:
break;
}
if (e.target.type < 0 || e.target.type >= SMART_TARGET_END)
if (e.target.type < 0 || (e.target.type >= SMART_TARGET_TC_END && e.target.type < SMART_TARGET_AC_START) || e.target.type >= SMART_TARGET_AC_END)
{
sLog->outErrorDb("SmartAIMgr: EntryOrGuid %d using event(%u) has an invalid target type (%u), skipped.",
e.entryOrGuid, e.event_id, e.GetTargetType());

View File

@@ -1294,7 +1294,7 @@ enum SMARTAI_TARGETS
SMART_TARGET_GAMEOBJECT_GUID = 14, // guid, entry
SMART_TARGET_GAMEOBJECT_DISTANCE = 15, // entry(0any), maxDist
SMART_TARGET_INVOKER_PARTY = 16, // invoker's party members
SMART_TARGET_PLAYER_RANGE = 17, // min, max, maxCount (maxCount by pussywizard)
SMART_TARGET_PLAYER_RANGE = 17, // min, max, maxCount (maxCount by pussywizard), set target.o to 1 if u want to search for all in range if min, max fails
SMART_TARGET_PLAYER_DISTANCE = 18, // maxDist
SMART_TARGET_CLOSEST_CREATURE = 19, // CreatureEntry(0any), maxDist, dead?
SMART_TARGET_CLOSEST_GAMEOBJECT = 20, // entry(0any), maxDist
@@ -1308,7 +1308,17 @@ enum SMARTAI_TARGETS
SMART_TARGET_FARTHEST = 28, // maxDist, playerOnly, isInLos
SMART_TARGET_VEHICLE_PASSENGER = 29, // TODO: NOT SUPPORTED YET
SMART_TARGET_END = 30
SMART_TARGET_TC_END = 30, // placeholder
// AC-only SmartTargets:
SMART_TARGET_AC_START = 200, // placeholder
SMART_TARGET_PLAYER_WITH_AURA = 201, // spellId, negation, MaxDist, MinDist, set target.o to a number to random resize the list
SMART_TARGET_RANDOM_POINT = 202, // range, amount (for summoning creature), self als middle (0/1) else use xyz
SMART_TARGET_ROLE_SELECTION = 203, // Range Max, TargetMask (Tanks (1), Healer (2) Damage (4)), resize list
SMART_TARGET_AC_END = 204 // placeholder
};
struct SmartTarget
@@ -1359,6 +1369,13 @@ struct SmartTarget
uint32 getFromHashMap; // Does not work in instances
} unitGUID;
struct
{
uint32 maxDist;
uint32 roleMask;
uint32 resize;
} roleSelection;
struct
{
uint32 creature;
@@ -1432,6 +1449,21 @@ struct SmartTarget
uint32 playerOnly;
} closestFriendly;
struct
{
uint32 range;
uint32 amount;
uint32 self;
} randomPoint;
struct
{
uint32 spellId;
uint32 negation;
uint32 distMax;
uint32 distMin;
} playerWithAura;
struct
{
uint32 param1;
@@ -1442,6 +1474,13 @@ struct SmartTarget
};
};
enum SmartTargetRoleFlags
{
SMART_TARGET_ROLE_FLAG_TANKS = 0x001,
SMART_TARGET_ROLE_FLAG_HEALERS = 0x002,
SMART_TARGET_ROLE_FLAG_DAMAGERS = 0x004
};
enum eSmartAI
{
SMART_EVENT_PARAM_COUNT = 4,

View File

@@ -159,6 +159,9 @@ DBCStorage <TalentEntry> sTalentStore(TalentEntryfmt);
TalentSpellPosMap sTalentSpellPosMap;
DBCStorage <TalentTabEntry> sTalentTabStore(TalentTabEntryfmt);
// store absolute bit position for first rank for talent inspect
static uint32 sTalentTabPages[MAX_CLASSES][3];
DBCStorage <TaxiNodesEntry> sTaxiNodesStore(TaxiNodesEntryfmt);
TaxiMask sTaxiNodesMask;
TaxiMask sOldContinentsNodesMask;
@@ -469,6 +472,26 @@ void LoadDBCStores(const std::string& dataPath)
LoadDBC(availableDbcLocales, bad_dbc_files, sTalentTabStore, dbcPath, "TalentTab.dbc");
// prepare fast data access to bit pos of talent ranks for use at inspecting
{
// now have all max ranks (and then bit amount used for store talent ranks in inspect)
for (uint32 talentTabId = 1; talentTabId < sTalentTabStore.GetNumRows(); ++talentTabId)
{
TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentTabId);
if (!talentTabInfo)
continue;
// prevent memory corruption; otherwise cls will become 12 below
if ((talentTabInfo->ClassMask & CLASSMASK_ALL_PLAYABLE) == 0)
continue;
// store class talent tab pages
for (uint32 cls = 1; cls < MAX_CLASSES; ++cls)
if (talentTabInfo->ClassMask & (1 << (cls - 1)))
sTalentTabPages[cls][talentTabInfo->tabpage] = talentTabId;
}
}
LoadDBC(availableDbcLocales, bad_dbc_files, sTaxiNodesStore, dbcPath, "TaxiNodes.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sTaxiPathStore, dbcPath, "TaxiPath.dbc");
for (uint32 i = 1; i < sTaxiPathStore.GetNumRows(); ++i)
@@ -784,9 +807,14 @@ PvPDifficultyEntry const* GetBattlegroundBracketById(uint32 mapid, BattlegroundB
return NULL;
}
uint32 const* GetTalentTabPages(uint8 cls)
{
return sTalentTabPages[cls];
}
bool IsSharedDifficultyMap(uint32 mapid)
{
return sWorld->getBoolConfig(CONFIG_INSTANCE_SHARED_ID) && (mapid == 631 || mapid == 724);
{
return sWorld->getBoolConfig(CONFIG_INSTANCE_SHARED_ID) && (mapid == 631 || mapid == 724);
}
uint32 GetLiquidFlags(uint32 liquidType)

View File

@@ -41,6 +41,8 @@ MapDifficulty const* GetDownscaledMapDifficultyData(uint32 mapId, Difficulty &di
bool IsSharedDifficultyMap(uint32 mapid);
uint32 const* /*[MAX_TALENT_TABS]*/ GetTalentTabPages(uint8 cls);
uint32 GetLiquidFlags(uint32 liquidType);
PvPDifficultyEntry const* GetBattlegroundBracketByLevel(uint32 mapid, uint32 level);

File diff suppressed because it is too large Load Diff

View File

@@ -104,6 +104,40 @@ struct PlayerTalent
bool IsInSpec(uint8 spec) { return (specMask & (1<<spec)); }
};
enum TalentTree // talent tabs
{
TALENT_TREE_WARRIOR_ARMS = 161,
TALENT_TREE_WARRIOR_FURY = 164,
TALENT_TREE_WARRIOR_PROTECTION = 163,
TALENT_TREE_PALADIN_HOLY = 382,
TALENT_TREE_PALADIN_PROTECTION = 383,
TALENT_TREE_PALADIN_RETRIBUTION = 381,
TALENT_TREE_HUNTER_BEAST_MASTERY = 361,
TALENT_TREE_HUNTER_MARKSMANSHIP = 363,
TALENT_TREE_HUNTER_SURVIVAL = 362,
TALENT_TREE_ROGUE_ASSASSINATION = 182,
TALENT_TREE_ROGUE_COMBAT = 181,
TALENT_TREE_ROGUE_SUBTLETY = 183,
TALENT_TREE_PRIEST_DISCIPLINE = 201,
TALENT_TREE_PRIEST_HOLY = 202,
TALENT_TREE_PRIEST_SHADOW = 203,
TALENT_TREE_DEATH_KNIGHT_BLOOD = 398,
TALENT_TREE_DEATH_KNIGHT_FROST = 399,
TALENT_TREE_DEATH_KNIGHT_UNHOLY = 400,
TALENT_TREE_SHAMAN_ELEMENTAL = 261,
TALENT_TREE_SHAMAN_ENHANCEMENT = 263,
TALENT_TREE_SHAMAN_RESTORATION = 262,
TALENT_TREE_MAGE_ARCANE = 81,
TALENT_TREE_MAGE_FIRE = 41,
TALENT_TREE_MAGE_FROST = 61,
TALENT_TREE_WARLOCK_AFFLICTION = 302,
TALENT_TREE_WARLOCK_DEMONOLOGY = 303,
TALENT_TREE_WARLOCK_DESTRUCTION = 301,
TALENT_TREE_DRUID_BALANCE = 283,
TALENT_TREE_DRUID_FERAL_COMBAT = 281,
TALENT_TREE_DRUID_RESTORATION = 282
};
#define SPEC_MASK_ALL 255
// Spell modifier (used for modify other spells)
@@ -1729,8 +1763,11 @@ class Player : public Unit, public GridObject<Player>
void ActivateSpec(uint8 spec);
void GetTalentTreePoints(uint8 (&specPoints)[3]) const;
uint8 GetMostPointsTalentTree() const;
bool IsHealerTalentSpec() const;
bool IsTankTalentSpec() const;
bool HasTankSpec();
bool HasMeleeSpec();
bool HasCasterSpec();
bool HasHealSpec();
uint32 GetSpec(int8 spec = -1);
void InitGlyphsForLevel();
void SetGlyphSlot(uint8 slot, uint32 slottype) { SetUInt32Value(PLAYER_FIELD_GLYPH_SLOTS_1 + slot, slottype); }
@@ -2986,7 +3023,7 @@ void RemoveItemsSetItem(Player* player, ItemTemplate const* proto);
// "the bodies of template functions must be made available in a header file"
template <class T> T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell* spell, bool temporaryPet)
{
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo)
return 0;