s&box Package Code Search

Search C# source code, UI razor templates, shaders, and configs across s&box packages.

Showing code results for query: * (212 total matches found)
facepunch.memory / FloaterLock.cs
Game game
using Sandbox;
using System.Drawing;

public class FloaterLock : Component
{
	public int NumTurns { get; set; }

	private TimeSince _timeSinceSpawn;

	public float Lifetime { get; set; }
	public bool ShowLocked { get; set ; }

	private float _startScale;
	private float _endScale;
	private Vector2 _velocity;
	private float _deceleration;

	public float Scale { get; set; }
	public float Opacity { get; set; }

	public void Init( int numTurns )
	{
		NumTurns = numTurns;

		Lifetime = 1.25f;
		_velocity = new Vector2( 0f, 12f );
		_deceleration = 4f;
		Scale = _startScale = 1f;
		_endScale = 1f;
	}

	protected override void OnStart()
	{
		base.OnStart();

		_timeSinceSpawn = 0f;

		Manager.Instance.PlaySfx( "lock_start", new Vector3( WorldPosition.x, WorldPosition.y, Manager.Instance.Camera.WorldPosition.z - 100f ), volume: 0.9f, pitch: Game.Random.Float( 1.05f, 1.13f ) );
	}

	protected override void OnUpdate()
	{
		Opacity = Utils.Map( _timeSinceSpawn, 0f, 0.3f, 0f, 1f, EasingType.QuadOut ) * Utils.Map( _timeSinceSpawn, 0f, Lifetime - 0.1f, 1f, 0.1f, EasingType.ExpoIn ) * Utils.Map( _timeSinceSpawn, Lifetime - 0.3f, Lifetime - 0.05f, 1f, 0f, EasingType.Linear );
		Scale = Utils.Map( _timeSinceSpawn, 0f, Lifetime, _startScale, _endScale, EasingType.SineOut );

		WorldPosition += new Vector3( _velocity.x, _velocity.y, 0f ) * Time.Delta;
		_velocity *= (1f - _deceleration * Time.Delta);

		if( !ShowLocked && _timeSinceSpawn > 0.5f )
		{
			ShowLocked = true;
			_velocity = new Vector2( 0f, -20f );
			_deceleration = 7f;

			Manager.Instance.PlaySfx( "lock", new Vector3( WorldPosition.x, WorldPosition.y, Manager.Instance.Camera.WorldPosition.z - 100f ), volume: 2f, pitch: Game.Random.Float( 0.97f, 1.03f ) );
		}

		if ( _timeSinceSpawn > Lifetime )
		{
			Manager.Instance.NumFloaters--;
			GameObject.Destroy();
		}
	}
}
facepunch.memory / cards/Card.cs
Game game
using Microsoft.VisualBasic;
using Sandbox;
using Sandbox.UI;
using Sandbox.ui;
using System.Runtime;
using System.Threading.Tasks;

public enum CardType { None, Apple, Bomb, Rocket, Telephone, Trumpet, Pawn, Balloon, Spider, Umbrella, UmbrellaClosed, Juggler, Coin, Vampire, Maracas, Microwave, Raccoon, Rock, Bear, Candle, 
	CrystalBall, Snake, Rose, RoseWilted, Bell, Ant, Twins, Compass, Antenna, Wizard, AncientScroll, JumpingSpider, Bento, Ninja, Flashlight, Bodybuilder, Helicopter, FlyingMoney,
	Syringe, Cellphone, Crab, Magnet, Stethoscope, UFO, Teacher, Tractor, FortuneCookie, Dolphin, Detective, Genie, Potion, Mirror, FaxMachine, Dice, Coffee, Tree, Chipmunk,
	Dancer, Wine, King, Police, Ogre, Clown, MoneyBag, Bank, Steak, MagicWand, Carrot, Caterpillar, Butterfly, Tornado, Map, Cockroach, Broccoli, Donut, Cheese, CheeseMoldy, GuideDog, Taco, Burrito,
	Skunk, CarePackage, Cash, BirthdayCake, Gem, Pickaxe, Mountain, DiamondRing, Rabbit, RedEnvelope,
}

// StockMarket, StockMarketDown, Key, AlarmClock, ATM

public class Card : Component, IEventHandler
{
	public GameObject Model { get; set; }
	public ModelRenderer ModelRenderer { get; set; }
	public GameObject IconObj { get; set; }
	public GameObject IconBackObj { get; set; }
	public bool IsFlagShown { get; set; }

	public float CardIconScale { get; set; } = 1f;

	public bool IsHovered { get; set; }
	public bool IsRevealed { get; set; }
	public bool IsSpawning { get; set; }

	public int NumTimesRevealed { get; set; }
	public int NumTimesChosen { get; set; }

	public CardType CardType { get; private set; }
	public CardType OriginalCardType { get; private set; }

	public virtual bool IsFoodOrBeverage => false;
	public virtual bool IsAlive => false;
	public virtual bool CantBeMoved => false;

	public IntVector2 GridPos { get; set; }

	public bool IsShaking { get; private set; }
	private TimeSince _timeSinceShakeStart; // todo: replace with timer? this can become negative somehow
	private float _shakeTime;
	private EasingType _shakeEasingType;
	public Vector2 ShakeOffset { get; set; }
	private float _shakeStrength;

	public bool IsMovementControlled { get; set; }
	private Vector3 _moveStartPos;
	private Vector3 _moveTargetPos;
	private float _moveTime;
	private TimeSince _timeSinceMoveStart;
	private EasingType _moveEasingType;
	private bool _moveRemoveControlAfter;

	public int HP { get; set; }

	public bool IsLocked { get; set; }
	public int LockTurnsRemaining { get; set; }
	public bool WasJustLocked { get; set; }

	public int CardTypeID { get; set; }

	public float IconOpacity { get; set; }
	public bool IsExploding;
	private TimeSince _timeSinceStartExploding;
	private float _explodeTime;
	private EasingType _explodeEasingType;
	private Vector3 _explodeStartPos;
	private EasingType _explodeShakeEasingType;

	public bool IsScaling;
	private TimeSince _timeSinceScale;
	private float _scaleTime;
	private float _scaleAmount;
	private EasingType _scaleEasingType;

	public bool AreRevealMarksShown { get; set; }

	protected override void OnAwake()
	{
		base.OnAwake();

	}

	public virtual void Init(CardType cardType)
	{
		CardType = OriginalCardType = cardType;

		Model = GameObject.Children.Find( x => x.Name == "model" );
		ModelRenderer = Model.Components.Get<ModelRenderer>();

		IconOpacity = 1f;

		IconObj = GameObject.Children.Find( x => x.Name == "icon" );
		var cardIcon = IconObj.Components.Get<CardIcon>( includeDisabled: true );
		cardIcon.Card = this;

		IconBackObj = GameObject.Children.Find( x => x.Name == "icon_back" );
		var cardIconBack = IconBackObj.Components.Get<CardIconBack>( includeDisabled: true );
		cardIconBack.Card = this;

		SetMaterial( cardType );
	}

	public void SetMaterial(CardType cardType)
	{
		var matFilename = GetMaterialFilename( cardType );
		if ( !string.IsNullOrEmpty( matFilename ) )
			ModelRenderer.MaterialOverride = Material.Load( matFilename );
	}

	public void SetCardType(CardType newType)
	{
		CardType = newType;
		SetMaterial( newType );
	}

	protected override void OnUpdate()
	{
		//if ( Input.Down( "Duck" ) )
		//{
		//	var emoji = Card.GetIconEmoji( CardType );
		//	if ( string.IsNullOrEmpty( emoji ) )
		//	{
		//		Gizmo.Draw.Color = Color.Black.WithAlpha( 1f );
		//		Gizmo.Draw.Text( $"{CardType}", new global::Transform( WorldPosition + new Vector3( -0.5f, -0.5f, -1f ) ), size: 24f );

		//		Gizmo.Draw.Color = Color.White.WithAlpha( 1f );
		//		Gizmo.Draw.Text( $"{CardType}", new global::Transform( WorldPosition ), size: 24f );
		//	}
		//	else
		//	{
		//		Gizmo.Draw.Color = Color.White.WithAlpha( 1f );
		//		Gizmo.Draw.Text( emoji, new global::Transform( WorldPosition ), size: 60f );
		//	}

		//	if ( IsMovementControlled )
		//	{
		//		Gizmo.Draw.Color = Color.White.WithAlpha( 1f );
		//		Gizmo.Draw.Text( "➡️", new global::Transform( WorldPosition + new Vector3( -15.5f, -19f, -1f ) ), size: 30f );
		//	}

		//	if ( IsLocked )
		//	{
		//		Gizmo.Draw.Color = Color.White.WithAlpha( 1f );
		//		Gizmo.Draw.Text( "🔒", new global::Transform( WorldPosition + new Vector3( 15.5f, -19f, -1f ) ), size: 30f );

		//		Gizmo.Draw.Color = Color.Red.WithAlpha( 1f );
		//		Gizmo.Draw.Text( $"{LockTurnsRemaining}", new global::Transform( WorldPosition + new Vector3( 15.5f, -21f, 1f ) ), size: 20f );
		//	}

		//	Gizmo.Draw.Color = Color.Black.WithAlpha( 1f );
		//	Gizmo.Draw.Text( $"ID: {CardTypeID}", new global::Transform( WorldPosition + new Vector3( -13.5f, 19f, -1f ) + new Vector3( -0.5f, -0.5f, -1f ) ), size: 16 );

		//	Gizmo.Draw.Color = Color.White.WithAlpha( 1f );
		//	Gizmo.Draw.Text( $"ID: {CardTypeID}", new global::Transform( WorldPosition + new Vector3( -13.5f, 19f, -1f ) ), size: 16 );
		//}

		HandleMoving();
		HandleShaking();

		if(IsExploding)
		{
			HandleExploding();
			return;
		}

		if ( IsScaling )
		{
			HandleScaling();
		}
		
		//if(!IsScaling && !IsExploding)
		//{
		//	Transform.LocalScale = new Vector3( Utils.DynamicEaseTo( Transform.LocalScale.x, IsHovered ? 1.05f : 1f, 0.3f, Time.Delta ) );
		//}

		if( IsSpawning )
		{
			// do nothing
		}
		else if( IsMovementControlled )
		{
			LocalPosition += new Vector3( ShakeOffset.x, ShakeOffset.y, 0f ) * Time.Delta * 10f;
		}
		else
		{
			var targetPos = Manager.GetCardPos( GridPos );

			if ( IsShaking )
				targetPos += new Vector3( ShakeOffset.x, ShakeOffset.y, 0f );

			var height = 10f + ((IsRevealed || IsHovered) ? Globals.CARD_ADD_HEIGHT_REVEALED_OR_HOVERED : 0f) + ((IsRevealed && IsHovered) ? Globals.CARD_ADD_HEIGHT_REVEALED_AND_HOVERED : 0f);
			var zPos = MathX.Lerp( LocalPosition.z, height, Time.Delta * 20f );

			LocalPosition = Vector3.Lerp( LocalPosition, targetPos, Time.Delta * 10f ).WithZ( zPos );
		}

		var targetRot = Rotation.FromPitch( IsRevealed ? 0f : 180f );
		WorldRotation = Rotation.Lerp( WorldRotation, targetRot, Time.Delta * 15f );
	}

	public void MoveToPos( Vector3 pos, float time, EasingType easingType = EasingType.Linear, bool removeControlAfter = false)
	{
		//Log.Info( $"Card - MoveToPos: {pos}" );

		IsMovementControlled = true;
		_moveStartPos = LocalPosition;
		_moveTargetPos = pos;
		_moveTime = time;
		_timeSinceMoveStart = 0f;
		_moveEasingType = easingType;
		_moveRemoveControlAfter = removeControlAfter;
	}

	void HandleMoving()
	{
		if ( !IsMovementControlled )
			return;

		if(_timeSinceMoveStart > _moveTime)
		{
			LocalPosition = _moveTargetPos;

			if( _moveRemoveControlAfter )
				IsMovementControlled = false;
		}
		else 
		{
			LocalPosition = Vector3.Lerp( _moveStartPos, _moveTargetPos, Utils.Map( _timeSinceMoveStart, 0f, _moveTime, 0f, 1f, _moveEasingType ) );
		}
	}

	protected void HandleShaking()
	{
		if ( IsShaking )
		{
			if ( _timeSinceShakeStart > _shakeTime )
				IsShaking = false;
			else
				ShakeOffset = Utils.GetRandomVector() * _shakeStrength * Utils.Map( _timeSinceShakeStart, 0f, _shakeTime, 1f, 0f, _shakeEasingType );
		}
	}

	void HandleExploding()
	{
		LocalScale = new Vector3( Utils.Map(_timeSinceStartExploding, 0f, _explodeTime, 1f, Globals.CARD_EXPLODE_SCALE, _explodeEasingType) );
		IconOpacity = Utils.Map( _timeSinceStartExploding, 0f, _explodeTime, 1f, 0f, EasingType.SineIn );

		LocalPosition = _explodeStartPos + Vector3.Random * Utils.Map( _timeSinceStartExploding, 0f, _explodeTime, 0f, 2f, _explodeShakeEasingType) * Utils.Map( _timeSinceStartExploding, _explodeTime * 0.9f, _explodeTime, 1f, 0f );
	}

	void HandleScaling()
	{
		if(_timeSinceScale > 0.25f)
		{
			IsScaling = false;
			LocalScale = new Vector3( 1f );
		}
		else
		{
			LocalScale = Utils.MapReturn( _timeSinceScale, 0f, _scaleTime, 1f, _scaleAmount, _scaleEasingType);
		}
	}

	public static string GetName( CardType cardType )
	{
		switch ( cardType )
		{
			case CardType.CrystalBall: return "Crystal Ball";
			case CardType.AncientScroll: return "Ancient Scroll";
			case CardType.JumpingSpider: return "Jumping Spider";
			case CardType.Rock: return "Boulder";
			case CardType.Syringe: return "Morphine";
			case CardType.FlyingMoney: return "Flying Money";
			case CardType.FortuneCookie: return "Fortune Cookie";
			//case CardType.AlarmClock: return "Alarm Clock";
			case CardType.FaxMachine: return "Fax Machine";
			case CardType.MoneyBag: return "Money Bag";
			case CardType.MagicWand: return "Magic Wand";
			//case CardType.StockMarket: return "Stock Market";
			case CardType.GuideDog: return "Guide Dog";
			case CardType.CarePackage: return "Care Package";
			case CardType.BirthdayCake: return "Birthday Cake";
			case CardType.DiamondRing: return "Diamond Ring";
			case CardType.RedEnvelope: return "Red Envelope";
		}

		return cardType.ToString();
	}

	public static string GetFilename( CardType cardType )
	{
		switch ( cardType )
		{
			case CardType.Apple: return "apple";
			case CardType.Bomb: return "bomb";
			case CardType.Rocket: return "rocket";
			case CardType.Telephone: return "telephone";
			case CardType.Trumpet: return "trumpet";
			case CardType.Pawn: return "pawn";
			case CardType.Balloon: return "balloon";
			case CardType.Spider: return "spider";
			case CardType.Umbrella: return "umbrella";
			case CardType.UmbrellaClosed: return "umbrella_closed";
			case CardType.Juggler: return "juggler";
			case CardType.Coin: return "coin";
			case CardType.Vampire: return "vampire";
			case CardType.Maracas: return "maracas";
			case CardType.Microwave: return "microwave";
			case CardType.Raccoon: return "raccoon";
			case CardType.Rock: return "rock";
			case CardType.Bear: return "bear";
			case CardType.Candle: return "candle";
			case CardType.CrystalBall: return "crystal_ball";
			case CardType.Snake: return "snake";
			case CardType.Rose: return "rose";
			case CardType.RoseWilted: return "rose_wilted";
			case CardType.Bell: return "bell";
			case CardType.Ant: return "ant";
			case CardType.Twins: return "twins";
			case CardType.Compass: return "compass";
			case CardType.Antenna: return "antenna";
			case CardType.Wizard: return "wizard";
			case CardType.AncientScroll: return "scroll";
			case CardType.JumpingSpider: return "spider_jumping";
			case CardType.Bento: return "bento";
			//case CardType.StockMarket: return "investment";
			//case CardType.StockMarketDown: return "investment_down";
			case CardType.Ninja: return "ninja";
			case CardType.Flashlight: return "flashlight";
			case CardType.Bodybuilder: return "bodybuilder";
			case CardType.Helicopter: return "helicopter";
			case CardType.FlyingMoney: return "flying_money";
			case CardType.Syringe: return "syringe";
			case CardType.Cellphone: return "cellphone";
			case CardType.Crab: return "crab";
			case CardType.Magnet: return "magnet";
			case CardType.Stethoscope: return "stethoscope";
			case CardType.UFO: return "ufo";
			case CardType.Teacher: return "teacher";
			case CardType.Tractor: return "tractor";
			case CardType.FortuneCookie: return "fortune_cookie";
			case CardType.Dolphin: return "dolphin";
			case CardType.Detective: return "detective";
			case CardType.Genie: return "genie";
			case CardType.Potion: return "potion";
			case CardType.Mirror: return "mirror";
			//case CardType.AlarmClock: return "alarm_clock";
			case CardType.FaxMachine: return "fax_machine";
			case CardType.Dice: return "dice";
			case CardType.Coffee: return "coffee";
			case CardType.Tree: return "tree";
			case CardType.Chipmunk: return "chipmunk";
			case CardType.Dancer: return "dancer";
			case CardType.Wine: return "wine";
			case CardType.King: return "king";
			case CardType.Police: return "police";
			case CardType.Ogre: return "ogre";
			case CardType.Clown: return "clown";
			case CardType.MoneyBag: return "money_bag";
			case CardType.Bank: return "bank";
			case CardType.Steak: return "steak";
			case CardType.MagicWand: return "magic_wand";
			case CardType.Carrot: return "carrot";
			case CardType.Caterpillar: return "caterpillar";
			case CardType.Butterfly: return "butterfly";
			case CardType.Tornado: return "tornado";
			case CardType.Map: return "map";
			case CardType.Cockroach: return "cockroach";
			//case CardType.Key: return "key";
			case CardType.Broccoli: return "broccoli";
			//case CardType.ATM: return "atm";
			case CardType.Donut: return "donut";
			case CardType.Cheese: return "cheese";
			case CardType.CheeseMoldy: return "cheese_moldy";
			case CardType.GuideDog: return "guide_dog";
			case CardType.Taco: return "taco";
			case CardType.Burrito: return "burrito";
			case CardType.Skunk: return "skunk";
			case CardType.CarePackage: return "care_package";
			case CardType.Cash: return "cash";
			case CardType.BirthdayCake: return "birthday_cake";
			case CardType.Gem: return "gem";
			case CardType.Pickaxe: return "pickaxe";
			case CardType.Mountain: return "mountain";
			case CardType.DiamondRing: return "diamond_ring";
			case CardType.Rabbit: return "rabbit";
			case CardType.RedEnvelope: return "red_envelope";
		}

		return "";
	}

	public static string GetIconFilename( CardType cardType )
	{
		return $"textures/{GetFilename( cardType )}.png";
	}

	public static string GetMaterialFilename( CardType cardType )
	{
		return $"materials/cards/{GetFilename( cardType )}.vmat";
	}

	public static string GetIconEmoji( CardType cardType )
	{
		switch ( cardType )
		{
			case CardType.Apple: return "🍎";
			case CardType.Bomb: return "💣";
			case CardType.Rocket: return "🚀";
			case CardType.Telephone: return "☎️";
			case CardType.Trumpet: return "🎺";
			case CardType.Pawn: return "♟️";
			case CardType.Balloon: return "🎈";
			case CardType.Spider: return "🕷️";
			case CardType.Umbrella: return "☂️";
			case CardType.UmbrellaClosed: return "🌂";
			case CardType.Juggler: return "🤹";
			case CardType.Coin: return "🟡";
			case CardType.Vampire: return "🧛";
			case CardType.Raccoon: return "🦝";
			case CardType.Rock: return "🗿";
			case CardType.Bear: return "🐻";
			case CardType.Candle: return "🕯️";
			case CardType.CrystalBall: return "🔮";
			case CardType.Snake: return "🐍";
			case CardType.Rose: return "🌹";
			case CardType.RoseWilted: return "🥀";
			case CardType.Bell: return "🔔";
			case CardType.Ant: return "🐜";
			case CardType.Twins: return "👯";
			case CardType.Compass: return "🧭";
			case CardType.Antenna: return "📡";
			case CardType.Wizard: return "🧙";
			case CardType.AncientScroll: return "📜";
			case CardType.JumpingSpider: return "🕷️";
			case CardType.Bento: return "🍱";
			//case CardType.StockMarket: return "📈";
			//case CardType.StockMarketDown: return "📉";
			case CardType.Ninja: return "🐱‍👤";
			case CardType.Flashlight: return "🔦";
			case CardType.Bodybuilder: return "🏋️";
			case CardType.Helicopter: return "🚁";
			case CardType.FlyingMoney: return "💸";
			case CardType.Syringe: return "💉";
			case CardType.Cellphone: return "📱";
			case CardType.Crab: return "🦀";
			case CardType.Magnet: return "🧲";
			case CardType.Stethoscope: return "🩺";
			case CardType.UFO: return "🛸";
			case CardType.Teacher: return "👩‍🏫";
			case CardType.Tractor: return "🚜";
			case CardType.FortuneCookie: return "🥠";
			case CardType.Dolphin: return "🐬";
			case CardType.Detective: return "🕵️";
			case CardType.Genie: return "🧞";
			case CardType.Potion: return "🧪";
			case CardType.Mirror: return "🔘";
			//case CardType.AlarmClock: return "⏰";
			case CardType.FaxMachine: return "📠";
			case CardType.Dice: return "🎲";
			case CardType.Coffee: return "☕";
			case CardType.Tree: return "🌳";
			case CardType.Chipmunk: return "🐿️";
			case CardType.Dancer: return "💃";
			case CardType.Wine: return "🍷";
			case CardType.King: return "🤴";
			case CardType.Police: return "👮";
			case CardType.Ogre: return "👹";
			case CardType.Clown: return "🤡";
			case CardType.MoneyBag: return "💰";
			case CardType.Bank: return "🏦";
			case CardType.Steak: return "🥩";
			case CardType.MagicWand: return "✨";
			case CardType.Carrot: return "🥕";
			case CardType.Caterpillar: return "🐛";
			case CardType.Butterfly: return "🦋";
			case CardType.Tornado: return "🌪️";
			case CardType.Map: return "🗺️";
			case CardType.Cockroach: return "𓆣";
			//case CardType.Key: return "🔑";
			case CardType.Broccoli: return "🥦";
			//case CardType.ATM: return "🏧";
			case CardType.Donut: return "🍩";
			case CardType.Cheese: return "🧀";
			case CardType.CheeseMoldy: return "🤮";
			case CardType.GuideDog: return "🦮";
			case CardType.Taco: return "🌮";
			case CardType.Burrito: return "🌯";
			case CardType.Skunk: return "🦨";
			case CardType.CarePackage: return "📦";
			case CardType.Cash: return "💵";
			case CardType.BirthdayCake: return "🎂";
			case CardType.Gem: return "💎";
			case CardType.Pickaxe: return "⛏️";
			case CardType.Mountain: return "⛰️";
			case CardType.DiamondRing: return "💍";
			case CardType.Rabbit: return "🐇";
			case CardType.RedEnvelope: return "🧧";
		}

		return "";
	}

	public static string GetCardDescription( CardType cardType )
	{
		switch ( cardType )
		{
			case CardType.Apple: return "✅Match: +1 HP";
			case CardType.Bomb: return "✅Match: reveal nearby cards then shuffle them";
			case CardType.Rocket: return "❌Mismatch: swap position";
			case CardType.Telephone: return "✅Match: shake nearby pairs";
			case CardType.Trumpet: return "Match other cards: 30% chance to reveal";
			case CardType.Pawn: return "➡️Turn start: 50% to try to move upward";
			case CardType.Balloon: return "Floats upward";
			case CardType.Spider: return "Starts in a corner";
			case CardType.Umbrella: return "👁️Revealed: toggle open/closed";
			case CardType.Juggler: return "❌Mismatch: rearrange nearby cards";
			case CardType.Coin: return "✅Match: +$2";
			case CardType.Vampire: return "While hovered: shake if you lose HP";
			case CardType.Maracas: return "Makes a distinctive sound when shook";
			case CardType.Microwave: return "➡️Turn start: shake a nearby food/drink";
			case CardType.Raccoon: return "➡️Turn start: shake an adjacent card";
			case CardType.Rock: return "Falls downward, pushing other cards";
			case CardType.Bear: return "Food/drink revealed: 20% chance to shake";
			case CardType.Candle: return "✅Match: each reveal a nearby card";
			case CardType.CrystalBall: return "❌Mismatch: shake cards, including other card's match";
			case CardType.Snake: return "Shake when an adjacent card is matched";
			case CardType.Rose: return "When moved: shake and wilt";
			case CardType.Bell: return "Rings when moved or shook";
			case CardType.Ant: return "➡️Turn start: move to nearby empty space";
			case CardType.Twins: return "👁️Revealed: nudge toward match";
			case CardType.Compass: return "❌Mismatch: nudge toward other card's match";
			case CardType.Antenna: return "👁️Revealed: sound indicates distance to match";
			case CardType.Wizard: return "⏩Every 3 turns: shuffle some cards\n💔Damage Wizard: respawn some cards and shuffle them";
			case CardType.AncientScroll: return "✅Match: reveal all cards (after next turn, shuffle all cards)";
			case CardType.JumpingSpider: return "Starts in a corner\n❌Mismatch: swaps with an unknown card";
			case CardType.Bento: return "✅Match: +1 HP for each consecutive match";
			//case CardType.StockMarket: return "✅Match: gain half your money if the market is up, lose half if the market is down";
			case CardType.Ninja: return "➡️Turn start: 25% chance to swap position";
			case CardType.Flashlight: return "👆Chosen: reveal a nearby card";
			case CardType.Bodybuilder: return "➡️Turn start: move adjacent card upwards, pushing other cards";
			case CardType.Helicopter: return "❌Mismatch: relocate along with an adjacent card";
			case CardType.FlyingMoney: return "✅Match: +$3\n❌Mismatch: shuffle with some nearby cards";
			case CardType.Syringe: return "✅Match: heal all HP, but time is halved until end of level";
			case CardType.Cellphone: return "❌Mismatch: vibrate nearest cellphone";
			case CardType.Crab: return "Moves sideways to empty spaces";
			case CardType.Magnet: return "➡️Turn start: pull card toward it";
			case CardType.Stethoscope: return "✅Match/❌Mismatch: shake nearby cards with a heartbeat";
			case CardType.UFO: return "✅Match: abduct and shuffle nearby cards";
			case CardType.Teacher: return "✅Match: arrange row alphabetically";
			case CardType.Tractor: return "✅Match/❌Mismatch: shift row one space";
			case CardType.FortuneCookie: return "✅Match: +1 HP, and reveal 5 cards that you haven't seen before";
			case CardType.Dolphin: return "Revealed on 4 consecutive mismatches";
			case CardType.Detective: return "➡️Turn start: 50% chance to reveal a nearby card";
			case CardType.Genie: return "✅Match: next card chosen shakes its match";
			case CardType.Potion: return "✅Match: your HP is set to half";
			case CardType.Mirror: return "✅Match: flip the level horizontally";
			//case CardType.AlarmClock: return "70% chance to shake when timer goes below 1s";
			case CardType.FaxMachine: return "👆Chosen: shake 3 cards, including another fax machine";
			case CardType.Dice: return "❌Mismatch: roll a 6 to get +$2 (rolling 1 loses $1)\n✅Match: Roll a 12 to get +$5 (rolling 2 loses $2)";
			case CardType.Coffee: return "✅Match: +2 HP, and your next turn lasts 3 seconds";
			case CardType.Tree: return "✅Match: reveal an apple";
			case CardType.Chipmunk: return "➡️Turn start: move toward a tree";
			case CardType.Dancer: return "❌Mismatch: dance with multiple partners";
			case CardType.Wine: return "✅Match: +2 HP\nShake if an adjacent card shakes";
			case CardType.King: return "💔Damage King: he decides your next choice";
			case CardType.Police: return "❌Mismatch: Reveal and lock a nearby card for 2 turns\n💔Damage Police: Lock some nearby cards for 1 turn, then shuffle";
			case CardType.Ogre: return "💔Damage Ogre: shuffle with nearby cards";
			case CardType.Clown: return "💔Damage Clown: swap multiple times";
			case CardType.MoneyBag: return "✅Match: +$5";
			case CardType.Bank: return "✅Match: transform all coins into money bags";
			case CardType.Steak: return "✅Match: -$1 and +4 HP";
			case CardType.MagicWand: return "❌Mismatch: send other card adjacent to its match";
			case CardType.Carrot: return "✅Match: +3 HP, and timer is doubled next turn";
			case CardType.Caterpillar: return "➡️Turn start: 20% chance to shake and transform into a butterfly";
			case CardType.Tornado: return "➡️Turn start: swap 2 adjacent cards";
			case CardType.Map: return "✅Match: reveal all cards between";
			case CardType.Cockroach: return "Can't be matched until no other cards remain";
			//case CardType.Key: return "❌Mismatch: unlock other card\n✅Match: unlock all cards";
			case CardType.Broccoli: return "✅Match: +3 HP";
			//case CardType.ATM: return "✅Match: +$1 for every 2 matches you've made this level (max: $7)";
			case CardType.Donut: return "✅Match: +1 HP for each adjacent empty space";
			case CardType.Cheese: return "✅Match: +3 HP if not moldy\nGoes moldy after 7 turns";
			case CardType.GuideDog: return "When you mismatch other cards, 50% chance to point toward one of their matches";
			case CardType.Taco: return "✅Match: +1 Max HP";
			case CardType.Burrito: return "✅Match: +2 Max HP";
			case CardType.Skunk: return "Can't be matched while adjacent to any non-skunks";
			case CardType.CarePackage: return "✅Match: +$1 for every 2 HP missing";
			case CardType.Cash: return "✅Match: +$3 if both cash are nearby eachother, otherwise +$1";
			case CardType.BirthdayCake: return "✅Match: +1 HP and +$2";
			case CardType.Gem: return "✅Match: +$4";
			case CardType.Pickaxe: return "✅Match: transform all boulders into gems";
			case CardType.Mountain: return "Can't be moved";
			case CardType.DiamondRing: return "✅Match: +$3 if no other cards remain, otherwise +$1";
			case CardType.Rabbit: return "➡️Turn start: 50% chance to move towards its match";
			case CardType.RedEnvelope: return "✅Match: if you have less than $5, set your money to $5";
		}

		return "";
	}

