contact-hud: the HUD reads the player's own ContactOf and book; TargetedBy and the spotted blink go
Selection is nothing the selected ship can sense: TargetedBy, TargetedByCount, their subscriptions and the music read of them are deleted. Indicator fills, the target panel, the compass, music and the enemy schematic rows now read CurrentEntity's ContactOf / RevealsOf / RevealedSubjects (new, the book's keys); the schematic rows are built by one builder from the book and rebuilt when the revealed set changes. Impact hull effect and projectile hit effects at another ship's position draw only through ActionGameManager.DrawsAt (the player's own ship or a Tracked contact). Builds untested in Unity at this commit; tests NothingReadsSelection and RevealedSubjectsListsTheBook. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
6292d82615
commit
4708a71a67
10 changed files with 149 additions and 57 deletions
|
|
@ -113,8 +113,11 @@ public class ActionGameManager : MonoBehaviour
|
|||
//public string StarterShipTemplate = "Longinus";
|
||||
// The run's credits: seeded from the scenario stage on a new game and from SavedGame on Continue, written by SaveRun.
|
||||
[NonSerialized] public int Credits;
|
||||
public float TargetSpottedBlinkFrequency = 20;
|
||||
public float TargetSpottedBlinkOffset = -.25f;
|
||||
// Presentation scale: the player's own Visibility at which the target panel's VisibilityToTargetFill reads full.
|
||||
public float OwnSignatureFullScale = 10;
|
||||
// The music last asked for: Update decides it each frame from what the player's own instruments hold and calls
|
||||
// PlayMusic only when it changes.
|
||||
private MusicType? _music;
|
||||
|
||||
[Header("Postprocessing")]
|
||||
public float DeathPostTransitionTime;
|
||||
|
|
@ -733,6 +736,7 @@ public class ActionGameManager : MonoBehaviour
|
|||
Zone = galaxyZone.Contents;
|
||||
_zoneEntered = true; // the clock forgets its lead between frames, never inside a step
|
||||
PlayMusic(MusicType.Overworld);
|
||||
_music = MusicType.Overworld;
|
||||
|
||||
Zone.Log = s => Debug.Log($"Zone: {s}");
|
||||
|
||||
|
|
@ -1065,11 +1069,6 @@ public class ActionGameManager : MonoBehaviour
|
|||
foreach (var group in _articulationGroups)
|
||||
group.crosshair.gameObject.SetActive(true);
|
||||
|
||||
_shipSubscriptions.Add(CurrentEntity.TargetedByCount.Subscribe(count =>
|
||||
{
|
||||
PlayMusic(count > 0 ? MusicType.Combat : MusicType.Overworld);
|
||||
}));
|
||||
|
||||
_shipSubscriptions.Add(CurrentEntity.Target.Subscribe(target =>
|
||||
{
|
||||
// Clear previous subscriptions related to currently targeted enemy
|
||||
|
|
@ -1264,6 +1263,42 @@ public class ActionGameManager : MonoBehaviour
|
|||
// TODO: SFX: Music
|
||||
}
|
||||
|
||||
// What the player's own instruments say about combat, nothing a hostile knows: a hostile tracked, a paint on the
|
||||
// player, or a remembered hostile the player still has a bearing on (a reveal younger than BearingMemory).
|
||||
private void UpdateMusic()
|
||||
{
|
||||
var combat = CurrentEntity.VisibleEnemies.Count > 0 || CurrentEntity.IncomingPaints.Count > 0;
|
||||
if (!combat)
|
||||
foreach (var subject in CurrentEntity.RevealedSubjects)
|
||||
if (CurrentEntity.IsHostileTo(subject) && CurrentEntity.ContactOf(subject).Detected)
|
||||
{
|
||||
combat = true;
|
||||
break;
|
||||
}
|
||||
var type = combat ? MusicType.Combat : MusicType.Overworld;
|
||||
if (_music == type) return;
|
||||
_music = type;
|
||||
PlayMusic(type);
|
||||
}
|
||||
|
||||
// An indicator's fill is how far the player's own sensors have resolved the ship from a track toward identifying
|
||||
// it (ContactOf.Info over the detection-to-gear span): nothing of what the ship sees of the player.
|
||||
private void PlaceFill(VisibleTargetIndicator indicator, Entity subject)
|
||||
{
|
||||
indicator.Fill.enabled = subject.Active;
|
||||
if (!subject.Active) return;
|
||||
var settings = Settings.GameplaySettings;
|
||||
indicator.Fill.fillAmount = saturate(unlerp(settings.TargetDetectionInfoThreshold, settings.TargetGearInfoThreshold,
|
||||
CurrentEntity.ContactOf(subject).Info));
|
||||
}
|
||||
|
||||
// Whether an effect drawn at `subject`'s true position may be drawn: the player's own ship, or a ship its
|
||||
// instruments track. The one seam for impact and hit effects (follow-up presentation-gated-on-tracked); a bearing
|
||||
// alone (Detected without Tracked) places nothing there. No player ship, nothing to gate on.
|
||||
public static bool DrawsAt(Entity subject) =>
|
||||
Instance == null || Instance.CurrentEntity == null || subject == null || subject == Instance.CurrentEntity ||
|
||||
Instance.CurrentEntity.ContactOf(subject).Tracked;
|
||||
|
||||
void Update()
|
||||
{
|
||||
if(!_paused)
|
||||
|
|
@ -1278,29 +1313,13 @@ public class ActionGameManager : MonoBehaviour
|
|||
{
|
||||
indicator.Value.gameObject.SetActive(indicator.Key!=CurrentEntity.Target.Value.Entity);
|
||||
indicator.Value.Place.Target = indicator.Key.Position.ToUnity();
|
||||
if (!indicator.Key.Active)
|
||||
indicator.Value.Fill.enabled = false;
|
||||
else
|
||||
{
|
||||
indicator.Value.Fill.fillAmount =
|
||||
saturate(indicator.Key.EntityInfoGathered[CurrentEntity] / Settings.GameplaySettings.TargetDetectionInfoThreshold);
|
||||
indicator.Value.Fill.enabled =
|
||||
!(indicator.Key.EntityInfoGathered[CurrentEntity] > Settings.GameplaySettings.TargetDetectionInfoThreshold) ||
|
||||
sin(TargetSpottedBlinkFrequency * Time.time) + TargetSpottedBlinkOffset > 0;
|
||||
}
|
||||
PlaceFill(indicator.Value, indicator.Key);
|
||||
}
|
||||
foreach (var indicator in _visibleFriendlyIndicators)
|
||||
{
|
||||
indicator.Value.gameObject.SetActive(indicator.Key!=CurrentEntity.Target.Value.Entity);
|
||||
indicator.Value.Place.Target = indicator.Key.Position.ToUnity();
|
||||
if (!indicator.Key.Active)
|
||||
indicator.Value.Fill.enabled = false;
|
||||
else
|
||||
{
|
||||
indicator.Value.Fill.enabled = true;
|
||||
indicator.Value.Fill.fillAmount =
|
||||
saturate(indicator.Key.EntityInfoGathered[CurrentEntity] / Settings.GameplaySettings.TargetDetectionInfoThreshold);
|
||||
}
|
||||
PlaceFill(indicator.Value, indicator.Key);
|
||||
}
|
||||
var look = Input.Player.Look.ReadValue<Vector2>();
|
||||
var sensitivity = PlayerSettings.InputSettings.Sensitivity;
|
||||
|
|
@ -1321,13 +1340,14 @@ public class ActionGameManager : MonoBehaviour
|
|||
ship.Turn = _helm.Demand(ship, _viewDirection.xz, Input.Player.Turn.ReadValue<float>());
|
||||
}
|
||||
|
||||
UpdateMusic();
|
||||
UpdateFireControlDebug(CurrentEntity.Target.Value);
|
||||
var target = CurrentEntity.Target.Value.Entity;
|
||||
if (target != null)
|
||||
{
|
||||
var threshold = Settings.GameplaySettings.TargetDetectionInfoThreshold;
|
||||
TargetVisibilityFill.fillAmount = lerp(.25f, .75f, (CurrentEntity.EntityInfoGathered[target] - threshold)/(1-threshold));
|
||||
VisibilityToTargetFill.fillAmount = lerp(.25f, .75f, target.EntityInfoGathered[CurrentEntity] / threshold);
|
||||
TargetVisibilityFill.fillAmount = lerp(.25f, .75f, (CurrentEntity.ContactOf(target).Info - threshold)/(1-threshold));
|
||||
VisibilityToTargetFill.fillAmount = lerp(.25f, .75f, saturate(CurrentEntity.Visibility / OwnSignatureFullScale));
|
||||
TargetHitpointsFill.fillAmount = lerp(.25f, .75f, target.Hull.Durability / target.EquippedHull.Data.Durability);
|
||||
TargetShieldsFill.fillAmount = target.Shield == null ? 0 : lerp(.25f, .75f, target.Shield.Progress);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using UnityEngine;
|
|||
// How one entity's landed impacts look and feel: the hull reaction at the drawn impact point, and the player's camera
|
||||
// kick. The simulation publishes each impact once on the entity's Capabilities (FireControl); Feedback.Cue decides how
|
||||
// hard it reads and whether the camera feels it, DrawAhead.Carried decides where it is drawn; this decides nothing.
|
||||
// The hull effect is drawn only where the player's instruments place the ship (ActionGameManager.DrawsAt).
|
||||
// The shield ripple is ShieldManager's. Everything here is display-only and runs in real time.
|
||||
public class ImpactPresenter : MonoBehaviour, IAbsorbPresenter
|
||||
{
|
||||
|
|
@ -23,7 +24,7 @@ public class ImpactPresenter : MonoBehaviour, IAbsorbPresenter
|
|||
ActionGameManager.PlayerSettings.GraphicsSettings.ScreenShake, feedback.IntensityScale);
|
||||
var point = DrawAhead.Carried(Entity, cue.Position, ActionGameManager.Clock.Lead).ToUnity();
|
||||
|
||||
if (e.Layer == AbsorbLayer.Hull && feedback.HullEffect != null)
|
||||
if (e.Layer == AbsorbLayer.Hull && feedback.HullEffect != null && ActionGameManager.DrawsAt(Entity))
|
||||
{
|
||||
_live.RemoveAll(live => live == null || !live.gameObject.activeSelf);
|
||||
var effect = feedback.HullEffect.Instantiate<Prototype>();
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ public class GuidedProjectile : MonoBehaviour
|
|||
// The resolution. A round that detonated (a hit, or a burst) bursts now, at the simulation's burst point when
|
||||
// it has one; a round the simulation resolved without a detonation (a contact or delayed round that missed,
|
||||
// one whose target is gone) just falls away, with no explosion to show.
|
||||
public void Resolve(bool detonated, Vector3? point)
|
||||
public void Resolve(bool detonated, Vector3? point, bool showHitEffect)
|
||||
{
|
||||
// A round that already faded out (overshot, out of flight) still shows the simulation's detonation: only a
|
||||
// round that has been killed is done. The fade is stopped so it cannot Kill the round a second time.
|
||||
|
|
@ -102,7 +102,7 @@ public class GuidedProjectile : MonoBehaviour
|
|||
// short of it), and is skipped when the round never knew where its target was.
|
||||
Vector3? effectAt = point ?? (TargetPosition != null ? TargetPosition() : Target ? Target.position : (Vector3?) null);
|
||||
if (point.HasValue) transform.position = point.Value;
|
||||
if (HitEffect != null && effectAt.HasValue)
|
||||
if (HitEffect != null && showHitEffect && effectAt.HasValue)
|
||||
{
|
||||
var ht = HitEffect.Instantiate<Transform>();
|
||||
ht.position = effectAt.Value;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ public class GuidedProjectileManager : InstantWeaponEffectManager
|
|||
var bindings = new CompositeDisposable();
|
||||
zone.ShotCommitted.Where(o => o.ShotId == shotId && o.HasBurstPoint).Subscribe(o => p.BurstAt(At(o))).AddTo(bindings);
|
||||
zone.ShotResolved.Where(o => o.ShotId == shotId)
|
||||
.Subscribe(o => p.Resolve(o.Result == ShotResult.Hit || o.Result == ShotResult.Burst, o.HasBurstPoint ? At(o) : (Vector3?) null))
|
||||
.Subscribe(o => p.Resolve(o.Result == ShotResult.Hit || o.Result == ShotResult.Burst, o.HasBurstPoint ? At(o) : (Vector3?) null,
|
||||
ActionGameManager.DrawsAt(o.Target)))
|
||||
.AddTo(bindings);
|
||||
// The binding outlives a fade-out until the shot's own arrival (the moment the simulation resolves it), plus a
|
||||
// second of margin; a shot no longer pending has nothing left to resolve.
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ public class Projectile : MonoBehaviour
|
|||
_known = outcome;
|
||||
if (outcome.Result == ShotResult.Miss) return;
|
||||
Draw();
|
||||
if (HitEffect != null) HitEffect.Instantiate<Transform>().position = transform.position;
|
||||
if (HitEffect != null && ActionGameManager.DrawsAt(outcome.Target)) HitEffect.Instantiate<Transform>().position = transform.position;
|
||||
Finish();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue