Skip to content

Commit

Permalink
Zombies
Browse files Browse the repository at this point in the history
  • Loading branch information
Blockitifluy committed Jul 29, 2024
1 parent ad7b99a commit a797d53
Show file tree
Hide file tree
Showing 15 changed files with 351 additions and 68 deletions.
87 changes: 80 additions & 7 deletions Characters/BasicCharacter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ public abstract partial class BasicCharacter : CharacterBody3D
[Export]
public float Stamina
{
get => stamina;
set { stamina = Mathf.Clamp(value, -StaminaLower, StaminaMax); }
get => _stamina;
set { _stamina = Mathf.Clamp(value, -StaminaLower, StaminaMax); }
}
/// <summary>
/// The lower bound of the stamina. If stamina is under 0, the character is exhasted
Expand All @@ -55,15 +55,43 @@ public float Stamina
/// </summary>
[Export] public float StaminaRegen { get; set; } = 4.0f;

private float stamina = 0.0f;
[ExportGroup("Health")]
[Export] public float MaxHealth { get; set; } = 100.0f;
[Export]
public float Health
{
get { return _health; }
set
{
if (value <= 0)
{
// TODO - On Death Behaviour
GD.Print("Died!");
OnDeath();
return;
}

if (value > MaxHealth) return;

_health = value;
}
}

private float _health = 100.0f;
private float _stamina = 0.0f;
/// <summary>
/// The target velocity
/// </summary>
protected Vector3 targetVelo = Vector3.Zero;
protected Vector3 TargetVelocity = Vector3.Zero;
/// <summary>
/// The charater pivot
/// </summary>
protected Node3D Pivot;
protected double _Timer = 0.0f;

protected abstract Vector3 GetMovementDirection();

protected abstract void OnDeath();

/// <summary>
/// Gets the falling speed of the character
Expand Down Expand Up @@ -106,16 +134,19 @@ public float GetSpeed()
/// <param name="delta">The frame's delta</param>
/// <param name="unitDir">The walk direction</param>
/// <returns>The look and velocity</returns>
protected (Basis, Vector3) Run(double delta, Vector3 unitDir)
protected (Basis, Vector3) GetRunData(double delta, Vector3 unitDir)
{
float currentSpeed = GetSpeed() * (float)delta;

Vector3 walkDisplace = unitDir.Normalized() * currentSpeed;

if (walkDisplace != Vector3.Zero)
float staminaGen = StaminaRegen * (float)delta;
Stamina += staminaGen;

if (walkDisplace.X != 0 || walkDisplace.Z != 0)
{
Basis LastBasis = Pivot.Basis,
LookingAt = Basis.LookingAt(walkDisplace),
LookingAt = Basis.LookingAt(new(walkDisplace.X, 0, walkDisplace.Z)),
LookingLerp = LastBasis.Slerp(LookingAt, LookWeight);

return (LookingLerp, walkDisplace);
Expand All @@ -124,6 +155,30 @@ public float GetSpeed()
return (new Basis(), new Vector3());
}

/// <summary>
/// The action of the player running
/// </summary>
/// <param name="delta">The time distance between the previous 2 frames</param>
/// <returns>The stamina taken when running</returns>
public float RunAction(double delta)
{
Vector3 dir = GetMovementDirection();

if (Stamina == -StaminaLower) return 0.0f;

var (lookAt, velo) = GetRunData(delta, dir);

TargetVelocity = velo;

if (velo != Vector3.Zero) Pivot.Basis = lookAt;

float runStamina = SpeedToStamina(velo.Length());

Stamina -= runStamina;

return runStamina;
}

/// <summary>
/// Converts the speed to stamina
/// </summary>
Expand All @@ -143,4 +198,22 @@ public override void _Ready()
Stamina = StaminaMax;
Pivot = GetNode<Node3D>("Pivot");
}

public override void _PhysicsProcess(double delta)
{
base._PhysicsProcess(delta);

Velocity = TargetVelocity - Vector3.Down * FallAcceleration;
MoveAndSlide();
}

public override void _Process(double delta)
{
base._Process(delta);

float staminaGen = StaminaRegen * (float)delta;
Stamina += staminaGen;

_Timer += delta;
}
}
2 changes: 1 addition & 1 deletion Characters/Player/CameraPivot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public Vector3 LerpPosition(double delta)

