ChristmassyGameLogic.cs
using Sandbox;
using System;
public sealed class ChristmassyGameLogic : Component
{
/// <summary>
/// The map we're going to rotate
/// </summary>
[Property]
[Category( "Map" )]
public GameObject Map { get; set; }
[Property]
[Category( "Map" )]
public float MapRadius { get; set; } = 2040f;
[Property]
[Category( "Map" )]
public float RoadWidth { get; set; } = 200f;
[Property]
[Category( "Map" )]
public int CottagesSpawned { get; set; } = 20;
[Property]
[Category( "Map" )]
public int DecorationsSpawned { get; set; } = 40;
[Property]
[Category( "Map" )]
public int LampsSpawned { get; set; } = 40;
[Property]
[Category( "Map" )]
public int RoadObjects { get; set; } = 10;
[Property]
[Category( "Map" )]
public Collider SlipperySlope { get; set; }
[Property]
[Category( "Map" )]
public GameObject Lamp { get; set; }
[Property]
[Category( "Components" )]
public SantaUI UI { get; set; }
[Property]
[Category( "Components" )]
public SoundPointComponent WindSound { get; set; }
[Property]
[Category( "Components" )]
public SoundPointComponent MusicSound { get; set; }
/// <summary>
/// How many degrees it rotates per second
/// </summary>
[Property]
[Category( "Gameplay" )]
[Range( 0f, 360f, 1f )]
public float RotationSpeed { get; set; } = 15f;
public GameObject MapClone;
public TimeSince StartTimer;
public int Points = 0;
public bool IsPlaying = true;
public static ChristmassyGameLogic Instance { get; private set; }
public List<PrefabFile> AllRoadObjects = new();
private float _originalSpeed;
protected override void OnStart()
{
Instance = this;
_originalSpeed = RotationSpeed;
var allPresents = ResourceLibrary.GetAll<PrefabFile>()
.Where( x => x.GetMetadata( "Tags" )?.Contains( "gift" ) ?? false );
var allObstacles = ResourceLibrary.GetAll<PrefabFile>()
.Where( x => x.GetMetadata( "Tags" )?.Contains( "obstacle" ) ?? false );
if ( !allPresents.Any() )
{
Log.Error( "No presents found" );
return;
}
if ( !allObstacles.Any() )
{
Log.Error( "No obstacle found" );
return;
}
AllRoadObjects = allPresents.Concat( allObstacles ).ToList();
StartGame();
}
protected override void OnUpdate()
{
if ( !Map.IsValid() ) return;
Map.WorldRotation *= Rotation.FromPitch( -RotationSpeed * Time.Delta );
MapClone.WorldRotation *= Rotation.FromPitch( -RotationSpeed * Time.Delta );
}
private TimeUntil _updateVelocity;
protected override void OnFixedUpdate()
{
if ( !IsPlaying )
{
MusicSound.Volume = MathX.Lerp( MusicSound.Volume, 0f, Time.Delta * 3f );
WindSound.Volume = 2f;
return;
}
MusicSound.Volume = MathX.Lerp( MusicSound.Volume, 0.7f, Time.Delta * 3f );
RotationSpeed += Time.Delta * 0.2f;
WindSound.SoundOverride = true;
WindSound.Volume = MathX.Remap( RotationSpeed, 20f, 100f, 2f, 1f );
WindSound.Pitch = MathX.Remap( RotationSpeed, 20f, 100f, 0.1f, 1f );
if ( !SlipperySlope.IsValid() ) return;
if ( _updateVelocity )
{
SlipperySlope.SurfaceVelocity = Vector3.Backward * RotationSpeed * 10000f;
_updateVelocity = 1f;
}
}
private List<GameObject> _cottages = new List<GameObject>();
[Button( "Generate Cottages Debug" )]
public void GenerateCottages()
{
if ( !Map.IsValid() ) return;
ClearCottages();
var allCottages = ResourceLibrary.GetAll<PrefabFile>()
.Where( x => x.GetMetadata( "Tags" )?.Contains( "cottage" ) ?? false )
.ToList();
if ( !allCottages.Any() )
{
Log.Error( "No cottages found" );
return;
}
var angleSlice = 360f / CottagesSpawned;
for ( int i = 0; i < CottagesSpawned; i++ )
{
var side = i % 2;
var randomCottage = SceneUtility.GetPrefabScene( Game.Random.FromList( allCottages ) )
.Clone();
var randomPitch = Game.Random.Float( -angleSlice / 4f, angleSlice / 4f );
randomCottage.WorldRotation = Rotation.FromPitch( angleSlice * i + randomPitch );
var sideRotation = Rotation.FromYaw( -90f + 180f * side );
randomCottage.WorldRotation *= sideRotation;
randomCottage.WorldPosition = Map.WorldPosition + randomCottage.WorldRotation.Up * MapRadius;
var randomDistance = Game.Random.Float( 350f, 600f );
var sidePosition = (RoadWidth / 2f + randomDistance) * (side == 0 ? Vector3.Left : Vector3.Right);
randomCottage.WorldPosition += sidePosition;
randomCottage.SetParent( Map );
_cottages.Add( randomCottage );
}
}
private List<GameObject> _lamps = new List<GameObject>();
[Button( "Generate Lamps Debug" )]
public void GenerateLamps()
{
if ( !Map.IsValid() ) return;
ClearLamps();
if ( !Lamp.IsValid() )
{
Log.Error( "No lamp found" );
return;
}
var angleSlice = 360f / LampsSpawned;
for ( int i = 0; i < LampsSpawned; i++ )
{
var side = i % 2;
var newLamp = Lamp.Clone();
var randomPitch = Game.Random.Float( -angleSlice / 6f, angleSlice / 6f );
newLamp.WorldRotation = Rotation.FromPitch( angleSlice * i + angleSlice / 2f + randomPitch );
var sideRotation = Rotation.FromYaw( -90f + 180f * side );
newLamp.WorldRotation *= sideRotation;
newLamp.WorldPosition = Map.WorldPosition + newLamp.WorldRotation.Up * MapRadius;
var randomDistance = Game.Random.Float( 150f, 200f );
var sidePosition = (RoadWidth / 2f + randomDistance) * (side == 0 ? Vector3.Left : Vector3.Right);
newLamp.WorldPosition += sidePosition;
newLamp.SetParent( Map );
_lamps.Add( newLamp );
}
}
private List<GameObject> _roadObjects = new List<GameObject>();
[Button( "Generate Road" )]
public void GenerateRoad()
{
if ( !Map.IsValid() ) return;
ClearRoad();
var angleSlice = 360f / RoadObjects;
for ( int i = 1; i < RoadObjects; i++ )
{
var side = Game.Random.Int( -1, 1 );
var randomObject = new GameObject();
var roadObject = randomObject.AddComponent<RoadObject>();
randomObject.WorldRotation = Rotation.FromPitch( angleSlice * i );
randomObject.WorldPosition = Map.WorldPosition + randomObject.WorldRotation.Up * MapRadius;
var sidePosition = Vector3.Left * (RoadWidth - 80f) * side;
randomObject.WorldPosition += sidePosition;
_roadObjects.Add( randomObject );
}
}
private List<GameObject> _trees = new List<GameObject>();
[Button( "Generate Trees" )]
public void GenerateTrees()
{
if ( !Map.IsValid() ) return;
ClearTrees();
var allTrees = ResourceLibrary.GetAll<PrefabFile>()
.Where( x => x.GetMetadata( "Tags" )?.Contains( "tree" ) ?? false )
.ToList();
if ( !allTrees.Any() )
{
Log.Error( "No trees found" );
return;
}
var angleSlice = 1f;
for ( int i = 0; i < 360; i++ )
{
var side = Game.Random.Int( 0, 1 );
var randomTree = SceneUtility.GetPrefabScene( Game.Random.FromList( allTrees ) )
.Clone();
randomTree.WorldRotation = Rotation.FromPitch( angleSlice * i );
randomTree.WorldPosition = Map.WorldPosition + randomTree.WorldRotation.Up * MapRadius;
var randomDistance = Game.Random.Float( 800f, 2400f );
var sidePosition = (RoadWidth / 2f + randomDistance) * (side == 0 ? Vector3.Left : Vector3.Right);
randomTree.WorldPosition += sidePosition;
randomTree.SetParent( Map );
_trees.Add( randomTree );
}
}
private List<GameObject> _decorations = new List<GameObject>();
[Button( "Generate Decorations" )]
public void GenerateDecorations()
{
if ( !Map.IsValid() ) return;
ClearDecorations();
var allDecorations = ResourceLibrary.GetAll<PrefabFile>()
.Where( x => x.GetMetadata( "Tags" )?.Contains( "decoration" ) ?? false )
.ToList();
if ( !allDecorations.Any() )
{
Log.Error( "No decorations found" );
return;
}
var angleSlice = 360f / DecorationsSpawned;
for ( int i = 1; i < DecorationsSpawned; i++ )
{
var side = i % 2;
var randomDecoration = SceneUtility.GetPrefabScene( Game.Random.FromList( allDecorations ) )
.Clone();
var randomPitch = Game.Random.Float( -angleSlice / 6f, angleSlice / 6f );
randomDecoration.WorldRotation = Rotation.FromPitch( angleSlice * i + angleSlice / 2f + randomPitch );
randomDecoration.LocalRotation *= Rotation.FromYaw( side * 180f );
randomDecoration.WorldPosition = Map.WorldPosition + randomDecoration.WorldRotation.Up * MapRadius;
var randomDistance = Game.Random.Float( 180f, 500f );
var sidePosition = (RoadWidth / 2f + randomDistance) * (side == 0 ? Vector3.Left : Vector3.Right);
randomDecoration.WorldPosition += sidePosition;
randomDecoration.SetParent( Map );
_decorations.Add( randomDecoration );
}
}
private List<GameObject> _snow = new List<GameObject>();
[Button( "Generate Snow" )]
public void GenerateSnow()
{
if ( !Map.IsValid() ) return;
ClearSnow();
var allSnow = ResourceLibrary.GetAll<PrefabFile>()
.Where( x => x.GetMetadata( "Tags" )?.Contains( "snow" ) ?? false )
.ToList();
if ( !allSnow.Any() )
{
Log.Error( "No snow found" );
return;
}
var angleSlice = 2f;
for ( int i = 0; i < 180; i++ )
{
var side = i % 2;
var randomSnow = SceneUtility.GetPrefabScene( Game.Random.FromList( allSnow ) )
.Clone();
randomSnow.WorldRotation = Rotation.FromPitch( angleSlice * i );
randomSnow.LocalRotation *= Rotation.FromYaw( Game.Random.Int( 0, 1 ) * 180f );
randomSnow.WorldPosition = Map.WorldPosition + randomSnow.WorldRotation.Up * MapRadius;
var randomDistance = Game.Random.Float( 90f, 100f );
var sidePosition = (RoadWidth / 2f + randomDistance) * (side == 0 ? Vector3.Left : Vector3.Right);
randomSnow.WorldPosition += sidePosition;
randomSnow.LocalScale *= new Vector3( Game.Random.Float( 0.8f, 1.2f ), Game.Random.Float( 0.8f, 1.2f ), Game.Random.Float( 1f, 1.2f ) );
randomSnow.SetParent( Map );
_snow.Add( randomSnow );
}
}
public void GenerateMap()
{
GenerateCottages();
GenerateRoad();
GenerateLamps();
GenerateDecorations();
Map.WorldRotation = Rotation.Identity;
MapClone = Map.Clone();
MapClone.WorldPosition += Vector3.Forward * 2500f;
MapClone.WorldRotation *= Rotation.FromPitch( -83f );
MapClone.WorldScale *= 0.7f;
foreach ( var roadObject in _roadObjects )
roadObject.SetParent( Map );
}
public void ClearMap()
{
ClearCottages();
ClearRoad();
ClearLamps();
ClearDecorations();
MapClone?.Destroy();
}
public void ClearDecorations()
{
if ( _decorations != null )
{
foreach ( var decoration in _decorations.ToList() )
decoration.Destroy();
_decorations.Clear();
}
}
public void ClearSnow()
{
if ( _snow != null )
{
foreach ( var snow in _snow.ToList() )
snow.Destroy();
_snow.Clear();
}
}
public void ClearCottages()
{
if ( _cottages != null )
{
foreach ( var cottage in _cottages.ToList() )
cottage.Destroy();
_cottages.Clear();
}
}
public void ClearTrees()
{
if ( _trees != null )
{
foreach ( var tree in _trees.ToList() )
tree.Destroy();
_trees.Clear();
}
}
public void ClearRoad()
{
if ( _roadObjects != null )
{
foreach ( var present in _roadObjects.ToList() )
present.Destroy();
_roadObjects.Clear();
}
}
public void ClearLamps()
{
if ( _lamps != null )
{
foreach ( var lamp in _lamps.ToList() )
lamp.Destroy();
_lamps.Clear();
}
}
public void StartGame()
{
ClearMap();
Points = 0;
StartTimer = 0f;
RotationSpeed = _originalSpeed;
GenerateMap();
var player = Scene.Components.GetAll<SantaPlayerSled>().FirstOrDefault();
player.Unragdoll();
IsPlaying = true;
MusicSound.StartSound();
}
public void EndGame()
{
IsPlaying = false;
}
protected override void DrawGizmos()
{
using ( Gizmo.Scope( "MapGizmo", new global::Transform( -WorldPosition, WorldRotation.Inverse ) ) )
{
var draw = Gizmo.Draw;
if ( !Map.IsValid() ) return;
draw.Color = Color.Blue.WithAlpha( 0.3f );
draw.SolidCylinder( Map.WorldPosition + Map.WorldRotation.Right * 2500f, Map.WorldPosition + Map.WorldRotation.Left * 2500f, MapRadius, 64 );
}
}
}