Rewrote the game in C#
Added sounds and music
This commit is contained in:
		
							
								
								
									
										44
									
								
								src/Game/Arrow.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/Game/Arrow.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
using Godot;
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Hellswipers.Game;
 | 
			
		||||
 | 
			
		||||
public partial class Arrow : Polygon2D {
 | 
			
		||||
	public enum Dirs { Right, Down, Left, Up }
 | 
			
		||||
	
 | 
			
		||||
	private static readonly Color NormalColor = Colors.White;
 | 
			
		||||
	private static readonly Color DoneColor = Colors.Khaki;
 | 
			
		||||
	private static readonly Color ErrorColor = Color.Color8(217, 70, 72);
 | 
			
		||||
	
 | 
			
		||||
	private Polygon2D _inner;
 | 
			
		||||
	private bool _done = false;
 | 
			
		||||
	public Dirs CurrentDir { private set; get; }
 | 
			
		||||
 | 
			
		||||
	public override void _Ready() {
 | 
			
		||||
		_inner = GetNode<Polygon2D>("Inner");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void SetDir(Dirs dir) {
 | 
			
		||||
		CurrentDir = dir;
 | 
			
		||||
		Rotation = dir switch {
 | 
			
		||||
			Dirs.Right => 0,
 | 
			
		||||
			Dirs.Down => (float)(Math.PI / 2),
 | 
			
		||||
			Dirs.Left => (float)Math.PI,
 | 
			
		||||
			Dirs.Up => (float)(Math.PI * 3 / 2),
 | 
			
		||||
			_ => Rotation
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void Done() {
 | 
			
		||||
		_done = true;
 | 
			
		||||
		_inner.Color = DoneColor;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void Error() {
 | 
			
		||||
		var tween = CreateTween();
 | 
			
		||||
		tween.TweenProperty(_inner, "color", ErrorColor, 0.15);
 | 
			
		||||
		tween.TweenInterval(0.15);
 | 
			
		||||
		tween.TweenProperty(_inner, "color", NormalColor, 0.2);
 | 
			
		||||
		_done = false;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								src/Game/Audio.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/Game/Audio.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
using Godot;
 | 
			
		||||
 | 
			
		||||
namespace Hellswipers.Game;
 | 
			
		||||
 | 
			
		||||
public partial class Audio : Node {
 | 
			
		||||
    private AudioStreamPlayer _okPlayer;
 | 
			
		||||
    private AudioStreamPlayer _errPlayer;
 | 
			
		||||
 | 
			
		||||
    public void Ok() => _okPlayer.Play();
 | 
			
		||||
    public void Err() => _errPlayer.Play();
 | 
			
		||||
    
 | 
			
		||||
    public override void _Ready() {
 | 
			
		||||
        _okPlayer = GetNode<AudioStreamPlayer>("OkSound");
 | 
			
		||||
        _errPlayer = GetNode<AudioStreamPlayer>("ErrorSound");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										149
									
								
								src/Game/Game.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								src/Game/Game.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,149 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using Godot;
 | 
			
		||||
 | 
			
		||||
namespace Hellswipers.Game;
 | 
			
		||||
 | 
			
		||||
public partial class Game : Control {
 | 
			
		||||
	private const int TotalWidth = 1920;
 | 
			
		||||
	private const int SpaceBetweenArrows = 8;
 | 
			
		||||
	private const long ErrorLengthMs = 400;
 | 
			
		||||
 | 
			
		||||
	private PackedScene _arrowScene;
 | 
			
		||||
	private AnimationPlayer _player;
 | 
			
		||||
	private Node2D _arrowsNode;
 | 
			
		||||
	private Label _nameLabel;
 | 
			
		||||
	private Icons _icons;
 | 
			
		||||
	
 | 
			
		||||
	private readonly Gem[] _gems = Gem.LoadGems();
 | 
			
		||||
	private readonly Random _rand = new();
 | 
			
		||||
	private readonly Stopwatch _errorWatch = new();
 | 
			
		||||
	
 | 
			
		||||
	private readonly Queue<Gem> _nextGems = new();
 | 
			
		||||
	private readonly List<Arrow> _arrows = new();
 | 
			
		||||
	
 | 
			
		||||
	private Gem _current;
 | 
			
		||||
	private bool _disabled = false;
 | 
			
		||||
	private int _pos = -1;
 | 
			
		||||
 | 
			
		||||
	public delegate void VoidEventHandler();
 | 
			
		||||
	public event VoidEventHandler SwipeOk;
 | 
			
		||||
	public event VoidEventHandler SwipeErr;
 | 
			
		||||
	public event VoidEventHandler Solved;
 | 
			
		||||
 | 
			
		||||
	private Gem RandomGem() => _gems[_rand.Next(_gems.Length)];
 | 
			
		||||
 | 
			
		||||
	private async void StartNewRound() {
 | 
			
		||||
		var gem = RandomGem();
 | 
			
		||||
		_icons.Update(gem);
 | 
			
		||||
		_nextGems.Enqueue(gem);
 | 
			
		||||
		
 | 
			
		||||
		_current = _nextGems.Dequeue();
 | 
			
		||||
		_disabled = true;
 | 
			
		||||
		var tween = CreateTween();
 | 
			
		||||
		tween.SetParallel();
 | 
			
		||||
		if (_pos != -1) {
 | 
			
		||||
			_arrows.ForEach(v => {
 | 
			
		||||
				var newPos = v.CurrentDir switch {
 | 
			
		||||
					Arrow.Dirs.Right => new Vector2(100, 0),
 | 
			
		||||
					Arrow.Dirs.Down => new Vector2(0, 100),
 | 
			
		||||
					Arrow.Dirs.Left => new Vector2(-100, 0),
 | 
			
		||||
					Arrow.Dirs.Up => new Vector2(0, -100),
 | 
			
		||||
					_ => new Vector2(0, 0)
 | 
			
		||||
				};
 | 
			
		||||
				tween.TweenProperty(v, "position", v.Position + newPos, 0.2);
 | 
			
		||||
			});
 | 
			
		||||
			_player.Play("round-end");
 | 
			
		||||
			await ToSignal(_player, AnimationMixer.SignalName.AnimationFinished);
 | 
			
		||||
			
 | 
			
		||||
			_arrows.ForEach(v => {
 | 
			
		||||
				_arrowsNode.RemoveChild(v);
 | 
			
		||||
				v.QueueFree();
 | 
			
		||||
			});
 | 
			
		||||
			_arrows.Clear();
 | 
			
		||||
			tween = CreateTween();
 | 
			
		||||
			tween.SetParallel();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		_pos = 0;
 | 
			
		||||
		_nameLabel.Text = _current.Name;
 | 
			
		||||
		
 | 
			
		||||
		var arrowCount = _current.Dirs.Length;
 | 
			
		||||
		var totalArrowWidth = 64 * arrowCount + SpaceBetweenArrows * (arrowCount - 1);
 | 
			
		||||
		var xPos = (TotalWidth - totalArrowWidth) / 2;
 | 
			
		||||
		foreach (var dir in _current.Dirs) {
 | 
			
		||||
			var arrow = _arrowScene.Instantiate<Arrow>();
 | 
			
		||||
			var startPos = dir switch {
 | 
			
		||||
				Arrow.Dirs.Right => new Vector2(-100, 0),
 | 
			
		||||
				Arrow.Dirs.Down => new Vector2(0, -100),
 | 
			
		||||
				Arrow.Dirs.Left => new Vector2(100, 0),
 | 
			
		||||
				Arrow.Dirs.Up => new Vector2(0, 100),
 | 
			
		||||
				_ => new Vector2(0, 0)
 | 
			
		||||
			};
 | 
			
		||||
			startPos.X += xPos;
 | 
			
		||||
			arrow.Position = startPos;
 | 
			
		||||
			tween.TweenProperty(arrow, "position", new Vector2(xPos, 0), 0.2);
 | 
			
		||||
			arrow.SetDir(dir);
 | 
			
		||||
			_arrows.Add(arrow);
 | 
			
		||||
			_arrowsNode.AddChild(arrow);
 | 
			
		||||
			xPos += 64 + SpaceBetweenArrows;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		_player.Play("round-start");
 | 
			
		||||
		await ToSignal(_player, AnimationMixer.SignalName.AnimationFinished);
 | 
			
		||||
 | 
			
		||||
		_disabled = false;
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	private void HandleInput(Arrow.Dirs dir) {
 | 
			
		||||
		if (_disabled) {
 | 
			
		||||
			if (_errorWatch.IsRunning && _errorWatch.ElapsedMilliseconds >= ErrorLengthMs) {
 | 
			
		||||
				_disabled = false;
 | 
			
		||||
				_errorWatch.Stop();
 | 
			
		||||
			} else return;
 | 
			
		||||
		}
 | 
			
		||||
		
 | 
			
		||||
		if (_pos >= _current.Dirs.Length) return;
 | 
			
		||||
 | 
			
		||||
		if (dir == _current.Dirs[_pos]) {
 | 
			
		||||
			_arrows[_pos].Done();
 | 
			
		||||
			_pos++;
 | 
			
		||||
			SwipeOk!.Invoke();
 | 
			
		||||
			if (_pos == _current.Dirs.Length) {
 | 
			
		||||
				Solved!.Invoke();
 | 
			
		||||
				StartNewRound();
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			_disabled = true;
 | 
			
		||||
			SwipeErr!.Invoke();
 | 
			
		||||
			_errorWatch.Start();
 | 
			
		||||
			_arrows.ForEach(v => v.Error());
 | 
			
		||||
			_pos = 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public override void _Ready() {
 | 
			
		||||
		_player = GetNode<AnimationPlayer>("AnimationPlayer");
 | 
			
		||||
		_arrowsNode = GetNode<Node2D>("Arrows");
 | 
			
		||||
		_nameLabel = GetNode<Label>("Name");
 | 
			
		||||
		_icons = GetNode<Icons>("Icons");
 | 
			
		||||
		
 | 
			
		||||
		var stats = GetNode<Stats>("StatContainer");
 | 
			
		||||
		SwipeOk += () => stats.Combo++;
 | 
			
		||||
		SwipeErr += () => stats.Combo = 0;
 | 
			
		||||
		Solved += () => stats.Solved++;
 | 
			
		||||
 | 
			
		||||
		var audio = GetNode<Audio>("Audio");
 | 
			
		||||
		SwipeOk += audio.Ok;
 | 
			
		||||
		SwipeErr += audio.Err;
 | 
			
		||||
		
 | 
			
		||||
		_arrowScene = ResourceLoader.Load<PackedScene>("res://game/arrow.tscn");
 | 
			
		||||
		
 | 
			
		||||
		for (int i = 0; i < 7; ++i)
 | 
			
		||||
			_nextGems.Enqueue(RandomGem());
 | 
			
		||||
 | 
			
		||||
		_icons.Init(_nextGems.ToArray());
 | 
			
		||||
		StartNewRound();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										88
									
								
								src/Game/GemData.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/Game/GemData.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using Godot;
 | 
			
		||||
 | 
			
		||||
namespace Hellswipers.Game;
 | 
			
		||||
 | 
			
		||||
public record struct Gem {
 | 
			
		||||
    public readonly Texture2D Texture;
 | 
			
		||||
    public readonly string Name;
 | 
			
		||||
    public readonly Arrow.Dirs[] Dirs;
 | 
			
		||||
    
 | 
			
		||||
    public Gem(string Name, string textureName, Arrow.Dirs[] Dirs) {
 | 
			
		||||
        this.Name = Name;
 | 
			
		||||
        this.Dirs = Dirs;
 | 
			
		||||
        Texture = ResourceLoader.Load<Texture2D>(textureName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Variant this[int i] {
 | 
			
		||||
        get {
 | 
			
		||||
            switch (i) {
 | 
			
		||||
                case 0:
 | 
			
		||||
                    return Name;
 | 
			
		||||
                case 1:
 | 
			
		||||
                    return Texture;
 | 
			
		||||
                case 2:
 | 
			
		||||
                    return Dirs.Select(v => (int)v).ToArray();
 | 
			
		||||
            }
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Gem[] LoadGems() => new Gem[] {
 | 
			
		||||
        new ("LIFT-850 Jump Pack", "res://game/gems/LIFT-850icon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Up,Arrow.Dirs.Down,Arrow.Dirs.Up}),
 | 
			
		||||
        new ("B-1 Supply Pack", "res://game/gems/B-1icon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Left,Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Up,Arrow.Dirs.Down}),
 | 
			
		||||
        new ("AX/LAS-5 \"Guard Dog\" Rover", "res://game/gems/AX-LAS-5icon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Left,Arrow.Dirs.Up,Arrow.Dirs.Right,Arrow.Dirs.Right}),
 | 
			
		||||
        new ("SH-20 Ballistic Shield Backpack", "res://game/gems/SH-20icon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Left,Arrow.Dirs.Down,Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Left}),
 | 
			
		||||
        new ("SH-32 Shield Generator Pack", "res://game/gems/SH-32icon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Left,Arrow.Dirs.Right,Arrow.Dirs.Left,Arrow.Dirs.Right}),
 | 
			
		||||
        new ("AX/AR-23 \"Guard Dog\"", "res://game/gems/AX-AR-23icon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Left,Arrow.Dirs.Up,Arrow.Dirs.Right,Arrow.Dirs.Down}),
 | 
			
		||||
        new ("MG-43 Machine Gun", "res://game/gems/MG-43icon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Left,Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Right}),
 | 
			
		||||
        new ("APW-1 Anti-Materiel Rifle", "res://game/gems/APW-1icon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Left,Arrow.Dirs.Right,Arrow.Dirs.Up,Arrow.Dirs.Down}),
 | 
			
		||||
        new ("M-105 Stalwart", "res://game/gems/M-105icon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Left,Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Up,Arrow.Dirs.Left}),
 | 
			
		||||
        new ("EAT-17 Expendable Anti-tank", "res://game/gems/EAT-17icon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Down,Arrow.Dirs.Left,Arrow.Dirs.Up,Arrow.Dirs.Right}),
 | 
			
		||||
        new ("GR-8 Recoilless Rifle", "res://game/gems/GR-8icon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Left,Arrow.Dirs.Right,Arrow.Dirs.Right,Arrow.Dirs.Left}),
 | 
			
		||||
        new ("FLAM-40 Flamethrower", "res://game/gems/FLAM-40icon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Left,Arrow.Dirs.Up,Arrow.Dirs.Down,Arrow.Dirs.Up}),
 | 
			
		||||
        new ("AC-8 Autocannon", "res://game/gems/AC-8icon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Left,Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Up,Arrow.Dirs.Right}),
 | 
			
		||||
        new ("RS-422 Railgun", "res://game/gems/RS-422icon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Right,Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Left,Arrow.Dirs.Right}),
 | 
			
		||||
        new ("FAF-14 SPEAR Launcher", "res://game/gems/FAF-14icon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Down,Arrow.Dirs.Down}),
 | 
			
		||||
        new ("GL-21 Grenade Launcher", "res://game/gems/GL-21icon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Left,Arrow.Dirs.Up,Arrow.Dirs.Left,Arrow.Dirs.Down}),
 | 
			
		||||
        new ("LAS-98 Laser Cannon", "res://game/gems/LAS-98icon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Left,Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Left}),
 | 
			
		||||
        new ("ARC-3 Arc Thrower", "res://game/gems/ARC-3icon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Right,Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Left,Arrow.Dirs.Left}),
 | 
			
		||||
        new ("Reinforce", "res://game/gems/Reinforceicon.png", new [] {Arrow.Dirs.Up,Arrow.Dirs.Down,Arrow.Dirs.Right,Arrow.Dirs.Left,Arrow.Dirs.Up}),
 | 
			
		||||
        new ("SOS Beacon", "res://game/gems/SOSicon.png", new [] {Arrow.Dirs.Up,Arrow.Dirs.Down,Arrow.Dirs.Right,Arrow.Dirs.Up}),
 | 
			
		||||
        new ("Resupply", "res://game/gems/ResupplyIcon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Right}),
 | 
			
		||||
        new ("NUX-223 Hellbomb", "res://game/gems/Strat_NUX-223_Hellbomb_mk1.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Left,Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Right,Arrow.Dirs.Down,Arrow.Dirs.Up}),
 | 
			
		||||
        new ("SSSD Delivery", "res://game/gems/DeliverSSSDicon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Down,Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Up}),
 | 
			
		||||
        new ("Seismic Probe", "res://game/gems/Seismic_probe_icon.png", new [] {Arrow.Dirs.Up,Arrow.Dirs.Up,Arrow.Dirs.Left,Arrow.Dirs.Right,Arrow.Dirs.Down,Arrow.Dirs.Down}),
 | 
			
		||||
        new ("Upload Data", "res://game/gems/DeliverSSSDicon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Up,Arrow.Dirs.Up}),
 | 
			
		||||
        new ("Eagle Rearm", "res://game/gems/HD2_Eagle_Rearm_Icon.jpg", new [] {Arrow.Dirs.Up,Arrow.Dirs.Up,Arrow.Dirs.Left,Arrow.Dirs.Up,Arrow.Dirs.Right}),
 | 
			
		||||
        new ("E/MG-101 HMG Emplacement", "res://game/gems/HMGTurreticon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Left,Arrow.Dirs.Right,Arrow.Dirs.Right,Arrow.Dirs.Left}),
 | 
			
		||||
        new ("FX-12 Shield Generator Relay", "res://game/gems/Shieldrelayicon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Down,Arrow.Dirs.Left,Arrow.Dirs.Right,Arrow.Dirs.Left,Arrow.Dirs.Right}),
 | 
			
		||||
        new ("A/ARC-3 Tesla Tower", "res://game/gems/Teslaicon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Right,Arrow.Dirs.Up,Arrow.Dirs.Left,Arrow.Dirs.Right}),
 | 
			
		||||
        new ("MD-6 Anti-Personnel Minefield", "res://game/gems/Minefieldicon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Left,Arrow.Dirs.Up,Arrow.Dirs.Right}),
 | 
			
		||||
        new ("MD-I4 Incendiary Mines", "res://game/gems/Fireminefieldicon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Left,Arrow.Dirs.Left,Arrow.Dirs.Down}),
 | 
			
		||||
        new ("A/MG-43 Machine Gun Sentry", "res://game/gems/MGsentryicon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Right,Arrow.Dirs.Right,Arrow.Dirs.Up}),
 | 
			
		||||
        new ("A/G-16 Gatling Sentry", "res://game/gems/Gatlingsentryicon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Right,Arrow.Dirs.Left}),
 | 
			
		||||
        new ("A/M-12 Mortar Sentry", "res://game/gems/Mortarsentryicon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Right,Arrow.Dirs.Right,Arrow.Dirs.Down}),
 | 
			
		||||
        new ("A/AC-8 Autocannon Sentry", "res://game/gems/Autocannoasentryicon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Right,Arrow.Dirs.Up,Arrow.Dirs.Left,Arrow.Dirs.Up}),
 | 
			
		||||
        new ("A/MLS-4X Rocket Sentry", "res://game/gems/Missilesentryicon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Right,Arrow.Dirs.Right,Arrow.Dirs.Left}),
 | 
			
		||||
        new ("A/M-23 EMS Mortar Sentry", "res://game/gems/EMSmortaricon.png", new [] {Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Right,Arrow.Dirs.Down,Arrow.Dirs.Right}),
 | 
			
		||||
        new ("Orbital Gatling Barrage", "res://game/gems/OrbitalGatlingicon.png", new [] {Arrow.Dirs.Right,Arrow.Dirs.Down,Arrow.Dirs.Left,Arrow.Dirs.Up,Arrow.Dirs.Up}),
 | 
			
		||||
        new ("Orbital Airburst Strike", "res://game/gems/OrbitalAirbursticon.png", new [] {Arrow.Dirs.Right,Arrow.Dirs.Right,Arrow.Dirs.Right}),
 | 
			
		||||
        new ("Orbital 120MM HE Barrage", "res://game/gems/Orbital120icon.png", new [] {Arrow.Dirs.Right,Arrow.Dirs.Right,Arrow.Dirs.Down,Arrow.Dirs.Left,Arrow.Dirs.Right,Arrow.Dirs.Down}),
 | 
			
		||||
        new ("Orbital 380MM HE Barrage", "res://game/gems/Orbital380icon.png", new [] {Arrow.Dirs.Right,Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Up,Arrow.Dirs.Left,Arrow.Dirs.Down,Arrow.Dirs.Down}),
 | 
			
		||||
        new ("Orbital Walking Barrage", "res://game/gems/OrbitalWalkingicon.png", new [] {Arrow.Dirs.Right,Arrow.Dirs.Down,Arrow.Dirs.Right,Arrow.Dirs.Down,Arrow.Dirs.Right,Arrow.Dirs.Down}),
 | 
			
		||||
        new ("Orbital Laser", "res://game/gems/OrbitalLasericon.png", new [] {Arrow.Dirs.Right,Arrow.Dirs.Down,Arrow.Dirs.Up,Arrow.Dirs.Right,Arrow.Dirs.Down}),
 | 
			
		||||
        new ("Orbital Railcannon Strike", "res://game/gems/OrbitalRailcannonicon.png", new [] {Arrow.Dirs.Right,Arrow.Dirs.Up,Arrow.Dirs.Down,Arrow.Dirs.Down,Arrow.Dirs.Right}),
 | 
			
		||||
        new ("Orbital Precision Strike", "res://game/gems/OrbitalPrecisionicon.png", new [] {Arrow.Dirs.Right,Arrow.Dirs.Right,Arrow.Dirs.Up}),
 | 
			
		||||
        new ("Orbital Gas Strike", "res://game/gems/OrbitalGasicon.png", new [] {Arrow.Dirs.Right,Arrow.Dirs.Right,Arrow.Dirs.Down,Arrow.Dirs.Right}),
 | 
			
		||||
        new ("Orbital EMS Strike", "res://game/gems/OrbitalEMSicon.png", new [] {Arrow.Dirs.Right,Arrow.Dirs.Right,Arrow.Dirs.Left,Arrow.Dirs.Down}),
 | 
			
		||||
        new ("Orbital Smoke Strike", "res://game/gems/OrbitalSmokeicon.png", new [] {Arrow.Dirs.Right,Arrow.Dirs.Right,Arrow.Dirs.Down,Arrow.Dirs.Up}),
 | 
			
		||||
        new ("Eagle Strafing Run", "res://game/gems/EagleStrafingicon.png", new [] {Arrow.Dirs.Up,Arrow.Dirs.Right,Arrow.Dirs.Right}),
 | 
			
		||||
        new ("Eagle Airstrike", "res://game/gems/EagleAirstrikeicon.png", new [] {Arrow.Dirs.Up,Arrow.Dirs.Right,Arrow.Dirs.Down,Arrow.Dirs.Right}),
 | 
			
		||||
        new ("Eagle Cluster Bomb", "res://game/gems/EagleClusterbombicon.png", new [] {Arrow.Dirs.Up,Arrow.Dirs.Right,Arrow.Dirs.Down,Arrow.Dirs.Down,Arrow.Dirs.Right}),
 | 
			
		||||
        new ("Eagle Napalm Airstrike", "res://game/gems/EagleNapalmicon.png", new [] {Arrow.Dirs.Up,Arrow.Dirs.Right,Arrow.Dirs.Down,Arrow.Dirs.Up}),
 | 
			
		||||
        new ("Eagle Smoke Strike", "res://game/gems/EagleSmokeicon.png", new [] {Arrow.Dirs.Up,Arrow.Dirs.Right,Arrow.Dirs.Up,Arrow.Dirs.Down}),
 | 
			
		||||
        new ("Eagle 110MM Rocket Pods", "res://game/gems/EagleRocketpodicon.png", new [] {Arrow.Dirs.Up,Arrow.Dirs.Right,Arrow.Dirs.Up,Arrow.Dirs.Left}),
 | 
			
		||||
        new ("Eagle 500kg Bomb", "res://game/gems/Eagle500icon.png", new [] {Arrow.Dirs.Up,Arrow.Dirs.Right,Arrow.Dirs.Down,Arrow.Dirs.Down,Arrow.Dirs.Down})
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										56
									
								
								src/Game/Icons.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/Game/Icons.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using Godot;
 | 
			
		||||
 | 
			
		||||
namespace Hellswipers.Game;
 | 
			
		||||
 | 
			
		||||
public partial class Icons : Node2D {
 | 
			
		||||
    private const int IconSize = 144;
 | 
			
		||||
    private const int SpaceBetween = 48;
 | 
			
		||||
    private const int TotalIconSize = IconSize + SpaceBetween;
 | 
			
		||||
    private const double AnimationLength = 0.4;
 | 
			
		||||
    private static readonly Vector2 IconScale = new(3, 3);
 | 
			
		||||
 | 
			
		||||
    private readonly Queue<Sprite2D> _icons = new();
 | 
			
		||||
 | 
			
		||||
    private Sprite2D CreateIcon(Texture2D tex) {
 | 
			
		||||
        var icon = new Sprite2D();
 | 
			
		||||
        icon.Texture = tex;
 | 
			
		||||
        icon.Scale = IconScale;
 | 
			
		||||
        AddChild(icon);
 | 
			
		||||
        _icons.Enqueue(icon);
 | 
			
		||||
        return icon;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Init(Gem[] nextGems) {
 | 
			
		||||
        CreateIcon(null);
 | 
			
		||||
        var pos = TotalIconSize;
 | 
			
		||||
        foreach (var gem in nextGems) {
 | 
			
		||||
            CreateIcon(gem.Texture).Position = new(pos, 0);
 | 
			
		||||
            pos += TotalIconSize;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void Update(Gem gem) {
 | 
			
		||||
        var tween = CreateTween();
 | 
			
		||||
        tween.SetParallel();
 | 
			
		||||
        tween.SetTrans(Tween.TransitionType.Cubic);
 | 
			
		||||
        tween.SetEase(Tween.EaseType.InOut);
 | 
			
		||||
 | 
			
		||||
        var oldIcon = _icons.Dequeue();
 | 
			
		||||
        tween.TweenProperty(oldIcon, "position:x", -Position.X - IconSize, AnimationLength);
 | 
			
		||||
        tween.TweenProperty(oldIcon, "self_modulate", new Color(1, 1, 1, 0), AnimationLength);
 | 
			
		||||
 | 
			
		||||
        var newIcon = CreateIcon(gem.Texture);
 | 
			
		||||
        newIcon.Position = new(_icons.Count * TotalIconSize, 0);
 | 
			
		||||
        newIcon.SelfModulate = new(1, 1, 1, 0);
 | 
			
		||||
        tween.TweenProperty(newIcon, "self_modulate", new Color(1, 1, 1, 1), AnimationLength);
 | 
			
		||||
 | 
			
		||||
        foreach (var icon in _icons)
 | 
			
		||||
            tween.TweenProperty(icon, "position:x", icon.Position.X - TotalIconSize, AnimationLength);
 | 
			
		||||
 | 
			
		||||
        tween.Finished += () => {
 | 
			
		||||
            RemoveChild(oldIcon);
 | 
			
		||||
            oldIcon.QueueFree();
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										46
									
								
								src/Game/Input.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/Game/Input.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,46 @@
 | 
			
		||||
using System;
 | 
			
		||||
using Godot;
 | 
			
		||||
 | 
			
		||||
namespace Hellswipers.Game;
 | 
			
		||||
 | 
			
		||||
public partial class Game {
 | 
			
		||||
    private Vector2? _swipeStart;
 | 
			
		||||
    
 | 
			
		||||
    private void CalcSwipe(Vector2 end) {
 | 
			
		||||
        if (!_swipeStart.HasValue)
 | 
			
		||||
            return;
 | 
			
		||||
        var delta = end - _swipeStart.Value;
 | 
			
		||||
        _swipeStart = null;
 | 
			
		||||
 | 
			
		||||
        var deltaAbs = delta.Abs();
 | 
			
		||||
        var biggest = Math.Max(deltaAbs.X, deltaAbs.Y);
 | 
			
		||||
        if (biggest < 50)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        HandleInput(deltaAbs.X > deltaAbs.Y
 | 
			
		||||
            ? (delta.X > 0 ? Arrow.Dirs.Right : Arrow.Dirs.Left)
 | 
			
		||||
            : (delta.Y > 0 ? Arrow.Dirs.Down : Arrow.Dirs.Up)
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
	
 | 
			
		||||
    public override void _UnhandledInput(InputEvent ev) {
 | 
			
		||||
        if (ev is InputEventMouseButton evm) {
 | 
			
		||||
            if (ev.IsPressed())
 | 
			
		||||
                _swipeStart = evm.Position;
 | 
			
		||||
            else
 | 
			
		||||
                CalcSwipe(evm.Position);
 | 
			
		||||
        } else {
 | 
			
		||||
            if (ev.IsActionPressed("key_up"))
 | 
			
		||||
                HandleInput(Arrow.Dirs.Up);
 | 
			
		||||
            else if (ev.IsActionPressed("key_down"))
 | 
			
		||||
                HandleInput(Arrow.Dirs.Down);
 | 
			
		||||
            else if (ev.IsActionPressed("key_left"))
 | 
			
		||||
                HandleInput(Arrow.Dirs.Left);
 | 
			
		||||
            else if (ev.IsActionPressed("key_right"))
 | 
			
		||||
                HandleInput(Arrow.Dirs.Right);
 | 
			
		||||
            else if (ev.IsActionPressed("escape"))
 | 
			
		||||
                GetTree().ChangeSceneToPacked(ResourceLoader.Load<PackedScene>("res://main_menu/main_menu.tscn"));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								src/Game/Stats.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/Game/Stats.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
using Godot;
 | 
			
		||||
 | 
			
		||||
namespace Hellswipers.Game;
 | 
			
		||||
 | 
			
		||||
public partial class Stats : Node {
 | 
			
		||||
    private Label _comboLabel;
 | 
			
		||||
    private Label _solvedLabel;
 | 
			
		||||
 | 
			
		||||
    private int _combo = 0;
 | 
			
		||||
    private int _solved = 0;
 | 
			
		||||
 | 
			
		||||
    public int Combo {
 | 
			
		||||
        get => _combo;
 | 
			
		||||
        set {
 | 
			
		||||
            _combo = value;
 | 
			
		||||
            _comboLabel.Text = _combo.ToString();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int Solved {
 | 
			
		||||
        get => _solved;
 | 
			
		||||
        set {
 | 
			
		||||
            _solved = value;
 | 
			
		||||
            _solvedLabel.Text = _solved.ToString();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public override void _Ready() {
 | 
			
		||||
        _comboLabel = GetNode<Label>("Combo");
 | 
			
		||||
        _solvedLabel = GetNode<Label>("Solved");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								src/MainMenu.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/MainMenu.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
using Godot;
 | 
			
		||||
 | 
			
		||||
namespace Hellswipers;
 | 
			
		||||
 | 
			
		||||
public partial class MainMenu : Control {
 | 
			
		||||
	public override void _Ready() {
 | 
			
		||||
		Settings.Init();
 | 
			
		||||
		
 | 
			
		||||
		var sb = GetNode<TextureToggleButton>("Settings/SoundToggle");
 | 
			
		||||
		sb.State = Settings.SoundMute;
 | 
			
		||||
		sb.StateChanged = v => Settings.SoundMute = v;
 | 
			
		||||
 | 
			
		||||
		var mb = GetNode<TextureToggleButton>("Settings/MusicToggle");
 | 
			
		||||
		mb.State = Settings.MusicMute;
 | 
			
		||||
		mb.StateChanged = v => Settings.MusicMute = v;
 | 
			
		||||
 | 
			
		||||
		GetNode<Button>("StartButton").Pressed +=
 | 
			
		||||
			() => GetTree().ChangeSceneToPacked(ResourceLoader.Load<PackedScene>("res://game/game.tscn"));
 | 
			
		||||
 | 
			
		||||
		GetNode<Button>("ExitButton").Pressed += () => {
 | 
			
		||||
			var tree = GetTree();
 | 
			
		||||
			tree.Root.PropagateNotification((int)NotificationWMCloseRequest);
 | 
			
		||||
			tree.Quit();
 | 
			
		||||
		};
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								src/Settings.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/Settings.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
using Godot;
 | 
			
		||||
 | 
			
		||||
namespace Hellswipers;
 | 
			
		||||
 | 
			
		||||
public static class Settings {
 | 
			
		||||
    private static bool _initDone;
 | 
			
		||||
    private static int _soundBus, _musicBus;
 | 
			
		||||
 | 
			
		||||
    public static bool SoundMute {
 | 
			
		||||
        get => AudioServer.IsBusMute(_soundBus);
 | 
			
		||||
        set => AudioServer.SetBusMute(_soundBus, value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static bool MusicMute {
 | 
			
		||||
        get => AudioServer.IsBusMute(_musicBus);
 | 
			
		||||
        set => AudioServer.SetBusMute(_musicBus, value);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void Init() {
 | 
			
		||||
        if (_initDone) return;
 | 
			
		||||
        _initDone = true;
 | 
			
		||||
 | 
			
		||||
        _soundBus = AudioServer.GetBusIndex("Sound");
 | 
			
		||||
        _musicBus = AudioServer.GetBusIndex("Music");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										54
									
								
								src/TextureToggleButton.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/TextureToggleButton.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
using System;
 | 
			
		||||
using Godot;
 | 
			
		||||
 | 
			
		||||
namespace Hellswipers;
 | 
			
		||||
 | 
			
		||||
[Tool]
 | 
			
		||||
[GlobalClass]
 | 
			
		||||
public partial class TextureToggleButton : BaseButton {
 | 
			
		||||
	public Action<bool> StateChanged = null;
 | 
			
		||||
	
 | 
			
		||||
	[Export] public Texture2D FalseTexture;
 | 
			
		||||
	[Export] public Texture2D TrueTexture;
 | 
			
		||||
	[Export]
 | 
			
		||||
	public bool State {
 | 
			
		||||
		get => _state;
 | 
			
		||||
		set {
 | 
			
		||||
			_state = value;
 | 
			
		||||
			StateChanged?.Invoke(value);
 | 
			
		||||
			QueueRedraw();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private bool _state;
 | 
			
		||||
	private Color _baseColor;
 | 
			
		||||
	private Color _hoverColor;
 | 
			
		||||
	private Color _pressColor;
 | 
			
		||||
 | 
			
		||||
	public override Vector2 _GetMinimumSize() => new(
 | 
			
		||||
		Math.Max(FalseTexture?.GetWidth() ?? 0, TrueTexture?.GetWidth() ?? 0),
 | 
			
		||||
		Math.Max(FalseTexture?.GetHeight() ?? 0, TrueTexture?.GetHeight() ?? 0)
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	public override void _Pressed() => State = !State;
 | 
			
		||||
 | 
			
		||||
	public override void _Draw() {
 | 
			
		||||
		SelfModulate = GetDrawMode() switch {
 | 
			
		||||
			DrawMode.Normal => _baseColor,
 | 
			
		||||
			DrawMode.Disabled => _baseColor,
 | 
			
		||||
			DrawMode.Hover => _hoverColor,
 | 
			
		||||
			DrawMode.Pressed => _pressColor,
 | 
			
		||||
			DrawMode.HoverPressed => _pressColor,
 | 
			
		||||
			_ => SelfModulate
 | 
			
		||||
		};
 | 
			
		||||
		
 | 
			
		||||
		if (TrueTexture != null && FalseTexture != null)
 | 
			
		||||
			DrawTexture(State ? TrueTexture : FalseTexture, Vector2.Zero);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public override void _Ready() {
 | 
			
		||||
		_baseColor = GetThemeColor("font_color", "Button");
 | 
			
		||||
		_hoverColor = GetThemeColor("font_hover_color", "Button");
 | 
			
		||||
		_pressColor = GetThemeColor("font_pressed_color", "Button");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user