diff --git a/soh/soh/Enhancements/ExtraModes/IvanCoop.cpp b/soh/soh/Enhancements/ExtraModes/IvanCoop.cpp index 2acd60a521..f33fbe6ffd 100644 --- a/soh/soh/Enhancements/ExtraModes/IvanCoop.cpp +++ b/soh/soh/Enhancements/ExtraModes/IvanCoop.cpp @@ -36,6 +36,67 @@ static void KillIvan() { Actor_Kill(ivan); } +// #region Patch distance checks (allows Ivan to break pots and crates while far from Link) + +static bool ShouldPatchDist(s16 actorId) { + switch (actorId) { + // AC is enabled when player is nearby: + case ACTOR_BG_BOMBWALL: + case ACTOR_BG_SPOT08_BAKUDANKABE: + case ACTOR_OBJ_KIBAKO2: // Note: Checks for explosions regardless of distance + return true; + // OC is enabled when player is nearby: + case ACTOR_EN_ICE_HONO: + case ACTOR_OBJ_HANA: + return true; + // AC/OC are enabled when player is nearby: + case ACTOR_EN_ISHI: + case ACTOR_EN_KUSA: + case ACTOR_EN_WOOD02: + case ACTOR_OBJ_BOMBIWA: // Note: Checks for explosions regardless of distance + case ACTOR_OBJ_HAMISHI: + case ACTOR_OBJ_KIBAKO: + case ACTOR_OBJ_TSUBO: + return true; + // Checks for explosions if player is nearby: + case ACTOR_BG_SPOT17_BAKUDANKABE: + return true; + } + return false; +} + +static f32 ClampDist(f32 distance, s16 actorId) { + switch (actorId) { + // Avoid offering bottle capture + case ACTOR_EN_ICE_HONO: + return fmaxf(distance, 60.0f); + // Avoid offering carry + case ACTOR_EN_ISHI: + return fmaxf(distance, 90.0f); + case ACTOR_EN_KUSA: + case ACTOR_OBJ_KIBAKO: + case ACTOR_OBJ_TSUBO: + return fmaxf(distance, 100.0f); + } + return distance; +} + +static void PatchDistIfNeeded(Actor* actor) { + if (!ShouldPatchDist(actor->id)) + return; + + Actor* ivan = Actor_Find(&gPlayState->actorCtx, gEnPartnerId, ACTORCAT_ITEMACTION); + if (ivan == nullptr) + return; + + f32 ivanDist = Actor_WorldDistXZToActor(actor, ivan); + ivanDist = ClampDist(ivanDist, actor->id); + if (ivanDist < actor->xzDistToPlayer) + actor->xzDistToPlayer = ivanDist; +} + +// #endregion + static void RegisterIvanCoop() { if (CVAR_VALUE) SpawnIvan(); @@ -43,6 +104,11 @@ static void RegisterIvanCoop() { KillIvan(); COND_ID_HOOK(OnActorSpawn, ACTOR_PLAYER, CVAR_VALUE, [](void*) { SpawnIvan(); }); + + COND_HOOK(ShouldActorUpdate, CVAR_VALUE, [](void* actorRef, bool*) { + Actor* actor = static_cast(actorRef); + PatchDistIfNeeded(actor); + }); } static RegisterShipInitFunc initFunc(RegisterIvanCoop, { CVAR_NAME });