Skip to content

Commit

Permalink
Overhaul how sword beams work on regular enemies
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Eblo committed Jun 23, 2024
1 parent ba81d56 commit 086f364
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 21 deletions.
2 changes: 2 additions & 0 deletions mm/2s2h/Enhancements/GameInteractor/GameInteractor.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ typedef enum {
GI_VB_CLOCK_TOWER_OPENING_CONSIDER_THIS_FIRST_CYCLE,
GI_VB_DAMAGE_MULTIPLIER,
GI_VB_DAMAGE_EFFECT,
GI_VB_DRAW_DAMAGE_EFFECT,
GI_VB_CHECK_BUMPER_COLLISION,
} GIVanillaBehavior;

typedef enum {
Expand Down
106 changes: 90 additions & 16 deletions mm/2s2h/Enhancements/Masks/FierceDeityAnywhere.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
#include <libultraship/bridge.h>
#include "Enhancements/GameInteractor/GameInteractor.h"
#include "overlays/actors/ovl_En_M_Thunder/z_en_m_thunder.h"
#include "overlays/actors/ovl_En_bigokuta/z_en_bigokuta.h"
#include "overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.h"
#include "z64.h"

/*
* Flag indicating that the damage effect comes from a sword beam
*/
static bool lightArrowsAsSwordBeams = false;
/*
* Flag indicating that the collision being processed involves a sword beam. Only currently used for Big Octos.
*/
static bool isSwordBeamCollision = false;

#define HIT_BY_SWORD_BEAM 1
#define NOT_HIT_BY_SWORD_BEAM 0

void RegisterFierceDeityAnywhere() {
REGISTER_VB_SHOULD(GI_VB_DISABLE_FD_MASK, {
if (CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0)) {
Expand All @@ -13,7 +26,9 @@ void RegisterFierceDeityAnywhere() {
REGISTER_VB_SHOULD(GI_VB_DAMAGE_MULTIPLIER, {
if (CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0)) {
DamageAndEffectHookInfo damageInfo = *((DamageAndEffectHookInfo*)(opt));
// 25 is the index of the sword beam damage effect
/*
* 25 is the index of the sword beam damage effect.
*/
if (damageInfo.index == 25) {
*should = false;
/*
Expand All @@ -27,36 +42,95 @@ void RegisterFierceDeityAnywhere() {

REGISTER_VB_SHOULD(GI_VB_DAMAGE_EFFECT, {
if (CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0)) {
Player* player = GET_PLAYER(gPlayState);
DamageAndEffectHookInfo damageInfo = *((DamageAndEffectHookInfo*)(opt));
// 25 is the index of the sword beam damage effect
/*
* 25 is the index of the sword beam damage effect.
*/
if (damageInfo.index == 25) {
*should = false;
/*
* If the default effect is 0, prefer the light arrow effect.
* If the light arrow effect is also 0, prefer the basic sword effect.
* If the sword beam effect is 0, use the light arrow effect instead.
*/
u8 defaultEffect = (damageInfo.damageTable->attack[damageInfo.index] >> 4) & 0xF;
if (defaultEffect == 0) {
// 13 is the index of the light arrow damage effect
u8 lightArrowEffect = (damageInfo.damageTable->attack[13] >> 4) & 0xF;
// 9 is the index of the sword damage effect
u8 swordEffect = (damageInfo.damageTable->attack[9] >> 4) & 0xF;
(*damageInfo.effect) = lightArrowEffect == 0 ? swordEffect : lightArrowEffect;
(*damageInfo.effect) = (damageInfo.damageTable->attack[13] >> 4) & 0xF;
} else {
(*damageInfo.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.
*/
damageInfo.actor->shape.face = HIT_BY_SWORD_BEAM;
} else if (damageInfo.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.
*/
damageInfo.actor->shape.face = NOT_HIT_BY_SWORD_BEAM;
}
}
});

/*
* Use a hook before an actor draws to confirm that the effect being drawn is the sword beam effect. This extra
* flag is necessary because some actors call Actor_DrawDamageEffects() with a null actor (e.g. leevers), so we
* cannot just check the same flag as before in the draw damage effect hook.
*/
GameInteractor::Instance->RegisterGameHook<GameInteractor::ShouldActorDraw>(
[](Actor* actor, bool* result) { lightArrowsAsSwordBeams = actor->shape.face & HIT_BY_SWORD_BEAM; });

/*
* 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(GI_VB_DRAW_DAMAGE_EFFECT, {
if (CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0)) {
if (lightArrowsAsSwordBeams) {
u8* type = (u8*)opt;
if (*type == ACTOR_DRAW_DMGEFF_LIGHT_ORBS) {
*type = ACTOR_DRAW_DMGEFF_BLUE_LIGHT_ORBS;
}
}
}
});

GameInteractor::Instance->RegisterGameHookForID<GameInteractor::OnActorInit>(ACTOR_EN_M_THUNDER, [](Actor* actor) {
/*
* 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. The isSwordBeamCollision flag exists for the sole
* purpose of having the blue light orbs effect on Big Octos; that actor handles damage effects differently from
* most other enemies.
*/
REGISTER_VB_SHOULD(GI_VB_CHECK_BUMPER_COLLISION, {
if (CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0)) {
/*
* This additional flag makes sword beams effective against enemies that have special collision processing,
* such as Big Octos and Skulltulas.
*/
EnMThunder* enMThunder = (EnMThunder*)actor;
enMThunder->collider.info.toucher.dmgFlags |= DMG_LIGHT_ARROW;
ColliderInfo* bumper = (ColliderInfo*)opt;
*should = isSwordBeamCollision = bumper->bumper.dmgFlags & DMG_SWORD_BEAM;
}
});

/*
* 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 (isSwordBeamCollision && enBigOkuta->bodyCollider.base.acFlags & AC_HIT) {
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));
isSwordBeamCollision = false;
}
}
});
}
5 changes: 3 additions & 2 deletions mm/include/z64collision_check.h
Original file line number Diff line number Diff line change
Expand Up @@ -462,15 +462,16 @@ typedef struct CollisionCheckInfo {
/* 0x1B */ u8 acHitEffect;
} CollisionCheckInfo; // size = 0x1C

/* Convenience struct used for passing damage effect information into hooks */
// #region 2S2H - Enhancements - Convenience struct used for passing damage effect information into hooks
typedef struct DamageAndEffectHookInfo {
DamageTable* damageTable;
u8 index;
f32* damage;
u32* effect;
f32* multipliers;
struct Actor* actor;
} DamageAndEffectHookInfo;

// #endregion

DamageTable* DamageTable_Get(s32 index);
void DamageTable_Clear(DamageTable* damageTable);
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 @@ -5034,6 +5034,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(GI_VB_DRAW_DAMAGE_EFFECT, true, &type)) {
return;
}

if (effectAlpha > 0.001f) {
s32 twoTexScrollParam;
s16 bodyPartIndex;
Expand Down
9 changes: 7 additions & 2 deletions mm/src/code/z_collision_check.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,15 @@ f32 CollisionCheck_GetDamageAndEffectOnBumper(Collider* at, ColliderInfo* atInfo
*effect = 0;
damage = CollisionCheck_GetToucherDamage(at, atInfo, ac, acInfo);

// #region 2S2H - Enhancements - Damage Multiplier and Effect
// Store damage information for hook purposes
DamageAndEffectHookInfo damageAndEffectInfo;
damageAndEffectInfo.damageTable = ac->actor->colChkInfo.damageTable;
damageAndEffectInfo.damage = &damage;
damageAndEffectInfo.multipliers = damageMultipliers;
damageAndEffectInfo.effect = effect;
damageAndEffectInfo.actor = ac->actor;
// #endregion

if (ac->actor->colChkInfo.damageTable != NULL) {
dmgFlags = atInfo->toucher.dmgFlags;
Expand Down Expand Up @@ -1377,8 +1380,10 @@ 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)) {
return 1;
if (GameInteractor_Should(GI_VB_CHECK_BUMPER_COLLISION, true, bumper)) {
if (!(toucher->toucher.dmgFlags & bumper->bumper.dmgFlags)) {
return 1;
}
}
return 0;
}
Expand Down
1 change: 0 additions & 1 deletion mm/src/overlays/actors/ovl_En_M_Thunder/z_en_m_thunder.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
#include "z64rumble.h"
#include "overlays/actors/ovl_Eff_Dust/z_eff_dust.h"
#include "objects/gameplay_keep/gameplay_keep.h"
#include "Enhancements/GameInteractor/GameInteractor.h"

#define FLAGS (ACTOR_FLAG_10)

Expand Down

0 comments on commit 086f364

Please sign in to comment.