Skip to content

Commit

Permalink
[Enhancement] Make sword beams work on any enemy (#682)
Browse files Browse the repository at this point in the history
* Make sword beam work on any enemy with FD anywhere

* Apply arrow effect to FD sword beams in hook

Add separate variable for FD sword beams on regular enemies

* Add damage multiplier and effect hooks

Use those for Fierce Deity sword beam stuff
Add DamageAndEffectHookInfo struct to pass multiple fields into hooks
Use ActorInit hook on sword beams for the arrow damage effect

* Add light arrow effect to sword beams

Actually pass in the DamageAndEffectHookInfo pointer for the damage effect hook
Add more clarification within FDA enhancement comments

* Overhaul how sword beams work on regular enemies

Revamp how sword beam handling works with Fierce Deity Anywhere
Remove light arrow damage flag and instead make sword beams always collide with actors
Give sword beams the light arrow effect if they do not already have one defined
Draw blue light orbs effect for sword beams using the light orbs effect
Add special sword beam damage effect for Big Octos since they're unique with drawing damage effects

* Use variadic args for damage hooks

Use va_arg for FierceDeityAnywhere arguments
Remove now-obsolete DamageAndEffectHookInfo struct

* Remove unused damage hook arguments

* Streamline sword beam collision

Only override CollisionCheck_NoSharedFlags if the toucher belongs to a sword beam
Condense FierceDeityAnywhere static variables to one for the actor being drawn

* Use new hook instead of static variable for nulls

* Pass bodyPartsPos and size into hook

---------

Co-authored-by: Garrett Cox <[email protected]>
  • Loading branch information
Eblo and garrettjoecox authored Nov 29, 2024
1 parent 6ca355f commit b38794e
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 10 deletions.
156 changes: 156 additions & 0 deletions mm/2s2h/Enhancements/Masks/FierceDeityAnywhere.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,166 @@
#include <libultraship/bridge.h>
#include "2s2h/GameInteractor/GameInteractor.h"
#include "z64.h"
#include "overlays/actors/ovl_En_Bigokuta/z_en_bigokuta.h"
#include "overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.h"
#include "overlays/actors/ovl_En_Firefly/z_en_firefly.h"
#include "overlays/actors/ovl_En_Fz/z_en_fz.h"
#include "overlays/actors/ovl_En_Neo_Reeba/z_en_neo_reeba.h"

#define HIT_BY_SWORD_BEAM 1
#define NOT_HIT_BY_SWORD_BEAM 0

void RegisterFierceDeityAnywhere() {
REGISTER_VB_SHOULD(VB_DISABLE_FD_MASK, {
if (CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0)) {
*should = false;
}
});

REGISTER_VB_SHOULD(VB_DAMAGE_MULTIPLIER, {
if (CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0)) {
int index = va_arg(args, int);
DamageTable* damageTable = va_arg(args, DamageTable*);
f32* damage = va_arg(args, f32*);
f32* damageMultipliers = va_arg(args, f32*);

/*
* 25 is the index of the sword beam damage effect.
*/
if (index == 25) {
*should = false;
/*
* If the existing damage multiplier for sword beams is 0, use a damage multiplier of 1.0 instead.
*/
u8 defaultMultiplier = damageTable->attack[index] & 0xF;
*damage *= damageMultipliers[defaultMultiplier == 0 ? 1 : defaultMultiplier];
}
}
});

REGISTER_VB_SHOULD(VB_DAMAGE_EFFECT, {
if (CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0)) {
int index = va_arg(args, int);
DamageTable* damageTable = va_arg(args, DamageTable*);
u32* effect = va_arg(args, u32*);
Actor* actor = va_arg(args, Actor*);
/*
* 25 is the index of the sword beam damage effect.
*/
if (index == 25) {
*should = false;
/*
* If the sword beam effect is 0, use the light arrow effect instead.
*/
u8 defaultEffect = (damageTable->attack[index] >> 4) & 0xF;
if (defaultEffect == 0) {
// 13 is the index of the light arrow damage effect
*effect = (damageTable->attack[13] >> 4) & 0xF;
} else {
*effect = defaultEffect;
}
/*
* shape.face is unused for any actor besides the player. We are hijacking this because we need to have
* some variable connected to the specific actor to indicate that the damage they received comes from a
* sword beam. Each stage of the pipeline (update, draw) goes through all actors in a batch.
*/
actor->shape.face = HIT_BY_SWORD_BEAM;
} else if (index != 9) {
/*
* 9 is the index of the sword damage effect. With how FD plays, it is possible for the sword to connect
* after sword beams have dealt damage. Without this check, the damage effect would revert back to the
* light arrows effect upon sword collision.
*/
actor->shape.face = NOT_HIT_BY_SWORD_BEAM;
}
}
});

