125 lines
3.6 KiB
C#
125 lines
3.6 KiB
C#
using NeoModLoader.services;
|
|
using UnityEngine;
|
|
|
|
namespace IdleSpectator;
|
|
|
|
public static class CameraDirector
|
|
{
|
|
public static void Watch(InterestEvent interest)
|
|
{
|
|
if (interest == null || !interest.HasValidPosition)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!Config.game_loaded || World.world == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
string tip = FormatWatchTip(interest);
|
|
if (ModSettings.ShowWatchReasons)
|
|
{
|
|
WorldTip.showNowTop(tip, pTranslate: false);
|
|
}
|
|
|
|
LogService.LogInfo($"[IdleSpectator] {tip}");
|
|
|
|
Actor follow = interest.HasFollowUnit
|
|
? interest.FollowUnit
|
|
: null;
|
|
|
|
// Never attach a random nearby unit to a "new species" tip - that caused ghost focuses
|
|
// (e.g. tip says crab, camera follows a cat).
|
|
if (follow == null && !string.IsNullOrEmpty(interest.AssetId) && interest.AssetId != "live_battle"
|
|
&& interest.AssetId != "scored_unit" && interest.Label != null && interest.Label.StartsWith("New species:"))
|
|
{
|
|
follow = WorldActivityScanner.FindNearestAliveUnit(interest.Position, 24f, interest.AssetId);
|
|
}
|
|
else if (follow == null)
|
|
{
|
|
follow = WorldActivityScanner.FindNearestAliveUnit(interest.Position, 24f);
|
|
}
|
|
|
|
if (follow != null && follow.isAlive())
|
|
{
|
|
// If this is a species event, refuse mismatched units.
|
|
if (interest.Label != null && interest.Label.StartsWith("New species:")
|
|
&& !string.IsNullOrEmpty(interest.AssetId)
|
|
&& follow.asset != null && follow.asset.id != interest.AssetId)
|
|
{
|
|
Actor matched = WorldActivityScanner.FindNearestAliveUnit(interest.Position, 24f, interest.AssetId);
|
|
if (matched == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
follow = matched;
|
|
}
|
|
|
|
RetargetFollow(follow);
|
|
return;
|
|
}
|
|
|
|
// No unit available: pan only if we are not already following someone.
|
|
// Never clear an existing focus unit here - that flashes the power bar.
|
|
if (!MoveCamera.hasFocusUnit())
|
|
{
|
|
PanCamera(interest.Position);
|
|
}
|
|
}
|
|
|
|
public static string FormatWatchTip(InterestEvent interest)
|
|
{
|
|
string tier = interest.Tier.ToString();
|
|
string reason = string.IsNullOrEmpty(interest.Label)
|
|
? "something interesting"
|
|
: interest.Label;
|
|
return $"Watching [{tier}]: {reason}";
|
|
}
|
|
|
|
public static void ClearFollow()
|
|
{
|
|
MoveCamera.clearFocusUnitOnly();
|
|
}
|
|
|
|
private static void RetargetFollow(Actor actor)
|
|
{
|
|
if (MoveCamera.isCameraFollowingUnit(actor))
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool firstFocus = !MoveCamera.hasFocusUnit();
|
|
MoveCamera.setFocusUnit(actor);
|
|
|
|
MoveCamera cam = MoveCamera.instance;
|
|
if (cam == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Same as vanilla killer handoff: reset ease so the camera lerps to the new unit.
|
|
cam._focus_timer = 0f;
|
|
if (firstFocus)
|
|
{
|
|
cam._target_zoom = 15f;
|
|
cam._focus_zoom = 15f;
|
|
}
|
|
}
|
|
|
|
private static void PanCamera(Vector3 pos)
|
|
{
|
|
MoveCamera cam = MoveCamera.instance;
|
|
if (cam == null)
|
|
{
|
|
World.world.locatePosition(pos);
|
|
return;
|
|
}
|
|
|
|
pos.z = cam.transform.position.z;
|
|
cam.transform.position = pos;
|
|
cam._target_zoom = 15f;
|
|
cam._focus_zoom = 15f;
|
|
}
|
|
}
|