A game entity class for a "Smile" block that toggles visibility on impacts and phases. It handles invisible/visible state, alpha fading over time, brief reveal logic for phase 2, plays sound effects when changing visibility, and responds to phase changes, player death and block death.
namespace BlockParty;
/// <summary>
/// Smile block — fades invisible on impact. Phase 1 toggles visible/invisible each impact.
/// Phase 2 stays invisible but briefly fades back in on each impact, then goes invisible
/// again during the eye-cycle pause so it's always invisible before it moves off. While
/// invisible it keeps moving and is still dangerous; its speed-lines still show as a tell.
/// Port of the original BlockSmile (the staggered greyscale fade is realised as an alpha fade).
/// </summary>
public sealed class BlockSmile : Block
{
const float FADE_TIME = 0.2f;
bool _invisible;
bool _briefReveal;
float _alpha = 1f, _alphaTarget = 1f;
public override void Impact( Block otherBlock )
{
ImpactEffects( otherBlock );
if ( IsDead ) return;
if ( Phase == 1 )
{
if ( !_invisible ) GoInvisible(); else GoVisible();
}
else if ( Phase == 2 )
{
if ( !_invisible ) GoInvisible();
else { GoVisible(); _briefReveal = true; } // re-hidden in OnEyesClosed (before it moves)
}
CloseEyesAndPickNewDirection();
}
// Eyes have shut and the new direction is chosen — re-hide now so the phase-2 "peek" is
// always over before the block opens its eyes and moves off.
protected override void OnEyesClosed()
{
if ( _briefReveal )
{
_briefReveal = false;
GoInvisible();
}
}
public override void Tick( float dt )
{
base.Tick( dt );
if ( _alpha != _alphaTarget )
{
float step = dt / FADE_TIME;
_alpha = _alpha < _alphaTarget ? MathF.Min( _alphaTarget, _alpha + step ) : MathF.Max( _alphaTarget, _alpha - step );
SetLayersAlpha( _alpha );
}
}
void GoInvisible()
{
if ( _invisible ) return;
_invisible = true;
_alphaTarget = 0f;
Audio.PlaySfx( SfxType.TurnInvisible, Position );
}
void GoVisible()
{
if ( !_invisible ) return;
_invisible = false;
_alphaTarget = 1f;
Audio.PlaySfx( SfxType.TurnVisible, Position );
}
protected override void NextPhase()
{
base.NextPhase();
_briefReveal = false;
if ( _invisible ) GoVisible();
}
public override void PlayerHasDied()
{
_briefReveal = false;
GoVisible();
}
public override void Die()
{
base.Die();
_briefReveal = false;
GoVisible();
}
}