Core/SAI: Add SMART_ACTION_ACTIVATE_GAMEOBJECT action (#27216)

Closes #27196
This commit is contained in:
Giacomo Pozzoni
2021-11-07 19:17:12 +01:00
committed by GitHub
parent 8f4bf97b44
commit 0817be8f76
7 changed files with 132 additions and 93 deletions

View File

@@ -2381,6 +2381,17 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
break;
}
case SMART_ACTION_ACTIVATE_GAMEOBJECT:
{
for (WorldObject* target : targets)
{
if (GameObject* targetGo = target->ToGameObject())
{
targetGo->ActivateObject(GameObjectActions(e.action.activateGameObject.gameObjectAction));
}
}
break;
}
default:
TC_LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry %d SourceType %u, Event %u, Unhandled Action type %u", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
break;

View File

@@ -943,6 +943,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::activateGameObject);
default:
TC_LOG_WARN("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u is using an action with no unused params specified in SmartAIMgr::CheckUnusedActionParams(), please report this.",
e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
@@ -2123,6 +2124,19 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
TC_SAI_IS_BOOLEAN_VALID(e, e.action.setUninteractible.uninteractible);
break;
}
case SMART_ACTION_ACTIVATE_GAMEOBJECT:
{
if (!NotNULL(e, e.action.activateGameObject.gameObjectAction))
return false;
if (e.action.activateGameObject.gameObjectAction >= uint32(GameObjectActions::Max))
{
TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d 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:
case SMART_ACTION_SET_ORIENTATION:
case SMART_ACTION_STORE_TARGET_LIST:

View File

@@ -589,8 +589,9 @@ enum SMART_ACTION
SMART_ACTION_SET_IMMUNE_PC = 144, // 0/1
SMART_ACTION_SET_IMMUNE_NPC = 145, // 0/1
SMART_ACTION_SET_UNINTERACTIBLE = 146, // 0/1
SMART_ACTION_ACTIVATE_GAMEOBJECT = 147, // GameObjectActions
SMART_ACTION_END = 147
SMART_ACTION_END = 148
};
enum class SmartActionSummonCreatureFlags
@@ -1197,6 +1198,11 @@ struct SmartAction
SAIBool uninteractible;
} setUninteractible;
struct
{
uint32 gameObjectAction;
} activateGameObject;
//! Note for any new future actions
//! All parameters must have type uint32

View File

