mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
Core/SAI: Add SMART_ACTION_ACTIVATE_GAMEOBJECT action (#27216)
Closes #27196
(cherry picked from commit 0817be8f76)
This commit is contained in:
@@ -2465,6 +2465,24 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
targetUnit->SetHealth(targetUnit->CountPctFromMaxHealth(e.action.setHealthPct.percent));
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_CREATE_CONVERSATION:
|
||||
{
|
||||
WorldObject* baseObject = GetBaseObject();
|
||||
|
||||
for (WorldObject* const target : targets)
|
||||
{
|
||||
if (Player* playerTarget = target->ToPlayer())
|
||||
{
|
||||
Conversation* conversation = Conversation::CreateConversation(e.action.conversation.id, playerTarget,
|
||||
*playerTarget, playerTarget->GetGUID(), nullptr);
|
||||
if (!conversation)
|
||||
TC_LOG_WARN("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_CREATE_CONVERSATION: id %u, baseObject %s, target %s - failed to create conversation",
|
||||
e.action.conversation.id, !baseObject ? "" : baseObject->GetName().c_str(), playerTarget->GetName().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_SET_IMMUNE_PC:
|
||||
{
|
||||
for (WorldObject* target : targets)
|
||||
@@ -2507,22 +2525,15 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_CREATE_CONVERSATION:
|
||||
case SMART_ACTION_ACTIVATE_GAMEOBJECT:
|
||||
{
|
||||
WorldObject* baseObject = GetBaseObject();
|
||||
|
||||
for (WorldObject* const target : targets)
|
||||
for (WorldObject* target : targets)
|
||||
{
|
||||
if (Player* playerTarget = target->ToPlayer())
|
||||
if (GameObject* targetGo = target->ToGameObject())
|
||||
{
|
||||
Conversation* conversation = Conversation::CreateConversation(e.action.conversation.id, playerTarget,
|
||||
*playerTarget, playerTarget->GetGUID(), nullptr);
|
||||
if (!conversation)
|
||||
TC_LOG_WARN("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_CREATE_CONVERSATION: id %u, baseObject %s, target %s - failed to create conversation",
|
||||
e.action.conversation.id, !baseObject ? "" : baseObject->GetName().c_str(), playerTarget->GetName().c_str());
|
||||
targetGo->ActivateObject(GameObjectActions(e.action.activateGameObject.gameObjectAction), e.action.activateGameObject.param);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_ADD_TO_STORED_TARGET_LIST:
|
||||
|
||||
@@ -1015,7 +1015,7 @@ bool SmartAIMgr::CheckUnusedActionParams(SmartScriptHolder const& e)
|
||||
case SMART_ACTION_SET_IMMUNE_PC: return sizeof(SmartAction::setImmunePC);
|
||||
case SMART_ACTION_SET_IMMUNE_NPC: return sizeof(SmartAction::setImmuneNPC);
|
||||
case SMART_ACTION_SET_UNINTERACTIBLE: return sizeof(SmartAction::setUninteractible);
|
||||
//case SMART_ACTION_ACTIVATE_GAMEOBJECT: return sizeof(SmartAction::raw);
|
||||
case SMART_ACTION_ACTIVATE_GAMEOBJECT: return sizeof(SmartAction::activateGameObject);
|
||||
case SMART_ACTION_ADD_TO_STORED_TARGET_LIST: return sizeof(SmartAction::addToStoredTargets);
|
||||
case SMART_ACTION_BECOME_PERSONAL_CLONE_FOR_PLAYER: return sizeof(SmartAction::becomePersonalClone);
|
||||
default:
|
||||
@@ -2291,6 +2291,16 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
|
||||
TC_SAI_IS_BOOLEAN_VALID(e, e.action.setHealthRegen.regenHealth);
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_CREATE_CONVERSATION:
|
||||
{
|
||||
if (!sConversationDataStore->GetConversationTemplate(e.action.conversation.id))
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "SmartAIMgr: SMART_ACTION_CREATE_CONVERSATION Entry " SI64FMTD " SourceType %u Event %u Action %u uses invalid entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.conversation.id);
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_SET_IMMUNE_PC:
|
||||
{
|
||||
TC_SAI_IS_BOOLEAN_VALID(e, e.action.setImmunePC.immunePC);
|
||||
@@ -2306,14 +2316,17 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
|
||||
TC_SAI_IS_BOOLEAN_VALID(e, e.action.setUninteractible.uninteractible);
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_CREATE_CONVERSATION:
|
||||
case SMART_ACTION_ACTIVATE_GAMEOBJECT:
|
||||
{
|
||||
if (!sConversationDataStore->GetConversationTemplate(e.action.conversation.id))
|
||||
if (!NotNULL(e, e.action.activateGameObject.gameObjectAction))
|
||||
return false;
|
||||
|
||||
if (e.action.activateGameObject.gameObjectAction >= uint32(GameObjectActions::Max))
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "SmartAIMgr: SMART_ACTION_CREATE_CONVERSATION Entry " SI64FMTD " SourceType %u Event %u Action %u uses invalid entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.conversation.id);
|
||||
TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry " SI64FMTD " SourceType %u Event %u Action %u has gameObjectAction parameter out of range (max allowed %u, current value %u), skipped.",
|
||||
e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), uint32(GameObjectActions::Max), e.action.activateGameObject.gameObjectAction);
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_FOLLOW:
|
||||
|
||||
@@ -1215,6 +1215,11 @@ struct SmartAction
|
||||
uint32 percent;
|
||||
} setHealthPct;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 id;
|
||||
} conversation;
|
||||
|
||||
struct
|
||||
{
|
||||
SAIBool immunePC;
|
||||
@@ -1232,8 +1237,9 @@ struct SmartAction
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 id;
|
||||
} conversation;
|
||||
uint32 gameObjectAction;
|
||||
uint32 param;
|
||||
} activateGameObject;
|
||||
|
||||
struct
|
||||
{
|
||||
|
||||
@@ -1637,6 +1637,132 @@ void GameObject::UseDoorOrButton(uint32 time_to_restore, bool alternative /* = f
|
||||
m_cooldownTime = time_to_restore ? (GameTime::GetGameTimeMS() + time_to_restore) : 0;
|
||||
}
|
||||
|
||||
void GameObject::ActivateObject(GameObjectActions action, int32 param, WorldObject* spellCaster /*= nullptr*/, uint32 spellId /*= 0*/, int32 effectIndex /*= -1*/)
|
||||
{
|
||||
Unit* unitCaster = spellCaster ? spellCaster->ToUnit() : nullptr;
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case GameObjectActions::AnimateCustom0:
|
||||
case GameObjectActions::AnimateCustom1:
|
||||
case GameObjectActions::AnimateCustom2:
|
||||
case GameObjectActions::AnimateCustom3:
|
||||
SendCustomAnim(uint32(action) - uint32(GameObjectActions::AnimateCustom0));
|
||||
break;
|
||||
case GameObjectActions::Disturb: // What's the difference with Open?
|
||||
case GameObjectActions::Open:
|
||||
if (unitCaster)
|
||||
Use(unitCaster);
|
||||
break;
|
||||
case GameObjectActions::OpenAndUnlock:
|
||||
if (unitCaster)
|
||||
UseDoorOrButton(0, false, unitCaster);
|
||||
[[fallthrough]];
|
||||
case GameObjectActions::Unlock:
|
||||
RemoveFlag(GO_FLAG_LOCKED);
|
||||
break;
|
||||
case GameObjectActions::Lock:
|
||||
AddFlag(GO_FLAG_LOCKED);
|
||||
break;
|
||||
case GameObjectActions::Close:
|
||||
case GameObjectActions::Rebuild:
|
||||
ResetDoorOrButton();
|
||||
break;
|
||||
case GameObjectActions::Despawn:
|
||||
DespawnOrUnsummon();
|
||||
break;
|
||||
case GameObjectActions::MakeInert:
|
||||
AddFlag(GO_FLAG_NOT_SELECTABLE);
|
||||
break;
|
||||
case GameObjectActions::MakeActive:
|
||||
RemoveFlag(GO_FLAG_NOT_SELECTABLE);
|
||||
break;
|
||||
case GameObjectActions::CloseAndLock:
|
||||
ResetDoorOrButton();
|
||||
AddFlag(GO_FLAG_LOCKED);
|
||||
break;
|
||||
case GameObjectActions::Destroy:
|
||||
if (unitCaster)
|
||||
UseDoorOrButton(0, true, unitCaster);
|
||||
break;
|
||||
case GameObjectActions::UseArtKit0:
|
||||
case GameObjectActions::UseArtKit1:
|
||||
case GameObjectActions::UseArtKit2:
|
||||
case GameObjectActions::UseArtKit3:
|
||||
case GameObjectActions::UseArtKit4:
|
||||
{
|
||||
GameObjectTemplateAddon const* templateAddon = GetTemplateAddon();
|
||||
|
||||
uint32 artKitIndex = action != GameObjectActions::UseArtKit4 ? uint32(action) - uint32(GameObjectActions::UseArtKit0) : 4;
|
||||
|
||||
uint32 artKitValue = 0;
|
||||
if (templateAddon != nullptr)
|
||||
artKitValue = templateAddon->ArtKits[artKitIndex];
|
||||
|
||||
if (artKitValue == 0)
|
||||
TC_LOG_ERROR("sql.sql", "GameObject %d hit by spell %d needs `artkit%d` in `gameobject_template_addon`", GetEntry(), spellId, artKitIndex);
|
||||
else
|
||||
SetGoArtKit(artKitValue);
|
||||
|
||||
break;
|
||||
}
|
||||
case GameObjectActions::GoTo1stFloor:
|
||||
case GameObjectActions::GoTo2ndFloor:
|
||||
case GameObjectActions::GoTo3rdFloor:
|
||||
case GameObjectActions::GoTo4thFloor:
|
||||
case GameObjectActions::GoTo5thFloor:
|
||||
case GameObjectActions::GoTo6thFloor:
|
||||
case GameObjectActions::GoTo7thFloor:
|
||||
case GameObjectActions::GoTo8thFloor:
|
||||
case GameObjectActions::GoTo9thFloor:
|
||||
case GameObjectActions::GoTo10thFloor:
|
||||
if (GetGoType() == GAMEOBJECT_TYPE_TRANSPORT)
|
||||
SetTransportState(GO_STATE_TRANSPORT_STOPPED, uint32(action) - uint32(GameObjectActions::GoTo1stFloor));
|
||||
else
|
||||
TC_LOG_ERROR("spell", "Spell %d targeted non-transport gameobject for transport only action \"Go to Floor\" %d in effect %d", spellId, int32(action), effectIndex);
|
||||
break;
|
||||
case GameObjectActions::PlayAnimKit:
|
||||
SetAnimKitId(param, false);
|
||||
break;
|
||||
case GameObjectActions::OpenAndPlayAnimKit:
|
||||
if (unitCaster)
|
||||
UseDoorOrButton(0, false, unitCaster);
|
||||
SetAnimKitId(param, false);
|
||||
break;
|
||||
case GameObjectActions::CloseAndPlayAnimKit:
|
||||
ResetDoorOrButton();
|
||||
SetAnimKitId(param, false);
|
||||
break;
|
||||
case GameObjectActions::PlayOneShotAnimKit:
|
||||
SetAnimKitId(param, true);
|
||||
break;
|
||||
case GameObjectActions::StopAnimKit:
|
||||
SetAnimKitId(0, false);
|
||||
break;
|
||||
case GameObjectActions::OpenAndStopAnimKit:
|
||||
if (unitCaster)
|
||||
UseDoorOrButton(0, false, unitCaster);
|
||||
SetAnimKitId(0, false);
|
||||
break;
|
||||
case GameObjectActions::CloseAndStopAnimKit:
|
||||
ResetDoorOrButton();
|
||||
SetAnimKitId(0, false);
|
||||
break;
|
||||
case GameObjectActions::PlaySpellVisual:
|
||||
SetSpellVisualId(param, Object::GetGUID(spellCaster));
|
||||
break;
|
||||
case GameObjectActions::StopSpellVisual:
|
||||
SetSpellVisualId(0);
|
||||
break;
|
||||
case GameObjectActions::None:
|
||||
TC_LOG_FATAL("spell", "Spell %d has action type NONE in effect %d", spellId, effectIndex);
|
||||
break;
|
||||
default:
|
||||
TC_LOG_ERROR("spell", "Spell %d has unhandled action %d in effect %d", spellId, int32(action), effectIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GameObject::SetGoArtKit(uint8 kit)
|
||||
{
|
||||
SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::ArtKit), kit);
|
||||
|
||||
@@ -266,6 +266,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
|
||||
void UseDoorOrButton(uint32 time_to_restore = 0, bool alternative = false, Unit* user = nullptr);
|
||||
// 0 = use `gameobject`.`spawntimesecs`
|
||||
void ResetDoorOrButton();
|
||||
void ActivateObject(GameObjectActions action, int32 param, WorldObject* spellCaster = nullptr, uint32 spellId = 0, int32 effectIndex = -1);
|
||||
|
||||
void TriggeringLinkedGameObject(uint32 trapEntry, Unit* target);
|
||||
|
||||
|
||||
@@ -1165,6 +1165,7 @@ enum class GameObjectActions : uint32
|
||||
PlaySpellVisual = 42, // Play Spell Visual "{SpellVisual}"
|
||||
StopSpellVisual = 43, // Stop Spell Visual
|
||||
SetTappedToChallengePlayers = 44, // Set Tapped to Challenge Players
|
||||
Max
|
||||
};
|
||||
|
||||
#endif // GameObjectData_h__
|
||||
|
||||
@@ -3317,126 +3317,7 @@ void Spell::EffectActivateObject()
|
||||
|
||||
GameObjectActions action = GameObjectActions(effectInfo->MiscValue);
|
||||
|
||||
switch (action)
|
||||
{
|
||||
case GameObjectActions::AnimateCustom0:
|
||||
case GameObjectActions::AnimateCustom1:
|
||||
case GameObjectActions::AnimateCustom2:
|
||||
case GameObjectActions::AnimateCustom3:
|
||||
gameObjTarget->SendCustomAnim(uint32(action) - uint32(GameObjectActions::AnimateCustom0));
|
||||
break;
|
||||
case GameObjectActions::Disturb: // What's the difference with Open?
|
||||
case GameObjectActions::Open:
|
||||
if (Unit* unitCaster = m_caster->ToUnit())
|
||||
gameObjTarget->Use(unitCaster);
|
||||
break;
|
||||
case GameObjectActions::OpenAndUnlock:
|
||||
if (Unit* unitCaster = m_caster->ToUnit())
|
||||
gameObjTarget->UseDoorOrButton(0, false, unitCaster);
|
||||
[[fallthrough]];
|
||||
case GameObjectActions::Unlock:
|
||||
gameObjTarget->RemoveFlag(GO_FLAG_LOCKED);
|
||||
break;
|
||||
case GameObjectActions::Lock:
|
||||
gameObjTarget->AddFlag(GO_FLAG_LOCKED);
|
||||
break;
|
||||
case GameObjectActions::Close:
|
||||
case GameObjectActions::Rebuild:
|
||||
gameObjTarget->ResetDoorOrButton();
|
||||
break;
|
||||
case GameObjectActions::Despawn:
|
||||
gameObjTarget->DespawnOrUnsummon();
|
||||
break;
|
||||
case GameObjectActions::MakeInert:
|
||||
gameObjTarget->AddFlag(GO_FLAG_NOT_SELECTABLE);
|
||||
break;
|
||||
case GameObjectActions::MakeActive:
|
||||
gameObjTarget->RemoveFlag(GO_FLAG_NOT_SELECTABLE);
|
||||
break;
|
||||
case GameObjectActions::CloseAndLock:
|
||||
gameObjTarget->ResetDoorOrButton();
|
||||
gameObjTarget->AddFlag(GO_FLAG_LOCKED);
|
||||
break;
|
||||
case GameObjectActions::Destroy:
|
||||
if (Unit* unitCaster = m_caster->ToUnit())
|
||||
gameObjTarget->UseDoorOrButton(0, true, unitCaster);
|
||||
break;
|
||||
case GameObjectActions::UseArtKit0:
|
||||
case GameObjectActions::UseArtKit1:
|
||||
case GameObjectActions::UseArtKit2:
|
||||
case GameObjectActions::UseArtKit3:
|
||||
case GameObjectActions::UseArtKit4:
|
||||
{
|
||||
GameObjectTemplateAddon const* templateAddon = gameObjTarget->GetTemplateAddon();
|
||||
|
||||
uint32 artKitIndex = action != GameObjectActions::UseArtKit4 ? uint32(action) - uint32(GameObjectActions::UseArtKit0) : 4;
|
||||
|
||||
uint32 artKitValue = 0;
|
||||
if (templateAddon != nullptr)
|
||||
artKitValue = templateAddon->ArtKits[artKitIndex];
|
||||
|
||||
if (artKitValue == 0)
|
||||
TC_LOG_ERROR("sql.sql", "GameObject %d hit by spell %d needs `artkit%d` in `gameobject_template_addon`", gameObjTarget->GetEntry(), m_spellInfo->Id, artKitIndex);
|
||||
else
|
||||
gameObjTarget->SetGoArtKit(artKitValue);
|
||||
|
||||
break;
|
||||
}
|
||||
case GameObjectActions::GoTo1stFloor:
|
||||
case GameObjectActions::GoTo2ndFloor:
|
||||
case GameObjectActions::GoTo3rdFloor:
|
||||
case GameObjectActions::GoTo4thFloor:
|
||||
case GameObjectActions::GoTo5thFloor:
|
||||
case GameObjectActions::GoTo6thFloor:
|
||||
case GameObjectActions::GoTo7thFloor:
|
||||
case GameObjectActions::GoTo8thFloor:
|
||||
case GameObjectActions::GoTo9thFloor:
|
||||
case GameObjectActions::GoTo10thFloor:
|
||||
if (gameObjTarget->GetGoType() == GAMEOBJECT_TYPE_TRANSPORT)
|
||||
gameObjTarget->SetTransportState(GO_STATE_TRANSPORT_STOPPED, uint32(action) - uint32(GameObjectActions::GoTo1stFloor));
|
||||
else
|
||||
TC_LOG_ERROR("spell", "Spell %d targeted non-transport gameobject for transport only action \"Go to Floor\" %d in effect %d", m_spellInfo->Id, int32(action), int32(effectInfo->EffectIndex));
|
||||
break;
|
||||
case GameObjectActions::PlayAnimKit:
|
||||
gameObjTarget->SetAnimKitId(effectInfo->MiscValueB, false);
|
||||
break;
|
||||
case GameObjectActions::OpenAndPlayAnimKit:
|
||||
if (Unit* unitCaster = m_caster->ToUnit())
|
||||
gameObjTarget->UseDoorOrButton(0, false, unitCaster);
|
||||
gameObjTarget->SetAnimKitId(effectInfo->MiscValueB, false);
|
||||
break;
|
||||
case GameObjectActions::CloseAndPlayAnimKit:
|
||||
gameObjTarget->ResetDoorOrButton();
|
||||
gameObjTarget->SetAnimKitId(effectInfo->MiscValueB, false);
|
||||
break;
|
||||
case GameObjectActions::PlayOneShotAnimKit:
|
||||
gameObjTarget->SetAnimKitId(effectInfo->MiscValueB, true);
|
||||
break;
|
||||
case GameObjectActions::StopAnimKit:
|
||||
gameObjTarget->SetAnimKitId(0, false);
|
||||
break;
|
||||
case GameObjectActions::OpenAndStopAnimKit:
|
||||
if (Unit* unitCaster = m_caster->ToUnit())
|
||||
gameObjTarget->UseDoorOrButton(0, false, unitCaster);
|
||||
gameObjTarget->SetAnimKitId(0, false);
|
||||
break;
|
||||
case GameObjectActions::CloseAndStopAnimKit:
|
||||
gameObjTarget->ResetDoorOrButton();
|
||||
gameObjTarget->SetAnimKitId(0, false);
|
||||
break;
|
||||
case GameObjectActions::PlaySpellVisual:
|
||||
gameObjTarget->SetSpellVisualId(effectInfo->MiscValueB, m_originalCasterGUID);
|
||||
break;
|
||||
case GameObjectActions::StopSpellVisual:
|
||||
gameObjTarget->SetSpellVisualId(0);
|
||||
break;
|
||||
case GameObjectActions::None:
|
||||
TC_LOG_FATAL("spell", "Spell %d has action type NONE in effect %d", m_spellInfo->Id, int32(effectInfo->EffectIndex));
|
||||
break;
|
||||
default:
|
||||
TC_LOG_ERROR("spell", "Spell %d has unhandled action %d in effect %d", m_spellInfo->Id, int32(action), int32(effectInfo->EffectIndex));
|
||||
break;
|
||||
}
|
||||
gameObjTarget->ActivateObject(action, effectInfo->MiscValueB, m_caster, m_spellInfo->Id, int32(effectInfo->EffectIndex));
|
||||
}
|
||||
|
||||
void Spell::EffectApplyGlyph()
|
||||
|
||||
Reference in New Issue
Block a user