	public static string GetCardExplanation( CardType cardType )
	{
		switch ( cardType )
		{
			case CardType.Ant: return "\"nearby\" means 1 space away\n(including diagonal)";
			case CardType.Snake: return "\"adjacent\" means 1 space away\n(NOT including diagonal)";
		}

		return "";
	}

	public static bool HasHP( CardType cardType )
	{
		switch ( cardType )
		{
			case CardType.Ogre: return true;
			case CardType.Clown: return true;
			case CardType.Police: return true;
			case CardType.Wizard: return true;
			case CardType.King: return true;
		}

		return false;
	}

	public static int GetMaxHP( CardType cardType )
	{
		switch ( cardType )
		{
			case CardType.Ogre: return 2;
			case CardType.Clown: return 2;
			case CardType.Police: return 2;
			case CardType.Wizard: return 3;
			case CardType.King: return 3;
		}

		return 0;
	}

	public void SetRevealedFromAsync(bool revealed)
	{
		IsRevealed = revealed;

		if( Card.HasHP(CardType) || IsLocked )
			IconObj.Enabled = revealed;

		if ( revealed )
		{
			NumTimesRevealed++;

			if(Manager.Instance.Stats[StatType.MaxCrayonMarks] > 0f && !AreRevealMarksShown)
				ShowRevealMarks();
		}
	}

	public void SetRevealedInstant()
	{
		IsRevealed = true;

		if ( Card.HasHP( CardType ) || IsLocked )
			IconObj.Enabled = true;

		WorldRotation = Rotation.FromPitch( 0f );

		NumTimesRevealed++;

		if ( Manager.Instance.Stats[StatType.MaxCrayonMarks] > 0f && !AreRevealMarksShown )
			ShowRevealMarks();
	}

	public void SetHiddenInstant()
	{
		IsRevealed = false;

		if ( Card.HasHP( CardType ) )
			IconObj.Enabled = false;

		WorldRotation = Rotation.FromPitch( 180f );
	}

	public void Shake( float strength, float time = 0.6f, EasingType easingType = EasingType.QuadOut, bool playSfx = true )
	{
		IsShaking = true;
		_timeSinceShakeStart = 0f;
		_shakeTime = time;
		_shakeEasingType = easingType;
		_shakeStrength = strength;

		if ( playSfx || ( OverrideShakeSfx && ( IsRevealed || !OnlyOverrideShakeSfxWhenRevealed ) )  )
			PlayShakeSfx();
	}

	public virtual void PlayShakeSfx()
	{
		//Manager.Instance.PlayCardSfx( "shake_1", this, volume: 1.1f, pitch: Game.Random.Float( 1.4f, 1.6f ) );
		Manager.Instance.PlayCardSfx( "shake_3", this, volume: 1.35f, pitch: Game.Random.Float( 0.94f, 1.06f ) );
	}

	public virtual bool OverrideShakeSfx => false;
	public virtual bool OnlyOverrideShakeSfxWhenRevealed => false;

	public virtual void PlayHurtSfx(Card card0, Card card1)
	{
		Manager.Instance.PlayCardSfxBetween( "shake_1", card0, card1, volume: 1.3f, pitch: Game.Random.Float( 1.4f, 1.6f ) );
	}

	// return false if needed to change start pos
	public virtual bool ValidateStartingGridPos()
	{
		return true;
	}

	public virtual bool ShouldHandleEvent( EventType eventType )
	{
		return false;
	}

	public virtual async Task HandleEventAsync(EventType eventType)
	{
		await Task.Frame();
	}

	public virtual string GetEventText( EventType eventType )
	{
		return GetCardDescription( CardType );
	}

	public async Task Hurt()
	{
		if(!Card.HasHP(CardType))
		{
			Log.Error( $"Trying to hurt card {CardType} with no HP!" );
			return;
		}

		HP--;
		await Manager.Instance.ShakeCard(this, playSfx: false);
	}

	public void Lock(int numTurns)
	{
		IsLocked = true;
		LockTurnsRemaining = numTurns;
		WasJustLocked = true;
	}

	public void Unlock()
	{
		IsLocked = false;
	}

	public virtual bool CanBeMatched()
	{
		return true;
	}

	public void StartExploding(float explodeTime)
	{
		IsExploding = true;
		_timeSinceStartExploding = 0f;
		_explodeTime = explodeTime;
		_explodeStartPos = LocalPosition;

		int rand = Game.Random.Int( 0, 5 );
		switch(rand)
		{
			case 0: _explodeEasingType = EasingType.SineIn; break;
			case 1: _explodeEasingType = EasingType.SineIn; break;
			case 2: _explodeEasingType = EasingType.QuadIn; break;
			case 3: _explodeEasingType = EasingType.QuadIn; break;
			case 4: _explodeEasingType = EasingType.QuartIn; break;
			case 5: _explodeEasingType = EasingType.Linear; break;
		}

		rand = Game.Random.Int( 0, 7 );
		switch ( rand )
		{
			case 0: _explodeShakeEasingType = EasingType.SineIn; break;
			case 1: _explodeShakeEasingType = EasingType.QuadIn; break;
			case 2: _explodeShakeEasingType = EasingType.QuartIn; break;
			case 3: _explodeShakeEasingType = EasingType.ExpoIn; break;
			case 4: _explodeShakeEasingType = EasingType.ExpoIn; break;
			case 5: _explodeShakeEasingType = EasingType.ExpoIn; break;
			case 6: _explodeShakeEasingType = EasingType.ExpoIn; break;
			case 7: _explodeShakeEasingType = EasingType.Linear; break;
		}
	}

	public void Explode( int breakNum )
	{
		var scale = LocalScale.x * Model.LocalScale.x;
		Manager.Instance.SpawnCardBreak( WorldPosition, LocalRotation, scale, ModelRenderer.MaterialOverride, breakNum );
		Manager.Instance.PlayCardSfx( "explosion", this, volume: Game.Random.Float( 0.4f, 0.55f ), pitch: Game.Random.Float( 0.4f, 2f ) );

		Manager.Instance.CardBreakParticlesPrefab.Clone( WorldPosition.WithZ( 25f ) );
		Manager.Instance.CardBreakShockwavePrefab.Clone( WorldPosition.WithZ( 55f ) );

		GameObject.Destroy();
	}

	public void StartScaling(float time, float amount, EasingType easingType)
	{
		IsScaling = true;
		_timeSinceScale = 0f;
		_scaleTime = time;
		_scaleAmount = amount;
		_scaleEasingType = easingType;
	}

	public void ShowFlag()
	{
		IsFlagShown = true;
		IconBackObj.Enabled = true;
	}

	public void HideFlag()
	{
		IsFlagShown = false;

		if ( !AreRevealMarksShown )
			IconBackObj.Enabled = false;
	}

	public void ShowRevealMarks()
	{
		AreRevealMarksShown = true;
		IconBackObj.Enabled = true;
	}
}
facepunch.memory / cards/CardBank.cs
Game game
using Sandbox;
using System.Net;
using System.Threading.Tasks;

public class CardBank : Card
{
	public override bool ShouldHandleEvent( EventType eventType )
	{
		switch ( eventType )
		{
			case EventType.Match:
				return Manager.Instance.ChosenCards[1] == this;
		}

		return false;
	}

	public override async Task HandleEventAsync( EventType eventType )
	{
		Manager.Instance.PushEventMessage( this, eventType );

		await Task.DelayRealtime( 150 );

		var coins = Manager.Instance.Cards.Where( x => x.CardType == CardType.Coin ).ToList();
		if(coins.Count > 0)
		{
			Manager.Instance.PlayCardSfxBetween( "bank", Manager.Instance.ChosenCards[0], Manager.Instance.ChosenCards[1], volume: 1f, pitch: Game.Random.Float( 0.9f, 1.1f ) );

			await Task.DelayRealtime( 50 );

			foreach ( var coin in coins )
				coin.SetCardType( CardType.MoneyBag );

			await Task.DelayRealtime( 300 );
		}

		await Task.DelayRealtime( 800 );

		Manager.Instance.PopEventMessage();
	}
}
facepunch.memory / cards/CardCockroach.cs
Game game
using Sandbox;
using System.Threading.Tasks;

public class CardCockroach : Card
{
	public override bool IsAlive => true;

	public override bool ShouldHandleEvent( EventType eventType )
	{
		return eventType == EventType.Mismatch && Manager.Instance.ChosenCards[0].CardType == CardType.Cockroach && Manager.Instance.ChosenCards[1].CardType == CardType.Cockroach && !IsLocked;
	}

	public override async Task HandleEventAsync( EventType eventType )
	{
		Manager.Instance.PushEventMessage( this, eventType );

		await Task.DelayRealtime( 850 );

		Manager.Instance.PopEventMessage();
	}

	public override bool CanBeMatched()
	{
		foreach(var card in Manager.Instance.Cards)
		{
			if ( card.CardType != CardType.Cockroach )
				return false;
		}

		return true;
	}
}
facepunch.memory / cards/CardDancer.cs
Game game
using Sandbox;
using System.Threading.Tasks;

public class CardDancer : Card
{
	public override bool IsAlive => true;

	public override bool ShouldHandleEvent( EventType eventType )
	{
		return eventType == EventType.Mismatch && Manager.Instance.ChosenCards.Contains( this ) && !Manager.Instance.IsMismatchALockedMatch;
	}

	public override async Task HandleEventAsync( EventType eventType )
	{
		Manager.Instance.PushEventMessage( this, eventType );

		List<Card> neighbourCards = new();
		List<IntVector2> neighboursEmpty = new();
		Card lastPartnerCard = null;

		await Task.DelayRealtime( 300 );

		Manager.Instance.PlayCardSfx( "dancer", this, volume: 1.1f, pitch: Game.Random.Float( 0.98f, 1.02f ) );

		for (int i = 0; i < 4; i++)
		{
			neighbourCards.Clear();
			neighboursEmpty.Clear();

			EvaluateNeighbour( GridPos + new IntVector2( -1, 0 ), neighbourCards, neighboursEmpty );
			EvaluateNeighbour( GridPos + new IntVector2( 1, 0 ), neighbourCards, neighboursEmpty );
			EvaluateNeighbour( GridPos + new IntVector2( 0, -1 ), neighbourCards, neighboursEmpty );
			EvaluateNeighbour( GridPos + new IntVector2( 0, 1 ), neighbourCards, neighboursEmpty );

			IntVector2 targetGridPos;
			Card targetCard = null;

			var validNeighbourCards = neighbourCards.Where( x => x != lastPartnerCard ).ToList();

			if ( validNeighbourCards.Count > 0 )
			{
				validNeighbourCards.Shuffle();
				targetCard = validNeighbourCards.First();
				targetGridPos = targetCard.GridPos;
			}
			else
			{
				neighboursEmpty.Shuffle();
				targetGridPos = neighboursEmpty.First();
			}

			if ( targetCard != null )
			{
				await Manager.Instance.ShakeCard( this );
				await Manager.Instance.ShakeCard( targetCard );

				lastPartnerCard = targetCard;

				await Task.DelayRealtime( 650 );

				if(i < 3)
				{
					await Manager.Instance.SwapCardPositions( this, targetCard );
					await Task.DelayRealtime( 250 );
				}
			}
			else
			{
				lastPartnerCard = null;

				if ( i < 3 )
				{
					Manager.Instance.RemoveCardGridPos( this );
					await Manager.Instance.SetCardGridPos( this, targetGridPos );

					await Task.DelayRealtime( 400 );
				}
			}
		}

		await Task.DelayRealtime( 100 );

		Manager.Instance.PopEventMessage();

		await Manager.Instance.EventHappened( EventType.AfterCardsMoved );
	}

	void EvaluateNeighbour(IntVector2 gridPos, List<Card> neighbourCards, List<IntVector2> empty)
	{
		if ( !Manager.Instance.IsGridPosInBounds( gridPos ) )
			return;

		var card = Manager.Instance.GetCardAtGridPos( gridPos );
		if ( card != null )
			neighbourCards.Add( card );
		else
			empty.Add( gridPos );
	}
}
facepunch.memory / cards/CardDolphin.cs
Game game
using Sandbox;
using System.Threading.Tasks;
using System.Xml.Linq;

public class CardDolphin : Card
{
	public override bool IsAlive => true;

	public override bool ShouldHandleEvent( EventType eventType )
	{
		return eventType == EventType.Mismatch && !Manager.Instance.ChosenCards.Contains(this) && Manager.Instance.Stats[StatType.ConsecutiveMismatches] == 3;
	}

	public override async Task HandleEventAsync( EventType eventType )
	{
		Manager.Instance.PushEventMessage( this, eventType );

		await Task.DelayRealtime( 250 );

		Manager.Instance.PlayCardSfx( "card_flip", this, volume: 0.8f, pitch: Game.Random.Float( 1.15f, 1.2f ) );
		await Task.DelayRealtime( 100 );
		Manager.Instance.PlayCardSfx( "dolphin", this, volume: 0.8f, pitch: Game.Random.Float( 1f, 1.15f ) );
		await Manager.Instance.RevealCard( this );

		await Task.DelayRealtime( Game.Random.Int(400, 700) );

		Manager.Instance.HideCard( this );
		Manager.Instance.PlayCardSfx( "card_flip", this, volume: 0.7f, pitch: Game.Random.Float( 0.65f, 0.75f ) );

		Manager.Instance.PopEventMessage();
	}
}
facepunch.memory / cards/CardGenie.cs
Game game
using Sandbox;
using System.Threading.Tasks;

public class CardGenie : Card
{
	public override bool IsAlive => true;

	public override bool ShouldHandleEvent( EventType eventType )
	{
		return eventType == EventType.Match && Manager.Instance.ChosenCards[0] == this;
	}

	public override async Task HandleEventAsync( EventType eventType )
	{
		Manager.Instance.PushEventMessage( this, eventType );

		await Task.DelayRealtime( 250 );

		Manager.Instance.PlayCardSfxBetween( "genie", Manager.Instance.ChosenCards[0], Manager.Instance.ChosenCards[1], volume: 0.6f, pitch: Game.Random.Float( 1.25f, 1.26f ) );

		Manager.Instance.AddStatus( "StatusGenie" );

		await Task.DelayRealtime( 1750 );

		Manager.Instance.PopEventMessage();
	}
}

public class StatusGenie : StatusEffect
{
	public float TimerReductionAmount { get; set; }

	public override bool ShouldHandleEvent( EventType eventType )
	{
		return eventType == EventType.Choose;
	}

	public override async Task HandleEventAsync( EventType eventType )
	{
		var validCards = Manager.Instance.Cards.Where(x => x.CardType == Manager.Instance.ChosenCard.CardType && x != Manager.Instance.ChosenCard).ToList();
		if ( validCards.Count == 0 )
			return;

		Manager.Instance.PushEventMessage( this, eventType );

		await Task.DelayRealtime( 250 );

		Manager.Instance.PlayCardSfx( "genie_choose", Manager.Instance.ChosenCard, volume: 0.9f, pitch: Game.Random.Float( 0.95f, 1.05f ) );

		await Task.DelayRealtime( 750 );

		validCards.Shuffle();

		await Manager.Instance.ShakeCard( validCards.FirstOrDefault() );
		
		await Task.DelayRealtime( 750 );

		Manager.Instance.PopEventMessage();

		Manager.Instance.RemoveStatus( this );
	}

	public override string GetEventIcon( EventType eventType )
	{
		return "textures/genie.png";
	}

	public override string GetEventText( EventType eventType )
	{
		return "Shake matching card";
	}
}
facepunch.memory / cards/CardKey.cs
Game game
using Sandbox;
using System.Threading.Tasks;

public class CardKey : Card
{
	//public override bool ShouldHandleEvent( EventType eventType )
	//{
	//	return eventType == EventType.Mismatch && Manager.Instance.ChosenCards.Contains(this) || eventType == EventType.Match && Manager.Instance.ChosenCards[1] == this;
	//}

	//public override async Task HandleEventAsync( EventType eventType )
	//{
	//	if ( eventType == EventType.Mismatch )
	//	{
	//		var key = Manager.Instance.ChosenCards[0].CardType == CardType.Key ? Manager.Instance.ChosenCards[0] : Manager.Instance.ChosenCards[1];
	//		var otherCard = Manager.Instance.ChosenCards[0].CardType == CardType.Key ? Manager.Instance.ChosenCards[1] : Manager.Instance.ChosenCards[0];

	//		if ( key.IsLocked || !otherCard.IsLocked )
	//			return;

	//		Manager.Instance.PushEventMessage( this, eventType );

	//		await Task.DelayRealtime( 50 );

	//		Manager.Instance.PlayCardSfxBetween( "key", Manager.Instance.ChosenCards[0], Manager.Instance.ChosenCards[1], volume: 1.5f, pitch: Game.Random.Float( 0.9f, 1.1f ) );

	//		await Task.DelayRealtime( 50 );

	//		await Manager.Instance.UnlockCard( otherCard );

	//		await Task.DelayRealtime( 900 );

	//		Manager.Instance.PopEventMessage();
	//	}
 //       else if (eventType == EventType.Match)
	//	{
	//		Manager.Instance.PushEventMessage( this, eventType );

	//		await Task.DelayRealtime( 50 );

	//		var lockedCards = Manager.Instance.Cards.Where( x => x.IsLocked ).ToList();

	//		if ( lockedCards.Count == 0 )
	//		{
	//			await Task.DelayRealtime( 800 );

	//			Manager.Instance.PopEventMessage();

	//			return;
	//		}

	//		Manager.Instance.PlayCardSfxBetween( "key", Manager.Instance.ChosenCards[0], Manager.Instance.ChosenCards[1], volume: 1.5f, pitch: Game.Random.Float( 0.9f, 1.1f ) );

	//		await Task.DelayRealtime( 50 );

	//		foreach ( var card in lockedCards )
	//		{
	//			await Manager.Instance.UnlockCard( card );
	//		}

	//		await Task.DelayRealtime( 1000 );

	//		Manager.Instance.PopEventMessage();
	//	}
	//}

	//public override string GetEventText( EventType eventType )
	//{
	//	if ( eventType == EventType.Mismatch )
	//	{
	//		return "❌Mismatch: unlock other card";
	//	}
	//	else
	//	{
	//		return "✅Match: unlock all cards";
	//	}
	//}
}
facepunch.memory / relics/RelicGuideDog.cs
Game game
using Sandbox;
using System.Reflection.PortableExecutable;
using System.Runtime.Versioning;
using System.Threading.Tasks;

public class RelicGuideDog : Relic
{
	public override void Init()
	{
		base.Init();

		MaxLevel = 1;
	}
}
facepunch.memory / relics/RelicMammothMeat.cs
Game game
using Sandbox;
using System.Reflection.PortableExecutable;
using System.Runtime.Versioning;
using System.Threading.Tasks;

public class RelicMammothMeat : Relic
{
	public override void LevelUp()
	{
		base.LevelUp();

		var mouse = Mouse.Position;
		var camera = Scene.Camera;
		var ray = camera.ScreenPixelToRay( mouse );

		var tr = Scene.Trace.Ray( ray, 10000f ).Run();

		Manager.Instance.PlaySfxCenter( "mammoth_meat", volume: 0.75f, pitch: Game.Random.Float( 0.85f, 0.9f ) );

		int maxHpAmount = 8;
		Manager.Instance.SpawnMaxHPFloater( maxHpAmount, tr.EndPosition );// + new Vector3(0f, -30f, 0f) );
		Manager.Instance.MaxHP += maxHpAmount;

		//int hpAmount = 5;
		//Manager.Instance.HP = Math.Min( Manager.Instance.HP + hpAmount, Manager.Instance.MaxHP );
		Manager.Instance.TimeSinceHPChanged = 0f;

		//Manager.Instance.SpawnHealHPFloater( hpAmount, tr.EndPosition );
	}
}
facepunch.memory / relics/RelicMantlepieceClock.cs
Game game
using Sandbox;
using System.Reflection.PortableExecutable;
using System.Runtime.Versioning;
using System.Threading.Tasks;

public class RelicMantlepieceClock : Relic
{
	public override void LevelUp()
	{
		base.LevelUp();

		Manager.Instance.Stats[StatType.NumExtraBountyTurns] += (Level == 1 ? 2f : 1f);
	}
}
facepunch.memory / relics/RelicMedicalLicense.cs
Game game
using Sandbox;
using System.Reflection.PortableExecutable;
using System.Runtime.Versioning;
using System.Threading.Tasks;

public class RelicMedicalLicense : Relic
{
	public override void Init()
	{
		base.Init();

		MaxLevel = 1;
	}

	public override bool ShouldHandleEvent( EventType eventType )
	{
		return eventType == EventType.OverhealHP;
	}

	public override async Task HandleEventAsync( EventType eventType )
	{
		await Task.DelayRealtime( 1000 );

		Manager.Instance.PushEventMessage( this, eventType );

		var camera = Scene.Camera;
		var ray = camera.ScreenPixelToRay( new Vector3( Screen.Width / 2, Screen.Height / 2, 0f ) );

		var tr = Scene.Trace.Ray( ray, 10000f ).Run();

		Manager.Instance.PlaySfx( "medical_license", tr.EndPosition.WithZ( Scene.Camera.WorldPosition.z - Globals.CARD_SFX_DEPTH_DIFF ), volume: 1.2f, pitch: Game.Random.Float( 0.95f, 1.05f ) );

		await Task.DelayRealtime( 350 );

		int moneyAmount = (int)Manager.Instance.Stats[StatType.LatestOverhealAmount] + (int)Manager.Instance.Stats[StatType.EarnExtraMoney];

		Manager.Instance.SpawnGainMoneyFloater( moneyAmount, tr.EndPosition );

		await Manager.Instance.GainMoney( moneyAmount );

		await Task.DelayRealtime( 650 );

		Manager.Instance.PopEventMessage();
	}
}
facepunch.memory / relics/RelicMouseTrap.cs
Game game
using Sandbox;
using System.Reflection.PortableExecutable;
using System.Runtime.Versioning;
using System.Threading.Tasks;

public class RelicMouseTrap : Relic
{
	public override bool ShouldHandleEvent( EventType eventType )
	{
		return eventType == EventType.ClaimBounty;
	}

	public override async Task HandleEventAsync(EventType eventType)
	{
		Manager.Instance.PushEventMessage( this, eventType );

		await Task.DelayRealtime( 100 );

		Manager.Instance.PlaySfxCenter( "mouse_trap", volume: 0.8f, pitch: Game.Random.Float( 0.95f, 1.05f ) );

		await Task.DelayRealtime( 400 );

		var camera = Scene.Camera;
		var ray = camera.ScreenPixelToRay( new Vector3( Screen.Width / 2, Screen.Height / 2, 0f ) );
		var tr = Scene.Trace.Ray( ray, 10000f ).Run();

		int maxHP = Level;
		Manager.Instance.SpawnMaxHPFloater( maxHP, tr.EndPosition );
		Manager.Instance.MaxHP += maxHP;

		Manager.Instance.TimeSinceHPChanged = 0f;

		await Task.DelayRealtime( 500 );
		
		Manager.Instance.PopEventMessage();
	}
}
facepunch.memory / relics/RelicSacrificialBlade.cs
Game game
using Sandbox;
using System.Reflection.PortableExecutable;
using System.Runtime.Versioning;
using System.Threading.Tasks;

public class RelicSacrificialBlade : Relic
{
	public override bool ShouldHandleEvent( EventType eventType )
	{
		return eventType == EventType.HurtCards;
	}

	public override async Task HandleEventAsync(EventType eventType)
	{
		Manager.Instance.PushEventMessage( this, eventType );

		var camera = Scene.Camera;
		var ray = camera.ScreenPixelToRay( new Vector3( Screen.Width / 2, Screen.Height / 2, 0f ) );
		var tr = Scene.Trace.Ray( ray, 10000f ).Run();

		Manager.Instance.PlaySfx( "sacrificial_blade", tr.EndPosition.WithZ( Scene.Camera.WorldPosition.z - Globals.CARD_SFX_DEPTH_DIFF ), volume: 0.75f, pitch: Game.Random.Float( 1.6f, 1.65f ) );

		await Task.DelayRealtime( 450 );

		int healAmount = Level;

		Manager.Instance.SpawnHealHPFloater( healAmount, tr.EndPosition );

		await Task.DelayRealtime( 350 );

		await Manager.Instance.GainHP( healAmount );

		await Task.DelayRealtime( 250 );

		Manager.Instance.PopEventMessage();
	}
}
facepunch.memory / relics/RelicSign.cs
Game game
using Sandbox;
using System.Reflection.PortableExecutable;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

public class RelicSign : Relic
{
	public override bool ShouldHandleEvent( EventType eventType )
	{
		return eventType == EventType.Mismatch && Game.Random.Float(0f, 1f) < Level * 0.25f;
	}

