Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Events: add NWNX_ON_BROADCAST_SAFE_PROJECTILE_{BEFORE|AFTER} #1634

Merged
merged 3 commits into from
Apr 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
https://github.com/nwnxee/unified/compare/build8193.35.37...HEAD

### Added
- Events: added skippable event `NWNX_ON_BROADCAST_SAFE_PROJECTILE_{BEFORE|AFTER}` which fires whenever a projectile VFX is created for ranged weapons and spells.
- Events: added skippable event `NWNX_ON_BROADCAST_ATTACK_OF_OPPORTUNITY_{BEFORE|AFTER}` which allows skipping a creature provoking attacks of opportunities from enemies.
- Events: added skippable event `NWNX_ON_COMBAT_ATTACK_OF_OPPORTUNITY_{BEFORE|AFTER}` which allows stopping a creature from performing an attack of opportunity against a target.

Expand Down
37 changes: 37 additions & 0 deletions Plugins/Events/Events/ObjectEvents.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ using namespace NWNXLib::API::Constants;
static NWNXLib::Hooks::Hook s_AddLockObjectActionHook;
static NWNXLib::Hooks::Hook s_AddUnlockObjectActionHook;
static NWNXLib::Hooks::Hook s_AddUseObjectActionHook;
static NWNXLib::Hooks::Hook s_BroadcastSafeProjectileHook;

static int32_t AddLockObjectActionHook(CNWSObject*, ObjectID);
static int32_t AddUnlockObjectActionHook(CNWSObject*, ObjectID, ObjectID, int32_t);
static int32_t AddUseObjectActionHook(CNWSObject*, ObjectID);
static void BroadcastSafeProjectileHook(CNWSObject*, ObjectID, ObjectID, Vector, Vector, uint32_t, uint8_t, uint32_t, uint8_t, uint8_t);

void ObjectEvents() __attribute__((constructor));
void ObjectEvents()
Expand All @@ -32,6 +34,11 @@ void ObjectEvents()
s_AddUseObjectActionHook = Hooks::HookFunction(&CNWSObject::AddUseObjectAction,
(void*)&AddUseObjectActionHook, Hooks::Order::Early);
});

InitOnFirstSubscribe("NWNX_ON_BROADCAST_SAFE_PROJECTILE_.*", []() {
s_BroadcastSafeProjectileHook = Hooks::HookFunction(&CNWSObject::BroadcastSafeProjectile,
(void*)&BroadcastSafeProjectileHook, Hooks::Order::Early);
});
}

int32_t AddLockObjectActionHook(CNWSObject *thisPtr, ObjectID oidDoor)
Expand Down Expand Up @@ -110,4 +117,34 @@ int32_t AddUseObjectActionHook(CNWSObject *thisPtr, ObjectID oidObjectToUse)
return retVal;
}

