Core/SAI: Add SMART_ACTION_ACTIVATE_GAMEOBJECT action (#27216)

Closes #27196

(cherry picked from commit 0817be8f76)
This commit is contained in:
Giacomo Pozzoni
2021-11-07 19:17:12 +01:00
committed by Shauren
parent 4c1b9d7455
commit 791b759332
7 changed files with 177 additions and 138 deletions

View File

@@ -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:

View File

@@ -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:

View File

@@ -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
{

View File

@@ -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);

View File

@@ -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);

View File

@@ -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__

View File

@@ -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()