	public override async Task HandleEventAsync( EventType eventType )
	{
		List<Card> validCards = new();
		foreach ( var card in Manager.Instance.Cards )
		{
			if ( card.NumTimesRevealed > 0 && !Manager.Instance.ChosenCards.Contains(card) && !card.IsRevealed )
				validCards.Add( card );
		}

		if ( validCards.Count == 0 )
			return;

		Manager.Instance.PushEventMessage( this, eventType );

		// todo: sfx

		await Task.DelayRealtime( 250 );

		validCards.Shuffle();

		var cardToReveal = validCards.First();

		Manager.Instance.PlayCardSfx( "card_flip", cardToReveal, volume: 0.8f, pitch: Game.Random.Float( 1.15f, 1.2f ) );
		await Manager.Instance.RevealCard( cardToReveal );

		await Task.DelayRealtime( 750 );

		Manager.Instance.HideCard( cardToReveal );
		Manager.Instance.PlayCardSfx( "card_flip", cardToReveal, volume: 0.7f, pitch: Game.Random.Float( 0.65f, 0.75f ) );

		await Task.DelayRealtime( 300 );

		Manager.Instance.PopEventMessage();
	}
}
facepunch.memory / ui/messagepanel.razor.scss
Game game
MessagePanel {
    position: absolute;
    //min-width: 60%;
    height: 60px;
    bottom: 10px;
    display: flex;
    justify-content: center;
    align-items: center;
    border-radius: 15px;
    //background-color: #00000088;
    flex-direction: row;
    padding: 8px;
    backdrop-filter: blur(5px);
    //transition: bottom 0.25s ease-out, transform 0.25s ease-out, opacity 0.25s ease-out;
    transition: all 0.25s ease-out;

    .message {
        font-size: 24px;
        color: #ffffffee;
    }

    .icon {
        width: 55px;
        height: 55px;
        background-repeat: no-repeat;
        background-size: contain;
        background-position: center center;
    }

    &.depth-0 {
        transform: scale(1);
        bottom: 10px;
        z-index: 0;
    }

    &.depth-1 {
        transform: scale(0.8);
        bottom: 50px;
        z-index: -3;
    }

    &.depth-2 {
        transform: scale(0.6);
        bottom: 90px;
        z-index: -6;
    }

    &.depth-3 {
        transform: scale(0.5);
        bottom: 110px;
        z-index: -9;
    }

    &.depth-4 {
        transform: scale(0.4);
        bottom: 140px;
        z-index: -12;
    }
}
facepunch.memory / ui/cardtypepanelelement.razor.scss
Game game
CardTypePanelElement {
	//border-radius: 10px;
	height: 80px;
	width: 80px;
	justify-content: center;
	//flex-shrink: 1;
	//margin: 2px;
	//background-color: blue;
	//border: 2px solid #111111;
	//overflow: visible;
	position: relative;

	.icon_container {
		width: 100%;
		height: 100%;
		transform: scale(1);
		transition: transform 0.075s ease-out;
		pointer-events: all;

		&:hover {
			//background-color: #301B3F99;
			//border: 2px solid #111111;
			//opacity: 1;
			transform: scale(1.2);
		}
	}

	.icon {
		position: absolute;
		width: 100%;
		height: 100%;
		background-repeat: no-repeat;
		background-size: contain;
		background-position: center center;
		pointer-events: none;
	}

	.normal {
		filter: brightness(1);
		opacity: 1;
	}

	.matched {
		filter: brightness(0);
		opacity: 0.4;
	}

	.bounty {
		position: absolute;
		width: 60px;
		height: 60px;
		background-color: #0000ff99;
	}
}
facepunch.memory / CardBreaker.cs
Game game
using Sandbox;

public sealed class CardBreaker : Component
{
	protected override void OnStart()
	{

	}

	public void Init(Material material, Rotation rot, float scale)
	{
		LocalRotation = rot;
		LocalScale = new Vector3( scale );

		for ( int i = GameObject.Children.Count - 1; i >= 0; i-- )
		{
			var child = GameObject.Children[i];
			child.SetParent( null );

			var modelRenderer = child.Components.Get<ModelRenderer>();
			modelRenderer.Tint = Color.White;
			modelRenderer.MaterialOverride = material;

			var rigidBody = child.Components.Get<Rigidbody>();
			rigidBody.ApplyImpulse( (child.WorldPosition - WorldPosition).Normal * Game.Random.Float( 10f, 100f ) * rigidBody.PhysicsBody.Mass );
			rigidBody.ApplyImpulse( new Vector3( 0f, 0f, 1f ) * Game.Random.Float( 400f, 800f ) * rigidBody.PhysicsBody.Mass );
			rigidBody.AngularVelocity = Vector3.Random * Game.Random.Float( -10f, 10f );
		}

		GameObject.Destroy();
	}
}
facepunch.memory / Utils.cs
Game game
using System;
using System.Collections.Generic;
using System.Linq;
using Sandbox;
using System.Text.Json;

public enum EasingType
{
	None = -1,
	Linear = 0,
	SineIn, SineOut, SineInOut,
	QuadIn, QuadOut, QuadInOut,
	CubicIn, CubicOut, CubicInOut,
	QuartIn, QuartOut, QuartInOut,
	QuintIn, QuintOut, QuintInOut,
	ExpoIn, ExpoOut, ExpoInOut,
	ExtremeIn, ExtremeOut, ExtremeInOut,
	ElasticIn, ElasticOut, ElasticInOut,
	ElasticSoftIn, ElasticSoftOut, ElasticSoftInOut,
	BackIn, BackOut, BackInOut,
	BounceIn, BounceOut, BounceInOut
};

public struct IntVector2 : IEquatable<IntVector2>
{
	public static implicit operator Vector2( IntVector2 vec )
	{
		return new Vector2( vec.x, vec.y );
	}

	public static explicit operator IntVector2( Vector2 vec )
	{
		return new IntVector2( (int)vec.x, (int)vec.y );
	}

	/// <summary>
	/// A vector with zero for all components.
	/// </summary>
	public static readonly IntVector2 Zero = new IntVector2( 0, 0 );

	/// <summary>
	/// A normalized vector along the positive X axis.
	/// </summary>
	public static readonly IntVector2 UnitX = new IntVector2( 1, 0 );

	/// <summary>
	/// A normalized vector along the positive Y axis.
	/// </summary>
	public static readonly IntVector2 UnitY = new IntVector2( 0, 1 );

	#region Operators
	/// <summary>
	/// The identity operator.
	/// </summary>
	public static IntVector2 operator +( IntVector2 vec )
	{
		return vec;
	}

	/// <summary>
	/// Component-wise addition of a vector to another.
	/// </summary>
	public static IntVector2 operator +( IntVector2 a, IntVector2 b )
	{
		return new IntVector2( a.x + b.x, a.y + b.y );
	}

	/// <summary>
	/// Finds the negation of a vector.
	/// </summary>
	public static IntVector2 operator -( IntVector2 vec )
	{
		return new IntVector2( -vec.x, -vec.y );
	}

	/// <summary>
	/// Component-wise subtraction of a vector from another.
	/// </summary>
	public static IntVector2 operator -( IntVector2 a, IntVector2 b )
	{
		return new IntVector2( a.x - b.x, a.y - b.y );
	}

	/// <summary>
	/// Component-wise multiplication of a vector by another.
	/// </summary>
	public static IntVector2 operator *( IntVector2 a, IntVector2 b )
	{
		return new IntVector2( a.x * b.x, a.y * b.y );
	}

	/// <summary>
	/// Multiplies a vector by a scalar.
	/// </summary>
	public static IntVector2 operator *( IntVector2 vec, int val )
	{
		return new IntVector2( vec.x * val, vec.y * val );
	}

	/// <summary>
	/// Multiplies a vector by a scalar.
	/// </summary>
	public static Vector2 operator *( IntVector2 vec, float val )
	{
		return new Vector2( vec.x * val, vec.y * val );
	}

	/// <summary>
	/// Multiplies a vector by a scalar.
	/// </summary>
	public static IntVector2 operator *( int val, IntVector2 vec )
	{
		return new IntVector2( vec.x * val, vec.y * val );
	}

	/// <summary>
	/// Multiplies a vector by a scalar.
	/// </summary>
	public static Vector2 operator *( float val, IntVector2 vec )
	{
		return new Vector2( vec.x * val, vec.y * val );
	}

	/// <summary>
	/// Component-wise division of a vector by another.
	/// </summary>
	public static IntVector2 operator /( IntVector2 a, IntVector2 b )
	{
		return new IntVector2( a.x / b.x, a.y / b.y );
	}

	/// <summary>
	/// Component-wise division of a vector by another.
	/// </summary>
	public static Vector2 operator /( Vector2 a, IntVector2 b )
	{
		return new Vector2( a.x / b.x, a.y / b.y );
	}

	/// <summary>
	/// Component-wise division of a vector by another.
	/// </summary>
	public static Vector2 operator /( IntVector2 a, Vector2 b )
	{
		return new Vector2( a.x / b.x, a.y / b.y );
	}

	/// <summary>
	/// Division of this vector by a scalar.
	/// </summary>
	public static IntVector2 operator /( IntVector2 vec, int val )
	{
		return new IntVector2( vec.x / val, vec.y / val );
	}

	public static Vector2 operator /( IntVector2 vec, float val )
	{
		return new Vector2( vec.x / val, vec.y / val );
	}

	public static IntVector2 operator /( int val, IntVector2 vec )
	{
		return new IntVector2( val / vec.x, val / vec.y );
	}

	public static Vector2 operator /( float val, IntVector2 vec )
	{
		return new Vector2( val / vec.x, val / vec.y );
	}
	#endregion

	/// <summary>
	/// Horizontal component.
	/// </summary>
	public int x;

	/// <summary>
	/// Vertical component.
	/// </summary>
	public int y;

	/// <summary>
	/// Floating point magnitude of the vector.
	/// </summary>
	public float Length { get { return (float)MathF.Sqrt( x * x + y * y ); } }

	/// <summary>
	/// Magnitude of the vector in Taxicab geometry.
	/// </summary>
	public int ManhattanLength { get { return Math.Abs( x ) + Math.Abs( y ); } }

	/// <summary>
	/// Sum of each component squared.
	/// </summary>
	public int LengthSquared { get { return x * x + y * y; } }

	/// <summary>
	/// Gets a normalized vector in the same direction as this one.
	/// </summary>
	public Vector2 Normalized { get { return LengthSquared >= float.Epsilon ? this / Length : Zero; } }

	/// <summary>
	/// Gets a vector equal to this one rotated counter-clockwise by 90 degrees.
	/// </summary>
	public IntVector2 Left { get { return new IntVector2( -y, x ); } }

	/// <summary>
	/// Gets a vector equal to this one rotated clockwise 90 degrees.
	/// </summary>
	public IntVector2 Right { get { return new IntVector2( y, -x ); } }

	/// <summary>
	/// Gets a vector equal to this one rotated 180 degrees.
	/// </summary>
	public IntVector2 Back { get { return -this; } }

	/// <summary>
	/// Constructs a vector from the given X and Y components.
	/// </summary>
	public IntVector2( int x, int y )
	{
		this.x = x; this.y = y;
	}

	/// <summary>
	/// Finds the scalar product of this vector and another.
	/// </summary>
	public int Dot( IntVector2 vec )
	{
		return x * vec.x + y * vec.y;
	}

	public override bool Equals( object obj )
	{
		return obj is IntVector2 && Equals( (IntVector2)obj );
	}

	/// <summary>
	/// Tests for equality with another vector.
	/// </summary>
	public bool Equals( IntVector2 vec )
	{
		return x == vec.x && y == vec.y;
	}

	public override int GetHashCode()
	{
		return x ^ y;
	}

	/// <summary>
	/// Gets a string representing this vector in (X, Y) format.
	/// </summary>
	public override string ToString()
	{
		return string.Format( "({0}, {1})", x, y );
	}
}

public struct AStarEdge<T>
{
	/// <summary>
	/// The node this connection leads to.
	/// </summary>
	public readonly T Dest;

	/// <summary>
	/// The cost of using this connection.
	/// </summary>
	public readonly float Cost;

	internal AStarEdge( T dest, float cost )
	{
		Dest = dest;
		Cost = cost;
	}
}

public static class Utils
{
	// projects vector A onto B
	public static Vector3 ProjectVector( Vector3 a, Vector3 b )
	{
		float dotProduct = Vector3.Dot( a, b );
		float lengthSquared = Vector3.Dot( b, b );
		return (dotProduct / lengthSquared) * b;
	}

	// returns vector B or C, whichever is in the closest direction compared to A
	public static Vector3 FindCloserVector( Vector3 a, Vector3 b, Vector3 c )
	{
		// Normalize B and C
		b = b.Normal;
		c = c.Normal;

		// Compute dot products
		float dotProductB = Vector3.Dot( a, b );
		float dotProductC = Vector3.Dot( a, c );

		// Determine which dot product is larger
		return dotProductB > dotProductC ? b : c;
	}

	public static GameObject FindChild( this Component component, string name )
	{
		return component.GameObject.Children.Where( x => x.Name == name ).FirstOrDefault();
	}

	public static Color GetRandomColor( float total = 1.5f )
	{
		float r = Game.Random.Float( 0f, 1f );
		total = Math.Max( total - r, 0f );
		float g = MathF.Min( Game.Random.Float( 0f, 1f ), total );
		total = Math.Max( total - g, 0f );
		float b = MathF.Min( Game.Random.Float( 0f, 1f ), total );

		return new Color( r, g, b );
	}

	public static T ReadJsonCustom<T>( this BaseFileSystem fs, string filename, T defaultValue = default( T ) )
	{
		string text = fs.ReadAllText( filename );
		if ( string.IsNullOrWhiteSpace( text ) )
		{
			return defaultValue;
		}

		JsonSerializerOptions options = new JsonSerializerOptions
		{
			ReadCommentHandling = JsonCommentHandling.Skip,
			PropertyNameCaseInsensitive = true,
			AllowTrailingCommas = true,
			Converters = { new System.Text.Json.Serialization.JsonStringEnumConverter() },
		};
		return JsonSerializer.Deserialize<T>( text, options );
	}

	public const float Deg2Rad = MathF.PI / 180f;
	public const float Rad2Deg = 360f / (MathF.PI * 2f);

	public static Vector2 RotateVector( Vector2 v, float degrees )
	{
		var rads = Deg2Rad * degrees;
		var ca = MathF.Cos( rads );
		var sa = MathF.Sin( rads );

		return new Vector2( ca * v.x - sa * v.y, sa * v.x + ca * v.y );
	}

	public static Vector2 RotatePointAround( Vector2 p, Vector2 anchor, float degrees )
	{
		var offset = p - anchor;
		var newVec = RotateVector( offset, degrees );
		return anchor + newVec;
	}

	public static Vector2 DegreesToVector( float degrees )
	{
		var rads = Deg2Rad * degrees;
		return new Vector2( MathF.Cos( rads ), MathF.Sin( rads ) );
	}

	public static float VectorToDegrees( Vector2 vec )
	{
		float radians = (float)Math.Atan2( vec.y, vec.x );
		float degrees = radians * (180f / (float)Math.PI);
		return degrees;
	}

	public static Vector2 GetPerpendicularVector( Vector2 vec )
	{
		return new Vector2( -vec.y, vec.x );
	}

	public static int GetDistance( int x, int y )
	{
		return (int)Math.Round( Math.Sqrt( x * x + y * y ) );
	}

	public static Vector2 GetRandomVector()
	{
		return DegreesToVector( Game.Random.Float( 0f, 360f ) );
	}

	public static float FastSin( float input )
	{
		// wrap input angle to -PI..PI
		while ( input < -3.14159265f )
			input += 6.28318531f;
		while ( input > 3.14159265f )
			input -= 6.28318531f;

		return 1.27323954f * input + 0.405284735f * (input < 0f ? 1f : -1f) * input * input;
	}

	public static float DynamicEaseTo( float current, float goal, float factorPercent, float dt, float referenceFrameRate = 60f )
	{
		if ( float.IsPositiveInfinity( dt ) )
			return goal;

		return current + (goal - current) * (1f - MathF.Pow( 1f - MathX.Clamp( factorPercent, 0f, 1f ), dt * referenceFrameRate ));
	}

	public static Vector2 DynamicEaseTo( Vector2 current, Vector2 goal, float factorPercent, float dt, float referenceFrameRate = 60f )
	{
		if ( float.IsPositiveInfinity( dt ) )
			return goal;
		var diff = goal - current;

		return current + diff * (1f - MathF.Pow( 1f - MathX.Clamp( factorPercent, 0f, 1f ), dt * referenceFrameRate ));
	}

	public static Vector3 DynamicEaseTo( Vector3 current, Vector3 goal, float factorPercent, float dt, float referenceFrameRate = 60f )
	{
		if ( float.IsPositiveInfinity( dt ) )
			return goal;
		var diff = goal - current;

		return current + diff * (1f - MathF.Pow( 1f - MathX.Clamp( factorPercent, 0f, 1f ), dt * referenceFrameRate ));
	}

	public static Rotation DynamicEaseTo( Rotation currentRot, Rotation goalRot, float factorPercent, float dt, float referenceFrameRate = 60f )
	{
		if ( float.IsPositiveInfinity( dt ) )
			return goalRot;

		Vector3 current = currentRot.Angles().AsVector3();
		Vector3 goal = goalRot.Angles().AsVector3();

		var diff = goal - current;

		var output = current + diff * (1f - MathF.Pow( 1f - MathX.Clamp( factorPercent, 0f, 1f ), dt * referenceFrameRate ));
		return Rotation.From( new Angles( output ) );
	}

	public static float EaseUnclamped( float value, EasingType easingType )
	{
		switch ( easingType )
		{
			case EasingType.SineIn: return SineIn( value );
			case EasingType.SineOut: return SineOut( value );
			case EasingType.SineInOut: return SineInOut( value );

			case EasingType.QuadIn: return QuadIn( value );
			case EasingType.QuadOut: return QuadOut( value );
			case EasingType.QuadInOut: return QuadInOut( value );

			case EasingType.CubicIn: return CubicIn( value );
			case EasingType.CubicOut: return CubicOut( value );
			case EasingType.CubicInOut: return CubicInOut( value );

			case EasingType.QuartIn: return QuartIn( value );
			case EasingType.QuartOut: return QuartOut( value );
			case EasingType.QuartInOut: return QuartInOut( value );

			case EasingType.QuintIn: return QuintIn( value );
			case EasingType.QuintOut: return QuintOut( value );
			case EasingType.QuintInOut: return QuintInOut( value );

			case EasingType.ExpoIn: return ExpoIn( value );
			case EasingType.ExpoOut: return ExpoOut( value );
			case EasingType.ExpoInOut: return ExpoInOut( value );

			case EasingType.ExtremeIn: return ExtremeIn( value );
			case EasingType.ExtremeOut: return ExtremeOut( value );
			case EasingType.ExtremeInOut: return ExtremeInOut( value );

			case EasingType.ElasticIn: return ElasticIn( value );
			case EasingType.ElasticOut: return ElasticOut( value );
			case EasingType.ElasticInOut: return ElasticInOut( value );

			case EasingType.ElasticSoftIn: return ElasticSoftIn( value );
			case EasingType.ElasticSoftOut: return ElasticSoftOut( value );
			case EasingType.ElasticSoftInOut: return ElasticSoftInOut( value );

			case EasingType.BackIn: return BackIn( value );
			case EasingType.BackOut: return BackOut( value );
			case EasingType.BackInOut: return BackInOut( value );

			case EasingType.BounceIn: return BounceIn( value );
			case EasingType.BounceOut: return BounceOut( value );
			case EasingType.BounceInOut: return BounceInOut( value );
			default: return value;
		}
	}

	public static float Map( float value, float inputMin, float inputMax, float outputMin, float outputMax, EasingType easingType = EasingType.Linear, bool clamp = true )
	{
		if ( inputMin.Equals( inputMax ) || outputMin.Equals( outputMax ) )
			return outputMin;

		//            if(inputMin.Equals(inputMax) || outputMin.Equals(outputMax))
		//                return outputMax;

		if ( clamp )
		{
			// clamp input
			if ( inputMax > inputMin )
			{
				if ( value < inputMin ) value = inputMin;
				else if ( value > inputMax ) value = inputMax;
			}
			else if ( inputMax < inputMin )
			{
				if ( value > inputMin ) value = inputMin;
				else if ( value < inputMax ) value = inputMax;
			}
		}

		var ratio = EaseUnclamped( (value - inputMin) / (inputMax - inputMin), easingType );

		var outVal = outputMin + ratio * (outputMax - outputMin);

		//            // clamp output
		//            if(outputMax < outputMin) {
		//                if(outVal < outputMax) outVal = outputMax;
		//                else if(outVal > outputMin) outVal = outputMin;
		//            } else {
		//                if(outVal > outputMax) outVal = outputMax;
		//                else if(outVal < outputMin) outVal = outputMin;
		//            }

		return outVal;
	}

	public static float MapReturn( float value, float inputMin, float inputMax, float outputMin, float outputMax, EasingType easingType = EasingType.Linear )
	{
		var halfway = inputMin + (inputMax - inputMin) * 0.5f;
		if ( value < halfway ) return Map( value, inputMin, halfway, outputMin, outputMax, easingType );
		else return Map( value, halfway, inputMax, outputMax, outputMin, GetOppositeEasingType( easingType ) );
	}

	public static float EasePercent( float percent, EasingType easingType )
	{
		return Map( percent, 0f, 1f, 0f, 1f, easingType );
	}

	public static EasingType GetOppositeEasingType( EasingType easingType )
	{
		var opposite = EasingType.Linear;
		switch ( easingType )
		{
			case EasingType.SineIn: opposite = EasingType.SineOut; break;
			case EasingType.SineOut: opposite = EasingType.SineIn; break;
			case EasingType.SineInOut: opposite = EasingType.SineInOut; break;

			case EasingType.QuadIn: opposite = EasingType.QuadOut; break;
			case EasingType.QuadOut: opposite = EasingType.QuadIn; break;
			case EasingType.QuadInOut: opposite = EasingType.QuadInOut; break;

			case EasingType.CubicIn: opposite = EasingType.CubicOut; break;
			case EasingType.CubicOut: opposite = EasingType.CubicIn; break;
			case EasingType.CubicInOut: opposite = EasingType.CubicInOut; break;

			case EasingType.QuartIn: opposite = EasingType.QuartOut; break;
			case EasingType.QuartOut: opposite = EasingType.QuartIn; break;
			case EasingType.QuartInOut: opposite = EasingType.QuartInOut; break;

			case EasingType.QuintIn: opposite = EasingType.QuintOut; break;
			case EasingType.QuintOut: opposite = EasingType.QuintIn; break;
			case EasingType.QuintInOut: opposite = EasingType.QuintInOut; break;

			case EasingType.ExpoIn: opposite = EasingType.ExpoOut; break;
			case EasingType.ExpoOut: opposite = EasingType.ExpoIn; break;
			case EasingType.ExpoInOut: opposite = EasingType.ExpoInOut; break;

			case EasingType.ExtremeIn: opposite = EasingType.ExtremeOut; break;
			case EasingType.ExtremeOut: opposite = EasingType.ExtremeIn; break;
			case EasingType.ExtremeInOut: opposite = EasingType.ExtremeInOut; break;

			case EasingType.ElasticIn: opposite = EasingType.ElasticOut; break;
			case EasingType.ElasticOut: opposite = EasingType.ElasticIn; break;
			case EasingType.ElasticInOut: opposite = EasingType.ElasticInOut; break;

			case EasingType.ElasticSoftIn: opposite = EasingType.ElasticSoftOut; break;
			case EasingType.ElasticSoftOut: opposite = EasingType.ElasticSoftIn; break;
			case EasingType.ElasticSoftInOut: opposite = EasingType.ElasticSoftInOut; break;

			case EasingType.BackIn: opposite = EasingType.BackOut; break;
			case EasingType.BackOut: opposite = EasingType.BackIn; break;
			case EasingType.BackInOut: opposite = EasingType.BackInOut; break;

			case EasingType.BounceIn: opposite = EasingType.BounceOut; break;
			case EasingType.BounceOut: opposite = EasingType.BounceIn; break;
			case EasingType.BounceInOut: opposite = EasingType.BounceInOut; break;
		}

		return opposite;
	}

	public static float SineIn( float t ) { return 1f - MathF.Cos( t * MathF.PI * 0.5f ); }
	public static float SineOut( float t ) { return MathF.Sin( t * (MathF.PI * 0.5f) ); }
	public static float SineInOut( float t ) { return -0.5f * (MathF.Cos( MathF.PI * t ) - 1f); }

	public static float QuadIn( float t ) { return t * t; }
	public static float QuadOut( float t ) { return t * (2f - t); }
	public static float QuadInOut( float t ) { return t < 0.5f ? 2f * t * t : -1f + (4f - 2f * t) * t; }

	public static float CubicIn( float t ) { return t * t * t; }
	public static float CubicOut( float t ) { var t1 = t - 1f; return t1 * t1 * t1 + 1f; }
	public static float CubicInOut( float t ) { return t < 0.5f ? 4f * t * t * t : (t - 1f) * (2f * t - 2f) * (2f * t - 2f) + 1f; }

	public static float QuartIn( float t ) { return t * t * t * t; }
	public static float QuartOut( float t ) { var t1 = t - 1f; return 1f - t1 * t1 * t1 * t1; }
	public static float QuartInOut( float t ) { return t < 0.5f ? 4f * t * t * t : (t - 1f) * (2f * t - 2f) * (2f * t - 2f) + 1f; }

	public static float QuintIn( float t ) { return t * t * t * t * t; }
	public static float QuintOut( float t ) { var t1 = t - 1f; return 1f + t1 * t1 * t1 * t1 * t1; }
	public static float QuintInOut( float t ) { var t1 = t - 1f; return t < 0.5f ? 16f * t * t * t * t * t : 1f + 16f * t1 * t1 * t1 * t1 * t1; }

	public static float ExpoIn( float t ) { return MathF.Pow( 2f, 10f * (t - 1f) ); }
	public static float ExpoOut( float t ) { return 1f - MathF.Pow( 2f, -10f * t ); }
	public static float ExpoInOut( float t ) { return t < 0.5f ? ExpoIn( t * 2f ) * 0.5f : 1f - ExpoIn( 2f - t * 2f ) * 0.5f; }

	public static float ExtremeIn( float t ) { return MathF.Pow( 10f, 10f * (t - 1f) ); }
	public static float ExtremeOut( float t ) { return 1f - MathF.Pow( 10f, -10f * t ); }
	public static float ExtremeInOut( float t ) { return t < 0.5f ? ExtremeIn( t * 2f ) * 0.5f : 1f - ExtremeIn( 2f - t * 2f ) * 0.5f; }

	public static float ElasticIn( float t ) { return 1f - ElasticOut( 1f - t ); }
	public static float ElasticOut( float t ) { var p = 0.3f; return MathF.Pow( 2f, -10f * t ) * MathF.Sin( (t - p / 4f) * (2f * (float)Math.PI) / p ) + 1f; }
	public static float ElasticInOut( float t ) { return t < 0.5f ? ElasticIn( t * 2f ) * 0.5f : 1f - ElasticIn( 2f - t * 2f ) * 0.5f; }

	public static float ElasticSoftIn( float t ) { return 1f - ElasticSoftOut( 1f - t ); }
	public static float ElasticSoftOut( float t ) { var p = 0.5f; return MathF.Pow( 2f, -10f * t ) * MathF.Sin( (t - p / 4f) * (2f * (float)Math.PI) / p ) + 1f; }
	public static float ElasticSoftInOut( float t ) { return t < 0.5f ? ElasticSoftIn( t * 2f ) * 0.5f : 1f - ElasticSoftIn( 2f - t * 2f ) * 0.5f; }

	public static float BackIn( float t ) { var p = 1f; return t * t * ((p + 1f) * t - p); }
	public static float BackOut( float t ) { var p = 1f; var scaledTime = t / 1f - 1f; return scaledTime * scaledTime * ((p + 1f) * scaledTime + p) + 1f; }
	public static float BackInOut( float t )
	{
		var p = 1f;
		var scaledTime = t * 2f;
		var scaledTime2 = scaledTime - 2f;
		var s = p * 1.525f;

		if ( scaledTime < 1f ) return 0.5f * scaledTime * scaledTime * ((s + 1f) * scaledTime - s);
		else return 0.5f * (scaledTime2 * scaledTime2 * ((s + 1f) * scaledTime2 + s) + 2f);
	}

	public static float BounceIn( float t ) { return 1f - BounceOut( 1f - t ); }
	public static float BounceOut( float t )
	{
		var scaledTime = t / 1f;

		if ( scaledTime < 1 / 2.75f )
		{
			return 7.5625f * scaledTime * scaledTime;
		}
		else if ( scaledTime < 2 / 2.75 )
		{
			var scaledTime2 = scaledTime - 1.5f / 2.75f;
			return 7.5625f * scaledTime2 * scaledTime2 + 0.75f;
		}
		else if ( scaledTime < 2.5 / 2.75 )
		{
			var scaledTime2 = scaledTime - 2.25f / 2.75f;
			return 7.5625f * scaledTime2 * scaledTime2 + 0.9375f;
		}
		else
		{
			var scaledTime2 = scaledTime - 2.625f / 2.75f;
			return 7.5625f * scaledTime2 * scaledTime2 + 0.984375f;
		}
	}
	public static float BounceInOut( float t )
	{
		if ( t < 0.5 ) return BounceIn( t * 2f ) * 0.5f;
		else return BounceOut( t * 2f - 1f ) * 0.5f + 0.5f;
	}

