diff --git a/MrBigsock/Assets/Code/Core/Abilities/BasicProjectile2.cs b/MrBigsock/Assets/Code/Core/Abilities/BasicProjectile2.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d17b36feec45a29709a3ce9bbd7167539cac63a1
--- /dev/null
+++ b/MrBigsock/Assets/Code/Core/Abilities/BasicProjectile2.cs
@@ -0,0 +1,93 @@
+using System.Collections;
+using System;
+using System.Collections.Generic;
+
+using UnityEngine;
+using UnityEngine.InputSystem;
+
+using BigSock.Service;
+
+namespace BigSock {
+
+	/*
+		Basic projectile attack for the player..
+	*/
+	public class BasicProjectile2 : BaseAttack {
+		//protected static readonly GameObject PROJECTILE_BASE = new AttackMovement();
+		public const string PROJECTILE_NAME = "bullets/basicsquarebullet";
+		public const string AUDIO_PATH = "The Essential Retro Video Game Sound Effects Collection [512 sounds] By Juhani Junkala/explosions/Shortest/sfx_exp_shortest_hard1";
+
+		
+		public override ulong Id => 104;
+		public override string Name => "Chargeable Player Attack";
+		public override string Description => "A basic projectile shooting attack the player has.";
+
+
+
+		public BasicProjectile2() {
+			AttackStats = new AttackStats{
+				Damage = 1f,
+				Knockback = 1f,
+				Range = 10f,
+				ProjectileSpeed = 10f,
+				AttackSpeed = 1f,
+				CritChance = 0.1f,
+				CritDamageModifier = 2f,
+			};
+			Cooldown = new TimeSpan(0, 0, 0, 1, 0);
+			ManaCost = 2;
+			FireType = FireType.Charge;
+			MinCharge = 0.01f;
+			MaxCharge = 4f;
+		}
+
+
+
+		/*
+			Activates the ability.
+			Returns true if the ability was successfully activated. 
+				- Even if nothing was hit, used to indicate that cooldowns should be updated.
+			This should be overridden in sub-classes for the actual abilities.
+		*/
+		protected override bool Activate(IAbilityParam par) {
+			var actor = par.Actor; 
+			var target = par.TargetPos;
+
+			if(target == null) return false;
+
+			var attack = (AttackStats) AttackStats.Calculate(actor.Stats);
+			attack.Actor = actor;
+
+			// If charged for 30% of max or more, increase.
+			var chargeProp = par.ChargeTimePercent;
+			if(chargeProp > 0.3f) {
+				attack.Damage          *= 1f + chargeProp * 2.5f;
+				attack.Knockback       *= 1f + chargeProp * 2.0f;
+				attack.Range           *= 1f + chargeProp * 1.25f;
+				attack.ProjectileSpeed *= 1f + chargeProp * 0.75f;
+				Debug.Log($"[BasicProjectile2.Activate()] Charged for long enough ({chargeProp:P0}).");
+			}
+
+			var bullet = PrefabService.SINGLETON.Instance(PROJECTILE_NAME, actor.transform.position);
+			var bulletScript = bullet.GetComponent<AttackMovement>();
+			bulletScript.Stats = attack;
+			bulletScript.Direction = (target.Value - (Vector2) actor.transform.position).normalized;
+
+			// Play sound effect
+			var source = actor.source;
+			var audioClip = AudioService.SINGLETON.Get(AUDIO_PATH);
+			if (source != null && audioClip != null) {
+				source.clip = audioClip;
+				source.Play();
+			} else {
+				if(source == null)    Debug.Log($"[BasicProjectile2.Activate()] audio source was null.");
+				if(audioClip == null) Debug.Log($"[BasicProjectile2.Activate()] audio clip was null.");
+			}
+
+
+			return true;
+		}
+
+	}
+
+}
\ No newline at end of file
diff --git a/MrBigsock/Assets/Code/Core/Abilities/BasicProjectile2.cs.meta b/MrBigsock/Assets/Code/Core/Abilities/BasicProjectile2.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..55d2daf9eabf1b690c7ca3b21dc42eff1c7f1b46
--- /dev/null
+++ b/MrBigsock/Assets/Code/Core/Abilities/BasicProjectile2.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6481464019713724ab63def992b3b60e
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/MrBigsock/Assets/Code/PlayerController.cs b/MrBigsock/Assets/Code/PlayerController.cs
index 6a2ca820ac2762d4a22fae2f014e3e3c6b1ab0ae..f27bd483930b9c3e48c2e1b28c775eeb65939953 100644
--- a/MrBigsock/Assets/Code/PlayerController.cs
+++ b/MrBigsock/Assets/Code/PlayerController.cs
@@ -42,6 +42,7 @@ namespace BigSock {
 
 		protected IAttack _testAttack;
 		protected IAttack _testAttack2;
+		protected IAttack _testAttack3;
 		protected IAbility _dodge;
 
 		public DateTime NextTimeCanAttack { get; private set; } = DateTime.Now;
@@ -80,8 +81,9 @@ namespace BigSock {
 			//var tmp = PrefabService.SINGLETON;
 			//var tmp = SpriteService.SINGLETON;
 
-			_testAttack = (IAttack) AbilityService.SINGLETON.Get(101);
+			_testAttack = (IAttack) AbilityService.SINGLETON.Get(104);
 			_testAttack2 = (IAttack) AbilityService.SINGLETON.Get(102);
+			_testAttack3 = (IAttack) AbilityService.SINGLETON.Get(101);
 			_dodge = AbilityService.SINGLETON.Get(201);
 		}
 
@@ -151,7 +153,10 @@ namespace BigSock {
 					else if(Input.GetKeyUp(key)) {
 						par.ChargeTime = Time.time - chargeStarts[key];
 						var t = ability.Use(par);
-						if(!t) Debug.Log($"[PlayerController.CheckAbilityInput({key})] {ability.Name} not fired ({par.ChargeTime}/{ability.MinCharge})");
+						if(!t) {
+							if(par.ChargeTime < ability.MinCharge)
+								Debug.Log($"[PlayerController.CheckAbilityInput({key})] {ability.Name} not fired ({par.ChargeTime:N3} < {ability.MinCharge:N3})");
+						}
 					}
 					break;
 				default:
@@ -184,6 +189,7 @@ namespace BigSock {
 			CheckAbilityInput(KeyCode.Z, _testAttack2);
 			// Check ability 3.
 			CheckAbilityInput(KeyCode.X, _dodge);
+			CheckAbilityInput(KeyCode.C, _testAttack3);
 			
 			
 			//if(Input.GetKeyDown(KeyCode.Z)) Debug.Log($"[PlayerController.Update()] Z was pressed.");