/*
* Keese, Freezards, and Leevers are unique in that they call Actor_DrawDamageEffects() with NULL for the actor.
* Normally, that method only uses the actor for playing positional sound effects. For sword beams only, we
* overwrite these calls and pass in the actor so that the sword beam draws can be handled properly in the
* VB_DRAW_DAMAGE_EFFECT hook.
*/
REGISTER_VB_SHOULD(VB_USE_NULL_FOR_DRAW_DAMAGE_EFFECTS, {
if (CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0)) {
Actor* actor = va_arg(args, Actor*);
// Only change the call if there is a sword beam collision
if (actor->shape.face & HIT_BY_SWORD_BEAM) {
*should = false;
Vec3f* bodyPartsPos = va_arg(args, Vec3f*);
int bodyPartsCount = va_arg(args, int);
if (actor->id == ACTOR_EN_FIREFLY) { // Keese
EnFirefly* enFireFly = (EnFirefly*)actor;
Actor_DrawDamageEffects(gPlayState, actor, bodyPartsPos, bodyPartsCount,
enFireFly->drawDmgEffScale * actor->scale.y * 200.0f,
enFireFly->drawDmgEffFrozenSteamScale, enFireFly->drawDmgEffAlpha,
enFireFly->drawDmgEffType);
} else if (actor->id == ACTOR_EN_FZ) { // Freezard
EnFz* enFz = (EnFz*)actor;
Actor_DrawDamageEffects(gPlayState, actor, bodyPartsPos, bodyPartsCount,
enFz->drawDmgEffScale * 4.0f, 0.5f, enFz->drawDmgEffAlpha,
ACTOR_DRAW_DMGEFF_LIGHT_ORBS);
} else if (actor->id == ACTOR_EN_NEO_REEBA) { // Leever
EnNeoReeba* enNeoReeba = (EnNeoReeba*)actor;
Actor_DrawDamageEffects(gPlayState, actor, bodyPartsPos, bodyPartsCount,
enNeoReeba->drawEffectScale, 0.5f, enNeoReeba->drawEffectAlpha,
enNeoReeba->drawEffectType);
}
}
}
});

/*
* If we're drawing the light arrow damage effect, but we know it's from a sword beam, then quietly change the type
* to the blue lights effect.
*/
REGISTER_VB_SHOULD(VB_DRAW_DAMAGE_EFFECT, {
if (CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0)) {
Actor* actor = va_arg(args, Actor*);
if (actor != nullptr && actor->shape.face & HIT_BY_SWORD_BEAM) {
u8* type = va_arg(args, u8*);
if (*type == ACTOR_DRAW_DMGEFF_LIGHT_ORBS) {
*type = ACTOR_DRAW_DMGEFF_BLUE_LIGHT_ORBS;
}
}
}
});

/*
* If this is a sword beam collision, just hand wave it as a valid collision. This allows for sword beams to hit
* enemies in a damaging way, such as Skulltulas and Big Octos.
*/
REGISTER_VB_SHOULD(VB_CHECK_BUMPER_COLLISION, {
if (CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0)) {
ColliderInfo* toucher = va_arg(args, ColliderInfo*);
ColliderInfo* bumper = va_arg(args, ColliderInfo*);
if (toucher->toucher.dmgFlags & DMG_SWORD_BEAM) {
bumper->bumper.dmgFlags |= DMG_SWORD_BEAM;
*should = false;
}
}
});