	public static void Shuffle<T>( this IList<T> list )
	{
		System.Random rng = new System.Random();
		int n = list.Count;
		while ( n > 1 )
		{
			n--;
			int k = rng.Next( n + 1 );
			T value = list[k];
			list[k] = list[n];
			list[n] = value;
		}
	}

	public static string FirstCharToUpper( this string input ) =>
		input switch
		{
			null => throw new ArgumentNullException( nameof( input ) ),
			"" => throw new ArgumentException( $"{nameof( input )} cannot be empty", nameof( input ) ),
			_ => string.Concat( input[0].ToString().ToUpper(), input.AsSpan( 1 ) )
		};

	public static string GetRandomIcon( string i0, string i1 ) { int rand = Game.Random.Int( 0, 1 ); switch ( rand ) { case 0: default: return i0; case 1: return i1; } }
	public static string GetRandomIcon( string i0, string i1, string i2 ) { int rand = Game.Random.Int( 0, 2 ); switch ( rand ) { case 0: default: return i0; case 1: return i1; case 2: return i2; } }
	public static string GetRandomIcon( string i0, string i1, string i2, string i3 ) { int rand = Game.Random.Int( 0, 3 ); switch ( rand ) { case 0: default: return i0; case 1: return i1; case 2: return i2; case 3: return i3; } }
	public static string GetRandomIcon( string i0, string i1, string i2, string i3, string i4 ) { int rand = Game.Random.Int( 0, 4 ); switch ( rand ) { case 0: default: return i0; case 1: return i1; case 2: return i2; case 3: return i3; case 4: return i4; } }
	public static string GetRandomIcon( string i0, string i1, string i2, string i3, string i4, string i5 ) { int rand = Game.Random.Int( 0, 5 ); switch ( rand ) { case 0: default: return i0; case 1: return i1; case 2: return i2; case 3: return i3; case 4: return i4; case 5: return i5; } }
	public static string GetRandomIcon( string i0, string i1, string i2, string i3, string i4, string i5, string i6 ) { int rand = Game.Random.Int( 0, 6 ); switch ( rand ) { case 0: default: return i0; case 1: return i1; case 2: return i2; case 3: return i3; case 4: return i4; case 5: return i5; case 6: return i6; } }
	public static string GetRandomIcon( string i0, string i1, string i2, string i3, string i4, string i5, string i6, string i7 ) { int rand = Game.Random.Int( 0, 7 ); switch ( rand ) { case 0: default: return i0; case 1: return i1; case 2: return i2; case 3: return i3; case 4: return i4; case 5: return i5; case 6: return i6; case 7: return i7; } }


	private class NodeInfo<T>
	{
		private const int MaxPoolSize = 8192;

		private static List<NodeInfo<T>> _sPool;

		internal static NodeInfo<T> Create( T node, NodeInfo<T> prev = null, float costAdd = 0f )
		{
			NodeInfo<T> nodeInfo;

			if ( _sPool == null || _sPool.Count == 0 )
			{
				nodeInfo = new NodeInfo<T>();
			}
			else
			{
				nodeInfo = _sPool[_sPool.Count - 1];
				_sPool.RemoveAt( _sPool.Count - 1 );
			}

			nodeInfo.Node = node;
			nodeInfo.Prev = prev;

			if ( prev == null )
			{
				nodeInfo.Depth = 0;
				nodeInfo.Cost = 0f;
			}
			else
			{
				nodeInfo.Depth = prev.Depth + 1;
				nodeInfo.Cost = prev.Cost + costAdd;
			}

			return nodeInfo;
		}

		internal static void Pool( NodeInfo<T> nodeInfo )
		{
			if ( _sPool == null ) _sPool = new List<NodeInfo<T>>( MaxPoolSize );
			if ( _sPool.Count >= MaxPoolSize ) return;

			_sPool.Add( nodeInfo );
		}

		private float _heuristic;

		public T Node { get; private set; }
		public NodeInfo<T> Prev { get; private set; }
		public int Depth { get; private set; }
		public float Cost { get; private set; }
		public float Total { get; private set; }

		public float Heuristic
		{
			get { return _heuristic; }
			set
			{
				_heuristic = value;
				Total = Cost + value;
			}
		}
	}

	/// <summary>
	/// Convenience method to produce a graph connection for use when calling AStar().
	/// </summary>
	/// <typeparam name="T">Graph node type.</typeparam>
	/// <param name="dest">Destination node of the connection.</param>
	/// <param name="cost">Cost of taking the connection.</param>
	public static AStarEdge<T> Edge<T>( T dest, float cost )
	{
		return new AStarEdge<T>( dest, cost );
	}

	private static class AStarWrapper<T>
		where T : IEquatable<T>
	{
		public static NodeInfo<T> FirstMatchOrDefault( List<NodeInfo<T>> list, T toCompare )
		{
			var count = list.Count;

			for ( var i = count - 1; i >= 0; --i )
			{
				var item = list[i];
				if ( item.Node.Equals( toCompare ) )
					return item;
			}

			return null;
		}

		private static List<NodeInfo<T>> _sOpen;
		private static List<NodeInfo<T>> _sClosed;

		public static bool AStar( T origin, T target, List<T> destList,
			Func<T, IEnumerable<AStarEdge<T>>> adjFunc, Func<T, T, float> heuristicFunc )
		{
			var open = _sOpen ?? (_sOpen = new List<NodeInfo<T>>());
			var clsd = _sClosed ?? (_sClosed = new List<NodeInfo<T>>());

			open.Clear();
			clsd.Clear();

			var first = NodeInfo<T>.Create( origin );
			first.Heuristic = heuristicFunc( origin, target );

			open.Add( first );

			try
			{
				while ( open.Count > 0 )
				{
					NodeInfo<T> cur = null;
					foreach ( var node in open )
					{
						if ( cur == null || node.Total < cur.Total ) cur = node;
					}

					if ( cur.Node.Equals( target ) )
					{
						for ( var i = cur.Depth; i >= 0; --i )
						{
							destList.Add( cur.Node );
							cur = cur.Prev;
						}
						destList.Reverse();
						return true;
					}

					open.Remove( cur );
					clsd.Add( cur );

					foreach ( var adj in adjFunc( cur.Node ) )
					{
						var node = NodeInfo<T>.Create( adj.Dest, cur, adj.Cost );
						var existing = FirstMatchOrDefault( clsd, adj.Dest );

						if ( existing != null )
						{
							if ( existing.Cost <= node.Cost ) continue;

							clsd.Remove( existing );
							node.Heuristic = existing.Heuristic;

							NodeInfo<T>.Pool( existing );
						}

						existing = FirstMatchOrDefault( open, adj.Dest );

						if ( existing != null )
						{
							if ( existing.Cost <= node.Cost ) continue;

							open.Remove( existing );
							node.Heuristic = existing.Heuristic;

							NodeInfo<T>.Pool( existing );
						}
						else
						{
							node.Heuristic = heuristicFunc( node.Node, target );
						}

						open.Add( node );
					}
				}
				return false;

			}
			finally
			{
				foreach ( var nodeInfo in open )
				{
					NodeInfo<T>.Pool( nodeInfo );
				}

				foreach ( var nodeInfo in clsd )
				{
					NodeInfo<T>.Pool( nodeInfo );
				}
			}
		}
	}

	/// <summary>
	/// An implementation of the AStar path finding algorithm.
	/// </summary>
	/// <typeparam name="T">Graph node type.</typeparam>
	/// <param name="origin">Node to start path finding from.</param>
	/// <param name="target">The goal node to reach.</param>
	/// <param name="adjFunc">Function returning the neighbouring connections for a node.</param>
	/// <param name="heuristicFunc">Function returning the estimated cost of travelling between two nodes.</param>
	/// <returns>A sequence of nodes representing a path if one is found, otherwise an empty array.</returns>
	public static bool AStar<T>( T origin, T target, List<T> destPath,
		Func<T, IEnumerable<AStarEdge<T>>> adjFunc, Func<T, T, float> heuristicFunc )
		where T : IEquatable<T>
	{
		return AStarWrapper<T>.AStar( origin, target, destPath, adjFunc, heuristicFunc );
	}
}
facepunch.memory / cards/CardAnt.cs
Game game
using Sandbox;
using System.Threading.Tasks;

public class CardAnt : Card
{
	public override bool IsAlive => true;

	public override bool ShouldHandleEvent( EventType eventType )
	{
		return eventType == EventType.TurnStart && Manager.Instance.GetNumNearbyEmptyGridPositions( GridPos, adjacentOnly: false ) > 0;
	}