void BroadcastSafeProjectileHook(CNWSObject *thisPtr, ObjectID oidOriginator, ObjectID oidTarget, Vector vOriginator, Vector vTarget, uint32_t nDelta,
uint8_t nProjectileType, uint32_t nSpellID, uint8_t nAttackResult, uint8_t nProjectilePathType)
{
if (!IsIDInWhitelist("NWNX_ON_BROADCAST_SAFE_PROJECTILE_TYPE", nProjectileType) || !IsIDInWhitelist("NWNX_ON_BROADCAST_SAFE_PROJECTILE_SPELL_ID", nSpellID))
{
s_BroadcastSafeProjectileHook->CallOriginal<void>(thisPtr, oidOriginator, oidTarget, vOriginator, vTarget, nDelta, nProjectileType, nSpellID, nAttackResult, nProjectilePathType);
return;
}

auto PushAndSignal = [&](const std::string& ev) -> bool {
PushEventData("TARGET_OBJECT_ID", Utils::ObjectIDToString(oidTarget));
PushEventData("TARGET_POSITION_X", std::to_string(vTarget.x));
PushEventData("TARGET_POSITION_Y", std::to_string(vTarget.y));
PushEventData("TARGET_POSITION_Z", std::to_string(vTarget.z));
PushEventData("DELTA", std::to_string(nDelta));
PushEventData("PROJECTILE_TYPE", std::to_string(nProjectileType));
PushEventData("SPELL_ID", std::to_string(nSpellID));
PushEventData("ATTACK_RESULT", std::to_string(nAttackResult));
PushEventData("PROJECTILE_PATH_TYPE", std::to_string(nProjectilePathType));
return SignalEvent(ev, thisPtr->m_idSelf);
};

if (PushAndSignal("NWNX_ON_BROADCAST_SAFE_PROJECTILE_BEFORE"))
{
s_BroadcastSafeProjectileHook->CallOriginal<void>(thisPtr, oidOriginator, oidTarget, vOriginator, vTarget, nDelta, nProjectileType, nSpellID, nAttackResult, nProjectilePathType);
}

PushAndSignal("NWNX_ON_BROADCAST_SAFE_PROJECTILE_AFTER");
}

}
42 changes: 42 additions & 0 deletions Plugins/Events/NWScript/nwnx_events.nss
Original file line number Diff line number Diff line change
Expand Up @@ -1559,6 +1559,31 @@ _______________________________________
Event Data Tag | Type | Notes
----------------------|--------|-------
OBJECT | object | The Object being used |
_______________________________________
## Broadcast Safe Projectile Events
- NWNX_ON_BROADCAST_SAFE_PROJECTILE_BEFORE
- NWNX_ON_BROADCAST_SAFE_PROJECTILE_AFTER

`OBJECT_SELF` = The creature casting the spell

Event Data Tag | Type | Notes |
----------------------|--------|-------|
TARGET_OBJECT_ID | string | Convert to object with StringToObject() |
TARGET_POSITION_X | float | |
TARGET_POSITION_Y | float | |
TARGET_POSITION_Z | float | |
DELTA | int | Time in milliseconds before the projectile reaches its destination |
PROJECTILE_TYPE | int | @ref events_projtype "NWNX_EVENTS_BROADCAST_SAFE_PROJECTILE_TYPE_*" |
SPELL_ID | int | |
ATTACK_RESULT | int | 0=n/a, 1=hit, 2=parried, 3=critical hit, 4=miss, 5=resisted, 7=automatic hit, 8=concealed, 9=miss chance, 10=devastating crit |
PROJECTILE_PATH_TYPE | int | @nwn{Projectile_path_type,PROJECTILE_PATH_TYPE_*} |

@note This event fires for all projectiles. It's recommended to use ID whitelists with this event. You can whitelist the event by the projectile type, spell id, or both:
\code{.c}
NWNX_Events_AddIDToWhitelist("NWNX_ON_BROADCAST_SAFE_PROJECTILE_TYPE", NWNX_EVENTS_BROADCAST_SAFE_PROJECTILE_TYPE_*);
NWNX_Events_AddIDToWhitelist("NWNX_ON_BROADCAST_SAFE_PROJECTILE_SPELL", SPELL_*);
\endcode
`TARGET_OBJECT_ID` will be `OBJECT_INVALID` if the projectile is cast at a location
_______________________________________
## Broadcast Attack of Opportunity Events
- NWNX_ON_BROADCAST_ATTACK_OF_OPPORTUNITY_BEFORE
Expand Down Expand Up @@ -1907,6 +1932,8 @@ const string NWNX_ON_RUN_EVENT_SCRIPT_BEFORE = "NWNX_ON_RUN_EVENT_SCRIPT_BEFORE"
const string NWNX_ON_RUN_EVENT_SCRIPT_AFTER = "NWNX_ON_RUN_EVENT_SCRIPT_AFTER";
const string NWNX_ON_OBJECT_USE_BEFORE = "NWNX_ON_OBJECT_USE_BEFORE";
const string NWNX_ON_OBJECT_USE_AFTER = "NWNX_ON_OBJECT_USE_AFTER";
const string NWNX_ON_BROADCAST_SAFE_PROJECTILE_BEFORE = "NWNX_ON_BROADCAST_SAFE_PROJECTILE_BEFORE";
const string NWNX_ON_BROADCAST_SAFE_PROJECTILE_AFTER = "NWNX_ON_BROADCAST_SAFE_PROJECTILE_AFTER";
const string NWNX_ON_BROADCAST_ATTACK_OF_OPPORTUNITY_BEFORE = "NWNX_ON_BROADCAST_ATTACK_OF_OPPORTUNITY_BEFORE";
const string NWNX_ON_BROADCAST_ATTACK_OF_OPPORTUNITY_AFTER = "NWNX_ON_BROADCAST_ATTACK_OF_OPPORTUNITY_AFTER";
const string NWNX_ON_COMBAT_ATTACK_OF_OPPORTUNITY_BEFORE = "NWNX_ON_COMBAT_ATTACK_OF_OPPORTUNITY_BEFORE";
Expand Down Expand Up @@ -1948,6 +1975,19 @@ const int NWNX_EVENTS_DM_SET_VARIABLE_TYPE_STRING = 2;
const int NWNX_EVENTS_DM_SET_VARIABLE_TYPE_OBJECT = 3;
/// @}

/// @name Events BroadcastSafeProjectile Constants
/// @anchor events_projtype
/// @{
const int NWNX_EVENTS_BROADCAST_SAFE_PROJECTILE_TYPE_WEAPON_VFX_NONE = 0;
const int NWNX_EVENTS_BROADCAST_SAFE_PROJECTILE_TYPE_WEAPON_VFX_ACID = 1;
const int NWNX_EVENTS_BROADCAST_SAFE_PROJECTILE_TYPE_WEAPON_VFX_COLD = 2;
const int NWNX_EVENTS_BROADCAST_SAFE_PROJECTILE_TYPE_WEAPON_VFX_ELECTRICAL = 3;
const int NWNX_EVENTS_BROADCAST_SAFE_PROJECTILE_TYPE_WEAPON_VFX_FIRE = 4;
const int NWNX_EVENTS_BROADCAST_SAFE_PROJECTILE_TYPE_WEAPON_VFX_SONIC = 5;
const int NWNX_EVENTS_BROADCAST_SAFE_PROJECTILE_TYPE_SPELL_DEFAULT = 6;
const int NWNX_EVENTS_BROADCAST_SAFE_PROJECTILE_TYPE_SPELL_USE_PATH = 7;
/// @}

/// @brief Scripts can subscribe to events.
///
/// Some events are dispatched via the NWNX plugin (see NWNX_EVENTS_EVENT_* constants).
Expand Down Expand Up @@ -2040,6 +2080,7 @@ string NWNX_Events_GetEventData(string tag);
/// - Decrement Spell Count event
/// - Play Visual Effect event
/// - EventScript event
/// - Broadcast Safe Projectile event
/// - Attack of Opportunity events
void NWNX_Events_SkipEvent();

Expand Down Expand Up @@ -2084,6 +2125,7 @@ void NWNX_Events_RemoveObjectFromDispatchList(string sEvent, string sScriptOrChu
/// - NWNX_ON_CAST_SPELL -> SpellID
/// - NWNX_ON_HAS_FEAT -> FeatID (default enabled)
/// - NWNX_ON_RUN_EVENT_SCRIPT -> EVENT_SCRIPT_* (default enabled)
/// - NWNX_ON_BROADCAST_SAFE_PROJECTILE -> NWNX_ON_BROADCAST_SAFE_PROJECTILE_TYPE for ProjectileType, NWNX_ON_BROADCAST_SAFE_PROJECTILE_SPELL for SpellID
///
/// @note This enables the whitelist for ALL scripts subscribed to sEvent.
/// @param sEvent The event name without _BEFORE / _AFTER.
Expand Down