mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-23 05:36:23 +00:00
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:
@@ -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:
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user