	public override async Task HandleEventAsync( EventType eventType )
	{
		var emptyGridPos = Manager.Instance.GetRandomNearbyGridPos( GridPos, empty: true, adjacentOnly: false );

		await Task.DelayRealtime( 200 );

		Manager.Instance.PlayCardSfx( "card_move", this, volume: 1.1f, pitch: Game.Random.Float( 0.85f, 1.15f ) );

		await Task.DelayRealtime( 100 );

		Manager.Instance.RemoveCardGridPos( this );
		await Manager.Instance.SetCardGridPos( this, emptyGridPos );

		await Task.DelayRealtime( 300 );

		await Manager.Instance.EventHappened( EventType.AfterCardsMoved );
	}
}
Debug: View Raw JSON Response
{
    "TotalCount": 212,
    "Files": [
        {
            "Ident": "facepunch.memory",
            "Path": "FloaterLock.cs",
            "FileName": "FloaterLock.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 398889,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System.Drawing;\r\n\r\npublic class FloaterLock : Component\r\n{\r\n\tpublic int NumTurns { get; set; }\r\n\r\n\tprivate TimeSince _timeSinceSpawn;\r\n\r\n\tpublic float Lifetime { get; set; }\r\n\tpublic bool ShowLocked { get; set ; }\r\n\r\n\tprivate float _startScale;\r\n\tprivate float _endScale;\r\n\tprivate Vector2 _velocity;\r\n\tprivate float _deceleration;\r\n\r\n\tpublic float Scale { get; set; }\r\n\tpublic float Opacity { get; set; }\r\n\r\n\tpublic void Init( int numTurns )\r\n\t{\r\n\t\tNumTurns = numTurns;\r\n\r\n\t\tLifetime = 1.25f;\r\n\t\t_velocity = new Vector2( 0f, 12f );\r\n\t\t_deceleration = 4f;\r\n\t\tScale = _startScale = 1f;\r\n\t\t_endScale = 1f;\r\n\t}\r\n\r\n\tprotected override void OnStart()\r\n\t{\r\n\t\tbase.OnStart();\r\n\r\n\t\t_timeSinceSpawn = 0f;\r\n\r\n\t\tManager.Instance.PlaySfx( \"lock_start\", new Vector3( WorldPosition.x, WorldPosition.y, Manager.Instance.Camera.WorldPosition.z - 100f ), volume: 0.9f, pitch: Game.Random.Float( 1.05f, 1.13f ) );\r\n\t}\r\n\r\n\tprotected override void OnUpdate()\r\n\t{\r\n\t\tOpacity = Utils.Map( _timeSinceSpawn, 0f, 0.3f, 0f, 1f, EasingType.QuadOut ) * Utils.Map( _timeSinceSpawn, 0f, Lifetime - 0.1f, 1f, 0.1f, EasingType.ExpoIn ) * Utils.Map( _timeSinceSpawn, Lifetime - 0.3f, Lifetime - 0.05f, 1f, 0f, EasingType.Linear );\r\n\t\tScale = Utils.Map( _timeSinceSpawn, 0f, Lifetime, _startScale, _endScale, EasingType.SineOut );\r\n\r\n\t\tWorldPosition += new Vector3( _velocity.x, _velocity.y, 0f ) * Time.Delta;\r\n\t\t_velocity *= (1f - _deceleration * Time.Delta);\r\n\r\n\t\tif( !ShowLocked && _timeSinceSpawn > 0.5f )\r\n\t\t{\r\n\t\t\tShowLocked = true;\r\n\t\t\t_velocity = new Vector2( 0f, -20f );\r\n\t\t\t_deceleration = 7f;\r\n\r\n\t\t\tManager.Instance.PlaySfx( \"lock\", new Vector3( WorldPosition.x, WorldPosition.y, Manager.Instance.Camera.WorldPosition.z - 100f ), volume: 2f, pitch: Game.Random.Float( 0.97f, 1.03f ) );\r\n\t\t}\r\n\r\n\t\tif ( _timeSinceSpawn > Lifetime )\r\n\t\t{\r\n\t\t\tManager.Instance.NumFloaters--;\r\n\t\t\tGameObject.Destroy();\r\n\t\t}\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "facepunch.memory",
            "Path": "cards/Card.cs",
            "FileName": "Card.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 398889,
            "IsPrivate": false,
            "Code": "using Microsoft.VisualBasic;\r\nusing Sandbox;\r\nusing Sandbox.UI;\r\nusing Sandbox.ui;\r\nusing System.Runtime;\r\nusing System.Threading.Tasks;\r\n\r\npublic enum CardType { None, Apple, Bomb, Rocket, Telephone, Trumpet, Pawn, Balloon, Spider, Umbrella, UmbrellaClosed, Juggler, Coin, Vampire, Maracas, Microwave, Raccoon, Rock, Bear, Candle, \r\n\tCrystalBall, Snake, Rose, RoseWilted, Bell, Ant, Twins, Compass, Antenna, Wizard, AncientScroll, JumpingSpider, Bento, Ninja, Flashlight, Bodybuilder, Helicopter, FlyingMoney,\r\n\tSyringe, Cellphone, Crab, Magnet, Stethoscope, UFO, Teacher, Tractor, FortuneCookie, Dolphin, Detective, Genie, Potion, Mirror, FaxMachine, Dice, Coffee, Tree, Chipmunk,\r\n\tDancer, Wine, King, Police, Ogre, Clown, MoneyBag, Bank, Steak, MagicWand, Carrot, Caterpillar, Butterfly, Tornado, Map, Cockroach, Broccoli, Donut, Cheese, CheeseMoldy, GuideDog, Taco, Burrito,\r\n\tSkunk, CarePackage, Cash, BirthdayCake, Gem, Pickaxe, Mountain, DiamondRing, Rabbit, RedEnvelope,\r\n}\r\n\r\n// StockMarket, StockMarketDown, Key, AlarmClock, ATM\r\n\r\npublic class Card : Component, IEventHandler\r\n{\r\n\tpublic GameObject Model { get; set; }\r\n\tpublic ModelRenderer ModelRenderer { get; set; }\r\n\tpublic GameObject IconObj { get; set; }\r\n\tpublic GameObject IconBackObj { get; set; }\r\n\tpublic bool IsFlagShown { get; set; }\r\n\r\n\tpublic float CardIconScale { get; set; } = 1f;\r\n\r\n\tpublic bool IsHovered { get; set; }\r\n\tpublic bool IsRevealed { get; set; }\r\n\tpublic bool IsSpawning { get; set; }\r\n\r\n\tpublic int NumTimesRevealed { get; set; }\r\n\tpublic int NumTimesChosen { get; set; }\r\n\r\n\tpublic CardType CardType { get; private set; }\r\n\tpublic CardType OriginalCardType { get; private set; }\r\n\r\n\tpublic virtual bool IsFoodOrBeverage => false;\r\n\tpublic virtual bool IsAlive => false;\r\n\tpublic virtual bool CantBeMoved => false;\r\n\r\n\tpublic IntVector2 GridPos { get; set; }\r\n\r\n\tpublic bool IsShaking { get; private set; }\r\n\tprivate TimeSince _timeSinceShakeStart; // todo: replace with timer? this can become negative somehow\r\n\tprivate float _shakeTime;\r\n\tprivate EasingType _shakeEasingType;\r\n\tpublic Vector2 ShakeOffset { get; set; }\r\n\tprivate float _shakeStrength;\r\n\r\n\tpublic bool IsMovementControlled { get; set; }\r\n\tprivate Vector3 _moveStartPos;\r\n\tprivate Vector3 _moveTargetPos;\r\n\tprivate float _moveTime;\r\n\tprivate TimeSince _timeSinceMoveStart;\r\n\tprivate EasingType _moveEasingType;\r\n\tprivate bool _moveRemoveControlAfter;\r\n\r\n\tpublic int HP { get; set; }\r\n\r\n\tpublic bool IsLocked { get; set; }\r\n\tpublic int LockTurnsRemaining { get; set; }\r\n\tpublic bool WasJustLocked { get; set; }\r\n\r\n\tpublic int CardTypeID { get; set; }\r\n\r\n\tpublic float IconOpacity { get; set; }\r\n\tpublic bool IsExploding;\r\n\tprivate TimeSince _timeSinceStartExploding;\r\n\tprivate float _explodeTime;\r\n\tprivate EasingType _explodeEasingType;\r\n\tprivate Vector3 _explodeStartPos;\r\n\tprivate EasingType _explodeShakeEasingType;\r\n\r\n\tpublic bool IsScaling;\r\n\tprivate TimeSince _timeSinceScale;\r\n\tprivate float _scaleTime;\r\n\tprivate float _scaleAmount;\r\n\tprivate EasingType _scaleEasingType;\r\n\r\n\tpublic bool AreRevealMarksShown { get; set; }\r\n\r\n\tprotected override void OnAwake()\r\n\t{\r\n\t\tbase.OnAwake();\r\n\r\n\t}\r\n\r\n\tpublic virtual void Init(CardType cardType)\r\n\t{\r\n\t\tCardType = OriginalCardType = cardType;\r\n\r\n\t\tModel = GameObject.Children.Find( x => x.Name == \"model\" );\r\n\t\tModelRenderer = Model.Components.Get<ModelRenderer>();\r\n\r\n\t\tIconOpacity = 1f;\r\n\r\n\t\tIconObj = GameObject.Children.Find( x => x.Name == \"icon\" );\r\n\t\tvar cardIcon = IconObj.Components.Get<CardIcon>( includeDisabled: true );\r\n\t\tcardIcon.Card = this;\r\n\r\n\t\tIconBackObj = GameObject.Children.Find( x => x.Name == \"icon_back\" );\r\n\t\tvar cardIconBack = IconBackObj.Components.Get<CardIconBack>( includeDisabled: true );\r\n\t\tcardIconBack.Card = this;\r\n\r\n\t\tSetMaterial( cardType );\r\n\t}\r\n\r\n\tpublic void SetMaterial(CardType cardType)\r\n\t{\r\n\t\tvar matFilename = GetMaterialFilename( cardType );\r\n\t\tif ( !string.IsNullOrEmpty( matFilename ) )\r\n\t\t\tModelRenderer.MaterialOverride = Material.Load( matFilename );\r\n\t}\r\n\r\n\tpublic void SetCardType(CardType newType)\r\n\t{\r\n\t\tCardType = newType;\r\n\t\tSetMaterial( newType );\r\n\t}\r\n\r\n\tprotected override void OnUpdate()\r\n\t{\r\n\t\t//if ( Input.Down( \"Duck\" ) )\r\n\t\t//{\r\n\t\t//\tvar emoji = Card.GetIconEmoji( CardType );\r\n\t\t//\tif ( string.IsNullOrEmpty( emoji ) )\r\n\t\t//\t{\r\n\t\t//\t\tGizmo.Draw.Color = Color.Black.WithAlpha( 1f );\r\n\t\t//\t\tGizmo.Draw.Text( $\"{CardType}\", new global::Transform( WorldPosition + new Vector3( -0.5f, -0.5f, -1f ) ), size: 24f );\r\n\r\n\t\t//\t\tGizmo.Draw.Color = Color.White.WithAlpha( 1f );\r\n\t\t//\t\tGizmo.Draw.Text( $\"{CardType}\", new global::Transform( WorldPosition ), size: 24f );\r\n\t\t//\t}\r\n\t\t//\telse\r\n\t\t//\t{\r\n\t\t//\t\tGizmo.Draw.Color = Color.White.WithAlpha( 1f );\r\n\t\t//\t\tGizmo.Draw.Text( emoji, new global::Transform( WorldPosition ), size: 60f );\r\n\t\t//\t}\r\n\r\n\t\t//\tif ( IsMovementControlled )\r\n\t\t//\t{\r\n\t\t//\t\tGizmo.Draw.Color = Color.White.WithAlpha( 1f );\r\n\t\t//\t\tGizmo.Draw.Text( \"\u27a1\ufe0f\", new global::Transform( WorldPosition + new Vector3( -15.5f, -19f, -1f ) ), size: 30f );\r\n\t\t//\t}\r\n\r\n\t\t//\tif ( IsLocked )\r\n\t\t//\t{\r\n\t\t//\t\tGizmo.Draw.Color = Color.White.WithAlpha( 1f );\r\n\t\t//\t\tGizmo.Draw.Text( \"\ud83d\udd12\", new global::Transform( WorldPosition + new Vector3( 15.5f, -19f, -1f ) ), size: 30f );\r\n\r\n\t\t//\t\tGizmo.Draw.Color = Color.Red.WithAlpha( 1f );\r\n\t\t//\t\tGizmo.Draw.Text( $\"{LockTurnsRemaining}\", new global::Transform( WorldPosition + new Vector3( 15.5f, -21f, 1f ) ), size: 20f );\r\n\t\t//\t}\r\n\r\n\t\t//\tGizmo.Draw.Color = Color.Black.WithAlpha( 1f );\r\n\t\t//\tGizmo.Draw.Text( $\"ID: {CardTypeID}\", new global::Transform( WorldPosition + new Vector3( -13.5f, 19f, -1f ) + new Vector3( -0.5f, -0.5f, -1f ) ), size: 16 );\r\n\r\n\t\t//\tGizmo.Draw.Color = Color.White.WithAlpha( 1f );\r\n\t\t//\tGizmo.Draw.Text( $\"ID: {CardTypeID}\", new global::Transform( WorldPosition + new Vector3( -13.5f, 19f, -1f ) ), size: 16 );\r\n\t\t//}\r\n\r\n\t\tHandleMoving();\r\n\t\tHandleShaking();\r\n\r\n\t\tif(IsExploding)\r\n\t\t{\r\n\t\t\tHandleExploding();\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tif ( IsScaling )\r\n\t\t{\r\n\t\t\tHandleScaling();\r\n\t\t}\r\n\t\t\r\n\t\t//if(!IsScaling && !IsExploding)\r\n\t\t//{\r\n\t\t//\tTransform.LocalScale = new Vector3( Utils.DynamicEaseTo( Transform.LocalScale.x, IsHovered ? 1.05f : 1f, 0.3f, Time.Delta ) );\r\n\t\t//}\r\n\r\n\t\tif( IsSpawning )\r\n\t\t{\r\n\t\t\t// do nothing\r\n\t\t}\r\n\t\telse if( IsMovementControlled )\r\n\t\t{\r\n\t\t\tLocalPosition += new Vector3( ShakeOffset.x, ShakeOffset.y, 0f ) * Time.Delta * 10f;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tvar targetPos = Manager.GetCardPos( GridPos );\r\n\r\n\t\t\tif ( IsShaking )\r\n\t\t\t\ttargetPos += new Vector3( ShakeOffset.x, ShakeOffset.y, 0f );\r\n\r\n\t\t\tvar height = 10f + ((IsRevealed || IsHovered) ? Globals.CARD_ADD_HEIGHT_REVEALED_OR_HOVERED : 0f) + ((IsRevealed && IsHovered) ? Globals.CARD_ADD_HEIGHT_REVEALED_AND_HOVERED : 0f);\r\n\t\t\tvar zPos = MathX.Lerp( LocalPosition.z, height, Time.Delta * 20f );\r\n\r\n\t\t\tLocalPosition = Vector3.Lerp( LocalPosition, targetPos, Time.Delta * 10f ).WithZ( zPos );\r\n\t\t}\r\n\r\n\t\tvar targetRot = Rotation.FromPitch( IsRevealed ? 0f : 180f );\r\n\t\tWorldRotation = Rotation.Lerp( WorldRotation, targetRot, Time.Delta * 15f );\r\n\t}\r\n\r\n\tpublic void MoveToPos( Vector3 pos, float time, EasingType easingType = EasingType.Linear, bool removeControlAfter = false)\r\n\t{\r\n\t\t//Log.Info( $\"Card - MoveToPos: {pos}\" );\r\n\r\n\t\tIsMovementControlled = true;\r\n\t\t_moveStartPos = LocalPosition;\r\n\t\t_moveTargetPos = pos;\r\n\t\t_moveTime = time;\r\n\t\t_timeSinceMoveStart = 0f;\r\n\t\t_moveEasingType = easingType;\r\n\t\t_moveRemoveControlAfter = removeControlAfter;\r\n\t}\r\n\r\n\tvoid HandleMoving()\r\n\t{\r\n\t\tif ( !IsMovementControlled )\r\n\t\t\treturn;\r\n\r\n\t\tif(_timeSinceMoveStart > _moveTime)\r\n\t\t{\r\n\t\t\tLocalPosition = _moveTargetPos;\r\n\r\n\t\t\tif( _moveRemoveControlAfter )\r\n\t\t\t\tIsMovementControlled = false;\r\n\t\t}\r\n\t\telse \r\n\t\t{\r\n\t\t\tLocalPosition = Vector3.Lerp( _moveStartPos, _moveTargetPos, Utils.Map( _timeSinceMoveStart, 0f, _moveTime, 0f, 1f, _moveEasingType ) );\r\n\t\t}\r\n\t}\r\n\r\n\tprotected void HandleShaking()\r\n\t{\r\n\t\tif ( IsShaking )\r\n\t\t{\r\n\t\t\tif ( _timeSinceShakeStart > _shakeTime )\r\n\t\t\t\tIsShaking = false;\r\n\t\t\telse\r\n\t\t\t\tShakeOffset = Utils.GetRandomVector() * _shakeStrength * Utils.Map( _timeSinceShakeStart, 0f, _shakeTime, 1f, 0f, _shakeEasingType );\r\n\t\t}\r\n\t}\r\n\r\n\tvoid HandleExploding()\r\n\t{\r\n\t\tLocalScale = new Vector3( Utils.Map(_timeSinceStartExploding, 0f, _explodeTime, 1f, Globals.CARD_EXPLODE_SCALE, _explodeEasingType) );\r\n\t\tIconOpacity = Utils.Map( _timeSinceStartExploding, 0f, _explodeTime, 1f, 0f, EasingType.SineIn );\r\n\r\n\t\tLocalPosition = _explodeStartPos + Vector3.Random * Utils.Map( _timeSinceStartExploding, 0f, _explodeTime, 0f, 2f, _explodeShakeEasingType) * Utils.Map( _timeSinceStartExploding, _explodeTime * 0.9f, _explodeTime, 1f, 0f );\r\n\t}\r\n\r\n\tvoid HandleScaling()\r\n\t{\r\n\t\tif(_timeSinceScale > 0.25f)\r\n\t\t{\r\n\t\t\tIsScaling = false;\r\n\t\t\tLocalScale = new Vector3( 1f );\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tLocalScale = Utils.MapReturn( _timeSinceScale, 0f, _scaleTime, 1f, _scaleAmount, _scaleEasingType);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static string GetName( CardType cardType )\r\n\t{\r\n\t\tswitch ( cardType )\r\n\t\t{\r\n\t\t\tcase CardType.CrystalBall: return \"Crystal Ball\";\r\n\t\t\tcase CardType.AncientScroll: return \"Ancient Scroll\";\r\n\t\t\tcase CardType.JumpingSpider: return \"Jumping Spider\";\r\n\t\t\tcase CardType.Rock: return \"Boulder\";\r\n\t\t\tcase CardType.Syringe: return \"Morphine\";\r\n\t\t\tcase CardType.FlyingMoney: return \"Flying Money\";\r\n\t\t\tcase CardType.FortuneCookie: return \"Fortune Cookie\";\r\n\t\t\t//case CardType.AlarmClock: return \"Alarm Clock\";\r\n\t\t\tcase CardType.FaxMachine: return \"Fax Machine\";\r\n\t\t\tcase CardType.MoneyBag: return \"Money Bag\";\r\n\t\t\tcase CardType.MagicWand: return \"Magic Wand\";\r\n\t\t\t//case CardType.StockMarket: return \"Stock Market\";\r\n\t\t\tcase CardType.GuideDog: return \"Guide Dog\";\r\n\t\t\tcase CardType.CarePackage: return \"Care Package\";\r\n\t\t\tcase CardType.BirthdayCake: return \"Birthday Cake\";\r\n\t\t\tcase CardType.DiamondRing: return \"Diamond Ring\";\r\n\t\t\tcase CardType.RedEnvelope: return \"Red Envelope\";\r\n\t\t}\r\n\r\n\t\treturn cardType.ToString();\r\n\t}\r\n\r\n\tpublic static string GetFilename( CardType cardType )\r\n\t{\r\n\t\tswitch ( cardType )\r\n\t\t{\r\n\t\t\tcase CardType.Apple: return \"apple\";\r\n\t\t\tcase CardType.Bomb: return \"bomb\";\r\n\t\t\tcase CardType.Rocket: return \"rocket\";\r\n\t\t\tcase CardType.Telephone: return \"telephone\";\r\n\t\t\tcase CardType.Trumpet: return \"trumpet\";\r\n\t\t\tcase CardType.Pawn: return \"pawn\";\r\n\t\t\tcase CardType.Balloon: return \"balloon\";\r\n\t\t\tcase CardType.Spider: return \"spider\";\r\n\t\t\tcase CardType.Umbrella: return \"umbrella\";\r\n\t\t\tcase CardType.UmbrellaClosed: return \"umbrella_closed\";\r\n\t\t\tcase CardType.Juggler: return \"juggler\";\r\n\t\t\tcase CardType.Coin: return \"coin\";\r\n\t\t\tcase CardType.Vampire: return \"vampire\";\r\n\t\t\tcase CardType.Maracas: return \"maracas\";\r\n\t\t\tcase CardType.Microwave: return \"microwave\";\r\n\t\t\tcase CardType.Raccoon: return \"raccoon\";\r\n\t\t\tcase CardType.Rock: return \"rock\";\r\n\t\t\tcase CardType.Bear: return \"bear\";\r\n\t\t\tcase CardType.Candle: return \"candle\";\r\n\t\t\tcase CardType.CrystalBall: return \"crystal_ball\";\r\n\t\t\tcase CardType.Snake: return \"snake\";\r\n\t\t\tcase CardType.Rose: return \"rose\";\r\n\t\t\tcase CardType.RoseWilted: return \"rose_wilted\";\r\n\t\t\tcase CardType.Bell: return \"bell\";\r\n\t\t\tcase CardType.Ant: return \"ant\";\r\n\t\t\tcase CardType.Twins: return \"twins\";\r\n\t\t\tcase CardType.Compass: return \"compass\";\r\n\t\t\tcase CardType.Antenna: return \"antenna\";\r\n\t\t\tcase CardType.Wizard: return \"wizard\";\r\n\t\t\tcase CardType.AncientScroll: return \"scroll\";\r\n\t\t\tcase CardType.JumpingSpider: return \"spider_jumping\";\r\n\t\t\tcase CardType.Bento: return \"bento\";\r\n\t\t\t//case CardType.StockMarket: return \"investment\";\r\n\t\t\t//case CardType.StockMarketDown: return \"investment_down\";\r\n\t\t\tcase CardType.Ninja: return \"ninja\";\r\n\t\t\tcase CardType.Flashlight: return \"flashlight\";\r\n\t\t\tcase CardType.Bodybuilder: return \"bodybuilder\";\r\n\t\t\tcase CardType.Helicopter: return \"helicopter\";\r\n\t\t\tcase CardType.FlyingMoney: return \"flying_money\";\r\n\t\t\tcase CardType.Syringe: return \"syringe\";\r\n\t\t\tcase CardType.Cellphone: return \"cellphone\";\r\n\t\t\tcase CardType.Crab: return \"crab\";\r\n\t\t\tcase CardType.Magnet: return \"magnet\";\r\n\t\t\tcase CardType.Stethoscope: return \"stethoscope\";\r\n\t\t\tcase CardType.UFO: return \"ufo\";\r\n\t\t\tcase CardType.Teacher: return \"teacher\";\r\n\t\t\tcase CardType.Tractor: return \"tractor\";\r\n\t\t\tcase CardType.FortuneCookie: return \"fortune_cookie\";\r\n\t\t\tcase CardType.Dolphin: return \"dolphin\";\r\n\t\t\tcase CardType.Detective: return \"detective\";\r\n\t\t\tcase CardType.Genie: return \"genie\";\r\n\t\t\tcase CardType.Potion: return \"potion\";\r\n\t\t\tcase CardType.Mirror: return \"mirror\";\r\n\t\t\t//case CardType.AlarmClock: return \"alarm_clock\";\r\n\t\t\tcase CardType.FaxMachine: return \"fax_machine\";\r\n\t\t\tcase CardType.Dice: return \"dice\";\r\n\t\t\tcase CardType.Coffee: return \"coffee\";\r\n\t\t\tcase CardType.Tree: return \"tree\";\r\n\t\t\tcase CardType.Chipmunk: return \"chipmunk\";\r\n\t\t\tcase CardType.Dancer: return \"dancer\";\r\n\t\t\tcase CardType.Wine: return \"wine\";\r\n\t\t\tcase CardType.King: return \"king\";\r\n\t\t\tcase CardType.Police: return \"police\";\r\n\t\t\tcase CardType.Ogre: return \"ogre\";\r\n\t\t\tcase CardType.Clown: return \"clown\";\r\n\t\t\tcase CardType.MoneyBag: return \"money_bag\";\r\n\t\t\tcase CardType.Bank: return \"bank\";\r\n\t\t\tcase CardType.Steak: return \"steak\";\r\n\t\t\tcase CardType.MagicWand: return \"magic_wand\";\r\n\t\t\tcase CardType.Carrot: return \"carrot\";\r\n\t\t\tcase CardType.Caterpillar: return \"caterpillar\";\r\n\t\t\tcase CardType.Butterfly: return \"butterfly\";\r\n\t\t\tcase CardType.Tornado: return \"tornado\";\r\n\t\t\tcase CardType.Map: return \"map\";\r\n\t\t\tcase CardType.Cockroach: return \"cockroach\";\r\n\t\t\t//case CardType.Key: return \"key\";\r\n\t\t\tcase CardType.Broccoli: return \"broccoli\";\r\n\t\t\t//case CardType.ATM: return \"atm\";\r\n\t\t\tcase CardType.Donut: return \"donut\";\r\n\t\t\tcase CardType.Cheese: return \"cheese\";\r\n\t\t\tcase CardType.CheeseMoldy: return \"cheese_moldy\";\r\n\t\t\tcase CardType.GuideDog: return \"guide_dog\";\r\n\t\t\tcase CardType.Taco: return \"taco\";\r\n\t\t\tcase CardType.Burrito: return \"burrito\";\r\n\t\t\tcase CardType.Skunk: return \"skunk\";\r\n\t\t\tcase CardType.CarePackage: return \"care_package\";\r\n\t\t\tcase CardType.Cash: return \"cash\";\r\n\t\t\tcase CardType.BirthdayCake: return \"birthday_cake\";\r\n\t\t\tcase CardType.Gem: return \"gem\";\r\n\t\t\tcase CardType.Pickaxe: return \"pickaxe\";\r\n\t\t\tcase CardType.Mountain: return \"mountain\";\r\n\t\t\tcase CardType.DiamondRing: return \"diamond_ring\";\r\n\t\t\tcase CardType.Rabbit: return \"rabbit\";\r\n\t\t\tcase CardType.RedEnvelope: return \"red_envelope\";\r\n\t\t}\r\n\r\n\t\treturn \"\";\r\n\t}\r\n\r\n\tpublic static string GetIconFilename( CardType cardType )\r\n\t{\r\n\t\treturn $\"textures/{GetFilename( cardType )}.png\";\r\n\t}\r\n\r\n\tpublic static string GetMaterialFilename( CardType cardType )\r\n\t{\r\n\t\treturn $\"materials/cards/{GetFilename( cardType )}.vmat\";\r\n\t}\r\n\r\n\tpublic static string GetIconEmoji( CardType cardType )\r\n\t{\r\n\t\tswitch ( cardType )\r\n\t\t{\r\n\t\t\tcase CardType.Apple: return \"\ud83c\udf4e\";\r\n\t\t\tcase CardType.Bomb: return \"\ud83d\udca3\";\r\n\t\t\tcase CardType.Rocket: return \"\ud83d\ude80\";\r\n\t\t\tcase CardType.Telephone: return \"\u260e\ufe0f\";\r\n\t\t\tcase CardType.Trumpet: return \"\ud83c\udfba\";\r\n\t\t\tcase CardType.Pawn: return \"\u265f\ufe0f\";\r\n\t\t\tcase CardType.Balloon: return \"\ud83c\udf88\";\r\n\t\t\tcase CardType.Spider: return \"\ud83d\udd77\ufe0f\";\r\n\t\t\tcase CardType.Umbrella: return \"\u2602\ufe0f\";\r\n\t\t\tcase CardType.UmbrellaClosed: return \"\ud83c\udf02\";\r\n\t\t\tcase CardType.Juggler: return \"\ud83e\udd39\";\r\n\t\t\tcase CardType.Coin: return \"\ud83d\udfe1\";\r\n\t\t\tcase CardType.Vampire: return \"\ud83e\udddb\";\r\n\t\t\tcase CardType.Raccoon: return \"\ud83e\udd9d\";\r\n\t\t\tcase CardType.Rock: return \"\ud83d\uddff\";\r\n\t\t\tcase CardType.Bear: return \"\ud83d\udc3b\";\r\n\t\t\tcase CardType.Candle: return \"\ud83d\udd6f\ufe0f\";\r\n\t\t\tcase CardType.CrystalBall: return \"\ud83d\udd2e\";\r\n\t\t\tcase CardType.Snake: return \"\ud83d\udc0d\";\r\n\t\t\tcase CardType.Rose: return \"\ud83c\udf39\";\r\n\t\t\tcase CardType.RoseWilted: return \"\ud83e\udd40\";\r\n\t\t\tcase CardType.Bell: return \"\ud83d\udd14\";\r\n\t\t\tcase CardType.Ant: return \"\ud83d\udc1c\";\r\n\t\t\tcase CardType.Twins: return \"\ud83d\udc6f\";\r\n\t\t\tcase CardType.Compass: return \"\ud83e\udded\";\r\n\t\t\tcase CardType.Antenna: return \"\ud83d\udce1\";\r\n\t\t\tcase CardType.Wizard: return \"\ud83e\uddd9\";\r\n\t\t\tcase CardType.AncientScroll: return \"\ud83d\udcdc\";\r\n\t\t\tcase CardType.JumpingSpider: return \"\ud83d\udd77\ufe0f\";\r\n\t\t\tcase CardType.Bento: return \"\ud83c\udf71\";\r\n\t\t\t//case CardType.StockMarket: return \"\ud83d\udcc8\";\r\n\t\t\t//case CardType.StockMarketDown: return \"\ud83d\udcc9\";\r\n\t\t\tcase CardType.Ninja: return \"\ud83d\udc31\u200d\ud83d\udc64\";\r\n\t\t\tcase CardType.Flashlight: return \"\ud83d\udd26\";\r\n\t\t\tcase CardType.Bodybuilder: return \"\ud83c\udfcb\ufe0f\";\r\n\t\t\tcase CardType.Helicopter: return \"\ud83d\ude81\";\r\n\t\t\tcase CardType.FlyingMoney: return \"\ud83d\udcb8\";\r\n\t\t\tcase CardType.Syringe: return \"\ud83d\udc89\";\r\n\t\t\tcase CardType.Cellphone: return \"\ud83d\udcf1\";\r\n\t\t\tcase CardType.Crab: return \"\ud83e\udd80\";\r\n\t\t\tcase CardType.Magnet: return \"\ud83e\uddf2\";\r\n\t\t\tcase CardType.Stethoscope: return \"\ud83e\ude7a\";\r\n\t\t\tcase CardType.UFO: return \"\ud83d\udef8\";\r\n\t\t\tcase CardType.Teacher: return \"\ud83d\udc69\u200d\ud83c\udfeb\";\r\n\t\t\tcase CardType.Tractor: return \"\ud83d\ude9c\";\r\n\t\t\tcase CardType.FortuneCookie: return \"\ud83e\udd60\";\r\n\t\t\tcase CardType.Dolphin: return \"\ud83d\udc2c\";\r\n\t\t\tcase CardType.Detective: return \"\ud83d\udd75\ufe0f\";\r\n\t\t\tcase CardType.Genie: return \"\ud83e\uddde\";\r\n\t\t\tcase CardType.Potion: return \"\ud83e\uddea\";\r\n\t\t\tcase CardType.Mirror: return \"\ud83d\udd18\";\r\n\t\t\t//case CardType.AlarmClock: return \"\u23f0\";\r\n\t\t\tcase CardType.FaxMachine: return \"\ud83d\udce0\";\r\n\t\t\tcase CardType.Dice: return \"\ud83c\udfb2\";\r\n\t\t\tcase CardType.Coffee: return \"\u2615\";\r\n\t\t\tcase CardType.Tree: return \"\ud83c\udf33\";\r\n\t\t\tcase CardType.Chipmunk: return \"\ud83d\udc3f\ufe0f\";\r\n\t\t\tcase CardType.Dancer: return \"\ud83d\udc83\";\r\n\t\t\tcase CardType.Wine: return \"\ud83c\udf77\";\r\n\t\t\tcase CardType.King: return \"\ud83e\udd34\";\r\n\t\t\tcase CardType.Police: return \"\ud83d\udc6e\";\r\n\t\t\tcase CardType.Ogre: return \"\ud83d\udc79\";\r\n\t\t\tcase CardType.Clown: return \"\ud83e\udd21\";\r\n\t\t\tcase CardType.MoneyBag: return \"\ud83d\udcb0\";\r\n\t\t\tcase CardType.Bank: return \"\ud83c\udfe6\";\r\n\t\t\tcase CardType.Steak: return \"\ud83e\udd69\";\r\n\t\t\tcase CardType.MagicWand: return \"\u2728\";\r\n\t\t\tcase CardType.Carrot: return \"\ud83e\udd55\";\r\n\t\t\tcase CardType.Caterpillar: return \"\ud83d\udc1b\";\r\n\t\t\tcase CardType.Butterfly: return \"\ud83e\udd8b\";\r\n\t\t\tcase CardType.Tornado: return \"\ud83c\udf2a\ufe0f\";\r\n\t\t\tcase CardType.Map: return \"\ud83d\uddfa\ufe0f\";\r\n\t\t\tcase CardType.Cockroach: return \"\ud80c\udda3\";\r\n\t\t\t//case CardType.Key: return \"\ud83d\udd11\";\r\n\t\t\tcase CardType.Broccoli: return \"\ud83e\udd66\";\r\n\t\t\t//case CardType.ATM: return \"\ud83c\udfe7\";\r\n\t\t\tcase CardType.Donut: return \"\ud83c\udf69\";\r\n\t\t\tcase CardType.Cheese: return \"\ud83e\uddc0\";\r\n\t\t\tcase CardType.CheeseMoldy: return \"\ud83e\udd2e\";\r\n\t\t\tcase CardType.GuideDog: return \"\ud83e\uddae\";\r\n\t\t\tcase CardType.Taco: return \"\ud83c\udf2e\";\r\n\t\t\tcase CardType.Burrito: return \"\ud83c\udf2f\";\r\n\t\t\tcase CardType.Skunk: return \"\ud83e\udda8\";\r\n\t\t\tcase CardType.CarePackage: return \"\ud83d\udce6\";\r\n\t\t\tcase CardType.Cash: return \"\ud83d\udcb5\";\r\n\t\t\tcase CardType.BirthdayCake: return \"\ud83c\udf82\";\r\n\t\t\tcase CardType.Gem: return \"\ud83d\udc8e\";\r\n\t\t\tcase CardType.Pickaxe: return \"\u26cf\ufe0f\";\r\n\t\t\tcase CardType.Mountain: return \"\u26f0\ufe0f\";\r\n\t\t\tcase CardType.DiamondRing: return \"\ud83d\udc8d\";\r\n\t\t\tcase CardType.Rabbit: return \"\ud83d\udc07\";\r\n\t\t\tcase CardType.RedEnvelope: return \"\ud83e\udde7\";\r\n\t\t}\r\n\r\n\t\treturn \"\";\r\n\t}\r\n\r\n\tpublic static string GetCardDescription( CardType cardType )\r\n\t{\r\n\t\tswitch ( cardType )\r\n\t\t{\r\n\t\t\tcase CardType.Apple: return \"\u2705Match: +1 HP\";\r\n\t\t\tcase CardType.Bomb: return \"\u2705Match: reveal nearby cards then shuffle them\";\r\n\t\t\tcase CardType.Rocket: return \"\u274cMismatch: swap position\";\r\n\t\t\tcase CardType.Telephone: return \"\u2705Match: shake nearby pairs\";\r\n\t\t\tcase CardType.Trumpet: return \"Match other cards: 30% chance to reveal\";\r\n\t\t\tcase CardType.Pawn: return \"\u27a1\ufe0fTurn start: 50% to try to move upward\";\r\n\t\t\tcase CardType.Balloon: return \"Floats upward\";\r\n\t\t\tcase CardType.Spider: return \"Starts in a corner\";\r\n\t\t\tcase CardType.Umbrella: return \"\ud83d\udc41\ufe0fRevealed: toggle open/closed\";\r\n\t\t\tcase CardType.Juggler: return \"\u274cMismatch: rearrange nearby cards\";\r\n\t\t\tcase CardType.Coin: return \"\u2705Match: +$2\";\r\n\t\t\tcase CardType.Vampire: return \"While hovered: shake if you lose HP\";\r\n\t\t\tcase CardType.Maracas: return \"Makes a distinctive sound when shook\";\r\n\t\t\tcase CardType.Microwave: return \"\u27a1\ufe0fTurn start: shake a nearby food/drink\";\r\n\t\t\tcase CardType.Raccoon: return \"\u27a1\ufe0fTurn start: shake an adjacent card\";\r\n\t\t\tcase CardType.Rock: return \"Falls downward, pushing other cards\";\r\n\t\t\tcase CardType.Bear: return \"Food/drink revealed: 20% chance to shake\";\r\n\t\t\tcase CardType.Candle: return \"\u2705Match: each reveal a nearby card\";\r\n\t\t\tcase CardType.CrystalBall: return \"\u274cMismatch: shake cards, including other card's match\";\r\n\t\t\tcase CardType.Snake: return \"Shake when an adjacent card is matched\";\r\n\t\t\tcase CardType.Rose: return \"When moved: shake and wilt\";\r\n\t\t\tcase CardType.Bell: return \"Rings when moved or shook\";\r\n\t\t\tcase CardType.Ant: return \"\u27a1\ufe0fTurn start: move to nearby empty space\";\r\n\t\t\tcase CardType.Twins: return \"\ud83d\udc41\ufe0fRevealed: nudge toward match\";\r\n\t\t\tcase CardType.Compass: return \"\u274cMismatch: nudge toward other card's match\";\r\n\t\t\tcase CardType.Antenna: return \"\ud83d\udc41\ufe0fRevealed: sound indicates distance to match\";\r\n\t\t\tcase CardType.Wizard: return \"\u23e9Every 3 turns: shuffle some cards\\n\ud83d\udc94Damage Wizard: respawn some cards and shuffle them\";\r\n\t\t\tcase CardType.AncientScroll: return \"\u2705Match: reveal all cards (after next turn, shuffle all cards)\";\r\n\t\t\tcase CardType.JumpingSpider: return \"Starts in a corner\\n\u274cMismatch: swaps with an unknown card\";\r\n\t\t\tcase CardType.Bento: return \"\u2705Match: +1 HP for each consecutive match\";\r\n\t\t\t//case CardType.StockMarket: return \"\u2705Match: gain half your money if the market is up, lose half if the market is down\";\r\n\t\t\tcase CardType.Ninja: return \"\u27a1\ufe0fTurn start: 25% chance to swap position\";\r\n\t\t\tcase CardType.Flashlight: return \"\ud83d\udc46Chosen: reveal a nearby card\";\r\n\t\t\tcase CardType.Bodybuilder: return \"\u27a1\ufe0fTurn start: move adjacent card upwards, pushing other cards\";\r\n\t\t\tcase CardType.Helicopter: return \"\u274cMismatch: relocate along with an adjacent card\";\r\n\t\t\tcase CardType.FlyingMoney: return \"\u2705Match: +$3\\n\u274cMismatch: shuffle with some nearby cards\";\r\n\t\t\tcase CardType.Syringe: return \"\u2705Match: heal all HP, but time is halved until end of level\";\r\n\t\t\tcase CardType.Cellphone: return \"\u274cMismatch: vibrate nearest cellphone\";\r\n\t\t\tcase CardType.Crab: return \"Moves sideways to empty spaces\";\r\n\t\t\tcase CardType.Magnet: return \"\u27a1\ufe0fTurn start: pull card toward it\";\r\n\t\t\tcase CardType.Stethoscope: return \"\u2705Match/\u274cMismatch: shake nearby cards with a heartbeat\";\r\n\t\t\tcase CardType.UFO: return \"\u2705Match: abduct and shuffle nearby cards\";\r\n\t\t\tcase CardType.Teacher: return \"\u2705Match: arrange row alphabetically\";\r\n\t\t\tcase CardType.Tractor: return \"\u2705Match/\u274cMismatch: shift row one space\";\r\n\t\t\tcase CardType.FortuneCookie: return \"\u2705Match: +1 HP, and reveal 5 cards that you haven't seen before\";\r\n\t\t\tcase CardType.Dolphin: return \"Revealed on 4 consecutive mismatches\";\r\n\t\t\tcase CardType.Detective: return \"\u27a1\ufe0fTurn start: 50% chance to reveal a nearby card\";\r\n\t\t\tcase CardType.Genie: return \"\u2705Match: next card chosen shakes its match\";\r\n\t\t\tcase CardType.Potion: return \"\u2705Match: your HP is set to half\";\r\n\t\t\tcase CardType.Mirror: return \"\u2705Match: flip the level horizontally\";\r\n\t\t\t//case CardType.AlarmClock: return \"70% chance to shake when timer goes below 1s\";\r\n\t\t\tcase CardType.FaxMachine: return \"\ud83d\udc46Chosen: shake 3 cards, including another fax machine\";\r\n\t\t\tcase CardType.Dice: return \"\u274cMismatch: roll a 6 to get +$2 (rolling 1 loses $1)\\n\u2705Match: Roll a 12 to get +$5 (rolling 2 loses $2)\";\r\n\t\t\tcase CardType.Coffee: return \"\u2705Match: +2 HP, and your next turn lasts 3 seconds\";\r\n\t\t\tcase CardType.Tree: return \"\u2705Match: reveal an apple\";\r\n\t\t\tcase CardType.Chipmunk: return \"\u27a1\ufe0fTurn start: move toward a tree\";\r\n\t\t\tcase CardType.Dancer: return \"\u274cMismatch: dance with multiple partners\";\r\n\t\t\tcase CardType.Wine: return \"\u2705Match: +2 HP\\nShake if an adjacent card shakes\";\r\n\t\t\tcase CardType.King: return \"\ud83d\udc94Damage King: he decides your next choice\";\r\n\t\t\tcase CardType.Police: return \"\u274cMismatch: Reveal and lock a nearby card for 2 turns\\n\ud83d\udc94Damage Police: Lock some nearby cards for 1 turn, then shuffle\";\r\n\t\t\tcase CardType.Ogre: return \"\ud83d\udc94Damage Ogre: shuffle with nearby cards\";\r\n\t\t\tcase CardType.Clown: return \"\ud83d\udc94Damage Clown: swap multiple times\";\r\n\t\t\tcase CardType.MoneyBag: return \"\u2705Match: +$5\";\r\n\t\t\tcase CardType.Bank: return \"\u2705Match: transform all coins into money bags\";\r\n\t\t\tcase CardType.Steak: return \"\u2705Match: -$1 and +4 HP\";\r\n\t\t\tcase CardType.MagicWand: return \"\u274cMismatch: send other card adjacent to its match\";\r\n\t\t\tcase CardType.Carrot: return \"\u2705Match: +3 HP, and timer is doubled next turn\";\r\n\t\t\tcase CardType.Caterpillar: return \"\u27a1\ufe0fTurn start: 20% chance to shake and transform into a butterfly\";\r\n\t\t\tcase CardType.Tornado: return \"\u27a1\ufe0fTurn start: swap 2 adjacent cards\";\r\n\t\t\tcase CardType.Map: return \"\u2705Match: reveal all cards between\";\r\n\t\t\tcase CardType.Cockroach: return \"Can't be matched until no other cards remain\";\r\n\t\t\t//case CardType.Key: return \"\u274cMismatch: unlock other card\\n\u2705Match: unlock all cards\";\r\n\t\t\tcase CardType.Broccoli: return \"\u2705Match: +3 HP\";\r\n\t\t\t//case CardType.ATM: return \"\u2705Match: +$1 for every 2 matches you've made this level (max: $7)\";\r\n\t\t\tcase CardType.Donut: return \"\u2705Match: +1 HP for each adjacent empty space\";\r\n\t\t\tcase CardType.Cheese: return \"\u2705Match: +3 HP if not moldy\\nGoes moldy after 7 turns\";\r\n\t\t\tcase CardType.GuideDog: return \"When you mismatch other cards, 50% chance to point toward one of their matches\";\r\n\t\t\tcase CardType.Taco: return \"\u2705Match: +1 Max HP\";\r\n\t\t\tcase CardType.Burrito: return \"\u2705Match: +2 Max HP\";\r\n\t\t\tcase CardType.Skunk: return \"Can't be matched while adjacent to any non-skunks\";\r\n\t\t\tcase CardType.CarePackage: return \"\u2705Match: +$1 for every 2 HP missing\";\r\n\t\t\tcase CardType.Cash: return \"\u2705Match: +$3 if both cash are nearby eachother, otherwise +$1\";\r\n\t\t\tcase CardType.BirthdayCake: return \"\u2705Match: +1 HP and +$2\";\r\n\t\t\tcase CardType.Gem: return \"\u2705Match: +$4\";\r\n\t\t\tcase CardType.Pickaxe: return \"\u2705Match: transform all boulders into gems\";\r\n\t\t\tcase CardType.Mountain: return \"Can't be moved\";\r\n\t\t\tcase CardType.DiamondRing: return \"\u2705Match: +$3 if no other cards remain, otherwise +$1\";\r\n\t\t\tcase CardType.Rabbit: return \"\u27a1\ufe0fTurn start: 50% chance to move towards its match\";\r\n\t\t\tcase CardType.RedEnvelope: return \"\u2705Match: if you have less than $5, set your money to $5\";\r\n\t\t}\r\n\r\n\t\treturn \"\";\r\n\t}\r\n\r\n\tpublic static string GetCardExplanation( CardType cardType )\r\n\t{\r\n\t\tswitch ( cardType )\r\n\t\t{\r\n\t\t\tcase CardType.Ant: return \"\\\"nearby\\\" means 1 space away\\n(including diagonal)\";\r\n\t\t\tcase CardType.Snake: return \"\\\"adjacent\\\" means 1 space away\\n(NOT including diagonal)\";\r\n\t\t}\r\n\r\n\t\treturn \"\";\r\n\t}\r\n\r\n\tpublic static bool HasHP( CardType cardType )\r\n\t{\r\n\t\tswitch ( cardType )\r\n\t\t{\r\n\t\t\tcase CardType.Ogre: return true;\r\n\t\t\tcase CardType.Clown: return true;\r\n\t\t\tcase CardType.Police: return true;\r\n\t\t\tcase CardType.Wizard: return true;\r\n\t\t\tcase CardType.King: return true;\r\n\t\t}\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\tpublic static int GetMaxHP( CardType cardType )\r\n\t{\r\n\t\tswitch ( cardType )\r\n\t\t{\r\n\t\t\tcase CardType.Ogre: return 2;\r\n\t\t\tcase CardType.Clown: return 2;\r\n\t\t\tcase CardType.Police: return 2;\r\n\t\t\tcase CardType.Wizard: return 3;\r\n\t\t\tcase CardType.King: return 3;\r\n\t\t}\r\n\r\n\t\treturn 0;\r\n\t}\r\n\r\n\tpublic void SetRevealedFromAsync(bool revealed)\r\n\t{\r\n\t\tIsRevealed = revealed;\r\n\r\n\t\tif( Card.HasHP(CardType) || IsLocked )\r\n\t\t\tIconObj.Enabled = revealed;\r\n\r\n\t\tif ( revealed )\r\n\t\t{\r\n\t\t\tNumTimesRevealed++;\r\n\r\n\t\t\tif(Manager.Instance.Stats[StatType.MaxCrayonMarks] > 0f && !AreRevealMarksShown)\r\n\t\t\t\tShowRevealMarks();\r\n\t\t}\r\n\t}\r\n\r\n\tpublic void SetRevealedInstant()\r\n\t{\r\n\t\tIsRevealed = true;\r\n\r\n\t\tif ( Card.HasHP( CardType ) || IsLocked )\r\n\t\t\tIconObj.Enabled = true;\r\n\r\n\t\tWorldRotation = Rotation.FromPitch( 0f );\r\n\r\n\t\tNumTimesRevealed++;\r\n\r\n\t\tif ( Manager.Instance.Stats[StatType.MaxCrayonMarks] > 0f && !AreRevealMarksShown )\r\n\t\t\tShowRevealMarks();\r\n\t}\r\n\r\n\tpublic void SetHiddenInstant()\r\n\t{\r\n\t\tIsRevealed = false;\r\n\r\n\t\tif ( Card.HasHP( CardType ) )\r\n\t\t\tIconObj.Enabled = false;\r\n\r\n\t\tWorldRotation = Rotation.FromPitch( 180f );\r\n\t}\r\n\r\n\tpublic void Shake( float strength, float time = 0.6f, EasingType easingType = EasingType.QuadOut, bool playSfx = true )\r\n\t{\r\n\t\tIsShaking = true;\r\n\t\t_timeSinceShakeStart = 0f;\r\n\t\t_shakeTime = time;\r\n\t\t_shakeEasingType = easingType;\r\n\t\t_shakeStrength = strength;\r\n\r\n\t\tif ( playSfx || ( OverrideShakeSfx && ( IsRevealed || !OnlyOverrideShakeSfxWhenRevealed ) )  )\r\n\t\t\tPlayShakeSfx();\r\n\t}\r\n\r\n\tpublic virtual void PlayShakeSfx()\r\n\t{\r\n\t\t//Manager.Instance.PlayCardSfx( \"shake_1\", this, volume: 1.1f, pitch: Game.Random.Float( 1.4f, 1.6f ) );\r\n\t\tManager.Instance.PlayCardSfx( \"shake_3\", this, volume: 1.35f, pitch: Game.Random.Float( 0.94f, 1.06f ) );\r\n\t}\r\n\r\n\tpublic virtual bool OverrideShakeSfx => false;\r\n\tpublic virtual bool OnlyOverrideShakeSfxWhenRevealed => false;\r\n\r\n\tpublic virtual void PlayHurtSfx(Card card0, Card card1)\r\n\t{\r\n\t\tManager.Instance.PlayCardSfxBetween( \"shake_1\", card0, card1, volume: 1.3f, pitch: Game.Random.Float( 1.4f, 1.6f ) );\r\n\t}\r\n\r\n\t// return false if needed to change start pos\r\n\tpublic virtual bool ValidateStartingGridPos()\r\n\t{\r\n\t\treturn true;\r\n\t}\r\n\r\n\tpublic virtual bool ShouldHandleEvent( EventType eventType )\r\n\t{\r\n\t\treturn false;\r\n\t}\r\n\r\n\tpublic virtual async Task HandleEventAsync(EventType eventType)\r\n\t{\r\n\t\tawait Task.Frame();\r\n\t}\r\n\r\n\tpublic virtual string GetEventText( EventType eventType )\r\n\t{\r\n\t\treturn GetCardDescription( CardType );\r\n\t}\r\n\r\n\tpublic async Task Hurt()\r\n\t{\r\n\t\tif(!Card.HasHP(CardType))\r\n\t\t{\r\n\t\t\tLog.Error( $\"Trying to hurt card {CardType} with no HP!\" );\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\tHP--;\r\n\t\tawait Manager.Instance.ShakeCard(this, playSfx: false);\r\n\t}\r\n\r\n\tpublic void Lock(int numTurns)\r\n\t{\r\n\t\tIsLocked = true;\r\n\t\tLockTurnsRemaining = numTurns;\r\n\t\tWasJustLocked = true;\r\n\t}\r\n\r\n\tpublic void Unlock()\r\n\t{\r\n\t\tIsLocked = false;\r\n\t}\r\n\r\n\tpublic virtual bool CanBeMatched()\r\n\t{\r\n\t\treturn true;\r\n\t}\r\n\r\n\tpublic void StartExploding(float explodeTime)\r\n\t{\r\n\t\tIsExploding = true;\r\n\t\t_timeSinceStartExploding = 0f;\r\n\t\t_explodeTime = explodeTime;\r\n\t\t_explodeStartPos = LocalPosition;\r\n\r\n\t\tint rand = Game.Random.Int( 0, 5 );\r\n\t\tswitch(rand)\r\n\t\t{\r\n\t\t\tcase 0: _explodeEasingType = EasingType.SineIn; break;\r\n\t\t\tcase 1: _explodeEasingType = EasingType.SineIn; break;\r\n\t\t\tcase 2: _explodeEasingType = EasingType.QuadIn; break;\r\n\t\t\tcase 3: _explodeEasingType = EasingType.QuadIn; break;\r\n\t\t\tcase 4: _explodeEasingType = EasingType.QuartIn; break;\r\n\t\t\tcase 5: _explodeEasingType = EasingType.Linear; break;\r\n\t\t}\r\n\r\n\t\trand = Game.Random.Int( 0, 7 );\r\n\t\tswitch ( rand )\r\n\t\t{\r\n\t\t\tcase 0: _explodeShakeEasingType = EasingType.SineIn; break;\r\n\t\t\tcase 1: _explodeShakeEasingType = EasingType.QuadIn; break;\r\n\t\t\tcase 2: _explodeShakeEasingType = EasingType.QuartIn; break;\r\n\t\t\tcase 3: _explodeShakeEasingType = EasingType.ExpoIn; break;\r\n\t\t\tcase 4: _explodeShakeEasingType = EasingType.ExpoIn; break;\r\n\t\t\tcase 5: _explodeShakeEasingType = EasingType.ExpoIn; break;\r\n\t\t\tcase 6: _explodeShakeEasingType = EasingType.ExpoIn; break;\r\n\t\t\tcase 7: _explodeShakeEasingType = EasingType.Linear; break;\r\n\t\t}\r\n\t}\r\n\r\n\tpublic void Explode( int breakNum )\r\n\t{\r\n\t\tvar scale = LocalScale.x * Model.LocalScale.x;\r\n\t\tManager.Instance.SpawnCardBreak( WorldPosition, LocalRotation, scale, ModelRenderer.MaterialOverride, breakNum );\r\n\t\tManager.Instance.PlayCardSfx( \"explosion\", this, volume: Game.Random.Float( 0.4f, 0.55f ), pitch: Game.Random.Float( 0.4f, 2f ) );\r\n\r\n\t\tManager.Instance.CardBreakParticlesPrefab.Clone( WorldPosition.WithZ( 25f ) );\r\n\t\tManager.Instance.CardBreakShockwavePrefab.Clone( WorldPosition.WithZ( 55f ) );\r\n\r\n\t\tGameObject.Destroy();\r\n\t}\r\n\r\n\tpublic void StartScaling(float time, float amount, EasingType easingType)\r\n\t{\r\n\t\tIsScaling = true;\r\n\t\t_timeSinceScale = 0f;\r\n\t\t_scaleTime = time;\r\n\t\t_scaleAmount = amount;\r\n\t\t_scaleEasingType = easingType;\r\n\t}\r\n\r\n\tpublic void ShowFlag()\r\n\t{\r\n\t\tIsFlagShown = true;\r\n\t\tIconBackObj.Enabled = true;\r\n\t}\r\n\r\n\tpublic void HideFlag()\r\n\t{\r\n\t\tIsFlagShown = false;\r\n\r\n\t\tif ( !AreRevealMarksShown )\r\n\t\t\tIconBackObj.Enabled = false;\r\n\t}\r\n\r\n\tpublic void ShowRevealMarks()\r\n\t{\r\n\t\tAreRevealMarksShown = true;\r\n\t\tIconBackObj.Enabled = true;\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "facepunch.memory",
            "Path": "cards/CardBank.cs",
            "FileName": "CardBank.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 398889,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System.Net;\r\nusing System.Threading.Tasks;\r\n\r\npublic class CardBank : Card\r\n{\r\n\tpublic override bool ShouldHandleEvent( EventType eventType )\r\n\t{\r\n\t\tswitch ( eventType )\r\n\t\t{\r\n\t\t\tcase EventType.Match:\r\n\t\t\t\treturn Manager.Instance.ChosenCards[1] == this;\r\n\t\t}\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\tpublic override async Task HandleEventAsync( EventType eventType )\r\n\t{\r\n\t\tManager.Instance.PushEventMessage( this, eventType );\r\n\r\n\t\tawait Task.DelayRealtime( 150 );\r\n\r\n\t\tvar coins = Manager.Instance.Cards.Where( x => x.CardType == CardType.Coin ).ToList();\r\n\t\tif(coins.Count > 0)\r\n\t\t{\r\n\t\t\tManager.Instance.PlayCardSfxBetween( \"bank\", Manager.Instance.ChosenCards[0], Manager.Instance.ChosenCards[1], volume: 1f, pitch: Game.Random.Float( 0.9f, 1.1f ) );\r\n\r\n\t\t\tawait Task.DelayRealtime( 50 );\r\n\r\n\t\t\tforeach ( var coin in coins )\r\n\t\t\t\tcoin.SetCardType( CardType.MoneyBag );\r\n\r\n\t\t\tawait Task.DelayRealtime( 300 );\r\n\t\t}\r\n\r\n\t\tawait Task.DelayRealtime( 800 );\r\n\r\n\t\tManager.Instance.PopEventMessage();\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "facepunch.memory",
            "Path": "cards/CardCockroach.cs",
            "FileName": "CardCockroach.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 398889,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System.Threading.Tasks;\r\n\r\npublic class CardCockroach : Card\r\n{\r\n\tpublic override bool IsAlive => true;\r\n\r\n\tpublic override bool ShouldHandleEvent( EventType eventType )\r\n\t{\r\n\t\treturn eventType == EventType.Mismatch && Manager.Instance.ChosenCards[0].CardType == CardType.Cockroach && Manager.Instance.ChosenCards[1].CardType == CardType.Cockroach && !IsLocked;\r\n\t}\r\n\r\n\tpublic override async Task HandleEventAsync( EventType eventType )\r\n\t{\r\n\t\tManager.Instance.PushEventMessage( this, eventType );\r\n\r\n\t\tawait Task.DelayRealtime( 850 );\r\n\r\n\t\tManager.Instance.PopEventMessage();\r\n\t}\r\n\r\n\tpublic override bool CanBeMatched()\r\n\t{\r\n\t\tforeach(var card in Manager.Instance.Cards)\r\n\t\t{\r\n\t\t\tif ( card.CardType != CardType.Cockroach )\r\n\t\t\t\treturn false;\r\n\t\t}\r\n\r\n\t\treturn true;\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "facepunch.memory",
            "Path": "cards/CardDancer.cs",
            "FileName": "CardDancer.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 398889,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System.Threading.Tasks;\r\n\r\npublic class CardDancer : Card\r\n{\r\n\tpublic override bool IsAlive => true;\r\n\r\n\tpublic override bool ShouldHandleEvent( EventType eventType )\r\n\t{\r\n\t\treturn eventType == EventType.Mismatch && Manager.Instance.ChosenCards.Contains( this ) && !Manager.Instance.IsMismatchALockedMatch;\r\n\t}\r\n\r\n\tpublic override async Task HandleEventAsync( EventType eventType )\r\n\t{\r\n\t\tManager.Instance.PushEventMessage( this, eventType );\r\n\r\n\t\tList<Card> neighbourCards = new();\r\n\t\tList<IntVector2> neighboursEmpty = new();\r\n\t\tCard lastPartnerCard = null;\r\n\r\n\t\tawait Task.DelayRealtime( 300 );\r\n\r\n\t\tManager.Instance.PlayCardSfx( \"dancer\", this, volume: 1.1f, pitch: Game.Random.Float( 0.98f, 1.02f ) );\r\n\r\n\t\tfor (int i = 0; i < 4; i++)\r\n\t\t{\r\n\t\t\tneighbourCards.Clear();\r\n\t\t\tneighboursEmpty.Clear();\r\n\r\n\t\t\tEvaluateNeighbour( GridPos + new IntVector2( -1, 0 ), neighbourCards, neighboursEmpty );\r\n\t\t\tEvaluateNeighbour( GridPos + new IntVector2( 1, 0 ), neighbourCards, neighboursEmpty );\r\n\t\t\tEvaluateNeighbour( GridPos + new IntVector2( 0, -1 ), neighbourCards, neighboursEmpty );\r\n\t\t\tEvaluateNeighbour( GridPos + new IntVector2( 0, 1 ), neighbourCards, neighboursEmpty );\r\n\r\n\t\t\tIntVector2 targetGridPos;\r\n\t\t\tCard targetCard = null;\r\n\r\n\t\t\tvar validNeighbourCards = neighbourCards.Where( x => x != lastPartnerCard ).ToList();\r\n\r\n\t\t\tif ( validNeighbourCards.Count > 0 )\r\n\t\t\t{\r\n\t\t\t\tvalidNeighbourCards.Shuffle();\r\n\t\t\t\ttargetCard = validNeighbourCards.First();\r\n\t\t\t\ttargetGridPos = targetCard.GridPos;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tneighboursEmpty.Shuffle();\r\n\t\t\t\ttargetGridPos = neighboursEmpty.First();\r\n\t\t\t}\r\n\r\n\t\t\tif ( targetCard != null )\r\n\t\t\t{\r\n\t\t\t\tawait Manager.Instance.ShakeCard( this );\r\n\t\t\t\tawait Manager.Instance.ShakeCard( targetCard );\r\n\r\n\t\t\t\tlastPartnerCard = targetCard;\r\n\r\n\t\t\t\tawait Task.DelayRealtime( 650 );\r\n\r\n\t\t\t\tif(i < 3)\r\n\t\t\t\t{\r\n\t\t\t\t\tawait Manager.Instance.SwapCardPositions( this, targetCard );\r\n\t\t\t\t\tawait Task.DelayRealtime( 250 );\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tlastPartnerCard = null;\r\n\r\n\t\t\t\tif ( i < 3 )\r\n\t\t\t\t{\r\n\t\t\t\t\tManager.Instance.RemoveCardGridPos( this );\r\n\t\t\t\t\tawait Manager.Instance.SetCardGridPos( this, targetGridPos );\r\n\r\n\t\t\t\t\tawait Task.DelayRealtime( 400 );\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tawait Task.DelayRealtime( 100 );\r\n\r\n\t\tManager.Instance.PopEventMessage();\r\n\r\n\t\tawait Manager.Instance.EventHappened( EventType.AfterCardsMoved );\r\n\t}\r\n\r\n\tvoid EvaluateNeighbour(IntVector2 gridPos, List<Card> neighbourCards, List<IntVector2> empty)\r\n\t{\r\n\t\tif ( !Manager.Instance.IsGridPosInBounds( gridPos ) )\r\n\t\t\treturn;\r\n\r\n\t\tvar card = Manager.Instance.GetCardAtGridPos( gridPos );\r\n\t\tif ( card != null )\r\n\t\t\tneighbourCards.Add( card );\r\n\t\telse\r\n\t\t\tempty.Add( gridPos );\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "facepunch.memory",
            "Path": "cards/CardDolphin.cs",
            "FileName": "CardDolphin.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 398889,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System.Threading.Tasks;\r\nusing System.Xml.Linq;\r\n\r\npublic class CardDolphin : Card\r\n{\r\n\tpublic override bool IsAlive => true;\r\n\r\n\tpublic override bool ShouldHandleEvent( EventType eventType )\r\n\t{\r\n\t\treturn eventType == EventType.Mismatch && !Manager.Instance.ChosenCards.Contains(this) && Manager.Instance.Stats[StatType.ConsecutiveMismatches] == 3;\r\n\t}\r\n\r\n\tpublic override async Task HandleEventAsync( EventType eventType )\r\n\t{\r\n\t\tManager.Instance.PushEventMessage( this, eventType );\r\n\r\n\t\tawait Task.DelayRealtime( 250 );\r\n\r\n\t\tManager.Instance.PlayCardSfx( \"card_flip\", this, volume: 0.8f, pitch: Game.Random.Float( 1.15f, 1.2f ) );\r\n\t\tawait Task.DelayRealtime( 100 );\r\n\t\tManager.Instance.PlayCardSfx( \"dolphin\", this, volume: 0.8f, pitch: Game.Random.Float( 1f, 1.15f ) );\r\n\t\tawait Manager.Instance.RevealCard( this );\r\n\r\n\t\tawait Task.DelayRealtime( Game.Random.Int(400, 700) );\r\n\r\n\t\tManager.Instance.HideCard( this );\r\n\t\tManager.Instance.PlayCardSfx( \"card_flip\", this, volume: 0.7f, pitch: Game.Random.Float( 0.65f, 0.75f ) );\r\n\r\n\t\tManager.Instance.PopEventMessage();\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "facepunch.memory",
            "Path": "cards/CardGenie.cs",
            "FileName": "CardGenie.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 398889,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System.Threading.Tasks;\r\n\r\npublic class CardGenie : Card\r\n{\r\n\tpublic override bool IsAlive => true;\r\n\r\n\tpublic override bool ShouldHandleEvent( EventType eventType )\r\n\t{\r\n\t\treturn eventType == EventType.Match && Manager.Instance.ChosenCards[0] == this;\r\n\t}\r\n\r\n\tpublic override async Task HandleEventAsync( EventType eventType )\r\n\t{\r\n\t\tManager.Instance.PushEventMessage( this, eventType );\r\n\r\n\t\tawait Task.DelayRealtime( 250 );\r\n\r\n\t\tManager.Instance.PlayCardSfxBetween( \"genie\", Manager.Instance.ChosenCards[0], Manager.Instance.ChosenCards[1], volume: 0.6f, pitch: Game.Random.Float( 1.25f, 1.26f ) );\r\n\r\n\t\tManager.Instance.AddStatus( \"StatusGenie\" );\r\n\r\n\t\tawait Task.DelayRealtime( 1750 );\r\n\r\n\t\tManager.Instance.PopEventMessage();\r\n\t}\r\n}\r\n\r\npublic class StatusGenie : StatusEffect\r\n{\r\n\tpublic float TimerReductionAmount { get; set; }\r\n\r\n\tpublic override bool ShouldHandleEvent( EventType eventType )\r\n\t{\r\n\t\treturn eventType == EventType.Choose;\r\n\t}\r\n\r\n\tpublic override async Task HandleEventAsync( EventType eventType )\r\n\t{\r\n\t\tvar validCards = Manager.Instance.Cards.Where(x => x.CardType == Manager.Instance.ChosenCard.CardType && x != Manager.Instance.ChosenCard).ToList();\r\n\t\tif ( validCards.Count == 0 )\r\n\t\t\treturn;\r\n\r\n\t\tManager.Instance.PushEventMessage( this, eventType );\r\n\r\n\t\tawait Task.DelayRealtime( 250 );\r\n\r\n\t\tManager.Instance.PlayCardSfx( \"genie_choose\", Manager.Instance.ChosenCard, volume: 0.9f, pitch: Game.Random.Float( 0.95f, 1.05f ) );\r\n\r\n\t\tawait Task.DelayRealtime( 750 );\r\n\r\n\t\tvalidCards.Shuffle();\r\n\r\n\t\tawait Manager.Instance.ShakeCard( validCards.FirstOrDefault() );\r\n\t\t\r\n\t\tawait Task.DelayRealtime( 750 );\r\n\r\n\t\tManager.Instance.PopEventMessage();\r\n\r\n\t\tManager.Instance.RemoveStatus( this );\r\n\t}\r\n\r\n\tpublic override string GetEventIcon( EventType eventType )\r\n\t{\r\n\t\treturn \"textures/genie.png\";\r\n\t}\r\n\r\n\tpublic override string GetEventText( EventType eventType )\r\n\t{\r\n\t\treturn \"Shake matching card\";\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "facepunch.memory",
            "Path": "cards/CardKey.cs",
            "FileName": "CardKey.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 398889,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System.Threading.Tasks;\r\n\r\npublic class CardKey : Card\r\n{\r\n\t//public override bool ShouldHandleEvent( EventType eventType )\r\n\t//{\r\n\t//\treturn eventType == EventType.Mismatch && Manager.Instance.ChosenCards.Contains(this) || eventType == EventType.Match && Manager.Instance.ChosenCards[1] == this;\r\n\t//}\r\n\r\n\t//public override async Task HandleEventAsync( EventType eventType )\r\n\t//{\r\n\t//\tif ( eventType == EventType.Mismatch )\r\n\t//\t{\r\n\t//\t\tvar key = Manager.Instance.ChosenCards[0].CardType == CardType.Key ? Manager.Instance.ChosenCards[0] : Manager.Instance.ChosenCards[1];\r\n\t//\t\tvar otherCard = Manager.Instance.ChosenCards[0].CardType == CardType.Key ? Manager.Instance.ChosenCards[1] : Manager.Instance.ChosenCards[0];\r\n\r\n\t//\t\tif ( key.IsLocked || !otherCard.IsLocked )\r\n\t//\t\t\treturn;\r\n\r\n\t//\t\tManager.Instance.PushEventMessage( this, eventType );\r\n\r\n\t//\t\tawait Task.DelayRealtime( 50 );\r\n\r\n\t//\t\tManager.Instance.PlayCardSfxBetween( \"key\", Manager.Instance.ChosenCards[0], Manager.Instance.ChosenCards[1], volume: 1.5f, pitch: Game.Random.Float( 0.9f, 1.1f ) );\r\n\r\n\t//\t\tawait Task.DelayRealtime( 50 );\r\n\r\n\t//\t\tawait Manager.Instance.UnlockCard( otherCard );\r\n\r\n\t//\t\tawait Task.DelayRealtime( 900 );\r\n\r\n\t//\t\tManager.Instance.PopEventMessage();\r\n\t//\t}\r\n //       else if (eventType == EventType.Match)\r\n\t//\t{\r\n\t//\t\tManager.Instance.PushEventMessage( this, eventType );\r\n\r\n\t//\t\tawait Task.DelayRealtime( 50 );\r\n\r\n\t//\t\tvar lockedCards = Manager.Instance.Cards.Where( x => x.IsLocked ).ToList();\r\n\r\n\t//\t\tif ( lockedCards.Count == 0 )\r\n\t//\t\t{\r\n\t//\t\t\tawait Task.DelayRealtime( 800 );\r\n\r\n\t//\t\t\tManager.Instance.PopEventMessage();\r\n\r\n\t//\t\t\treturn;\r\n\t//\t\t}\r\n\r\n\t//\t\tManager.Instance.PlayCardSfxBetween( \"key\", Manager.Instance.ChosenCards[0], Manager.Instance.ChosenCards[1], volume: 1.5f, pitch: Game.Random.Float( 0.9f, 1.1f ) );\r\n\r\n\t//\t\tawait Task.DelayRealtime( 50 );\r\n\r\n\t//\t\tforeach ( var card in lockedCards )\r\n\t//\t\t{\r\n\t//\t\t\tawait Manager.Instance.UnlockCard( card );\r\n\t//\t\t}\r\n\r\n\t//\t\tawait Task.DelayRealtime( 1000 );\r\n\r\n\t//\t\tManager.Instance.PopEventMessage();\r\n\t//\t}\r\n\t//}\r\n\r\n\t//public override string GetEventText( EventType eventType )\r\n\t//{\r\n\t//\tif ( eventType == EventType.Mismatch )\r\n\t//\t{\r\n\t//\t\treturn \"\u274cMismatch: unlock other card\";\r\n\t//\t}\r\n\t//\telse\r\n\t//\t{\r\n\t//\t\treturn \"\u2705Match: unlock all cards\";\r\n\t//\t}\r\n\t//}\r\n}\r\n"
        },
        {
            "Ident": "facepunch.memory",
            "Path": "relics/RelicGuideDog.cs",
            "FileName": "RelicGuideDog.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 398889,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System.Reflection.PortableExecutable;\r\nusing System.Runtime.Versioning;\r\nusing System.Threading.Tasks;\r\n\r\npublic class RelicGuideDog : Relic\r\n{\r\n\tpublic override void Init()\r\n\t{\r\n\t\tbase.Init();\r\n\r\n\t\tMaxLevel = 1;\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "facepunch.memory",
            "Path": "relics/RelicMammothMeat.cs",
            "FileName": "RelicMammothMeat.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 398889,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System.Reflection.PortableExecutable;\r\nusing System.Runtime.Versioning;\r\nusing System.Threading.Tasks;\r\n\r\npublic class RelicMammothMeat : Relic\r\n{\r\n\tpublic override void LevelUp()\r\n\t{\r\n\t\tbase.LevelUp();\r\n\r\n\t\tvar mouse = Mouse.Position;\r\n\t\tvar camera = Scene.Camera;\r\n\t\tvar ray = camera.ScreenPixelToRay( mouse );\r\n\r\n\t\tvar tr = Scene.Trace.Ray( ray, 10000f ).Run();\r\n\r\n\t\tManager.Instance.PlaySfxCenter( \"mammoth_meat\", volume: 0.75f, pitch: Game.Random.Float( 0.85f, 0.9f ) );\r\n\r\n\t\tint maxHpAmount = 8;\r\n\t\tManager.Instance.SpawnMaxHPFloater( maxHpAmount, tr.EndPosition );// + new Vector3(0f, -30f, 0f) );\r\n\t\tManager.Instance.MaxHP += maxHpAmount;\r\n\r\n\t\t//int hpAmount = 5;\r\n\t\t//Manager.Instance.HP = Math.Min( Manager.Instance.HP + hpAmount, Manager.Instance.MaxHP );\r\n\t\tManager.Instance.TimeSinceHPChanged = 0f;\r\n\r\n\t\t//Manager.Instance.SpawnHealHPFloater( hpAmount, tr.EndPosition );\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "facepunch.memory",
            "Path": "relics/RelicMantlepieceClock.cs",
            "FileName": "RelicMantlepieceClock.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 398889,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System.Reflection.PortableExecutable;\r\nusing System.Runtime.Versioning;\r\nusing System.Threading.Tasks;\r\n\r\npublic class RelicMantlepieceClock : Relic\r\n{\r\n\tpublic override void LevelUp()\r\n\t{\r\n\t\tbase.LevelUp();\r\n\r\n\t\tManager.Instance.Stats[StatType.NumExtraBountyTurns] += (Level == 1 ? 2f : 1f);\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "facepunch.memory",
            "Path": "relics/RelicMedicalLicense.cs",
            "FileName": "RelicMedicalLicense.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 398889,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System.Reflection.PortableExecutable;\r\nusing System.Runtime.Versioning;\r\nusing System.Threading.Tasks;\r\n\r\npublic class RelicMedicalLicense : Relic\r\n{\r\n\tpublic override void Init()\r\n\t{\r\n\t\tbase.Init();\r\n\r\n\t\tMaxLevel = 1;\r\n\t}\r\n\r\n\tpublic override bool ShouldHandleEvent( EventType eventType )\r\n\t{\r\n\t\treturn eventType == EventType.OverhealHP;\r\n\t}\r\n\r\n\tpublic override async Task HandleEventAsync( EventType eventType )\r\n\t{\r\n\t\tawait Task.DelayRealtime( 1000 );\r\n\r\n\t\tManager.Instance.PushEventMessage( this, eventType );\r\n\r\n\t\tvar camera = Scene.Camera;\r\n\t\tvar ray = camera.ScreenPixelToRay( new Vector3( Screen.Width / 2, Screen.Height / 2, 0f ) );\r\n\r\n\t\tvar tr = Scene.Trace.Ray( ray, 10000f ).Run();\r\n\r\n\t\tManager.Instance.PlaySfx( \"medical_license\", tr.EndPosition.WithZ( Scene.Camera.WorldPosition.z - Globals.CARD_SFX_DEPTH_DIFF ), volume: 1.2f, pitch: Game.Random.Float( 0.95f, 1.05f ) );\r\n\r\n\t\tawait Task.DelayRealtime( 350 );\r\n\r\n\t\tint moneyAmount = (int)Manager.Instance.Stats[StatType.LatestOverhealAmount] + (int)Manager.Instance.Stats[StatType.EarnExtraMoney];\r\n\r\n\t\tManager.Instance.SpawnGainMoneyFloater( moneyAmount, tr.EndPosition );\r\n\r\n\t\tawait Manager.Instance.GainMoney( moneyAmount );\r\n\r\n\t\tawait Task.DelayRealtime( 650 );\r\n\r\n\t\tManager.Instance.PopEventMessage();\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "facepunch.memory",
            "Path": "relics/RelicMouseTrap.cs",
            "FileName": "RelicMouseTrap.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 398889,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System.Reflection.PortableExecutable;\r\nusing System.Runtime.Versioning;\r\nusing System.Threading.Tasks;\r\n\r\npublic class RelicMouseTrap : Relic\r\n{\r\n\tpublic override bool ShouldHandleEvent( EventType eventType )\r\n\t{\r\n\t\treturn eventType == EventType.ClaimBounty;\r\n\t}\r\n\r\n\tpublic override async Task HandleEventAsync(EventType eventType)\r\n\t{\r\n\t\tManager.Instance.PushEventMessage( this, eventType );\r\n\r\n\t\tawait Task.DelayRealtime( 100 );\r\n\r\n\t\tManager.Instance.PlaySfxCenter( \"mouse_trap\", volume: 0.8f, pitch: Game.Random.Float( 0.95f, 1.05f ) );\r\n\r\n\t\tawait Task.DelayRealtime( 400 );\r\n\r\n\t\tvar camera = Scene.Camera;\r\n\t\tvar ray = camera.ScreenPixelToRay( new Vector3( Screen.Width / 2, Screen.Height / 2, 0f ) );\r\n\t\tvar tr = Scene.Trace.Ray( ray, 10000f ).Run();\r\n\r\n\t\tint maxHP = Level;\r\n\t\tManager.Instance.SpawnMaxHPFloater( maxHP, tr.EndPosition );\r\n\t\tManager.Instance.MaxHP += maxHP;\r\n\r\n\t\tManager.Instance.TimeSinceHPChanged = 0f;\r\n\r\n\t\tawait Task.DelayRealtime( 500 );\r\n\t\t\r\n\t\tManager.Instance.PopEventMessage();\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "facepunch.memory",
            "Path": "relics/RelicSacrificialBlade.cs",
            "FileName": "RelicSacrificialBlade.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 398889,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System.Reflection.PortableExecutable;\r\nusing System.Runtime.Versioning;\r\nusing System.Threading.Tasks;\r\n\r\npublic class RelicSacrificialBlade : Relic\r\n{\r\n\tpublic override bool ShouldHandleEvent( EventType eventType )\r\n\t{\r\n\t\treturn eventType == EventType.HurtCards;\r\n\t}\r\n\r\n\tpublic override async Task HandleEventAsync(EventType eventType)\r\n\t{\r\n\t\tManager.Instance.PushEventMessage( this, eventType );\r\n\r\n\t\tvar camera = Scene.Camera;\r\n\t\tvar ray = camera.ScreenPixelToRay( new Vector3( Screen.Width / 2, Screen.Height / 2, 0f ) );\r\n\t\tvar tr = Scene.Trace.Ray( ray, 10000f ).Run();\r\n\r\n\t\tManager.Instance.PlaySfx( \"sacrificial_blade\", tr.EndPosition.WithZ( Scene.Camera.WorldPosition.z - Globals.CARD_SFX_DEPTH_DIFF ), volume: 0.75f, pitch: Game.Random.Float( 1.6f, 1.65f ) );\r\n\r\n\t\tawait Task.DelayRealtime( 450 );\r\n\r\n\t\tint healAmount = Level;\r\n\r\n\t\tManager.Instance.SpawnHealHPFloater( healAmount, tr.EndPosition );\r\n\r\n\t\tawait Task.DelayRealtime( 350 );\r\n\r\n\t\tawait Manager.Instance.GainHP( healAmount );\r\n\r\n\t\tawait Task.DelayRealtime( 250 );\r\n\r\n\t\tManager.Instance.PopEventMessage();\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "facepunch.memory",
            "Path": "relics/RelicSign.cs",
            "FileName": "RelicSign.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 398889,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System.Reflection.PortableExecutable;\r\nusing System.Runtime.Versioning;\r\nusing System.Text.RegularExpressions;\r\nusing System.Threading.Tasks;\r\n\r\npublic class RelicSign : Relic\r\n{\r\n\tpublic override bool ShouldHandleEvent( EventType eventType )\r\n\t{\r\n\t\treturn eventType == EventType.Mismatch && Game.Random.Float(0f, 1f) < Level * 0.25f;\r\n\t}\r\n\r\n\tpublic override async Task HandleEventAsync( EventType eventType )\r\n\t{\r\n\t\tList<Card> validCards = new();\r\n\t\tforeach ( var card in Manager.Instance.Cards )\r\n\t\t{\r\n\t\t\tif ( card.NumTimesRevealed > 0 && !Manager.Instance.ChosenCards.Contains(card) && !card.IsRevealed )\r\n\t\t\t\tvalidCards.Add( card );\r\n\t\t}\r\n\r\n\t\tif ( validCards.Count == 0 )\r\n\t\t\treturn;\r\n\r\n\t\tManager.Instance.PushEventMessage( this, eventType );\r\n\r\n\t\t// todo: sfx\r\n\r\n\t\tawait Task.DelayRealtime( 250 );\r\n\r\n\t\tvalidCards.Shuffle();\r\n\r\n\t\tvar cardToReveal = validCards.First();\r\n\r\n\t\tManager.Instance.PlayCardSfx( \"card_flip\", cardToReveal, volume: 0.8f, pitch: Game.Random.Float( 1.15f, 1.2f ) );\r\n\t\tawait Manager.Instance.RevealCard( cardToReveal );\r\n\r\n\t\tawait Task.DelayRealtime( 750 );\r\n\r\n\t\tManager.Instance.HideCard( cardToReveal );\r\n\t\tManager.Instance.PlayCardSfx( \"card_flip\", cardToReveal, volume: 0.7f, pitch: Game.Random.Float( 0.65f, 0.75f ) );\r\n\r\n\t\tawait Task.DelayRealtime( 300 );\r\n\r\n\t\tManager.Instance.PopEventMessage();\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "facepunch.memory",
            "Path": "ui/messagepanel.razor.scss",
            "FileName": "messagepanel.razor.scss",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 398889,
            "IsPrivate": false,
            "Code": "MessagePanel {\r\n    position: absolute;\r\n    //min-width: 60%;\r\n    height: 60px;\r\n    bottom: 10px;\r\n    display: flex;\r\n    justify-content: center;\r\n    align-items: center;\r\n    border-radius: 15px;\r\n    //background-color: #00000088;\r\n    flex-direction: row;\r\n    padding: 8px;\r\n    backdrop-filter: blur(5px);\r\n    //transition: bottom 0.25s ease-out, transform 0.25s ease-out, opacity 0.25s ease-out;\r\n    transition: all 0.25s ease-out;\r\n\r\n    .message {\r\n        font-size: 24px;\r\n        color: #ffffffee;\r\n    }\r\n\r\n    .icon {\r\n        width: 55px;\r\n        height: 55px;\r\n        background-repeat: no-repeat;\r\n        background-size: contain;\r\n        background-position: center center;\r\n    }\r\n\r\n    &.depth-0 {\r\n        transform: scale(1);\r\n        bottom: 10px;\r\n        z-index: 0;\r\n    }\r\n\r\n    &.depth-1 {\r\n        transform: scale(0.8);\r\n        bottom: 50px;\r\n        z-index: -3;\r\n    }\r\n\r\n    &.depth-2 {\r\n        transform: scale(0.6);\r\n        bottom: 90px;\r\n        z-index: -6;\r\n    }\r\n\r\n    &.depth-3 {\r\n        transform: scale(0.5);\r\n        bottom: 110px;\r\n        z-index: -9;\r\n    }\r\n\r\n    &.depth-4 {\r\n        transform: scale(0.4);\r\n        bottom: 140px;\r\n        z-index: -12;\r\n    }\r\n}\r\n"
        },
        {
            "Ident": "facepunch.memory",
            "Path": "ui/cardtypepanelelement.razor.scss",
            "FileName": "cardtypepanelelement.razor.scss",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 398889,
            "IsPrivate": false,
            "Code": "CardTypePanelElement {\r\n\t//border-radius: 10px;\r\n\theight: 80px;\r\n\twidth: 80px;\r\n\tjustify-content: center;\r\n\t//flex-shrink: 1;\r\n\t//margin: 2px;\r\n\t//background-color: blue;\r\n\t//border: 2px solid #111111;\r\n\t//overflow: visible;\r\n\tposition: relative;\r\n\r\n\t.icon_container {\r\n\t\twidth: 100%;\r\n\t\theight: 100%;\r\n\t\ttransform: scale(1);\r\n\t\ttransition: transform 0.075s ease-out;\r\n\t\tpointer-events: all;\r\n\r\n\t\t&:hover {\r\n\t\t\t//background-color: #301B3F99;\r\n\t\t\t//border: 2px solid #111111;\r\n\t\t\t//opacity: 1;\r\n\t\t\ttransform: scale(1.2);\r\n\t\t}\r\n\t}\r\n\r\n\t.icon {\r\n\t\tposition: absolute;\r\n\t\twidth: 100%;\r\n\t\theight: 100%;\r\n\t\tbackground-repeat: no-repeat;\r\n\t\tbackground-size: contain;\r\n\t\tbackground-position: center center;\r\n\t\tpointer-events: none;\r\n\t}\r\n\r\n\t.normal {\r\n\t\tfilter: brightness(1);\r\n\t\topacity: 1;\r\n\t}\r\n\r\n\t.matched {\r\n\t\tfilter: brightness(0);\r\n\t\topacity: 0.4;\r\n\t}\r\n\r\n\t.bounty {\r\n\t\tposition: absolute;\r\n\t\twidth: 60px;\r\n\t\theight: 60px;\r\n\t\tbackground-color: #0000ff99;\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "facepunch.memory",
            "Path": "CardBreaker.cs",
            "FileName": "CardBreaker.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 398889,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\n\r\npublic sealed class CardBreaker : Component\r\n{\r\n\tprotected override void OnStart()\r\n\t{\r\n\r\n\t}\r\n\r\n\tpublic void Init(Material material, Rotation rot, float scale)\r\n\t{\r\n\t\tLocalRotation = rot;\r\n\t\tLocalScale = new Vector3( scale );\r\n\r\n\t\tfor ( int i = GameObject.Children.Count - 1; i >= 0; i-- )\r\n\t\t{\r\n\t\t\tvar child = GameObject.Children[i];\r\n\t\t\tchild.SetParent( null );\r\n\r\n\t\t\tvar modelRenderer = child.Components.Get<ModelRenderer>();\r\n\t\t\tmodelRenderer.Tint = Color.White;\r\n\t\t\tmodelRenderer.MaterialOverride = material;\r\n\r\n\t\t\tvar rigidBody = child.Components.Get<Rigidbody>();\r\n\t\t\trigidBody.ApplyImpulse( (child.WorldPosition - WorldPosition).Normal * Game.Random.Float( 10f, 100f ) * rigidBody.PhysicsBody.Mass );\r\n\t\t\trigidBody.ApplyImpulse( new Vector3( 0f, 0f, 1f ) * Game.Random.Float( 400f, 800f ) * rigidBody.PhysicsBody.Mass );\r\n\t\t\trigidBody.AngularVelocity = Vector3.Random * Game.Random.Float( -10f, 10f );\r\n\t\t}\r\n\r\n\t\tGameObject.Destroy();\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "facepunch.memory",
            "Path": "Utils.cs",
            "FileName": "Utils.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 398889,
            "IsPrivate": false,
            "Code": "using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing Sandbox;\r\nusing System.Text.Json;\r\n\r\npublic enum EasingType\r\n{\r\n\tNone = -1,\r\n\tLinear = 0,\r\n\tSineIn, SineOut, SineInOut,\r\n\tQuadIn, QuadOut, QuadInOut,\r\n\tCubicIn, CubicOut, CubicInOut,\r\n\tQuartIn, QuartOut, QuartInOut,\r\n\tQuintIn, QuintOut, QuintInOut,\r\n\tExpoIn, ExpoOut, ExpoInOut,\r\n\tExtremeIn, ExtremeOut, ExtremeInOut,\r\n\tElasticIn, ElasticOut, ElasticInOut,\r\n\tElasticSoftIn, ElasticSoftOut, ElasticSoftInOut,\r\n\tBackIn, BackOut, BackInOut,\r\n\tBounceIn, BounceOut, BounceInOut\r\n};\r\n\r\npublic struct IntVector2 : IEquatable<IntVector2>\r\n{\r\n\tpublic static implicit operator Vector2( IntVector2 vec )\r\n\t{\r\n\t\treturn new Vector2( vec.x, vec.y );\r\n\t}\r\n\r\n\tpublic static explicit operator IntVector2( Vector2 vec )\r\n\t{\r\n\t\treturn new IntVector2( (int)vec.x, (int)vec.y );\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// A vector with zero for all components.\r\n\t/// </summary>\r\n\tpublic static readonly IntVector2 Zero = new IntVector2( 0, 0 );\r\n\r\n\t/// <summary>\r\n\t/// A normalized vector along the positive X axis.\r\n\t/// </summary>\r\n\tpublic static readonly IntVector2 UnitX = new IntVector2( 1, 0 );\r\n\r\n\t/// <summary>\r\n\t/// A normalized vector along the positive Y axis.\r\n\t/// </summary>\r\n\tpublic static readonly IntVector2 UnitY = new IntVector2( 0, 1 );\r\n\r\n\t#region Operators\r\n\t/// <summary>\r\n\t/// The identity operator.\r\n\t/// </summary>\r\n\tpublic static IntVector2 operator +( IntVector2 vec )\r\n\t{\r\n\t\treturn vec;\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Component-wise addition of a vector to another.\r\n\t/// </summary>\r\n\tpublic static IntVector2 operator +( IntVector2 a, IntVector2 b )\r\n\t{\r\n\t\treturn new IntVector2( a.x + b.x, a.y + b.y );\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Finds the negation of a vector.\r\n\t/// </summary>\r\n\tpublic static IntVector2 operator -( IntVector2 vec )\r\n\t{\r\n\t\treturn new IntVector2( -vec.x, -vec.y );\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Component-wise subtraction of a vector from another.\r\n\t/// </summary>\r\n\tpublic static IntVector2 operator -( IntVector2 a, IntVector2 b )\r\n\t{\r\n\t\treturn new IntVector2( a.x - b.x, a.y - b.y );\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Component-wise multiplication of a vector by another.\r\n\t/// </summary>\r\n\tpublic static IntVector2 operator *( IntVector2 a, IntVector2 b )\r\n\t{\r\n\t\treturn new IntVector2( a.x * b.x, a.y * b.y );\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Multiplies a vector by a scalar.\r\n\t/// </summary>\r\n\tpublic static IntVector2 operator *( IntVector2 vec, int val )\r\n\t{\r\n\t\treturn new IntVector2( vec.x * val, vec.y * val );\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Multiplies a vector by a scalar.\r\n\t/// </summary>\r\n\tpublic static Vector2 operator *( IntVector2 vec, float val )\r\n\t{\r\n\t\treturn new Vector2( vec.x * val, vec.y * val );\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Multiplies a vector by a scalar.\r\n\t/// </summary>\r\n\tpublic static IntVector2 operator *( int val, IntVector2 vec )\r\n\t{\r\n\t\treturn new IntVector2( vec.x * val, vec.y * val );\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Multiplies a vector by a scalar.\r\n\t/// </summary>\r\n\tpublic static Vector2 operator *( float val, IntVector2 vec )\r\n\t{\r\n\t\treturn new Vector2( vec.x * val, vec.y * val );\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Component-wise division of a vector by another.\r\n\t/// </summary>\r\n\tpublic static IntVector2 operator /( IntVector2 a, IntVector2 b )\r\n\t{\r\n\t\treturn new IntVector2( a.x / b.x, a.y / b.y );\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Component-wise division of a vector by another.\r\n\t/// </summary>\r\n\tpublic static Vector2 operator /( Vector2 a, IntVector2 b )\r\n\t{\r\n\t\treturn new Vector2( a.x / b.x, a.y / b.y );\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Component-wise division of a vector by another.\r\n\t/// </summary>\r\n\tpublic static Vector2 operator /( IntVector2 a, Vector2 b )\r\n\t{\r\n\t\treturn new Vector2( a.x / b.x, a.y / b.y );\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Division of this vector by a scalar.\r\n\t/// </summary>\r\n\tpublic static IntVector2 operator /( IntVector2 vec, int val )\r\n\t{\r\n\t\treturn new IntVector2( vec.x / val, vec.y / val );\r\n\t}\r\n\r\n\tpublic static Vector2 operator /( IntVector2 vec, float val )\r\n\t{\r\n\t\treturn new Vector2( vec.x / val, vec.y / val );\r\n\t}\r\n\r\n\tpublic static IntVector2 operator /( int val, IntVector2 vec )\r\n\t{\r\n\t\treturn new IntVector2( val / vec.x, val / vec.y );\r\n\t}\r\n\r\n\tpublic static Vector2 operator /( float val, IntVector2 vec )\r\n\t{\r\n\t\treturn new Vector2( val / vec.x, val / vec.y );\r\n\t}\r\n\t#endregion\r\n\r\n\t/// <summary>\r\n\t/// Horizontal component.\r\n\t/// </summary>\r\n\tpublic int x;\r\n\r\n\t/// <summary>\r\n\t/// Vertical component.\r\n\t/// </summary>\r\n\tpublic int y;\r\n\r\n\t/// <summary>\r\n\t/// Floating point magnitude of the vector.\r\n\t/// </summary>\r\n\tpublic float Length { get { return (float)MathF.Sqrt( x * x + y * y ); } }\r\n\r\n\t/// <summary>\r\n\t/// Magnitude of the vector in Taxicab geometry.\r\n\t/// </summary>\r\n\tpublic int ManhattanLength { get { return Math.Abs( x ) + Math.Abs( y ); } }\r\n\r\n\t/// <summary>\r\n\t/// Sum of each component squared.\r\n\t/// </summary>\r\n\tpublic int LengthSquared { get { return x * x + y * y; } }\r\n\r\n\t/// <summary>\r\n\t/// Gets a normalized vector in the same direction as this one.\r\n\t/// </summary>\r\n\tpublic Vector2 Normalized { get { return LengthSquared >= float.Epsilon ? this / Length : Zero; } }\r\n\r\n\t/// <summary>\r\n\t/// Gets a vector equal to this one rotated counter-clockwise by 90 degrees.\r\n\t/// </summary>\r\n\tpublic IntVector2 Left { get { return new IntVector2( -y, x ); } }\r\n\r\n\t/// <summary>\r\n\t/// Gets a vector equal to this one rotated clockwise 90 degrees.\r\n\t/// </summary>\r\n\tpublic IntVector2 Right { get { return new IntVector2( y, -x ); } }\r\n\r\n\t/// <summary>\r\n\t/// Gets a vector equal to this one rotated 180 degrees.\r\n\t/// </summary>\r\n\tpublic IntVector2 Back { get { return -this; } }\r\n\r\n\t/// <summary>\r\n\t/// Constructs a vector from the given X and Y components.\r\n\t/// </summary>\r\n\tpublic IntVector2( int x, int y )\r\n\t{\r\n\t\tthis.x = x; this.y = y;\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Finds the scalar product of this vector and another.\r\n\t/// </summary>\r\n\tpublic int Dot( IntVector2 vec )\r\n\t{\r\n\t\treturn x * vec.x + y * vec.y;\r\n\t}\r\n\r\n\tpublic override bool Equals( object obj )\r\n\t{\r\n\t\treturn obj is IntVector2 && Equals( (IntVector2)obj );\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Tests for equality with another vector.\r\n\t/// </summary>\r\n\tpublic bool Equals( IntVector2 vec )\r\n\t{\r\n\t\treturn x == vec.x && y == vec.y;\r\n\t}\r\n\r\n\tpublic override int GetHashCode()\r\n\t{\r\n\t\treturn x ^ y;\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Gets a string representing this vector in (X, Y) format.\r\n\t/// </summary>\r\n\tpublic override string ToString()\r\n\t{\r\n\t\treturn string.Format( \"({0}, {1})\", x, y );\r\n\t}\r\n}\r\n\r\npublic struct AStarEdge<T>\r\n{\r\n\t/// <summary>\r\n\t/// The node this connection leads to.\r\n\t/// </summary>\r\n\tpublic readonly T Dest;\r\n\r\n\t/// <summary>\r\n\t/// The cost of using this connection.\r\n\t/// </summary>\r\n\tpublic readonly float Cost;\r\n\r\n\tinternal AStarEdge( T dest, float cost )\r\n\t{\r\n\t\tDest = dest;\r\n\t\tCost = cost;\r\n\t}\r\n}\r\n\r\npublic static class Utils\r\n{\r\n\t// projects vector A onto B\r\n\tpublic static Vector3 ProjectVector( Vector3 a, Vector3 b )\r\n\t{\r\n\t\tfloat dotProduct = Vector3.Dot( a, b );\r\n\t\tfloat lengthSquared = Vector3.Dot( b, b );\r\n\t\treturn (dotProduct / lengthSquared) * b;\r\n\t}\r\n\r\n\t// returns vector B or C, whichever is in the closest direction compared to A\r\n\tpublic static Vector3 FindCloserVector( Vector3 a, Vector3 b, Vector3 c )\r\n\t{\r\n\t\t// Normalize B and C\r\n\t\tb = b.Normal;\r\n\t\tc = c.Normal;\r\n\r\n\t\t// Compute dot products\r\n\t\tfloat dotProductB = Vector3.Dot( a, b );\r\n\t\tfloat dotProductC = Vector3.Dot( a, c );\r\n\r\n\t\t// Determine which dot product is larger\r\n\t\treturn dotProductB > dotProductC ? b : c;\r\n\t}\r\n\r\n\tpublic static GameObject FindChild( this Component component, string name )\r\n\t{\r\n\t\treturn component.GameObject.Children.Where( x => x.Name == name ).FirstOrDefault();\r\n\t}\r\n\r\n\tpublic static Color GetRandomColor( float total = 1.5f )\r\n\t{\r\n\t\tfloat r = Game.Random.Float( 0f, 1f );\r\n\t\ttotal = Math.Max( total - r, 0f );\r\n\t\tfloat g = MathF.Min( Game.Random.Float( 0f, 1f ), total );\r\n\t\ttotal = Math.Max( total - g, 0f );\r\n\t\tfloat b = MathF.Min( Game.Random.Float( 0f, 1f ), total );\r\n\r\n\t\treturn new Color( r, g, b );\r\n\t}\r\n\r\n\tpublic static T ReadJsonCustom<T>( this BaseFileSystem fs, string filename, T defaultValue = default( T ) )\r\n\t{\r\n\t\tstring text = fs.ReadAllText( filename );\r\n\t\tif ( string.IsNullOrWhiteSpace( text ) )\r\n\t\t{\r\n\t\t\treturn defaultValue;\r\n\t\t}\r\n\r\n\t\tJsonSerializerOptions options = new JsonSerializerOptions\r\n\t\t{\r\n\t\t\tReadCommentHandling = JsonCommentHandling.Skip,\r\n\t\t\tPropertyNameCaseInsensitive = true,\r\n\t\t\tAllowTrailingCommas = true,\r\n\t\t\tConverters = { new System.Text.Json.Serialization.JsonStringEnumConverter() },\r\n\t\t};\r\n\t\treturn JsonSerializer.Deserialize<T>( text, options );\r\n\t}\r\n\r\n\tpublic const float Deg2Rad = MathF.PI / 180f;\r\n\tpublic const float Rad2Deg = 360f / (MathF.PI * 2f);\r\n\r\n\tpublic static Vector2 RotateVector( Vector2 v, float degrees )\r\n\t{\r\n\t\tvar rads = Deg2Rad * degrees;\r\n\t\tvar ca = MathF.Cos( rads );\r\n\t\tvar sa = MathF.Sin( rads );\r\n\r\n\t\treturn new Vector2( ca * v.x - sa * v.y, sa * v.x + ca * v.y );\r\n\t}\r\n\r\n\tpublic static Vector2 RotatePointAround( Vector2 p, Vector2 anchor, float degrees )\r\n\t{\r\n\t\tvar offset = p - anchor;\r\n\t\tvar newVec = RotateVector( offset, degrees );\r\n\t\treturn anchor + newVec;\r\n\t}\r\n\r\n\tpublic static Vector2 DegreesToVector( float degrees )\r\n\t{\r\n\t\tvar rads = Deg2Rad * degrees;\r\n\t\treturn new Vector2( MathF.Cos( rads ), MathF.Sin( rads ) );\r\n\t}\r\n\r\n\tpublic static float VectorToDegrees( Vector2 vec )\r\n\t{\r\n\t\tfloat radians = (float)Math.Atan2( vec.y, vec.x );\r\n\t\tfloat degrees = radians * (180f / (float)Math.PI);\r\n\t\treturn degrees;\r\n\t}\r\n\r\n\tpublic static Vector2 GetPerpendicularVector( Vector2 vec )\r\n\t{\r\n\t\treturn new Vector2( -vec.y, vec.x );\r\n\t}\r\n\r\n\tpublic static int GetDistance( int x, int y )\r\n\t{\r\n\t\treturn (int)Math.Round( Math.Sqrt( x * x + y * y ) );\r\n\t}\r\n\r\n\tpublic static Vector2 GetRandomVector()\r\n\t{\r\n\t\treturn DegreesToVector( Game.Random.Float( 0f, 360f ) );\r\n\t}\r\n\r\n\tpublic static float FastSin( float input )\r\n\t{\r\n\t\t// wrap input angle to -PI..PI\r\n\t\twhile ( input < -3.14159265f )\r\n\t\t\tinput += 6.28318531f;\r\n\t\twhile ( input > 3.14159265f )\r\n\t\t\tinput -= 6.28318531f;\r\n\r\n\t\treturn 1.27323954f * input + 0.405284735f * (input < 0f ? 1f : -1f) * input * input;\r\n\t}\r\n\r\n\tpublic static float DynamicEaseTo( float current, float goal, float factorPercent, float dt, float referenceFrameRate = 60f )\r\n\t{\r\n\t\tif ( float.IsPositiveInfinity( dt ) )\r\n\t\t\treturn goal;\r\n\r\n\t\treturn current + (goal - current) * (1f - MathF.Pow( 1f - MathX.Clamp( factorPercent, 0f, 1f ), dt * referenceFrameRate ));\r\n\t}\r\n\r\n\tpublic static Vector2 DynamicEaseTo( Vector2 current, Vector2 goal, float factorPercent, float dt, float referenceFrameRate = 60f )\r\n\t{\r\n\t\tif ( float.IsPositiveInfinity( dt ) )\r\n\t\t\treturn goal;\r\n\t\tvar diff = goal - current;\r\n\r\n\t\treturn current + diff * (1f - MathF.Pow( 1f - MathX.Clamp( factorPercent, 0f, 1f ), dt * referenceFrameRate ));\r\n\t}\r\n\r\n\tpublic static Vector3 DynamicEaseTo( Vector3 current, Vector3 goal, float factorPercent, float dt, float referenceFrameRate = 60f )\r\n\t{\r\n\t\tif ( float.IsPositiveInfinity( dt ) )\r\n\t\t\treturn goal;\r\n\t\tvar diff = goal - current;\r\n\r\n\t\treturn current + diff * (1f - MathF.Pow( 1f - MathX.Clamp( factorPercent, 0f, 1f ), dt * referenceFrameRate ));\r\n\t}\r\n\r\n\tpublic static Rotation DynamicEaseTo( Rotation currentRot, Rotation goalRot, float factorPercent, float dt, float referenceFrameRate = 60f )\r\n\t{\r\n\t\tif ( float.IsPositiveInfinity( dt ) )\r\n\t\t\treturn goalRot;\r\n\r\n\t\tVector3 current = currentRot.Angles().AsVector3();\r\n\t\tVector3 goal = goalRot.Angles().AsVector3();\r\n\r\n\t\tvar diff = goal - current;\r\n\r\n\t\tvar output = current + diff * (1f - MathF.Pow( 1f - MathX.Clamp( factorPercent, 0f, 1f ), dt * referenceFrameRate ));\r\n\t\treturn Rotation.From( new Angles( output ) );\r\n\t}\r\n\r\n\tpublic static float EaseUnclamped( float value, EasingType easingType )\r\n\t{\r\n\t\tswitch ( easingType )\r\n\t\t{\r\n\t\t\tcase EasingType.SineIn: return SineIn( value );\r\n\t\t\tcase EasingType.SineOut: return SineOut( value );\r\n\t\t\tcase EasingType.SineInOut: return SineInOut( value );\r\n\r\n\t\t\tcase EasingType.QuadIn: return QuadIn( value );\r\n\t\t\tcase EasingType.QuadOut: return QuadOut( value );\r\n\t\t\tcase EasingType.QuadInOut: return QuadInOut( value );\r\n\r\n\t\t\tcase EasingType.CubicIn: return CubicIn( value );\r\n\t\t\tcase EasingType.CubicOut: return CubicOut( value );\r\n\t\t\tcase EasingType.CubicInOut: return CubicInOut( value );\r\n\r\n\t\t\tcase EasingType.QuartIn: return QuartIn( value );\r\n\t\t\tcase EasingType.QuartOut: return QuartOut( value );\r\n\t\t\tcase EasingType.QuartInOut: return QuartInOut( value );\r\n\r\n\t\t\tcase EasingType.QuintIn: return QuintIn( value );\r\n\t\t\tcase EasingType.QuintOut: return QuintOut( value );\r\n\t\t\tcase EasingType.QuintInOut: return QuintInOut( value );\r\n\r\n\t\t\tcase EasingType.ExpoIn: return ExpoIn( value );\r\n\t\t\tcase EasingType.ExpoOut: return ExpoOut( value );\r\n\t\t\tcase EasingType.ExpoInOut: return ExpoInOut( value );\r\n\r\n\t\t\tcase EasingType.ExtremeIn: return ExtremeIn( value );\r\n\t\t\tcase EasingType.ExtremeOut: return ExtremeOut( value );\r\n\t\t\tcase EasingType.ExtremeInOut: return ExtremeInOut( value );\r\n\r\n\t\t\tcase EasingType.ElasticIn: return ElasticIn( value );\r\n\t\t\tcase EasingType.ElasticOut: return ElasticOut( value );\r\n\t\t\tcase EasingType.ElasticInOut: return ElasticInOut( value );\r\n\r\n\t\t\tcase EasingType.ElasticSoftIn: return ElasticSoftIn( value );\r\n\t\t\tcase EasingType.ElasticSoftOut: return ElasticSoftOut( value );\r\n\t\t\tcase EasingType.ElasticSoftInOut: return ElasticSoftInOut( value );\r\n\r\n\t\t\tcase EasingType.BackIn: return BackIn( value );\r\n\t\t\tcase EasingType.BackOut: return BackOut( value );\r\n\t\t\tcase EasingType.BackInOut: return BackInOut( value );\r\n\r\n\t\t\tcase EasingType.BounceIn: return BounceIn( value );\r\n\t\t\tcase EasingType.BounceOut: return BounceOut( value );\r\n\t\t\tcase EasingType.BounceInOut: return BounceInOut( value );\r\n\t\t\tdefault: return value;\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static float Map( float value, float inputMin, float inputMax, float outputMin, float outputMax, EasingType easingType = EasingType.Linear, bool clamp = true )\r\n\t{\r\n\t\tif ( inputMin.Equals( inputMax ) || outputMin.Equals( outputMax ) )\r\n\t\t\treturn outputMin;\r\n\r\n\t\t//            if(inputMin.Equals(inputMax) || outputMin.Equals(outputMax))\r\n\t\t//                return outputMax;\r\n\r\n\t\tif ( clamp )\r\n\t\t{\r\n\t\t\t// clamp input\r\n\t\t\tif ( inputMax > inputMin )\r\n\t\t\t{\r\n\t\t\t\tif ( value < inputMin ) value = inputMin;\r\n\t\t\t\telse if ( value > inputMax ) value = inputMax;\r\n\t\t\t}\r\n\t\t\telse if ( inputMax < inputMin )\r\n\t\t\t{\r\n\t\t\t\tif ( value > inputMin ) value = inputMin;\r\n\t\t\t\telse if ( value < inputMax ) value = inputMax;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tvar ratio = EaseUnclamped( (value - inputMin) / (inputMax - inputMin), easingType );\r\n\r\n\t\tvar outVal = outputMin + ratio * (outputMax - outputMin);\r\n\r\n\t\t//            // clamp output\r\n\t\t//            if(outputMax < outputMin) {\r\n\t\t//                if(outVal < outputMax) outVal = outputMax;\r\n\t\t//                else if(outVal > outputMin) outVal = outputMin;\r\n\t\t//            } else {\r\n\t\t//                if(outVal > outputMax) outVal = outputMax;\r\n\t\t//                else if(outVal < outputMin) outVal = outputMin;\r\n\t\t//            }\r\n\r\n\t\treturn outVal;\r\n\t}\r\n\r\n\tpublic static float MapReturn( float value, float inputMin, float inputMax, float outputMin, float outputMax, EasingType easingType = EasingType.Linear )\r\n\t{\r\n\t\tvar halfway = inputMin + (inputMax - inputMin) * 0.5f;\r\n\t\tif ( value < halfway ) return Map( value, inputMin, halfway, outputMin, outputMax, easingType );\r\n\t\telse return Map( value, halfway, inputMax, outputMax, outputMin, GetOppositeEasingType( easingType ) );\r\n\t}\r\n\r\n\tpublic static float EasePercent( float percent, EasingType easingType )\r\n\t{\r\n\t\treturn Map( percent, 0f, 1f, 0f, 1f, easingType );\r\n\t}\r\n\r\n\tpublic static EasingType GetOppositeEasingType( EasingType easingType )\r\n\t{\r\n\t\tvar opposite = EasingType.Linear;\r\n\t\tswitch ( easingType )\r\n\t\t{\r\n\t\t\tcase EasingType.SineIn: opposite = EasingType.SineOut; break;\r\n\t\t\tcase EasingType.SineOut: opposite = EasingType.SineIn; break;\r\n\t\t\tcase EasingType.SineInOut: opposite = EasingType.SineInOut; break;\r\n\r\n\t\t\tcase EasingType.QuadIn: opposite = EasingType.QuadOut; break;\r\n\t\t\tcase EasingType.QuadOut: opposite = EasingType.QuadIn; break;\r\n\t\t\tcase EasingType.QuadInOut: opposite = EasingType.QuadInOut; break;\r\n\r\n\t\t\tcase EasingType.CubicIn: opposite = EasingType.CubicOut; break;\r\n\t\t\tcase EasingType.CubicOut: opposite = EasingType.CubicIn; break;\r\n\t\t\tcase EasingType.CubicInOut: opposite = EasingType.CubicInOut; break;\r\n\r\n\t\t\tcase EasingType.QuartIn: opposite = EasingType.QuartOut; break;\r\n\t\t\tcase EasingType.QuartOut: opposite = EasingType.QuartIn; break;\r\n\t\t\tcase EasingType.QuartInOut: opposite = EasingType.QuartInOut; break;\r\n\r\n\t\t\tcase EasingType.QuintIn: opposite = EasingType.QuintOut; break;\r\n\t\t\tcase EasingType.QuintOut: opposite = EasingType.QuintIn; break;\r\n\t\t\tcase EasingType.QuintInOut: opposite = EasingType.QuintInOut; break;\r\n\r\n\t\t\tcase EasingType.ExpoIn: opposite = EasingType.ExpoOut; break;\r\n\t\t\tcase EasingType.ExpoOut: opposite = EasingType.ExpoIn; break;\r\n\t\t\tcase EasingType.ExpoInOut: opposite = EasingType.ExpoInOut; break;\r\n\r\n\t\t\tcase EasingType.ExtremeIn: opposite = EasingType.ExtremeOut; break;\r\n\t\t\tcase EasingType.ExtremeOut: opposite = EasingType.ExtremeIn; break;\r\n\t\t\tcase EasingType.ExtremeInOut: opposite = EasingType.ExtremeInOut; break;\r\n\r\n\t\t\tcase EasingType.ElasticIn: opposite = EasingType.ElasticOut; break;\r\n\t\t\tcase EasingType.ElasticOut: opposite = EasingType.ElasticIn; break;\r\n\t\t\tcase EasingType.ElasticInOut: opposite = EasingType.ElasticInOut; break;\r\n\r\n\t\t\tcase EasingType.ElasticSoftIn: opposite = EasingType.ElasticSoftOut; break;\r\n\t\t\tcase EasingType.ElasticSoftOut: opposite = EasingType.ElasticSoftIn; break;\r\n\t\t\tcase EasingType.ElasticSoftInOut: opposite = EasingType.ElasticSoftInOut; break;\r\n\r\n\t\t\tcase EasingType.BackIn: opposite = EasingType.BackOut; break;\r\n\t\t\tcase EasingType.BackOut: opposite = EasingType.BackIn; break;\r\n\t\t\tcase EasingType.BackInOut: opposite = EasingType.BackInOut; break;\r\n\r\n\t\t\tcase EasingType.BounceIn: opposite = EasingType.BounceOut; break;\r\n\t\t\tcase EasingType.BounceOut: opposite = EasingType.BounceIn; break;\r\n\t\t\tcase EasingType.BounceInOut: opposite = EasingType.BounceInOut; break;\r\n\t\t}\r\n\r\n\t\treturn opposite;\r\n\t}\r\n\r\n\tpublic static float SineIn( float t ) { return 1f - MathF.Cos( t * MathF.PI * 0.5f ); }\r\n\tpublic static float SineOut( float t ) { return MathF.Sin( t * (MathF.PI * 0.5f) ); }\r\n\tpublic static float SineInOut( float t ) { return -0.5f * (MathF.Cos( MathF.PI * t ) - 1f); }\r\n\r\n\tpublic static float QuadIn( float t ) { return t * t; }\r\n\tpublic static float QuadOut( float t ) { return t * (2f - t); }\r\n\tpublic static float QuadInOut( float t ) { return t < 0.5f ? 2f * t * t : -1f + (4f - 2f * t) * t; }\r\n\r\n\tpublic static float CubicIn( float t ) { return t * t * t; }\r\n\tpublic static float CubicOut( float t ) { var t1 = t - 1f; return t1 * t1 * t1 + 1f; }\r\n\tpublic static float CubicInOut( float t ) { return t < 0.5f ? 4f * t * t * t : (t - 1f) * (2f * t - 2f) * (2f * t - 2f) + 1f; }\r\n\r\n\tpublic static float QuartIn( float t ) { return t * t * t * t; }\r\n\tpublic static float QuartOut( float t ) { var t1 = t - 1f; return 1f - t1 * t1 * t1 * t1; }\r\n\tpublic static float QuartInOut( float t ) { return t < 0.5f ? 4f * t * t * t : (t - 1f) * (2f * t - 2f) * (2f * t - 2f) + 1f; }\r\n\r\n\tpublic static float QuintIn( float t ) { return t * t * t * t * t; }\r\n\tpublic static float QuintOut( float t ) { var t1 = t - 1f; return 1f + t1 * t1 * t1 * t1 * t1; }\r\n\tpublic static float QuintInOut( float t ) { var t1 = t - 1f; return t < 0.5f ? 16f * t * t * t * t * t : 1f + 16f * t1 * t1 * t1 * t1 * t1; }\r\n\r\n\tpublic static float ExpoIn( float t ) { return MathF.Pow( 2f, 10f * (t - 1f) ); }\r\n\tpublic static float ExpoOut( float t ) { return 1f - MathF.Pow( 2f, -10f * t ); }\r\n\tpublic static float ExpoInOut( float t ) { return t < 0.5f ? ExpoIn( t * 2f ) * 0.5f : 1f - ExpoIn( 2f - t * 2f ) * 0.5f; }\r\n\r\n\tpublic static float ExtremeIn( float t ) { return MathF.Pow( 10f, 10f * (t - 1f) ); }\r\n\tpublic static float ExtremeOut( float t ) { return 1f - MathF.Pow( 10f, -10f * t ); }\r\n\tpublic static float ExtremeInOut( float t ) { return t < 0.5f ? ExtremeIn( t * 2f ) * 0.5f : 1f - ExtremeIn( 2f - t * 2f ) * 0.5f; }\r\n\r\n\tpublic static float ElasticIn( float t ) { return 1f - ElasticOut( 1f - t ); }\r\n\tpublic static float ElasticOut( float t ) { var p = 0.3f; return MathF.Pow( 2f, -10f * t ) * MathF.Sin( (t - p / 4f) * (2f * (float)Math.PI) / p ) + 1f; }\r\n\tpublic static float ElasticInOut( float t ) { return t < 0.5f ? ElasticIn( t * 2f ) * 0.5f : 1f - ElasticIn( 2f - t * 2f ) * 0.5f; }\r\n\r\n\tpublic static float ElasticSoftIn( float t ) { return 1f - ElasticSoftOut( 1f - t ); }\r\n\tpublic static float ElasticSoftOut( float t ) { var p = 0.5f; return MathF.Pow( 2f, -10f * t ) * MathF.Sin( (t - p / 4f) * (2f * (float)Math.PI) / p ) + 1f; }\r\n\tpublic static float ElasticSoftInOut( float t ) { return t < 0.5f ? ElasticSoftIn( t * 2f ) * 0.5f : 1f - ElasticSoftIn( 2f - t * 2f ) * 0.5f; }\r\n\r\n\tpublic static float BackIn( float t ) { var p = 1f; return t * t * ((p + 1f) * t - p); }\r\n\tpublic static float BackOut( float t ) { var p = 1f; var scaledTime = t / 1f - 1f; return scaledTime * scaledTime * ((p + 1f) * scaledTime + p) + 1f; }\r\n\tpublic static float BackInOut( float t )\r\n\t{\r\n\t\tvar p = 1f;\r\n\t\tvar scaledTime = t * 2f;\r\n\t\tvar scaledTime2 = scaledTime - 2f;\r\n\t\tvar s = p * 1.525f;\r\n\r\n\t\tif ( scaledTime < 1f ) return 0.5f * scaledTime * scaledTime * ((s + 1f) * scaledTime - s);\r\n\t\telse return 0.5f * (scaledTime2 * scaledTime2 * ((s + 1f) * scaledTime2 + s) + 2f);\r\n\t}\r\n\r\n\tpublic static float BounceIn( float t ) { return 1f - BounceOut( 1f - t ); }\r\n\tpublic static float BounceOut( float t )\r\n\t{\r\n\t\tvar scaledTime = t / 1f;\r\n\r\n\t\tif ( scaledTime < 1 / 2.75f )\r\n\t\t{\r\n\t\t\treturn 7.5625f * scaledTime * scaledTime;\r\n\t\t}\r\n\t\telse if ( scaledTime < 2 / 2.75 )\r\n\t\t{\r\n\t\t\tvar scaledTime2 = scaledTime - 1.5f / 2.75f;\r\n\t\t\treturn 7.5625f * scaledTime2 * scaledTime2 + 0.75f;\r\n\t\t}\r\n\t\telse if ( scaledTime < 2.5 / 2.75 )\r\n\t\t{\r\n\t\t\tvar scaledTime2 = scaledTime - 2.25f / 2.75f;\r\n\t\t\treturn 7.5625f * scaledTime2 * scaledTime2 + 0.9375f;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tvar scaledTime2 = scaledTime - 2.625f / 2.75f;\r\n\t\t\treturn 7.5625f * scaledTime2 * scaledTime2 + 0.984375f;\r\n\t\t}\r\n\t}\r\n\tpublic static float BounceInOut( float t )\r\n\t{\r\n\t\tif ( t < 0.5 ) return BounceIn( t * 2f ) * 0.5f;\r\n\t\telse return BounceOut( t * 2f - 1f ) * 0.5f + 0.5f;\r\n\t}\r\n\r\n\tpublic static void Shuffle<T>( this IList<T> list )\r\n\t{\r\n\t\tSystem.Random rng = new System.Random();\r\n\t\tint n = list.Count;\r\n\t\twhile ( n > 1 )\r\n\t\t{\r\n\t\t\tn--;\r\n\t\t\tint k = rng.Next( n + 1 );\r\n\t\t\tT value = list[k];\r\n\t\t\tlist[k] = list[n];\r\n\t\t\tlist[n] = value;\r\n\t\t}\r\n\t}\r\n\r\n\tpublic static string FirstCharToUpper( this string input ) =>\r\n\t\tinput switch\r\n\t\t{\r\n\t\t\tnull => throw new ArgumentNullException( nameof( input ) ),\r\n\t\t\t\"\" => throw new ArgumentException( $\"{nameof( input )} cannot be empty\", nameof( input ) ),\r\n\t\t\t_ => string.Concat( input[0].ToString().ToUpper(), input.AsSpan( 1 ) )\r\n\t\t};\r\n\r\n\tpublic static string GetRandomIcon( string i0, string i1 ) { int rand = Game.Random.Int( 0, 1 ); switch ( rand ) { case 0: default: return i0; case 1: return i1; } }\r\n\tpublic static string GetRandomIcon( string i0, string i1, string i2 ) { int rand = Game.Random.Int( 0, 2 ); switch ( rand ) { case 0: default: return i0; case 1: return i1; case 2: return i2; } }\r\n\tpublic static string GetRandomIcon( string i0, string i1, string i2, string i3 ) { int rand = Game.Random.Int( 0, 3 ); switch ( rand ) { case 0: default: return i0; case 1: return i1; case 2: return i2; case 3: return i3; } }\r\n\tpublic static string GetRandomIcon( string i0, string i1, string i2, string i3, string i4 ) { int rand = Game.Random.Int( 0, 4 ); switch ( rand ) { case 0: default: return i0; case 1: return i1; case 2: return i2; case 3: return i3; case 4: return i4; } }\r\n\tpublic static string GetRandomIcon( string i0, string i1, string i2, string i3, string i4, string i5 ) { int rand = Game.Random.Int( 0, 5 ); switch ( rand ) { case 0: default: return i0; case 1: return i1; case 2: return i2; case 3: return i3; case 4: return i4; case 5: return i5; } }\r\n\tpublic static string GetRandomIcon( string i0, string i1, string i2, string i3, string i4, string i5, string i6 ) { int rand = Game.Random.Int( 0, 6 ); switch ( rand ) { case 0: default: return i0; case 1: return i1; case 2: return i2; case 3: return i3; case 4: return i4; case 5: return i5; case 6: return i6; } }\r\n\tpublic static string GetRandomIcon( string i0, string i1, string i2, string i3, string i4, string i5, string i6, string i7 ) { int rand = Game.Random.Int( 0, 7 ); switch ( rand ) { case 0: default: return i0; case 1: return i1; case 2: return i2; case 3: return i3; case 4: return i4; case 5: return i5; case 6: return i6; case 7: return i7; } }\r\n\r\n\r\n\tprivate class NodeInfo<T>\r\n\t{\r\n\t\tprivate const int MaxPoolSize = 8192;\r\n\r\n\t\tprivate static List<NodeInfo<T>> _sPool;\r\n\r\n\t\tinternal static NodeInfo<T> Create( T node, NodeInfo<T> prev = null, float costAdd = 0f )\r\n\t\t{\r\n\t\t\tNodeInfo<T> nodeInfo;\r\n\r\n\t\t\tif ( _sPool == null || _sPool.Count == 0 )\r\n\t\t\t{\r\n\t\t\t\tnodeInfo = new NodeInfo<T>();\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tnodeInfo = _sPool[_sPool.Count - 1];\r\n\t\t\t\t_sPool.RemoveAt( _sPool.Count - 1 );\r\n\t\t\t}\r\n\r\n\t\t\tnodeInfo.Node = node;\r\n\t\t\tnodeInfo.Prev = prev;\r\n\r\n\t\t\tif ( prev == null )\r\n\t\t\t{\r\n\t\t\t\tnodeInfo.Depth = 0;\r\n\t\t\t\tnodeInfo.Cost = 0f;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tnodeInfo.Depth = prev.Depth + 1;\r\n\t\t\t\tnodeInfo.Cost = prev.Cost + costAdd;\r\n\t\t\t}\r\n\r\n\t\t\treturn nodeInfo;\r\n\t\t}\r\n\r\n\t\tinternal static void Pool( NodeInfo<T> nodeInfo )\r\n\t\t{\r\n\t\t\tif ( _sPool == null ) _sPool = new List<NodeInfo<T>>( MaxPoolSize );\r\n\t\t\tif ( _sPool.Count >= MaxPoolSize ) return;\r\n\r\n\t\t\t_sPool.Add( nodeInfo );\r\n\t\t}\r\n\r\n\t\tprivate float _heuristic;\r\n\r\n\t\tpublic T Node { get; private set; }\r\n\t\tpublic NodeInfo<T> Prev { get; private set; }\r\n\t\tpublic int Depth { get; private set; }\r\n\t\tpublic float Cost { get; private set; }\r\n\t\tpublic float Total { get; private set; }\r\n\r\n\t\tpublic float Heuristic\r\n\t\t{\r\n\t\t\tget { return _heuristic; }\r\n\t\t\tset\r\n\t\t\t{\r\n\t\t\t\t_heuristic = value;\r\n\t\t\t\tTotal = Cost + value;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// Convenience method to produce a graph connection for use when calling AStar().\r\n\t/// </summary>\r\n\t/// <typeparam name=\"T\">Graph node type.</typeparam>\r\n\t/// <param name=\"dest\">Destination node of the connection.</param>\r\n\t/// <param name=\"cost\">Cost of taking the connection.</param>\r\n\tpublic static AStarEdge<T> Edge<T>( T dest, float cost )\r\n\t{\r\n\t\treturn new AStarEdge<T>( dest, cost );\r\n\t}\r\n\r\n\tprivate static class AStarWrapper<T>\r\n\t\twhere T : IEquatable<T>\r\n\t{\r\n\t\tpublic static NodeInfo<T> FirstMatchOrDefault( List<NodeInfo<T>> list, T toCompare )\r\n\t\t{\r\n\t\t\tvar count = list.Count;\r\n\r\n\t\t\tfor ( var i = count - 1; i >= 0; --i )\r\n\t\t\t{\r\n\t\t\t\tvar item = list[i];\r\n\t\t\t\tif ( item.Node.Equals( toCompare ) )\r\n\t\t\t\t\treturn item;\r\n\t\t\t}\r\n\r\n\t\t\treturn null;\r\n\t\t}\r\n\r\n\t\tprivate static List<NodeInfo<T>> _sOpen;\r\n\t\tprivate static List<NodeInfo<T>> _sClosed;\r\n\r\n\t\tpublic static bool AStar( T origin, T target, List<T> destList,\r\n\t\t\tFunc<T, IEnumerable<AStarEdge<T>>> adjFunc, Func<T, T, float> heuristicFunc )\r\n\t\t{\r\n\t\t\tvar open = _sOpen ?? (_sOpen = new List<NodeInfo<T>>());\r\n\t\t\tvar clsd = _sClosed ?? (_sClosed = new List<NodeInfo<T>>());\r\n\r\n\t\t\topen.Clear();\r\n\t\t\tclsd.Clear();\r\n\r\n\t\t\tvar first = NodeInfo<T>.Create( origin );\r\n\t\t\tfirst.Heuristic = heuristicFunc( origin, target );\r\n\r\n\t\t\topen.Add( first );\r\n\r\n\t\t\ttry\r\n\t\t\t{\r\n\t\t\t\twhile ( open.Count > 0 )\r\n\t\t\t\t{\r\n\t\t\t\t\tNodeInfo<T> cur = null;\r\n\t\t\t\t\tforeach ( var node in open )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tif ( cur == null || node.Total < cur.Total ) cur = node;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif ( cur.Node.Equals( target ) )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tfor ( var i = cur.Depth; i >= 0; --i )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tdestList.Add( cur.Node );\r\n\t\t\t\t\t\t\tcur = cur.Prev;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tdestList.Reverse();\r\n\t\t\t\t\t\treturn true;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\topen.Remove( cur );\r\n\t\t\t\t\tclsd.Add( cur );\r\n\r\n\t\t\t\t\tforeach ( var adj in adjFunc( cur.Node ) )\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tvar node = NodeInfo<T>.Create( adj.Dest, cur, adj.Cost );\r\n\t\t\t\t\t\tvar existing = FirstMatchOrDefault( clsd, adj.Dest );\r\n\r\n\t\t\t\t\t\tif ( existing != null )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif ( existing.Cost <= node.Cost ) continue;\r\n\r\n\t\t\t\t\t\t\tclsd.Remove( existing );\r\n\t\t\t\t\t\t\tnode.Heuristic = existing.Heuristic;\r\n\r\n\t\t\t\t\t\t\tNodeInfo<T>.Pool( existing );\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\texisting = FirstMatchOrDefault( open, adj.Dest );\r\n\r\n\t\t\t\t\t\tif ( existing != null )\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tif ( existing.Cost <= node.Cost ) continue;\r\n\r\n\t\t\t\t\t\t\topen.Remove( existing );\r\n\t\t\t\t\t\t\tnode.Heuristic = existing.Heuristic;\r\n\r\n\t\t\t\t\t\t\tNodeInfo<T>.Pool( existing );\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\tnode.Heuristic = heuristicFunc( node.Node, target );\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\topen.Add( node );\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\treturn false;\r\n\r\n\t\t\t}\r\n\t\t\tfinally\r\n\t\t\t{\r\n\t\t\t\tforeach ( var nodeInfo in open )\r\n\t\t\t\t{\r\n\t\t\t\t\tNodeInfo<T>.Pool( nodeInfo );\r\n\t\t\t\t}\r\n\r\n\t\t\t\tforeach ( var nodeInfo in clsd )\r\n\t\t\t\t{\r\n\t\t\t\t\tNodeInfo<T>.Pool( nodeInfo );\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// An implementation of the AStar path finding algorithm.\r\n\t/// </summary>\r\n\t/// <typeparam name=\"T\">Graph node type.</typeparam>\r\n\t/// <param name=\"origin\">Node to start path finding from.</param>\r\n\t/// <param name=\"target\">The goal node to reach.</param>\r\n\t/// <param name=\"adjFunc\">Function returning the neighbouring connections for a node.</param>\r\n\t/// <param name=\"heuristicFunc\">Function returning the estimated cost of travelling between two nodes.</param>\r\n\t/// <returns>A sequence of nodes representing a path if one is found, otherwise an empty array.</returns>\r\n\tpublic static bool AStar<T>( T origin, T target, List<T> destPath,\r\n\t\tFunc<T, IEnumerable<AStarEdge<T>>> adjFunc, Func<T, T, float> heuristicFunc )\r\n\t\twhere T : IEquatable<T>\r\n\t{\r\n\t\treturn AStarWrapper<T>.AStar( origin, target, destPath, adjFunc, heuristicFunc );\r\n\t}\r\n}\r\n"
        },
        {
            "Ident": "facepunch.memory",
            "Path": "cards/CardAnt.cs",
            "FileName": "CardAnt.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 398889,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System.Threading.Tasks;\r\n\r\npublic class CardAnt : Card\r\n{\r\n\tpublic override bool IsAlive => true;\r\n\r\n\tpublic override bool ShouldHandleEvent( EventType eventType )\r\n\t{\r\n\t\treturn eventType == EventType.TurnStart && Manager.Instance.GetNumNearbyEmptyGridPositions( GridPos, adjacentOnly: false ) > 0;\r\n\t}\r\n\r\n\tpublic override async Task HandleEventAsync( EventType eventType )\r\n\t{\r\n\t\tvar emptyGridPos = Manager.Instance.GetRandomNearbyGridPos( GridPos, empty: true, adjacentOnly: false );\r\n\r\n\t\tawait Task.DelayRealtime( 200 );\r\n\r\n\t\tManager.Instance.PlayCardSfx( \"card_move\", this, volume: 1.1f, pitch: Game.Random.Float( 0.85f, 1.15f ) );\r\n\r\n\t\tawait Task.DelayRealtime( 100 );\r\n\r\n\t\tManager.Instance.RemoveCardGridPos( this );\r\n\t\tawait Manager.Instance.SetCardGridPos( this, emptyGridPos );\r\n\r\n\t\tawait Task.DelayRealtime( 300 );\r\n\r\n\t\tawait Manager.Instance.EventHappened( EventType.AfterCardsMoved );\r\n\t}\r\n}\r\n"
        }
    ]
}