private void TurnPivotFromUser()
{
float turnPower = Input.GetAxis("camera-right", "camera-left");
float turnPower = Input.GetAxis("camera-left", "camera-right");

float toRad = Mathf.DegToRad(TurnPower);

Expand Down
51 changes: 21 additions & 30 deletions Characters/Player/Player.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using Godot;

[GlobalClass]
Expand All @@ -25,29 +24,25 @@ public override bool IsRunning()
public Inventory inventory = new(new Vector2I(10, 10), 3);
public Inventory.Hotbar Hotbar { get { return inventory.hotbar; } }

/// <summary>
/// The action of the player running
/// </summary>
/// <param name="delta">The time distance between the previous 2 frames</param>
/// <returns>The stamina taken when running</returns>
public float RunAction(double delta)
protected override Vector3 GetMovementDirection()
{
Vector3 cameraRot = CameraPivot.Rotation;

Vector2 unit2D = Input.GetVector("move-left", "move-right", "move-forward", "move-backward");
Vector3 unit = new Vector3(unit2D.X, 0, unit2D.Y)
.Rotated(cameraRot.Normalized(), cameraRot.Length())
.Normalized();
Vector3 unit3D = new(unit2D.X, 0, unit2D.Y);

if (Stamina == -StaminaLower) return 0.0f;
if (cameraRot == Vector3.Zero) return unit3D.Normalized();

var (lookAt, velo) = Run(delta, unit);
Vector3 unit = unit3D
.Rotated(cameraRot.Normalized(), cameraRot.Length())
.Normalized();

targetVelo = velo;
return unit;
}

if (velo != Vector3.Zero) Pivot.Basis = lookAt;
protected override void OnDeath()
{

return SpeedToStamina(velo.Length());
}

/// <summary>
Expand All @@ -70,6 +65,9 @@ public int SlotInput()
public void EquipAction()
{
int slotIndex = SlotInput();

if (Input.IsActionJustPressed("unequip")) Hotbar.UnequipCurrent();

if (slotIndex == -1) return;

Hotbar.UnequipCurrent();
Expand Down Expand Up @@ -236,27 +234,20 @@ public override void _Ready()
Hotbar.AddToFromHotbar(shovel, 1);
}

public override void _PhysicsProcess(double delta)
public override void _Process(double delta)
{
base._PhysicsProcess(delta);
base._Process(delta);

PickupAction(delta);

// Running
float runStamina = RunAction(delta),
staminaGen = StaminaRegen * (float)delta;

Stamina -= runStamina;
Stamina += staminaGen;

Velocity = targetVelo - Vector3.Down * FallAcceleration;
MoveAndSlide();
EquipAction();

PickupTimer += delta;
}

EquipAction();
public override void _PhysicsProcess(double delta)
{
base._PhysicsProcess(delta);

if (Input.IsActionJustPressed("unequip"))
Hotbar.UnequipCurrent();
RunAction(delta);
}
}
155 changes: 155 additions & 0 deletions Characters/Zombie/Zombie.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
using System.Collections.Generic;
using Godot;

[GlobalClass]
public partial class Zombie : BasicCharacter
{
public enum ZombieState : uint
{
Idle,
Wandering,
Chasing,
Attacking
}

[Export] public ZombieState State { get; set; } = ZombieState.Idle;

[ExportGroup("Wander")]
[Export] public float WanderRange { get; set; }

[ExportGroup("Search")]
[Export] public float SearchRadius { get; set; }
[Export] public double SearchCacheLength { get; set; }

[ExportGroup("Attack")]
[Export] public float AttackDistance { get; set; } = 2.5f;
[Export] public float AttackRange { get; set; }
[Export] public float AttackDamage { get; set; } = 45.0f;
[Export] public double AttackRate { get; set; } = 45.0d;

protected override void OnDeath()
{
QueueFree();
}

private Vector3 _ToMove = Vector3.Inf;
private double _LastAttack = 0.0d;

private bool IsSearchValid(BasicCharacter target)
{
// TODO - Add Raycasting
if (target == null)
return false;

float distance = target.Position.DistanceSquaredTo(Position);
bool inRange = distance < SearchRadius * SearchRadius;

return inRange;
}

private void Wander()
{
float x = GD.Randf(),
z = GD.Randf();

Vector3 dir = new Vector3(x, 0, z).Normalized();
float distance = GD.Randf() * WanderRange;

_ToMove = Position + (dir * distance);

State = ZombieState.Wandering;
}

private void Attack(BasicCharacter enemy)
{
double attackCooldown = 60.0d / AttackRate;

if (_Timer - _LastAttack >= attackCooldown)
return;

enemy.Health -= AttackDamage;
_LastAttack = _Timer;

GD.Print("Zombie attacked Target!");
}

private double _LastSearchCache = double.MinValue;
private double _SearchCacheDelay;
private List<BasicCharacter> _SearchCache = new();

private List<BasicCharacter> SearchForEnemies()
{
double searchDif = _Timer - (_LastSearchCache + _SearchCacheDelay);
if (searchDif < SearchCacheLength)
return _SearchCache;

List<BasicCharacter> enemies = new();

Player player = GetTree().Root.GetChildByType<Player>();

if (!IsSearchValid(player))
return new();

enemies.Add(player);
_SearchCache = enemies;
_LastSearchCache = _Timer;
return enemies;
}

protected override Vector3 GetMovementDirection()
{
if (_ToMove == Vector3.Inf)
return Vector3.Zero;

Vector3 dir = Position.DirectionTo(_ToMove);


return dir;
}

private void Chase(BasicCharacter enemy, float distance)
{
Vector3 targetDir = Position.DirectionTo(enemy.Position);
float spaceBetweenTarget = Mathf.Min(distance, AttackDistance);

if (distance > AttackDistance)
State = ZombieState.Chasing;

_ToMove = enemy.Position - (targetDir * spaceBetweenTarget);
}

public override bool IsRunning()
{
return false; // TODO
}

public override void _Ready()
{
base._Ready();

_SearchCacheDelay = GD.RandRange(0.0d, 15.0d);
}

public override void _Process(double delta)
{
base._Process(delta);

List<BasicCharacter> enemySearch = SearchForEnemies();
bool hasFoundEnemies = enemySearch.Count != 0;

if (hasFoundEnemies)
{
var enemy = enemySearch[0];

float distance = enemy.Position.DistanceTo(Position);

Chase(enemy, distance);

if (distance <= AttackRange)
Attack(enemy);
}
else Wander();

RunAction(delta);
}
}
Loading

0 comments on commit a797d53

Please sign in to comment.