You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
200 lines
5.9 KiB
C#
200 lines
5.9 KiB
C#
|
5 days ago
|
using Godot;
|
||
|
|
using RogueTank.Combat;
|
||
|
|
using RogueTank.Util;
|
||
|
|
|
||
|
|
namespace RogueTank.Actors;
|
||
|
|
|
||
|
|
public partial class PlayerTank : CharacterBody2D, IDamageable
|
||
|
|
{
|
||
|
|
[Signal] public delegate void StatsChangedEventHandler();
|
||
|
|
[Signal] public delegate void DiedEventHandler();
|
||
|
|
[Signal] public delegate void ShotFiredEventHandler();
|
||
|
|
|
||
|
|
public Vector2 DebugLastMoveInput { get; private set; } = Vector2.Zero;
|
||
|
|
|
||
|
|
// Stats
|
||
|
|
public float MoveSpeed { get; set; } = 165f;
|
||
|
|
public float FireRate { get; set; } = 4.0f; // shots/sec
|
||
|
|
public float Damage { get; set; } = 12f;
|
||
|
|
public float Armor { get; set; } = 0f; // flat reduction
|
||
|
|
public float BulletSpeed { get; set; } = 520f;
|
||
|
|
public int BulletPierce { get; set; } = 0;
|
||
|
|
public float MagnetRadius { get; set; } = 42f;
|
||
|
|
|
||
|
|
public float MaxHp { get; set; } = 100f;
|
||
|
|
public float Hp { get; private set; } = 100f;
|
||
|
|
|
||
|
|
public int Level { get; private set; } = 1;
|
||
|
|
public int Xp { get; private set; }
|
||
|
|
public int XpToNext { get; private set; } = 30;
|
||
|
|
public int Coins { get; private set; }
|
||
|
|
|
||
|
|
// References
|
||
|
|
public Node2D? ProjectilesRoot { get; set; }
|
||
|
|
public PackedScene? BulletScene { get; set; }
|
||
|
|
public Node2D? PickupsRoot { get; set; }
|
||
|
|
|
||
|
|
private Sprite2D _sprite = null!;
|
||
|
|
private Sprite2D _turret = null!;
|
||
|
|
private Area2D _magnet = null!;
|
||
|
|
private CollisionShape2D _magnetShape = null!;
|
||
|
|
|
||
|
|
private float _shootCd;
|
||
|
|
|
||
|
|
public override void _Ready()
|
||
|
|
{
|
||
|
|
Hp = MaxHp;
|
||
|
|
|
||
|
|
CollisionLayer = 1u << 3; // player
|
||
|
|
CollisionMask = (1u << 0) | (1u << 1); // walls + enemies
|
||
|
|
|
||
|
|
_sprite = new Sprite2D
|
||
|
|
{
|
||
|
|
Texture = PixelArt.MakeTankTexture(16, new Color(0.15f, 0.75f, 0.35f), new Color(0.1f, 0.9f, 0.35f)),
|
||
|
|
Centered = true,
|
||
|
|
TextureFilter = CanvasItem.TextureFilterEnum.Nearest
|
||
|
|
};
|
||
|
|
AddChild(_sprite);
|
||
|
|
|
||
|
|
_turret = new Sprite2D
|
||
|
|
{
|
||
|
|
Texture = PixelArt.MakeTankTexture(16, new Color(0.0f, 0.0f, 0.0f, 0.0f), new Color(0.95f, 0.95f, 0.95f)),
|
||
|
|
Centered = true,
|
||
|
|
TextureFilter = CanvasItem.TextureFilterEnum.Nearest,
|
||
|
|
Modulate = new Color(0.85f, 0.95f, 0.9f)
|
||
|
|
};
|
||
|
|
AddChild(_turret);
|
||
|
|
|
||
|
|
var col = new CollisionShape2D
|
||
|
|
{
|
||
|
|
Shape = new CircleShape2D { Radius = 7f }
|
||
|
|
};
|
||
|
|
AddChild(col);
|
||
|
|
|
||
|
|
_magnet = new Area2D();
|
||
|
|
_magnet.CollisionLayer = 1u << 5; // magnet
|
||
|
|
_magnet.CollisionMask = 0;
|
||
|
|
_magnetShape = new CollisionShape2D { Shape = new CircleShape2D { Radius = MagnetRadius } };
|
||
|
|
_magnet.AddChild(_magnetShape);
|
||
|
|
AddChild(_magnet);
|
||
|
|
}
|
||
|
|
|
||
|
|
public override void _PhysicsProcess(double delta)
|
||
|
|
{
|
||
|
|
var d = (float)delta;
|
||
|
|
var input = ReadMoveInput();
|
||
|
|
DebugLastMoveInput = input;
|
||
|
|
if (input.Length() > 1f)
|
||
|
|
input = input.Normalized();
|
||
|
|
|
||
|
|
Velocity = input * MoveSpeed;
|
||
|
|
MoveAndSlide();
|
||
|
|
|
||
|
|
AimTurret();
|
||
|
|
HandleShooting(d);
|
||
|
|
UpdateMagnet();
|
||
|
|
}
|
||
|
|
|
||
|
|
private static Vector2 ReadMoveInput()
|
||
|
|
{
|
||
|
|
// Prefer InputMap actions; if they're missing/broken, fallback to raw keys.
|
||
|
|
if (InputMap.HasAction("move_left") && InputMap.HasAction("move_right") &&
|
||
|
|
InputMap.HasAction("move_up") && InputMap.HasAction("move_down"))
|
||
|
|
{
|
||
|
|
return Input.GetVector("move_left", "move_right", "move_up", "move_down");
|
||
|
|
}
|
||
|
|
|
||
|
|
float x = 0f;
|
||
|
|
float y = 0f;
|
||
|
|
if (Input.IsKeyPressed(Key.A) || Input.IsKeyPressed(Key.Left)) x -= 1f;
|
||
|
|
if (Input.IsKeyPressed(Key.D) || Input.IsKeyPressed(Key.Right)) x += 1f;
|
||
|
|
if (Input.IsKeyPressed(Key.W) || Input.IsKeyPressed(Key.Up)) y -= 1f;
|
||
|
|
if (Input.IsKeyPressed(Key.S) || Input.IsKeyPressed(Key.Down)) y += 1f;
|
||
|
|
return new Vector2(x, y);
|
||
|
|
}
|
||
|
|
|
||
|
|
private void AimTurret()
|
||
|
|
{
|
||
|
|
var viewport = GetViewport();
|
||
|
|
if (viewport == null) return;
|
||
|
|
|
||
|
|
var worldMouse = GetGlobalMousePosition();
|
||
|
|
_turret.Rotation = (worldMouse - GlobalPosition).Angle();
|
||
|
|
}
|
||
|
|
|
||
|
|
private void HandleShooting(float delta)
|
||
|
|
{
|
||
|
|
_shootCd -= delta;
|
||
|
|
var wantShoot =
|
||
|
|
(InputMap.HasAction("shoot") && Input.IsActionPressed("shoot")) ||
|
||
|
|
Input.IsMouseButtonPressed(MouseButton.Left);
|
||
|
|
if (!wantShoot || _shootCd > 0f) return;
|
||
|
|
if (BulletScene == null || ProjectilesRoot == null) return;
|
||
|
|
|
||
|
|
_shootCd = 1f / Mathf.Max(0.1f, FireRate);
|
||
|
|
|
||
|
|
var bullet = BulletScene.Instantiate<Bullet>();
|
||
|
|
var dir = new Vector2(Mathf.Cos(_turret.Rotation), Mathf.Sin(_turret.Rotation));
|
||
|
|
bullet.GlobalPosition = GlobalPosition + dir * 10f;
|
||
|
|
bullet.Speed = BulletSpeed;
|
||
|
|
bullet.Damage = Damage;
|
||
|
|
bullet.Pierce = BulletPierce;
|
||
|
|
bullet.Init(dir);
|
||
|
|
ProjectilesRoot.AddChild(bullet);
|
||
|
|
|
||
|
|
EmitSignal(SignalName.ShotFired);
|
||
|
|
}
|
||
|
|
|
||
|
|
private void UpdateMagnet()
|
||
|
|
{
|
||
|
|
if (_magnetShape.Shape is CircleShape2D cs)
|
||
|
|
cs.Radius = MagnetRadius;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void AddCoins(int amount)
|
||
|
|
{
|
||
|
|
Coins += amount;
|
||
|
|
EmitSignal(SignalName.StatsChanged);
|
||
|
|
}
|
||
|
|
|
||
|
|
public bool AddXp(int amount)
|
||
|
|
{
|
||
|
|
Xp += amount;
|
||
|
|
var leveled = false;
|
||
|
|
while (Xp >= XpToNext)
|
||
|
|
{
|
||
|
|
Xp -= XpToNext;
|
||
|
|
Level++;
|
||
|
|
XpToNext = Mathf.RoundToInt(XpToNext * 1.22f + 10);
|
||
|
|
leveled = true;
|
||
|
|
}
|
||
|
|
EmitSignal(SignalName.StatsChanged);
|
||
|
|
return leveled;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Heal(float amount)
|
||
|
|
{
|
||
|
|
Hp = Mathf.Min(MaxHp, Hp + amount);
|
||
|
|
EmitSignal(SignalName.StatsChanged);
|
||
|
|
}
|
||
|
|
|
||
|
|
public void TakeDamage(float amount)
|
||
|
|
{
|
||
|
|
var final = Mathf.Max(1f, amount - Armor);
|
||
|
|
Hp -= final;
|
||
|
|
if (Hp <= 0f)
|
||
|
|
{
|
||
|
|
Hp = 0f;
|
||
|
|
EmitSignal(SignalName.StatsChanged);
|
||
|
|
EmitSignal(SignalName.Died);
|
||
|
|
QueueFree();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
EmitSignal(SignalName.StatsChanged);
|
||
|
|
}
|
||
|
|
|
||
|
|
public Area2D GetMagnetArea() => _magnet;
|
||
|
|
}
|
||
|
|
|
||
|
|
|