@@ -1432,6 +1432,79 @@ 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, WorldObject* spellCaster, uint32 spellId, int32 effectIndex)
{
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:
case GameObjectActions::Lock:
ApplyModFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED, action == GameObjectActions::Lock);
break;
case GameObjectActions::Close:
case GameObjectActions::Rebuild:
ResetDoorOrButton();
break;
case GameObjectActions::Despawn:
DespawnOrUnsummon();
break;
case GameObjectActions::MakeInert:
case GameObjectActions::MakeActive:
ApplyModFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE, action == GameObjectActions::MakeInert);
break;
case GameObjectActions::CloseAndLock:
ResetDoorOrButton();
SetFlag(GAMEOBJECT_FLAGS, 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:
{
GameObjectTemplateAddon const* templateAddon = GetTemplateAddon();
uint32 artKitIndex = uint32(action) - uint32(GameObjectActions::UseArtKit0);
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::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)
{
SetByteValue(GAMEOBJECT_BYTES_1, 2, kit);

View File

@@ -226,6 +226,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, WorldObject* spellCaster = nullptr, uint32 spellId = 0, int32 effectIndex = -1);
void TriggeringLinkedGameObject(uint32 trapEntry, Unit* target);

View File

@@ -675,30 +675,31 @@ struct GameObjectData : public SpawnData
enum class GameObjectActions : uint32
{
// Name from client executable // Comments
None, // -NONE-
AnimateCustom0, // Animate Custom0
AnimateCustom1, // Animate Custom1
AnimateCustom2, // Animate Custom2
AnimateCustom3, // Animate Custom3
Disturb, // Disturb // Triggers trap
Unlock, // Unlock // Resets GO_FLAG_LOCKED
Lock, // Lock // Sets GO_FLAG_LOCKED
Open, // Open // Sets GO_STATE_ACTIVE
OpenAndUnlock, // Open + Unlock // Sets GO_STATE_ACTIVE and resets GO_FLAG_LOCKED
Close, // Close // Sets GO_STATE_READY
ToggleOpen, // Toggle Open
Destroy, // Destroy // Sets GO_STATE_DESTROYED
Rebuild, // Rebuild // Resets from GO_STATE_DESTROYED
Creation, // Creation
Despawn, // Despawn
MakeInert, // Make Inert // Disables interactions
MakeActive, // Make Active // Enables interactions
CloseAndLock, // Close + Lock // Sets GO_STATE_READY and sets GO_FLAG_LOCKED
UseArtKit0, // Use ArtKit0 // 46904: 121
UseArtKit1, // Use ArtKit1 // 36639: 81, 46903: 122
UseArtKit2, // Use ArtKit2
UseArtKit3, // Use ArtKit3
SetTapList, // Set Tap List
None = 0, // -NONE-
AnimateCustom0 = 1, // Animate Custom0
AnimateCustom1 = 2, // Animate Custom1
AnimateCustom2 = 3, // Animate Custom2
AnimateCustom3 = 4, // Animate Custom3
Disturb = 5, // Disturb // Triggers trap
Unlock = 6, // Unlock // Resets GO_FLAG_LOCKED
Lock = 7, // Lock // Sets GO_FLAG_LOCKED
Open = 8, // Open // Sets GO_STATE_ACTIVE
OpenAndUnlock = 9, // Open + Unlock // Sets GO_STATE_ACTIVE and resets GO_FLAG_LOCKED
Close = 10, // Close // Sets GO_STATE_READY
ToggleOpen = 11, // Toggle Open
Destroy = 12, // Destroy // Sets GO_STATE_DESTROYED
Rebuild = 13, // Rebuild // Resets from GO_STATE_DESTROYED
Creation = 14, // Creation
Despawn = 15, // Despawn
MakeInert = 16, // Make Inert // Disables interactions
MakeActive = 17, // Make Active // Enables interactions
CloseAndLock = 18, // Close + Lock // Sets GO_STATE_READY and sets GO_FLAG_LOCKED
UseArtKit0 = 19, // Use ArtKit0 // 46904: 121
UseArtKit1 = 20, // Use ArtKit1 // 36639: 81, 46903: 122
UseArtKit2 = 21, // Use ArtKit2
UseArtKit3 = 22, // Use ArtKit3
SetTapList = 23, // Set Tap List
Max
};
#endif // GameObjectData_h__

View File

@@ -3762,74 +3762,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:
case GameObjectActions::Lock:
gameObjTarget->ApplyModFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED, action == GameObjectActions::Lock);
break;
case GameObjectActions::Close:
case GameObjectActions::Rebuild:
gameObjTarget->ResetDoorOrButton();
break;
case GameObjectActions::Despawn:
gameObjTarget->DespawnOrUnsummon();
break;
case GameObjectActions::MakeInert:
case GameObjectActions::MakeActive:
gameObjTarget->ApplyModFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE, action == GameObjectActions::MakeInert);
break;
case GameObjectActions::CloseAndLock:
gameObjTarget->ResetDoorOrButton();
gameObjTarget->SetFlag(GAMEOBJECT_FLAGS, 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:
{
GameObjectTemplateAddon const* templateAddon = gameObjTarget->GetTemplateAddon();
uint32 artKitIndex = uint32(action) - uint32(GameObjectActions::UseArtKit0);
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::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, m_caster, m_spellInfo->Id, int32(effectInfo->EffectIndex));
}
void Spell::EffectApplyGlyph()