Critters.cs
namespace NiuLai;
public sealed class LarkDrift : Component
{
float _t;
protected override void OnUpdate()
{
if ( GameFlow.BlocksWorld )
return;
_t += Time.Delta;
var player = CalfPlayer.Instance;
if ( !player.IsValid() )
return;
var radius = GameState.InVoid ? 40f : GameState.Beat == Beat.Wake ? 70f : 95f;
var height = GameState.InVoid ? 48f : 90f;
WorldPosition = player.WorldPosition + new Vector3( MathF.Cos( _t * 0.7f ) * radius, MathF.Sin( _t * 0.7f ) * radius, height + MathF.Sin( _t * 2.2f ) * 10f );
WorldRotation = Rotation.FromYaw( _t * 50f ) * Rotation.FromRoll( MathF.Sin( _t * 6f ) * 18f );
}
}
public sealed class SnakeSlink : Component
{
Vector3 _origin;
float _t;
protected override void OnStart() => _origin = WorldPosition;
protected override void OnUpdate()
{
if ( GameFlow.BlocksWorld )
return;
_t += Time.Delta;
WorldPosition = _origin + new Vector3( MathF.Sin( _t ) * 48f, MathF.Cos( _t * 0.4f ) * 18f, 6f + MathF.Abs( MathF.Sin( _t * 8f ) ) * 3f );
WorldRotation = Rotation.FromYaw( _t * 40f );
var n = 0;
foreach ( var child in GameObject.Children )
{
if ( !child.IsValid() )
continue;
child.LocalRotation = Rotation.FromYaw( MathF.Sin( _t * 6f + n * 0.7f ) * 8f );
n++;
}
var player = CalfPlayer.Instance;
if ( player.IsValid() && GameState.Beat == Beat.Snake && !GameState.Bitten )
{
if ( (player.WorldPosition - WorldPosition).Length < 55f )
GameState.GetBitten();
}
}
}
public sealed class BaoLaFollow : Component
{
public static GameObject Body { get; private set; }
float _gym;
protected override void OnStart() => Body = GameObject;
protected override void OnDestroy()
{
if ( Body == GameObject )
Body = null;
}
protected override void OnUpdate()
{
if ( GameFlow.BlocksWorld && !FilmCut.Playing )
return;
if ( GameState.WolvesHunt )
{
_gym += Time.Delta;
WorldRotation = Rotation.FromYaw( _gym * 420f ) * Rotation.FromPitch( 35f );
WorldPosition += Vector3.Up * MathF.Sin( _gym * 8f ) * 2f;
}
if ( GameFlow.BlocksWorld || !GameState.BaoLaFollows )
return;
var player = CalfPlayer.Instance;
if ( !player.IsValid() )
return;
var dest = player.WorldPosition - player.WorldRotation.Forward * 70f;
dest.z = WorldPosition.z;
var delta = dest - WorldPosition;
if ( delta.Length > 20f )
{
WorldPosition += delta.Normal * 160f * Time.Delta;
WorldRotation = Rotation.LookAt( delta.WithZ( 0 ), Vector3.Up );
}
}
}
public sealed class WolfHunt : Component
{
[Property] public Vector3 Home { get; set; }
float _cooldown;
bool _howled;
protected override void OnUpdate()
{
if ( GameFlow.BlocksWorld )
return;
_cooldown -= Time.Delta;
var player = CalfPlayer.Instance;
if ( !player.IsValid() )
return;
var hunt = GameState.WolvesHunt;
if ( hunt && !_howled )
{
_howled = true;
GameAudio.PlayWolvesAt( WorldPosition );
}
Vector3 target;
if ( hunt && GameState.BaoLaFollows && BaoLaFollow.Body.IsValid() )
{
target = BaoLaFollow.Body.WorldPosition;
}
else if ( hunt )
{
target = player.WorldPosition;
}
else
{
target = Home;
var rock = new Vector3( 1140, 40, WorldPosition.z );
WorldRotation = Rotation.LookAt( (rock - WorldPosition).WithZ( 0 ), Vector3.Up );
}
var delta = (target.WithZ( WorldPosition.z ) - WorldPosition);
if ( delta.Length > 10f )
{
WorldPosition += delta.Normal * (hunt ? 200f : 70f) * Time.Delta;
WorldRotation = Rotation.LookAt( delta.WithZ( 0 ), Vector3.Up );
}
if ( hunt && delta.Length < 55f && _cooldown <= 0f )
{
_cooldown = 3.2f;
if ( GameState.Beat == Beat.Wolves )
GameState.FirstWolfWave();
}
}
}
public sealed class CartGrow : Component
{
Vector3 _start;
float _z;
protected override void OnStart()
{
_start = LocalScale;
_z = WorldPosition.z;
}
protected override void OnUpdate()
{
if ( GameState.Beat != Beat.Cart )
return;
var t = Math.Clamp( GameState.CartMoos / 3f + (GameState.PlayTime % 20f) / 40f, 0f, 1.4f );
LocalScale = _start * (1f + t * 4.2f );
var p = WorldPosition;
p.z = _z + t * 36f;
WorldPosition = p;
}
}
public sealed class MomActor : Component
{
float _sink;
protected override void OnUpdate()
{
if ( !GameState.MomGone )
return;
_sink += Time.Delta;
WorldPosition += Vector3.Down * 28f * Time.Delta;
if ( _sink > 3.5f )
GameObject.Enabled = false;
}
}
public sealed class HerdMarch : Component
{
[Property] public float Speed { get; set; } = 55f;
protected override void OnUpdate()
{
if ( GameFlow.BlocksWorld )
return;
if ( GameState.Beat == Beat.Migrate && !GameState.Talking )
{
WorldPosition += Vector3.Forward * (Speed * 0.15f) * Time.Delta;
return;
}
if ( !GameState.SongWalk )
return;
WorldPosition += Vector3.Forward * Speed * Time.Delta;
WorldRotation = Rotation.LookAt( Vector3.Forward, Vector3.Up );
}
}
public sealed class GrassTuft : Component
{
[Property] public float HideFor { get; set; } = 10f;
Vector3 _home;
float _cd = -1f;
bool _out;
protected override void OnStart() => _home = WorldPosition;
protected override void OnUpdate()
{
if ( !_out )
return;
_cd -= Time.Delta;
if ( _cd > 0f )
return;
_out = false;
WorldPosition = _home;
var hs = Components.Get<Hotspot>();
if ( hs.IsValid() )
hs.Enabled = true;
if ( !GameState.Talking && !FilmCut.Playing && Random.Shared.Float() < 0.55f )
Chatter.Say( "草 / Grass", Chatter.Pick( Lines.GrassBack ), 2.2f );
}
public void Eat()
{
if ( _out )
return;
GameState.EatGrass();
GameAudio.PlayClip( GameAudio.SlowDown, 0.75f );
_out = true;
_cd = HideFor + Random.Shared.Float() * 5f;
WorldPosition = _home + Vector3.Down * 80f;
var hs = Components.Get<Hotspot>();
if ( hs.IsValid() )
hs.Enabled = false;
}
}
public sealed class HerdGossip : Component
{
float _cool;
protected override void OnUpdate()
{
if ( GameFlow.BlocksWorld || GameState.Talking )
return;
_cool -= Time.Delta;
var p = CalfPlayer.Instance;
if ( !p.IsValid() || _cool > 0f )
return;
if ( (p.WorldPosition - WorldPosition).Length > 70f )
return;
_cool = 7f + Random.Shared.Float() * 6f;
Chatter.Say( "牛群 / Herd", Chatter.Pick( Lines.Gossip ), 2.6f );
}
}
public sealed class FlyBeacon : Component
{
float _t;
protected override void OnUpdate()
{
_t += Time.Delta;
var on = GameState.Beat >= Beat.Wake;
var s = on ? 0.22f + MathF.Abs( MathF.Sin( _t * 3f ) ) * 0.12f : 0.06f;
LocalScale = new Vector3( s, s, s );
}
}
public sealed class VoidDrift : Component
{
[Property] public int Index { get; set; }
Vector3 _home;
float _t;
protected override void OnStart() => _home = WorldPosition;
protected override void OnUpdate()
{
if ( !GameState.InVoid )
return;
_t += Time.Delta;
WorldPosition = _home + new Vector3( MathF.Sin( _t + Index ) * 12f, MathF.Cos( _t * 0.6f ) * 10f, MathF.Sin( _t * 1.4f ) * 8f );
if ( Random.Shared.Float() < 0.004f )
Chatter.Say( "黑 / Black", Chatter.Pick( Lines.VoidBits ), 2.1f );
}
}
public sealed class BobIdle : Component
{
[Property] public float Height { get; set; } = 4f;
[Property] public float Rate { get; set; } = 1.4f;
Vector3 _origin;
float _t;
protected override void OnStart() => _origin = WorldPosition;
protected override void OnUpdate()
{
_t += Time.Delta * Rate;
WorldPosition = _origin.WithZ( _origin.z + MathF.Sin( _t ) * Height );
}
}