/*
* Define a custom damage effect for sword beams for the Big Octo, which handles drawing damage effects differently
* from most enemies. We cannot easily piggyback off of the light arrows effect like we do for everybody else.
*/
GameInteractor::Instance->RegisterGameHookForID<GameInteractor::ShouldActorUpdate>(
ACTOR_EN_BIGOKUTA, [](Actor* actor, bool* result) {
if (CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0)) {
EnBigokuta* enBigOkuta = (EnBigokuta*)actor;
if (enBigOkuta->bodyCollider.base.acFlags & AC_HIT &&
enBigOkuta->bodyCollider.info.acHitInfo->toucher.dmgFlags & DMG_SWORD_BEAM) {
enBigOkuta->drawDmgEffType = ACTOR_DRAW_DMGEFF_BLUE_LIGHT_ORBS;
enBigOkuta->drawDmgEffScale = 1.2f;
enBigOkuta->drawDmgEffAlpha = 4.0f;
Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_CLEAR_TAG,
enBigOkuta->bodyCollider.info.bumper.hitPos.x,
enBigOkuta->bodyCollider.info.bumper.hitPos.y,
enBigOkuta->bodyCollider.info.bumper.hitPos.z, 0, 0, 0,
CLEAR_TAG_PARAMS(CLEAR_TAG_LARGE_LIGHT_RAYS));
}
}
});
}
5 changes: 5 additions & 0 deletions mm/2s2h/GameInteractor/GameInteractor.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ typedef enum {
VB_CHECK_HELD_ITEM_BUTTON_PRESS,
VB_MAGIC_SPIN_ATTACK_CHECK_FORM,
VB_TRANSFORM_THUNDER_MATRIX,
VB_DAMAGE_MULTIPLIER,
VB_DAMAGE_EFFECT,
VB_DRAW_DAMAGE_EFFECT,
VB_USE_NULL_FOR_DRAW_DAMAGE_EFFECTS,
VB_CHECK_BUMPER_COLLISION,
VB_PLAY_HEART_CONTAINER_GET_FANFARE,
VB_BE_HOOKSHOT_SURFACE,
VB_DEKU_GUARD_SHOW_SEARCH_BALLS,
Expand Down
4 changes: 4 additions & 0 deletions mm/src/code/z_actor.c
Original file line number Diff line number Diff line change
Expand Up @@ -5077,6 +5077,10 @@ TexturePtr sElectricSparkTextures[] = {
*/
void Actor_DrawDamageEffects(PlayState* play, Actor* actor, Vec3f bodyPartsPos[], s16 bodyPartsCount, f32 effectScale,
f32 frozenSteamScale, f32 effectAlpha, u8 type) {
if (!GameInteractor_Should(VB_DRAW_DAMAGE_EFFECT, true, actor, &type)) {
return;
}

if (effectAlpha > 0.001f) {
s32 twoTexScrollParam;
s16 bodyPartIndex;
Expand Down
16 changes: 13 additions & 3 deletions mm/src/code/z_collision_check.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "global.h"
#include "z64collision_check.h"
#include "2s2h/GameInteractor/GameInteractor.h"

typedef s32 (*ColChkResetFunc)(struct PlayState*, Collider*);
typedef void (*ColChkBloodFunc)(struct PlayState*, Collider*, Vec3f*);
Expand Down Expand Up @@ -59,8 +60,16 @@ f32 CollisionCheck_GetDamageAndEffectOnBumper(Collider* at, ColliderInfo* atInfo
dmgFlags >>= 1;
}

damage *= damageMultipliers[ac->actor->colChkInfo.damageTable->attack[i] & 0xF];
*effect = (ac->actor->colChkInfo.damageTable->attack[i] >> 4) & 0xF;
// #region 2S2H - Enhancements - Damage Multiplier and Effect
if ((GameInteractor_Should(VB_DAMAGE_MULTIPLIER, true, i, ac->actor->colChkInfo.damageTable, &damage,
damageMultipliers))) {
damage *= damageMultipliers[ac->actor->colChkInfo.damageTable->attack[i] & 0xF];
}

if ((GameInteractor_Should(VB_DAMAGE_EFFECT, true, i, ac->actor->colChkInfo.damageTable, effect, ac->actor))) {
*effect = (ac->actor->colChkInfo.damageTable->attack[i] >> 4) & 0xF;
}
// #endregion
}
return damage;
}
Expand Down Expand Up @@ -1361,7 +1370,8 @@ s32 CollisionCheck_SkipBump(ColliderInfo* info) {
* If the AT element has no dmgFlags in common with the AC element, no collision happens.
*/
s32 CollisionCheck_NoSharedFlags(ColliderInfo* toucher, ColliderInfo* bumper) {
if (!(toucher->toucher.dmgFlags & bumper->bumper.dmgFlags)) {
if (GameInteractor_Should(VB_CHECK_BUMPER_COLLISION, !(toucher->toucher.dmgFlags & bumper->bumper.dmgFlags),
toucher, bumper)) {
return 1;
}
return 0;
Expand Down
10 changes: 7 additions & 3 deletions mm/src/overlays/actors/ovl_En_Firefly/z_en_firefly.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "z_en_firefly.h"
#include "overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.h"
#include "overlays/actors/ovl_Obj_Syokudai/z_obj_syokudai.h"
#include "2s2h/GameInteractor/GameInteractor.h"

#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_UNFRIENDLY | ACTOR_FLAG_IGNORE_QUAKE | ACTOR_FLAG_4000)

Expand Down Expand Up @@ -844,9 +845,12 @@ void EnFirefly_Draw(Actor* thisx, PlayState* play) {
POLY_OPA_DISP = gfx;
}

Actor_DrawDamageEffects(play, NULL, this->bodyPartsPos, KEESE_BODYPART_MAX,
this->drawDmgEffScale * this->actor.scale.y * 200.0f, this->drawDmgEffFrozenSteamScale,
this->drawDmgEffAlpha, this->drawDmgEffType);
if (GameInteractor_Should(VB_USE_NULL_FOR_DRAW_DAMAGE_EFFECTS, true, this, this->bodyPartsPos,
KEESE_BODYPART_MAX)) {
Actor_DrawDamageEffects(play, NULL, this->bodyPartsPos, KEESE_BODYPART_MAX,
this->drawDmgEffScale * this->actor.scale.y * 200.0f, this->drawDmgEffFrozenSteamScale,
this->drawDmgEffAlpha, this->drawDmgEffType);
}
this->lastDrawnFrame = play->gameplayFrames;

CLOSE_DISPS(play->state.gfxCtx);
Expand Down
8 changes: 6 additions & 2 deletions mm/src/overlays/actors/ovl_En_Fz/z_en_fz.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "objects/object_fz/object_fz.h"
#include "objects/gameplay_keep/gameplay_keep.h"
#include "2s2h/Enhancements/FrameInterpolation/FrameInterpolation.h"
#include "2s2h/GameInteractor/GameInteractor.h"

#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_UNFRIENDLY | ACTOR_FLAG_10)

Expand Down Expand Up @@ -871,8 +872,11 @@ void EnFz_Draw(Actor* thisx, PlayState* play) {
bodyPartsPos[1] = this->actor.world.pos;
bodyPartsPos[0].y += 20.0f;
bodyPartsPos[1].y += 40.0f;
Actor_DrawDamageEffects(play, NULL, bodyPartsPos, ARRAY_COUNT(bodyPartsPos), this->drawDmgEffScale * 4.0f, 0.5f,
this->drawDmgEffAlpha, ACTOR_DRAW_DMGEFF_LIGHT_ORBS);
if (GameInteractor_Should(VB_USE_NULL_FOR_DRAW_DAMAGE_EFFECTS, true, this, bodyPartsPos,
ARRAY_COUNT(bodyPartsPos))) {
Actor_DrawDamageEffects(play, NULL, bodyPartsPos, ARRAY_COUNT(bodyPartsPos), this->drawDmgEffScale * 4.0f,
0.5f, this->drawDmgEffAlpha, ACTOR_DRAW_DMGEFF_LIGHT_ORBS);
}
}

CLOSE_DISPS(play->state.gfxCtx);
Expand Down
8 changes: 6 additions & 2 deletions mm/src/overlays/actors/ovl_En_Neo_Reeba/z_en_neo_reeba.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "z_en_neo_reeba.h"
#include "objects/object_rb/object_rb.h"
#include "2s2h/GameInteractor/GameInteractor.h"

#define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_UNFRIENDLY | ACTOR_FLAG_200)

Expand Down Expand Up @@ -656,8 +657,11 @@ void EnNeoReeba_DrawEffects(EnNeoReeba* this, PlayState* play) {
}

this->bodyPartsPos[EN_NEO_REEBA_BODYPART_3] = this->actor.world.pos;
Actor_DrawDamageEffects(play, NULL, this->bodyPartsPos, EN_NEO_REEBA_BODYPART_MAX, this->drawEffectScale, 0.5f,
this->drawEffectAlpha, this->drawEffectType);
if (GameInteractor_Should(VB_USE_NULL_FOR_DRAW_DAMAGE_EFFECTS, true, this, this->bodyPartsPos,
EN_NEO_REEBA_BODYPART_MAX)) {
Actor_DrawDamageEffects(play, NULL, this->bodyPartsPos, EN_NEO_REEBA_BODYPART_MAX, this->drawEffectScale,
0.5f, this->drawEffectAlpha, this->drawEffectType);
}
}
}

Expand Down

0 comments on commit b38794e

Please sign in to comment.