diff --git a/DoomDeathmatch/src/Component/ConsumableComponent.cs b/DoomDeathmatch/src/Component/ConsumableComponent.cs index 9a06378..867abf2 100644 --- a/DoomDeathmatch/src/Component/ConsumableComponent.cs +++ b/DoomDeathmatch/src/Component/ConsumableComponent.cs @@ -8,16 +8,42 @@ using OpenTK.Mathematics; namespace DoomDeathmatch.Component; +/// +/// Represents a consumable item in the game, handling interaction with players and other objects. +/// public class ConsumableComponent : Engine.Scene.Component.Component { + /// + /// The consumable logic tied to this component. + /// private readonly IConsumable _consumable; + + /// + /// The collider used for detecting interactions with other objects. + /// private readonly AABBColliderComponent _collider; + + /// + /// The 2D box renderer used to display the consumable's icon. + /// private readonly Box2DRenderer _box2DRenderer; + /// + /// Provides random values for movement adjustments. + /// private readonly Random _random = new(); + /// + /// Controls the movement of the consumable object. + /// private MovementController _movementController = null!; + /// + /// Initializes a new instance of the class. + /// + /// The consumable logic associated with this component. + /// The collider for detecting interactions. + /// The renderer for displaying the consumable's visual representation. public ConsumableComponent(IConsumable parConsumable, AABBColliderComponent parCollider, Box2DRenderer parBox2DRenderer) { @@ -36,6 +62,10 @@ public class ConsumableComponent : Engine.Scene.Component.Component ArgumentNullException.ThrowIfNull(_movementController); } + /// + /// Handles collision events with other objects. + /// + /// The collider of the object that this collided with. private void OnCollision(AABBColliderComponent parCollider) { if (parCollider.ColliderGroups.Contains("player")) @@ -48,6 +78,10 @@ public class ConsumableComponent : Engine.Scene.Component.Component } } + /// + /// Attempts to consume the consumable if it collided with a player. + /// + /// The player's collider component. private void TryConsume(AABBColliderComponent parCollider) { var playerController = parCollider.GameObject.GetComponent(); @@ -61,6 +95,10 @@ public class ConsumableComponent : Engine.Scene.Component.Component EngineUtil.SceneManager.CurrentScene!.Remove(GameObject); } + /// + /// Moves the consumable away if it collides with another consumable. + /// + /// The collider of the other consumable. private void MoveAway(AABBColliderComponent parCollider) { var direction = _collider.GameObject.Transform.GetFullTranslation() - @@ -78,6 +116,10 @@ public class ConsumableComponent : Engine.Scene.Component.Component _movementController.ApplyMovement(direction); } + /// + /// Generates a random direction vector for movement. + /// + /// A normalized random direction vector. private Vector3 GetRandomDirection() { var x = (_random.NextSingle() * 2) - 1; diff --git a/DoomDeathmatch/src/Component/MVC/Enemy/EnemyController.cs b/DoomDeathmatch/src/Component/MVC/Enemy/EnemyController.cs index a95526e..93d459c 100644 --- a/DoomDeathmatch/src/Component/MVC/Enemy/EnemyController.cs +++ b/DoomDeathmatch/src/Component/MVC/Enemy/EnemyController.cs @@ -2,21 +2,50 @@ using DoomDeathmatch.Component.Util; using DoomDeathmatch.Script.Model.Enemy; using DoomDeathmatch.Script.Model.Enemy.Attack; +using DoomDeathmatch.Script.MVC; using Engine.Util; namespace DoomDeathmatch.Component.MVC.Enemy; +/// +/// Controls enemy behavior, including movement, health, and attacks. +/// public class EnemyController : Engine.Scene.Component.Component { + /// + /// Manages the health of the enemy. + /// public HealthController HealthController { get; private set; } = null!; + /// + /// Data associated with the enemy, including stats and behaviors. + /// private readonly EnemyData _enemyData; + /// + /// Reference to the game controller. + /// private GameController _gameController = null!; - private MovementController _movementController = null!; - private AttackBehavior _attackBehavior = null!; - private EnemyView _enemyView = null!; + /// + /// Controls the movement of the enemy. + /// + private MovementController _movementController = null!; + + /// + /// Handles the enemy's attack behavior. + /// + private AttackBehavior _attackBehavior = null!; + + /// + /// Handles visual representation of the enemy. + /// + private IView _enemyView = null!; + + /// + /// Initializes a new instance of the class. + /// + /// Data for the enemy, including stats and behaviors. public EnemyController(EnemyData parEnemyData) { _enemyData = parEnemyData; @@ -26,7 +55,7 @@ public class EnemyController : Engine.Scene.Component.Component { _gameController = EngineUtil.SceneManager.CurrentScene!.FindFirstComponent()!; HealthController = GameObject.GetComponent()!; - _enemyView = GameObject.GetComponent()!; + _enemyView = GameObject.GetComponent>()!; _movementController = GameObject.GetComponent()!; ArgumentNullException.ThrowIfNull(_gameController); @@ -69,6 +98,9 @@ public class EnemyController : Engine.Scene.Component.Component _attackBehavior.Attack(parDeltaTime); } + /// + /// Handles the death of the enemy by removing it from the scene and updating the score. + /// private void OnDeath() { EngineUtil.SceneManager.CurrentScene!.Remove(GameObject); diff --git a/DoomDeathmatch/src/Component/MVC/Enemy/EnemyView.cs b/DoomDeathmatch/src/Component/MVC/Enemy/EnemyView.cs index 90594e3..f3608a6 100644 --- a/DoomDeathmatch/src/Component/MVC/Enemy/EnemyView.cs +++ b/DoomDeathmatch/src/Component/MVC/Enemy/EnemyView.cs @@ -1,19 +1,31 @@ using DoomDeathmatch.Script.Model.Enemy; +using DoomDeathmatch.Script.MVC; using Engine.Graphics.Texture; using Engine.Scene.Component.BuiltIn.Renderer; using Engine.Util; namespace DoomDeathmatch.Component.MVC.Enemy; -public class EnemyView : Engine.Scene.Component.Component +/// +/// View component for displaying the enemy. +/// +public class EnemyView : Engine.Scene.Component.Component, IView { + /// + /// The box2d renderer used to display the enemy. + /// private readonly Box2DRenderer _box2DRenderer; + /// + /// Initializes a new instance of the class. + /// + /// Box2d renderer for displaying the enemy. public EnemyView(Box2DRenderer parBox2DRenderer) { _box2DRenderer = parBox2DRenderer; } + /// public void UpdateView(EnemyData parEnemyData) { _box2DRenderer.Texture = EngineUtil.AssetResourceManager.Load(parEnemyData.Texture); diff --git a/DoomDeathmatch/src/Component/MVC/GameController.cs b/DoomDeathmatch/src/Component/MVC/GameController.cs index 8418a59..8bfb09f 100644 --- a/DoomDeathmatch/src/Component/MVC/GameController.cs +++ b/DoomDeathmatch/src/Component/MVC/GameController.cs @@ -7,19 +7,50 @@ using Engine.Util; namespace DoomDeathmatch.Component.MVC; +/// +/// Controls the main game logic, including pausing, unpausing, and handling game over states. +/// public class GameController : Engine.Scene.Component.Component { + /// + /// Indicates whether the game is currently paused. + /// public bool IsPaused { get; private set; } + + /// + /// Indicates whether the game is over. + /// public bool IsGameOver { get; private set; } + /// + /// The player controller managing the player's state and actions. + /// public PlayerController PlayerController { get; private set; } = null!; + + /// + /// The score controller tracking the player's score. + /// public ScoreController ScoreController { get; private set; } = null!; + /// + /// The input handler for handling keyboard and mouse input. + /// private readonly IInputHandler _inputHandler = EngineUtil.InputHandler; + + /// + /// The menu controller for handling the main menu. + /// private readonly MenuControllerComponent _menuController; + /// + /// The timer controller for managing the game timer. + /// private TimerController _timerController = null!; + /// + /// Initializes a new instance of the class with a specified menu controller. + /// + /// The menu controller used for menu navigation during the game. public GameController(MenuControllerComponent parMenuController) { _menuController = parMenuController; @@ -39,20 +70,6 @@ public class GameController : Engine.Scene.Component.Component _timerController.OnFinished += GameOver; } - public void Unpause() - { - EngineUtil.SceneManager.CurrentScene!.TimeScale = 1.0f; - IsPaused = false; - _menuController.SelectMenuItem("play"); - } - - public void Pause() - { - EngineUtil.SceneManager.CurrentScene!.TimeScale = 0.0f; - IsPaused = true; - _menuController.SelectMenuItem("escape"); - } - public override void Update(double parDeltaTime) { if (_inputHandler.IsKeyJustPressed(KeyboardButtonCode.Escape)) @@ -68,6 +85,29 @@ public class GameController : Engine.Scene.Component.Component } } + /// + /// Pauses the game. + /// + public void Pause() + { + EngineUtil.SceneManager.CurrentScene!.TimeScale = 0.0f; + IsPaused = true; + _menuController.SelectMenuItem("escape"); + } + + /// + /// Resumes the game from a paused state. + /// + public void Unpause() + { + EngineUtil.SceneManager.CurrentScene!.TimeScale = 1.0f; + IsPaused = false; + _menuController.SelectMenuItem("play"); + } + + /// + /// Handles the game over state, transitioning to the Game Over scene. + /// private void GameOver() { IsGameOver = true; diff --git a/DoomDeathmatch/src/Component/MVC/Health/EnemyHealthView.cs b/DoomDeathmatch/src/Component/MVC/Health/EnemyHealthView.cs index b56e19b..c1d12c2 100644 --- a/DoomDeathmatch/src/Component/MVC/Health/EnemyHealthView.cs +++ b/DoomDeathmatch/src/Component/MVC/Health/EnemyHealthView.cs @@ -1,15 +1,30 @@ using DoomDeathmatch.Script.Model; +using DoomDeathmatch.Script.MVC; using Engine.Scene.Component.BuiltIn.Renderer; namespace DoomDeathmatch.Component.MVC.Health; -public class EnemyHealthView : HealthView +/// +/// View component for displaying the health status of an enemy. +/// +public class EnemyHealthView : Engine.Scene.Component.Component, IView { - public EnemyHealthView(TextRenderer parHealthTextRenderer) : base(parHealthTextRenderer) + /// + /// The text renderer used to display the health. + /// + private readonly TextRenderer _healthTextRenderer; + + /// + /// Initializes a new instance of the class. + /// + /// Text renderer for displaying the health status text. + public EnemyHealthView(TextRenderer parHealthTextRenderer) { + _healthTextRenderer = parHealthTextRenderer; } - public override void UpdateView(HealthModel parHealthModel) + /// + public void UpdateView(HealthModel parHealthModel) { _healthTextRenderer.Text = $"Здоровье: {parHealthModel.Health:000}"; } diff --git a/DoomDeathmatch/src/Component/MVC/Health/HealthController.cs b/DoomDeathmatch/src/Component/MVC/Health/HealthController.cs index 6854e1d..a84cb0f 100644 --- a/DoomDeathmatch/src/Component/MVC/Health/HealthController.cs +++ b/DoomDeathmatch/src/Component/MVC/Health/HealthController.cs @@ -1,15 +1,37 @@ using DoomDeathmatch.Script.Model; +using DoomDeathmatch.Script.MVC; namespace DoomDeathmatch.Component.MVC.Health; +/// +/// Manages health-related logic, including damage, healing, and health changes. +/// public class HealthController : Engine.Scene.Component.Component { + /// + /// Triggered when the entity dies. + /// public event Action? OnDeath; + + /// + /// Indicates whether the entity is alive. + /// public bool IsAlive => _healthModel.Health > 0; + /// + /// The health model containing the current and maximum health. + /// private readonly HealthModel _healthModel; - private HealthView? _healthView; + /// + /// The view responsible for displaying health information. + /// + private IView? _healthView; + + /// + /// Initializes a new instance of the class with an optional starting health value. + /// + /// Initial health value. Defaults to 100. public HealthController(float parHealth = 100) { _healthModel = new HealthModel(parHealth); @@ -18,7 +40,7 @@ public class HealthController : Engine.Scene.Component.Component public override void Awake() { _healthModel.HealthChanged += OnHealthChanged; - _healthView = GameObject.GetComponent(); + _healthView = GameObject.GetComponent>(); if (_healthView != null) { @@ -27,22 +49,38 @@ public class HealthController : Engine.Scene.Component.Component } } + /// + /// Sets the maximum health and restores the entity's health to the maximum value. + /// + /// The maximum health value. public void SetMaxHealth(float parMaxHealth) { _healthModel.MaxHealth = parMaxHealth; _healthModel.Health = parMaxHealth; } + /// + /// Reduces the entity's health by the specified damage value. + /// + /// The amount of damage to apply. public void TakeDamage(float parDamage) { _healthModel.Health -= parDamage; } + /// + /// Increases the entity's health by the specified heal value. + /// + /// The amount of healing to apply. public void Heal(float parHeal) { _healthModel.Health += parHeal; } + /// + /// Handles health changes and triggers death logic if health reaches zero. + /// + /// The updated health model. private void OnHealthChanged(HealthModel parHealthModel) { if (!IsAlive) diff --git a/DoomDeathmatch/src/Component/MVC/Health/HealthView.cs b/DoomDeathmatch/src/Component/MVC/Health/HealthView.cs index 9141eae..176a90a 100644 --- a/DoomDeathmatch/src/Component/MVC/Health/HealthView.cs +++ b/DoomDeathmatch/src/Component/MVC/Health/HealthView.cs @@ -1,17 +1,29 @@ using DoomDeathmatch.Script.Model; +using DoomDeathmatch.Script.MVC; using Engine.Scene.Component.BuiltIn.Renderer; namespace DoomDeathmatch.Component.MVC.Health; -public class HealthView : Engine.Scene.Component.Component +/// +/// View component for displaying the health status. +/// +public class HealthView : Engine.Scene.Component.Component, IView { - protected readonly TextRenderer _healthTextRenderer; + /// + /// The text renderer used to display the health percentage. + /// + private readonly TextRenderer _healthTextRenderer; + /// + /// Initializes a new instance of the class. + /// + /// Text renderer for displaying the health status text. public HealthView(TextRenderer parHealthTextRenderer) { _healthTextRenderer = parHealthTextRenderer; } + /// public virtual void UpdateView(HealthModel parHealthModel) { var percentage = parHealthModel.Health / parHealthModel.MaxHealth * 100; diff --git a/DoomDeathmatch/src/Component/MVC/MovementController.cs b/DoomDeathmatch/src/Component/MVC/MovementController.cs index a23498c..2ee7ac5 100644 --- a/DoomDeathmatch/src/Component/MVC/MovementController.cs +++ b/DoomDeathmatch/src/Component/MVC/MovementController.cs @@ -3,11 +3,24 @@ using OpenTK.Mathematics; namespace DoomDeathmatch.Component.MVC; +/// +/// Controls the movement logic for a game object, applying movement forces to the rigidbody. +/// public class MovementController : Engine.Scene.Component.Component { + /// + /// The movement speed. + /// public float Speed { get; set; } = 10.0f; + /// + /// The rigidbody component for the game object. + /// private RigidbodyComponent _rigidbody = null!; + + /// + /// The drag component for the game object. + /// private DragComponent _dragComponent = null!; public override void Awake() @@ -19,6 +32,10 @@ public class MovementController : Engine.Scene.Component.Component ArgumentNullException.ThrowIfNull(_dragComponent); } + /// + /// Applies a directional movement force to the object. + /// + /// The direction of movement. public void ApplyMovement(Vector3 parDirection) { _rigidbody.Force += _dragComponent.Drag * Speed * parDirection.Normalized(); diff --git a/DoomDeathmatch/src/Component/MVC/PlayerController.cs b/DoomDeathmatch/src/Component/MVC/PlayerController.cs index 36c4736..b4af6b1 100644 --- a/DoomDeathmatch/src/Component/MVC/PlayerController.cs +++ b/DoomDeathmatch/src/Component/MVC/PlayerController.cs @@ -9,17 +9,45 @@ using OpenTK.Mathematics; namespace DoomDeathmatch.Component.MVC; +/// +/// Controls the player's actions and state, including health, weapons, and input handling. +/// public class PlayerController : Engine.Scene.Component.Component { + /// + /// Event triggered when the player dies. + /// public event Action? OnDeath; + /// + /// Indicates whether the player is alive. + /// public bool IsAlive => HealthController.IsAlive; + + /// + /// The health controller managing the player's health. + /// public HealthController HealthController { get; private set; } = null!; + + /// + /// The weapon controller managing the player's weapons. + /// public WeaponController WeaponController { get; private set; } = null!; + + /// + /// The player's camera providing a perspective view. + /// public PerspectiveCamera Camera { get; } + /// + /// The input handler for handling keyboard and mouse input. + /// private readonly IInputHandler _inputHandler = EngineUtil.InputHandler; + /// + /// Initializes a new instance of the class with a specified camera. + /// + /// The player's perspective camera. public PlayerController(PerspectiveCamera parCamera) { Camera = parCamera; @@ -66,24 +94,32 @@ public class PlayerController : Engine.Scene.Component.Component return; } - var position = Camera.GameObject.Transform.GetFullTranslation(); - var forward = (Camera.Forward - position).Normalized(); - var right = Vector3.Cross(forward, Vector3.UnitZ).Normalized(); + HandleShootingRaycast(); + } + } - var collisionManager = EngineUtil.SceneManager.CurrentScene!.FindFirstComponent(); + /// + /// Handles the shooting raycast for the player. + /// + private void HandleShootingRaycast() + { + var position = Camera.GameObject.Transform.GetFullTranslation(); + var forward = (Camera.Forward - position).Normalized(); + var right = Vector3.Cross(forward, Vector3.UnitZ).Normalized(); - var offsets = WeaponController.WeaponData.ShootPattern.GetShootPattern(forward, Vector3.UnitZ, right); - foreach (var offset in offsets) + var collisionManager = EngineUtil.SceneManager.CurrentScene!.FindFirstComponent(); + + var offsets = WeaponController.WeaponData.ShootPattern.GetShootPattern(forward, Vector3.UnitZ, right); + foreach (var offset in offsets) + { + var direction = forward + offset; + if (!collisionManager!.Raycast(position, direction, ["enemy", "wall"], out var result)) { - var direction = forward + offset; - if (!collisionManager!.Raycast(position, direction, ["enemy", "wall"], out var result)) - { - continue; - } - - var enemyController = result.HitObject.GetComponent(); - enemyController?.HealthController.TakeDamage(WeaponController.WeaponData.Damage); + continue; } + + var enemyController = result.HitObject.GetComponent(); + enemyController?.HealthController.TakeDamage(WeaponController.WeaponData.Damage); } } } \ No newline at end of file diff --git a/DoomDeathmatch/src/Component/MVC/Score/ScoreController.cs b/DoomDeathmatch/src/Component/MVC/Score/ScoreController.cs index 5d6bf6a..02e90d1 100644 --- a/DoomDeathmatch/src/Component/MVC/Score/ScoreController.cs +++ b/DoomDeathmatch/src/Component/MVC/Score/ScoreController.cs @@ -1,21 +1,39 @@ using DoomDeathmatch.Script.Model; +using DoomDeathmatch.Script.MVC; namespace DoomDeathmatch.Component.MVC.Score; +/// +/// Manages the player's score, including updating and displaying it. +/// public class ScoreController : Engine.Scene.Component.Component { + /// + /// Current score value. + /// public int Score => _scoreModel.Score; + /// + /// The score model containing the current score. + /// private readonly ScoreModel _scoreModel = new(); - private ScoreView _scoreView = null!; + + /// + /// The view responsible for displaying score information. + /// + private IView _scoreView = null!; public override void Awake() { - _scoreView = GameObject.GetComponent()!; + _scoreView = GameObject.GetComponent>()!; _scoreView.UpdateView(_scoreModel); _scoreModel.ScoreChanged += _scoreView.UpdateView; } + /// + /// Adds the specified value to the current score. + /// + /// The score value to add. public void AddScore(int parScore) { _scoreModel.Score += parScore; diff --git a/DoomDeathmatch/src/Component/MVC/Score/ScoreView.cs b/DoomDeathmatch/src/Component/MVC/Score/ScoreView.cs index 63868b4..7f46332 100644 --- a/DoomDeathmatch/src/Component/MVC/Score/ScoreView.cs +++ b/DoomDeathmatch/src/Component/MVC/Score/ScoreView.cs @@ -1,17 +1,29 @@ using DoomDeathmatch.Script.Model; +using DoomDeathmatch.Script.MVC; using Engine.Scene.Component.BuiltIn.Renderer; namespace DoomDeathmatch.Component.MVC.Score; -public class ScoreView : Engine.Scene.Component.Component +/// +/// View component for displaying the score. +/// +public class ScoreView : Engine.Scene.Component.Component, IView { + /// + /// The text renderer used to display the score. + /// private readonly TextRenderer _scoreTextRenderer; + /// + /// Initializes a new instance of the class. + /// + /// Text renderer for displaying the score text. public ScoreView(TextRenderer parScoreTextRenderer) { _scoreTextRenderer = parScoreTextRenderer; } + /// public void UpdateView(ScoreModel parScoreModel) { _scoreTextRenderer.Text = $"Счет: {parScoreModel.Score:00000}"; diff --git a/DoomDeathmatch/src/Component/MVC/Timer/TimerController.cs b/DoomDeathmatch/src/Component/MVC/Timer/TimerController.cs index 77b9b95..2e31e93 100644 --- a/DoomDeathmatch/src/Component/MVC/Timer/TimerController.cs +++ b/DoomDeathmatch/src/Component/MVC/Timer/TimerController.cs @@ -1,19 +1,41 @@ -using Engine.Util; +using DoomDeathmatch.Script.MVC; +using Engine.Util; namespace DoomDeathmatch.Component.MVC.Timer; +/// +/// Controls the timer functionality, including updates and view synchronization. +/// public class TimerController : Engine.Scene.Component.Component { + /// + /// Triggered when the timer finishes. + /// public event Action? OnFinished; - private readonly TickableTimer _tickableTimer = new(60 + 10); + /// + /// Internal timer that tracks the current countdown time. + /// + private readonly TickableTimer _tickableTimer; - private TimerView _timerView = null!; + /// + /// View responsible for displaying the timer's current state. + /// + private IView _timerView = null!; + + /// + /// Initializes a new instance of the class with an optional starting time. + /// + /// The initial time value. Defaults to 60 seconds. + public TimerController(float parTime = 60) + { + _tickableTimer = new TickableTimer(parTime); + } public override void Awake() { - _timerView = GameObject.GetComponent()!; - _timerView.UpdateView(_tickableTimer.CurrentTime); + _timerView = GameObject.GetComponent>()!; + _timerView.UpdateView(_tickableTimer); _tickableTimer.OnUpdate += OnTimeChanged; _tickableTimer.OnFinished += OnFinished; @@ -24,8 +46,12 @@ public class TimerController : Engine.Scene.Component.Component _tickableTimer.Update(parDeltaTime); } + /// + /// Updates the timer view with the new time value. + /// + /// The updated time value. private void OnTimeChanged(double parTime) { - _timerView.UpdateView(parTime); + _timerView.UpdateView(_tickableTimer); } } \ No newline at end of file diff --git a/DoomDeathmatch/src/Component/MVC/Timer/TimerView.cs b/DoomDeathmatch/src/Component/MVC/Timer/TimerView.cs index fb7193a..125e4b0 100644 --- a/DoomDeathmatch/src/Component/MVC/Timer/TimerView.cs +++ b/DoomDeathmatch/src/Component/MVC/Timer/TimerView.cs @@ -1,20 +1,34 @@ -using Engine.Scene.Component.BuiltIn.Renderer; +using DoomDeathmatch.Script.MVC; +using Engine.Scene.Component.BuiltIn.Renderer; +using Engine.Util; namespace DoomDeathmatch.Component.MVC.Timer; -public class TimerView : Engine.Scene.Component.Component +/// +/// View component for displaying the timer. +/// +public class TimerView : Engine.Scene.Component.Component, IView { + /// + /// The text renderer used to display the timer. + /// private readonly TextRenderer _timerTextRenderer; + /// + /// Initializes a new instance of the class. + /// + /// Text renderer for displaying the timer text. public TimerView(TextRenderer parTimerTextRenderer) { _timerTextRenderer = parTimerTextRenderer; } - public void UpdateView(double parTime) + /// + public void UpdateView(TickableTimer parTime) { - var seconds = Math.Floor(parTime) % 60; - var minutes = Math.Floor(parTime / 60) % 60; + var time = parTime.CurrentTime; + var seconds = Math.Floor(time) % 60; + var minutes = Math.Floor(time / 60) % 60; _timerTextRenderer.Text = $"Время: {minutes:00}:{seconds:00}"; } diff --git a/DoomDeathmatch/src/Component/MVC/Weapon/AmmoData.cs b/DoomDeathmatch/src/Component/MVC/Weapon/AmmoData.cs new file mode 100644 index 0000000..ecea149 --- /dev/null +++ b/DoomDeathmatch/src/Component/MVC/Weapon/AmmoData.cs @@ -0,0 +1,17 @@ +namespace DoomDeathmatch.Component.MVC.Weapon; + +/// +/// Data for ammo information. +/// +public struct AmmoData +{ + /// + /// The maximum amount of ammo. + /// + public int MaxAmmo { get; set; } + + /// + /// The current amount of ammo. + /// + public int Ammo { get; set; } +} \ No newline at end of file diff --git a/DoomDeathmatch/src/Component/MVC/Weapon/IWeaponView.cs b/DoomDeathmatch/src/Component/MVC/Weapon/IWeaponView.cs new file mode 100644 index 0000000..09ec8a9 --- /dev/null +++ b/DoomDeathmatch/src/Component/MVC/Weapon/IWeaponView.cs @@ -0,0 +1,15 @@ +using DoomDeathmatch.Script.Model.Weapon; +using DoomDeathmatch.Script.MVC; + +namespace DoomDeathmatch.Component.MVC.Weapon; + +/// +/// Interface for weapon view components. +/// +public interface IWeaponView : IView, IView +{ + /// + /// Starts the weapon fire animation. + /// + public void PlayFireAnimation(); +} \ No newline at end of file diff --git a/DoomDeathmatch/src/Component/MVC/Weapon/WeaponController.cs b/DoomDeathmatch/src/Component/MVC/Weapon/WeaponController.cs index 174f9ae..b50f0b8 100644 --- a/DoomDeathmatch/src/Component/MVC/Weapon/WeaponController.cs +++ b/DoomDeathmatch/src/Component/MVC/Weapon/WeaponController.cs @@ -3,23 +3,48 @@ using DoomDeathmatch.Script.Model.Weapon; namespace DoomDeathmatch.Component.MVC.Weapon; +/// +/// Manages weapon functionality, including shooting, reloading, and weapon selection. +/// public class WeaponController : Engine.Scene.Component.Component { + /// + /// Triggered when a weapon is fired. + /// public event Action? OnWeaponShot; + + /// + /// The currently selected weapon's data. + /// public WeaponData WeaponData => _weaponModel.SelectedWeapon; + /// + /// The internal weapon model containing weapon-related data. + /// private readonly WeaponModel _weaponModel = new(); - private WeaponView _weaponView = null!; + + /// + /// View responsible for displaying weapon information and animations. + /// + private IWeaponView _weaponView = null!; public override void Awake() { - _weaponView = GameObject.GetComponent()!; + _weaponView = GameObject.GetComponent()!; _weaponView.UpdateView(_weaponModel.SelectedWeapon); + _weaponView.UpdateView(new AmmoData + { + Ammo = _weaponModel.SelectedWeapon.Ammo, MaxAmmo = _weaponModel.SelectedWeapon.MaxAmmo + }); _weaponModel.OnWeaponSelected += WeaponSelected; WeaponSelected(null, _weaponModel.SelectedWeapon); } + /// + /// Attempts to fire the currently selected weapon. + /// + /// true if the weapon is fired successfully; otherwise, false. public bool TryShoot() { if (_weaponModel.SelectedWeapon.Ammo <= 0) @@ -30,16 +55,23 @@ public class WeaponController : Engine.Scene.Component.Component _weaponModel.SelectedWeapon.Ammo--; OnWeaponShot?.Invoke(_weaponModel.SelectedWeapon); - _weaponView.Fire(); + _weaponView.PlayFireAnimation(); return true; } + /// + /// Reloads the currently selected weapon, restoring its ammo to the maximum value. + /// public void Reload() { _weaponModel.SelectedWeapon.Ammo = _weaponModel.SelectedWeapon.MaxAmmo; } + /// + /// Adds a new weapon to the weapon model if it does not already exist. + /// + /// The weapon data to add. public void AddWeapon(WeaponData parWeaponData) { if (_weaponModel.Weapons.Contains(parWeaponData)) @@ -50,6 +82,10 @@ public class WeaponController : Engine.Scene.Component.Component _weaponModel.Weapons.Add(parWeaponData); } + /// + /// Adds a weapon to the model or merges its ammo with an existing weapon of the same type. + /// + /// The weapon data to add or merge. public void AddOrMergeWeapon(WeaponData parWeaponData) { if (!_weaponModel.Weapons.Contains(parWeaponData)) @@ -62,6 +98,10 @@ public class WeaponController : Engine.Scene.Component.Component } } + /// + /// Removes a weapon from the model by its index. + /// + /// The index of the weapon to remove. public void RemoveWeapon(int parIndex) { if (parIndex <= 0 || parIndex >= _weaponModel.Weapons.Count) @@ -75,6 +115,10 @@ public class WeaponController : Engine.Scene.Component.Component _weaponModel.Weapons.RemoveAt(parIndex); } + /// + /// Selects a weapon from the model by its index. + /// + /// The index of the weapon to select. public void SelectWeapon(int parIndex) { if (parIndex >= _weaponModel.Weapons.Count) @@ -85,14 +129,26 @@ public class WeaponController : Engine.Scene.Component.Component _weaponModel.SelectedWeaponIndex = parIndex; } + /// + /// Updates the view when the selected weapon changes. + /// + /// The previously selected weapon. + /// The newly selected weapon. private void WeaponSelected(WeaponData? parOldWeapon, WeaponData parNewWeapon) { if (parOldWeapon != null) { - parOldWeapon.OnAmmoChanged -= _weaponView.UpdateAmmoView; + parOldWeapon.OnAmmoChanged -= AmmoChanged; } - parNewWeapon.OnAmmoChanged += _weaponView.UpdateAmmoView; + parNewWeapon.OnAmmoChanged += AmmoChanged; _weaponView.UpdateView(parNewWeapon); } + + private void AmmoChanged(WeaponData parWeapon) + { + var ammoData = new AmmoData { Ammo = parWeapon.Ammo, MaxAmmo = parWeapon.MaxAmmo }; + + _weaponView.UpdateView(ammoData); + } } \ No newline at end of file diff --git a/DoomDeathmatch/src/Component/MVC/Weapon/WeaponView.cs b/DoomDeathmatch/src/Component/MVC/Weapon/WeaponView.cs index dd2c446..d4af643 100644 --- a/DoomDeathmatch/src/Component/MVC/Weapon/WeaponView.cs +++ b/DoomDeathmatch/src/Component/MVC/Weapon/WeaponView.cs @@ -6,16 +6,42 @@ using Engine.Util; namespace DoomDeathmatch.Component.MVC.Weapon; -public class WeaponView : Engine.Scene.Component.Component +/// +/// View component for displaying weapon information such as name, ammo, and sprite. +/// +public class WeaponView : Engine.Scene.Component.Component, IWeaponView { + /// + /// The text renderer used to display the weapon name. + /// private readonly TextRenderer _weaponName; + + /// + /// The text renderer used to display the weapon ammo. + /// private readonly TextRenderer _weaponAmmo; + + /// + /// The box2d renderer used to display the weapon sprite. + /// private readonly Box2DRenderer _weaponSprite; + /// + /// The texture used for the weapon sprite when it is idle. + /// private Texture? _idleTexture; + /// + /// The animation player used to display the weapon sprite when it is firing. + /// private AnimationPlayer? _weaponFireAnimation; + /// + /// Initializes a new instance of the class. + /// + /// Text renderer for displaying the weapon name. + /// Text renderer for displaying the ammo count. + /// Renderer for displaying the weapon sprite. public WeaponView(TextRenderer parWeaponName, TextRenderer parWeaponAmmo, Box2DRenderer parWeaponSprite) { _weaponName = parWeaponName; @@ -28,9 +54,9 @@ public class WeaponView : Engine.Scene.Component.Component _weaponFireAnimation?.Update(parDeltaTime); } + /// public void UpdateView(WeaponData parWeaponData) { - UpdateAmmoView(parWeaponData); _weaponName.Text = $"Оружие: {parWeaponData.Name}"; _idleTexture = EngineUtil.AssetResourceManager.Load(parWeaponData.IdleTexture); @@ -50,21 +76,30 @@ public class WeaponView : Engine.Scene.Component.Component _weaponFireAnimation.OnFinish += OnAnimationFinish; } - public void UpdateAmmoView(WeaponData parWeaponData) + /// + public void UpdateView(AmmoData parAmmoData) { - _weaponAmmo.Text = $"Патроны: {parWeaponData.Ammo}/{parWeaponData.MaxAmmo}"; + _weaponAmmo.Text = $"Патроны: {parAmmoData.Ammo}/{parAmmoData.MaxAmmo}"; } - public void Fire() + /// + public void PlayFireAnimation() { _weaponFireAnimation?.Start(); } + /// + /// Handles the animation frame change event. + /// + /// The current frame texture. private void OnAnimationFrame(Texture parFrameTexture) { _weaponSprite.Texture = parFrameTexture; } + /// + /// Handles the animation finish event. + /// private void OnAnimationFinish() { _weaponSprite.Texture = _idleTexture; diff --git a/DoomDeathmatch/src/Component/ObjectSpawnerComponent.cs b/DoomDeathmatch/src/Component/ObjectSpawnerComponent.cs index e91b0b3..2edb7ff 100644 --- a/DoomDeathmatch/src/Component/ObjectSpawnerComponent.cs +++ b/DoomDeathmatch/src/Component/ObjectSpawnerComponent.cs @@ -2,6 +2,9 @@ namespace DoomDeathmatch.Component; +/// +/// Component for spawning objects using . +/// public class ObjectSpawnerComponent(ObjectSpawner parObjectSpawner) : Engine.Scene.Component.Component { public override void Update(double parDeltaTime) diff --git a/DoomDeathmatch/src/Component/Physics/Collision/AABBColliderComponent.cs b/DoomDeathmatch/src/Component/Physics/Collision/AABBColliderComponent.cs index 67bb0ec..b980307 100644 --- a/DoomDeathmatch/src/Component/Physics/Collision/AABBColliderComponent.cs +++ b/DoomDeathmatch/src/Component/Physics/Collision/AABBColliderComponent.cs @@ -3,16 +3,44 @@ using OpenTK.Mathematics; namespace DoomDeathmatch.Component.Physics.Collision; +/// +/// Represents an axis-aligned bounding box (AABB) collider component used for collision detection. +/// public class AABBColliderComponent : Engine.Scene.Component.Component { + /// + /// Triggered when a collision occurs with another . + /// public event Action? OnCollision; + /// + /// The collider defining the bounds of this component. + /// public AABBCollider Collider { get; set; } = new(); + + /// + /// Offset applied to the collider position relative to the parent object. + /// public Vector3 Offset { get; set; } = Vector3.Zero; + + /// + /// A set of groups that this collider belongs to. + /// public ISet ColliderGroups => _colliderGroups; + + /// + /// A set of groups that this collider will not collide with. + /// public ISet ExcludeColliderCollideGroups => _excludeColliderCollideGroups; + /// + /// A set of groups that this collider belongs to. + /// private readonly HashSet _colliderGroups = ["default"]; + + /// + /// A set of groups that this collider will not collide with. + /// private readonly HashSet _excludeColliderCollideGroups = []; public override void Update(double parDeltaTime) @@ -20,13 +48,12 @@ public class AABBColliderComponent : Engine.Scene.Component.Component Collider.Position = GameObject.Transform.GetFullTranslation() + Offset; } + /// + /// Invokes the collision logic for this collider with the specified collider. + /// + /// The collider with which this component collided. public void CollideWith(AABBColliderComponent parCollider) { OnCollision?.Invoke(parCollider); } - - public bool InColliderGroup(string parGroup) - { - return ColliderGroups.Contains(parGroup); - } } \ No newline at end of file diff --git a/DoomDeathmatch/src/Component/Physics/Collision/ColliderForceFieldComponent.cs b/DoomDeathmatch/src/Component/Physics/Collision/ColliderForceFieldComponent.cs index 6e08e47..4c5db22 100644 --- a/DoomDeathmatch/src/Component/Physics/Collision/ColliderForceFieldComponent.cs +++ b/DoomDeathmatch/src/Component/Physics/Collision/ColliderForceFieldComponent.cs @@ -2,6 +2,9 @@ namespace DoomDeathmatch.Component.Physics.Collision; +/// +/// A force field component that applies collision-based velocity changes to rigid bodies. +/// public class ColliderForceFieldComponent : Engine.Scene.Component.Component { private AABBColliderComponent _collider = null!; @@ -12,6 +15,10 @@ public class ColliderForceFieldComponent : Engine.Scene.Component.Component _collider.OnCollision += OnCollision; } + /// + /// Handles collision events and modifies the velocity of colliding rigid bodies. + /// + /// The collider involved in the collision. private void OnCollision(AABBColliderComponent parCollider) { var rigidbody = parCollider.GameObject.GetComponent(); @@ -27,6 +34,6 @@ public class ColliderForceFieldComponent : Engine.Scene.Component.Component return; } - rigidbody.Velocity -= normal * (speedAlongNormal * 1.75f); + rigidbody.Velocity -= normal * speedAlongNormal; } } \ No newline at end of file diff --git a/DoomDeathmatch/src/Component/Physics/Collision/CollisionManagerComponent.cs b/DoomDeathmatch/src/Component/Physics/Collision/CollisionManagerComponent.cs index e82021e..a73cbd9 100644 --- a/DoomDeathmatch/src/Component/Physics/Collision/CollisionManagerComponent.cs +++ b/DoomDeathmatch/src/Component/Physics/Collision/CollisionManagerComponent.cs @@ -5,8 +5,14 @@ using OpenTK.Mathematics; namespace DoomDeathmatch.Component.Physics.Collision; +/// +/// Manages collisions between AABB colliders in the current scene. +/// public class CollisionManagerComponent : Engine.Scene.Component.Component { + /// + /// A list of colliders in the current scene. + /// private List _colliders = []; public override void PreUpdate(double parDeltaTime) @@ -24,34 +30,19 @@ public class CollisionManagerComponent : Engine.Scene.Component.Component { var colliderB = _colliders[j]; - var canCollideAB = colliderA.ExcludeColliderCollideGroups.Count == 0 || - !colliderB.ColliderGroups.Overlaps(colliderA.ExcludeColliderCollideGroups); - var canCollideBA = colliderB.ExcludeColliderCollideGroups.Count == 0 || - !colliderA.ColliderGroups.Overlaps(colliderB.ExcludeColliderCollideGroups); - - if (!canCollideAB && !canCollideBA) - { - continue; - } - - if (!colliderA.Collider.Intersects(colliderB.Collider)) - { - continue; - } - - if (canCollideAB) - { - colliderA.CollideWith(colliderB); - } - - if (canCollideBA) - { - colliderB.CollideWith(colliderA); - } + PerformCollisionCheck(colliderA, colliderB); } } } + /// + /// Performs a raycast and finds the closest collider intersected by the ray. + /// + /// The starting point of the ray. + /// The direction of the ray. + /// The collider groups to consider for the raycast. + /// The result of the raycast, if an intersection occurs. + /// true if an intersection occurs; otherwise, false. public bool Raycast(Vector3 parStart, Vector3 parDirection, HashSet parColliderGroups, [MaybeNullWhen(false)] out RaycastResult parResult) { @@ -90,6 +81,48 @@ public class CollisionManagerComponent : Engine.Scene.Component.Component return parResult != null; } + /// + /// Performs a collision check between two colliders and invokes the appropriate collision events. + /// + /// The first collider. + /// The second collider. + private static void PerformCollisionCheck(AABBColliderComponent parColliderA, AABBColliderComponent parColliderB) + { + var canCollideAB = parColliderA.ExcludeColliderCollideGroups.Count == 0 || + !parColliderB.ColliderGroups.Overlaps(parColliderA.ExcludeColliderCollideGroups); + var canCollideBA = parColliderB.ExcludeColliderCollideGroups.Count == 0 || + !parColliderA.ColliderGroups.Overlaps(parColliderB.ExcludeColliderCollideGroups); + + if (!canCollideAB && !canCollideBA) + { + return; + } + + if (!parColliderA.Collider.Intersects(parColliderB.Collider)) + { + return; + } + + if (canCollideAB) + { + parColliderA.CollideWith(parColliderB); + } + + if (canCollideBA) + { + parColliderB.CollideWith(parColliderA); + } + } + + /// + /// Checks for intersection between a ray and an AABB collider. + /// + /// The origin of the ray. + /// The direction of the ray. + /// The collider to test against. + /// The intersection point, if the ray intersects. + /// The normal of the intersection surface. + /// true if the ray intersects the collider; otherwise, false. private static bool RaycastAABB(Vector3 parOrigin, Vector3 parDirection, AABBCollider parCollider, out Vector3 parHitPoint, out Vector3 parHitNormal) diff --git a/DoomDeathmatch/src/Component/Physics/DragComponent.cs b/DoomDeathmatch/src/Component/Physics/DragComponent.cs index f9fa47b..8d865f3 100644 --- a/DoomDeathmatch/src/Component/Physics/DragComponent.cs +++ b/DoomDeathmatch/src/Component/Physics/DragComponent.cs @@ -2,11 +2,24 @@ namespace DoomDeathmatch.Component.Physics; +/// +/// Applies drag to a rigidbody to simulate resistance to motion. +/// public class DragComponent : Engine.Scene.Component.Component { + /// + /// The drag coefficient applied to the velocity of the rigidbody. + /// public float Drag { get; set; } = 1f; + + /// + /// A multiplier applied to each axis of the velocity when calculating drag. + /// public Vector3 Multiplier { get; set; } = Vector3.One; + /// + /// The rigidbody to apply drag to. + /// private RigidbodyComponent _rigidbody = null!; public override void Awake() diff --git a/DoomDeathmatch/src/Component/Physics/GravityComponent.cs b/DoomDeathmatch/src/Component/Physics/GravityComponent.cs index fa9d6d3..f663314 100644 --- a/DoomDeathmatch/src/Component/Physics/GravityComponent.cs +++ b/DoomDeathmatch/src/Component/Physics/GravityComponent.cs @@ -2,24 +2,45 @@ namespace DoomDeathmatch.Component.Physics; +/// +/// Simulates the effect of gravity on a rigidbody. +/// public class GravityComponent : Engine.Scene.Component.Component { + /// + /// Indicates whether the object is currently in the air. + /// public bool IsInAir { get; private set; } + /// + /// The strength of the gravitational force. + /// public float Strength { get; set; } = 10.0f; + /// + /// The direction of the gravitational force. + /// public Vector3 Direction { get => _direction; set => _direction = value.Normalized(); } + /// + /// The height at which gravity stops affecting the object. + /// public float Floor { get; set; } = 5.0f; - - private RigidbodyComponent _rigidbody = null!; + /// + /// The direction of the gravitational force. + /// private Vector3 _direction = -Vector3.UnitZ; + /// + /// The rigidbody to apply gravity to. + /// + private RigidbodyComponent _rigidbody = null!; + public override void Awake() { _rigidbody = GameObject.GetComponent()!; diff --git a/DoomDeathmatch/src/Component/Physics/RigidbodyComponent.cs b/DoomDeathmatch/src/Component/Physics/RigidbodyComponent.cs index dcceded..19fb26b 100644 --- a/DoomDeathmatch/src/Component/Physics/RigidbodyComponent.cs +++ b/DoomDeathmatch/src/Component/Physics/RigidbodyComponent.cs @@ -2,11 +2,24 @@ namespace DoomDeathmatch.Component.Physics; +/// +/// Represents a rigidbody component that simulates physics behavior such as force, velocity, and acceleration. +/// public class RigidbodyComponent : Engine.Scene.Component.Component { + /// + /// The mass of the rigidbody. + /// public float Mass { get; set; } = 1.0f; + + /// + /// Indicates whether the rigidbody is static and unaffected by forces. + /// public bool IsStatic { get; set; } = false; + /// + /// The force currently applied to the rigidbody. + /// public Vector3 Force { get => _force; @@ -21,6 +34,9 @@ public class RigidbodyComponent : Engine.Scene.Component.Component } } + /// + /// The velocity of the rigidbody. + /// public Vector3 Velocity { get => _velocity; @@ -35,11 +51,22 @@ public class RigidbodyComponent : Engine.Scene.Component.Component } } + /// + /// The force currently applied to the rigidbody. + /// private Vector3 _force = Vector3.Zero; - private Vector3 _velocity = Vector3.Zero; + + /// + /// The acceleration of the rigidbody. + /// private Vector3 _acceleration = Vector3.Zero; - public override void Update(double parDeltaTime) + /// + /// The velocity of the rigidbody. + /// + private Vector3 _velocity = Vector3.Zero; + + public override void PostUpdate(double parDeltaTime) { if (IsStatic) { diff --git a/DoomDeathmatch/src/Component/UI/MenuControllerComponent.cs b/DoomDeathmatch/src/Component/UI/MenuControllerComponent.cs index 4a2475b..b8801e8 100644 --- a/DoomDeathmatch/src/Component/UI/MenuControllerComponent.cs +++ b/DoomDeathmatch/src/Component/UI/MenuControllerComponent.cs @@ -2,20 +2,39 @@ namespace DoomDeathmatch.Component.UI; +/// +/// A component responsible for managing and selecting menu items in a UI. +/// public class MenuControllerComponent : Engine.Scene.Component.Component { + /// + /// A dictionary of menu items, mapping their names to their game objects. + /// private readonly Dictionary _menuItems = new(); + /// + /// Adds a new menu item to the menu. + /// + /// The name of the menu item. + /// The game object representing the menu item. public void AddMenuItem(string parName, GameObject parGameObject) { _menuItems.Add(parName, parGameObject); } + /// + /// Removes a menu item from the menu by name. + /// + /// The name of the menu item to remove. public void RemoveMenuItem(string parName) { _menuItems.Remove(parName); } + /// + /// Selects a menu item by name, enabling only the selected item. + /// + /// The name of the menu item to select. public void SelectMenuItem(string parName) { foreach (var (name, menuItem) in _menuItems) diff --git a/DoomDeathmatch/src/Component/UI/SelectorComponent.cs b/DoomDeathmatch/src/Component/UI/SelectorComponent.cs index 44614f0..2ca50d3 100644 --- a/DoomDeathmatch/src/Component/UI/SelectorComponent.cs +++ b/DoomDeathmatch/src/Component/UI/SelectorComponent.cs @@ -3,17 +3,44 @@ using Engine.Util; namespace DoomDeathmatch.Component.UI; +/// +/// A component that manages selection of UI components using keyboard inputs. +/// public class SelectorComponent : Engine.Scene.Component.Component { + /// + /// Event triggered when a UI component is selected. + /// public event Action? OnSelect; + /// + /// The list of selectable UI components. + /// public List Children { get; } = []; + + /// + /// The key used to navigate to the next item in the list. + /// public KeyboardButtonCode NextKey { get; set; } = KeyboardButtonCode.Down; + + /// + /// The key used to navigate to the previous item in the list. + /// public KeyboardButtonCode PrevKey { get; set; } = KeyboardButtonCode.Up; + + /// + /// The key used to select the currently selected item. + /// public KeyboardButtonCode SelectKey { get; set; } = KeyboardButtonCode.Space; + /// + /// The input handler used to check for keyboard input. + /// private readonly IInputHandler _inputHandler = EngineUtil.InputHandler; + /// + /// The index of the currently selected item. + /// private int _selectedIndex; public override void Start() @@ -58,11 +85,17 @@ public class SelectorComponent : Engine.Scene.Component.Component UpdatePosition(); } + /// + /// Triggers the event for the currently selected child. + /// private void SelectionChanged() { OnSelect?.Invoke(Children[_selectedIndex]); } + /// + /// Updates the position of the selector based on the selected child component. + /// private void UpdatePosition() { var child = Children[_selectedIndex]; @@ -77,6 +110,10 @@ public class SelectorComponent : Engine.Scene.Component.Component GameObject.Transform.Size.Y = scale.Y; } + /// + /// Selects a UI component when the mouse hovers over it. + /// + /// The UI component to select. private void Select(UiComponent parComponent) { var index = Children.IndexOf(parComponent); diff --git a/DoomDeathmatch/src/Component/UI/StackComponent.cs b/DoomDeathmatch/src/Component/UI/StackComponent.cs index 29e92f8..e2ae5ed 100644 --- a/DoomDeathmatch/src/Component/UI/StackComponent.cs +++ b/DoomDeathmatch/src/Component/UI/StackComponent.cs @@ -3,9 +3,19 @@ using OpenTK.Mathematics; namespace DoomDeathmatch.Component.UI; +/// +/// A UI container that arranges its child components in a stack, either horizontally or vertically. +/// public class StackComponent : UiContainerComponent { + /// + /// The child components contained in this stack. + /// public List Children { get; } = []; + + /// + /// The orientation of the stack, which determines whether the components are arranged horizontally or vertically. + /// public Orientation Orientation { get; set; } = Orientation.Vertical; public override void Update(double parDeltaTime) diff --git a/DoomDeathmatch/src/Component/UI/TextAlignComponent.cs b/DoomDeathmatch/src/Component/UI/TextAlignComponent.cs index 4ef9b4a..eaff416 100644 --- a/DoomDeathmatch/src/Component/UI/TextAlignComponent.cs +++ b/DoomDeathmatch/src/Component/UI/TextAlignComponent.cs @@ -4,11 +4,24 @@ using OpenTK.Mathematics; namespace DoomDeathmatch.Component.UI; +/// +/// A component that handles text alignment for UI elements that use text rendering. +/// public class TextAlignComponent : Engine.Scene.Component.Component { + /// + /// The alignment of the text within its bounding box. + /// public Align Alignment { get; set; } = Align.Left; + /// + /// The text renderer used to measure the text size. + /// private TextRenderer _textRenderer = null!; + + /// + /// The cached text to avoid unnecessary measurements. + /// private string? _cachedText; public override void Awake() @@ -36,6 +49,11 @@ public class TextAlignComponent : Engine.Scene.Component.Component GameObject.Transform.Translation.Xy = offset; } + /// + /// Calculates the offset for the text alignment based on the given size. + /// + /// The size of the text. + /// The calculated offset. public Vector2 GetOffset(Vector2 parSize) { return Alignment switch diff --git a/DoomDeathmatch/src/Component/UI/TextInputComponent.cs b/DoomDeathmatch/src/Component/UI/TextInputComponent.cs index 0984de6..fe49f76 100644 --- a/DoomDeathmatch/src/Component/UI/TextInputComponent.cs +++ b/DoomDeathmatch/src/Component/UI/TextInputComponent.cs @@ -3,26 +3,60 @@ using Engine.Util; namespace DoomDeathmatch.Component.UI; +/// +/// A component that handles text input from the user, such as typing and backspacing. +/// public class TextInputComponent : Engine.Scene.Component.Component { + /// + /// Event triggered when there is new input. + /// public event Action? OnInput; + /// + /// Indicates whether the input component is active and accepting input. + /// public bool IsActive { get; set; } = false; + + /// + /// The current input string the user has typed. + /// public string Input { get; private set; } = ""; + /// + /// The time delay before input is registered after a key is pressed. + /// public float InputDelay { get => (float)_inputTimer.TotalTime; set => _inputTimer.TotalTime = value; } + /// + /// The keys that are accepted as input. + /// private readonly KeyboardButtonCode[] _acceptedKeys = KeyboardButtonCodeHelper.GetAllPrintableKeys().Append(KeyboardButtonCode.Backspace).ToArray(); + /// + /// The input handler used to check for keyboard input. + /// private readonly IInputHandler _inputHandler = EngineUtil.InputHandler; + + /// + /// The timer used to delay input. + /// private readonly TickableTimer _inputTimer; + + /// + /// The keys that were last pressed. + /// private readonly HashSet _lastKeys = []; + /// + /// Initializes a new instance of the class with an optional input delay. + /// + /// The input delay before registering the next key press. public TextInputComponent(float parInputDelay = 0.2f) { _inputTimer = new TickableTimer(parInputDelay) { CurrentTime = 0 }; diff --git a/DoomDeathmatch/src/Component/UI/UiComponent.cs b/DoomDeathmatch/src/Component/UI/UiComponent.cs index f349c8b..c9ec6bd 100644 --- a/DoomDeathmatch/src/Component/UI/UiComponent.cs +++ b/DoomDeathmatch/src/Component/UI/UiComponent.cs @@ -6,16 +6,44 @@ using Math = System.Math; namespace DoomDeathmatch.Component.UI; +/// +/// Represents a UI element that can interact with user input. +/// public class UiComponent : Engine.Scene.Component.Component { + /// + /// Invoked when this UI component is clicked. + /// public event Action? OnClick; + + /// + /// Invoked when the mouse is hovering over this UI component. + /// public event Action? OnMouseOver; + /// + /// The parent container of this UI component. + /// public UiContainerComponent? Container { get; set; } + + /// + /// The alignment of this component's center within its container. + /// public Anchor Center { get; set; } = Anchor.Center; + + /// + /// The anchor point of this component relative to its container. + /// public Anchor Anchor { get; set; } = Anchor.Center; + + /// + /// The positional offset of this component relative to its anchor point. + /// public Vector2 Offset { get; set; } = Vector2.Zero; + /// + /// The input handler used to check for mouse input. + /// private readonly IInputHandler _inputHandler = EngineUtil.InputHandler; public override void Update(double parDeltaTime) @@ -26,8 +54,8 @@ public class UiComponent : Engine.Scene.Component.Component } var size = GameObject.Transform.Size * GameObject.Transform.Scale; - GameObject.Transform.Translation.Xy = GetAnchorPosition(Container.GameObject.Transform.Size.Xy, Anchor) + Offset - - GetAnchorPosition(size.Xy, Center); + GameObject.Transform.Translation.Xy = Anchor.GetPosition(Container.GameObject.Transform.Size.Xy) + Offset - + Center.GetPosition(size.Xy); var transformMatrix = GameObject.Transform.FullTransformMatrix; var actualSize = transformMatrix.ExtractScale(); @@ -46,30 +74,11 @@ public class UiComponent : Engine.Scene.Component.Component } } + /// + /// Manually triggers the click event for this component. + /// public void InvokeClick() { OnClick?.Invoke(this); } - - private static Vector2 GetAnchorPosition(Vector2 parSize, Anchor parAnchor) - { - return parSize * GetAnchorRatio(parAnchor); - } - - private static Vector2 GetAnchorRatio(Anchor parAnchor) - { - return parAnchor switch - { - Anchor.TopLeft => new Vector2(-0.5f, 0.5f), - Anchor.TopCenter => new Vector2(0, 0.5f), - Anchor.TopRight => new Vector2(0.5f, 0.5f), - Anchor.CenterLeft => new Vector2(-0.5f, 0), - Anchor.Center => new Vector2(0, 0), - Anchor.CenterRight => new Vector2(0.5f, 0), - Anchor.BottomLeft => new Vector2(-0.5f, -0.5f), - Anchor.BottomCenter => new Vector2(0, -0.5f), - Anchor.BottomRight => new Vector2(0.5f, -0.5f), - _ => throw new ArgumentOutOfRangeException(nameof(parAnchor), parAnchor, null) - }; - } } \ No newline at end of file diff --git a/DoomDeathmatch/src/Component/UI/UiContainerComponent.cs b/DoomDeathmatch/src/Component/UI/UiContainerComponent.cs index 31ba59d..bc9e5a1 100644 --- a/DoomDeathmatch/src/Component/UI/UiContainerComponent.cs +++ b/DoomDeathmatch/src/Component/UI/UiContainerComponent.cs @@ -5,11 +5,24 @@ using OpenTK.Mathematics; namespace DoomDeathmatch.Component.UI; +/// +/// Represents a container for UI components, responsible for handling mouse position and camera interactions. +/// public class UiContainerComponent : UiComponent { + /// + /// The camera used to calculate the mouse position in world space. + /// public Camera? Camera { get; set; } + + /// + /// The current mouse position in world coordinates. + /// public Vector3 MousePosition { get; private set; } + /// + /// The input handler used to check for mouse input. + /// private readonly IInputHandler _inputHandler = EngineUtil.InputHandler; public override void Update(double parDeltaTime) diff --git a/DoomDeathmatch/src/Component/Util/BillboardComponent.cs b/DoomDeathmatch/src/Component/Util/BillboardComponent.cs index 80e7108..de2e32b 100644 --- a/DoomDeathmatch/src/Component/Util/BillboardComponent.cs +++ b/DoomDeathmatch/src/Component/Util/BillboardComponent.cs @@ -3,9 +3,19 @@ using OpenTK.Mathematics; namespace DoomDeathmatch.Component.Util; +/// +/// A component that aligns the orientation of an object to always face a target, similar to a billboard. +/// public class BillboardComponent : Engine.Scene.Component.Component { + /// + /// The target transform that the object will face. + /// public Transform? Target { get; set; } + + /// + /// The upward direction used as a reference for recalculating orientation. + /// public Vector3 Up { get; set; } = Vector3.UnitZ; public override void Update(double parDeltaTime) @@ -17,15 +27,28 @@ public class BillboardComponent : Engine.Scene.Component.Component var targetPosition = Target.GetFullTranslation(); var currentPosition = GameObject.Transform.GetFullTranslation(); + var rotationMatrix = CalculateRotationMatrix(currentPosition, targetPosition, Up); - var forward = targetPosition - currentPosition; - forward -= Vector3.Dot(forward, Up) * Up; + GameObject.Transform.Rotation = Quaternion.FromMatrix(rotationMatrix); + } + + /// + /// Calculates the rotation matrix required to align the object to face the target. + /// + /// The current position of the object. + /// The target position the object should face. + /// The upward direction used for orientation adjustment. + /// A representing the required rotation. + private static Matrix3 CalculateRotationMatrix(Vector3 parCurrentPosition, Vector3 parTargetPosition, Vector3 parUp) + { + var forward = parTargetPosition - parCurrentPosition; + forward -= Vector3.Dot(forward, parUp) * parUp; if (forward.LengthSquared > 0) { forward.Normalize(); } - var right = Vector3.Cross(Up, forward); + var right = Vector3.Cross(parUp, forward); if (right.LengthSquared > 0) { right.Normalize(); @@ -39,6 +62,6 @@ public class BillboardComponent : Engine.Scene.Component.Component right.Z, recalculatedUp.Z, forward.Z ); - GameObject.Transform.Rotation = Quaternion.FromMatrix(rotationMatrix); + return rotationMatrix; } } \ No newline at end of file diff --git a/DoomDeathmatch/src/Component/Util/CopySizeComponent.cs b/DoomDeathmatch/src/Component/Util/CopySizeComponent.cs index 7313929..5bd1e47 100644 --- a/DoomDeathmatch/src/Component/Util/CopySizeComponent.cs +++ b/DoomDeathmatch/src/Component/Util/CopySizeComponent.cs @@ -3,9 +3,19 @@ using OpenTK.Mathematics; namespace DoomDeathmatch.Component.Util; +/// +/// A component that synchronizes the size of an object with a target object's size, applying a multiplier. +/// public class CopySizeComponent : Engine.Scene.Component.Component { + /// + /// The target transform whose size is copied. + /// public Transform? Target { get; set; } + + /// + /// The multiplier applied to the target's size when copying. + /// public Vector3 Multiplier { get; set; } = Vector3.One; public override void Update(double parDeltaTime) diff --git a/DoomDeathmatch/src/Component/Util/FireballComponent.cs b/DoomDeathmatch/src/Component/Util/FireballComponent.cs index d7e9129..22eeabb 100644 --- a/DoomDeathmatch/src/Component/Util/FireballComponent.cs +++ b/DoomDeathmatch/src/Component/Util/FireballComponent.cs @@ -4,8 +4,14 @@ using Engine.Util; namespace DoomDeathmatch.Component.Util; +/// +/// Represents a fireball that deals damage on collision. +/// public class FireballComponent : Engine.Scene.Component.Component { + /// + /// The amount of damage the fireball inflicts on collision. + /// public float Damage { get; set; } public override void Awake() @@ -17,6 +23,10 @@ public class FireballComponent : Engine.Scene.Component.Component collider.OnCollision += OnCollision; } + /// + /// Handles collision logic, dealing damage to colliding objects. + /// + /// The collider of the object this fireball collided with. private void OnCollision(AABBColliderComponent parCollider) { var healthController = parCollider.GameObject.GetComponent(); diff --git a/DoomDeathmatch/src/Component/Util/PlayerMovementComponent.cs b/DoomDeathmatch/src/Component/Util/PlayerMovementComponent.cs index 77e2bdb..100c107 100644 --- a/DoomDeathmatch/src/Component/Util/PlayerMovementComponent.cs +++ b/DoomDeathmatch/src/Component/Util/PlayerMovementComponent.cs @@ -5,12 +5,24 @@ using OpenTK.Mathematics; namespace DoomDeathmatch.Component.Util; +/// +/// Handles player movement based on input and applies rotation to the player object. +/// public class PlayerMovementComponent : Engine.Scene.Component.Component { + /// + /// The speed at which the player rotates. + /// public float RotationSpeed { get; set; } = 110.0f; + /// + /// Handles input from the player. + /// private readonly IInputHandler _inputHandler = EngineUtil.InputHandler; + /// + /// Controls the movement logic of the player. + /// private MovementController _movementController = null!; public override void Awake() diff --git a/DoomDeathmatch/src/Scene/Play/PlayScene.cs b/DoomDeathmatch/src/Scene/Play/PlayScene.cs index e208c26..15d26d1 100644 --- a/DoomDeathmatch/src/Scene/Play/PlayScene.cs +++ b/DoomDeathmatch/src/Scene/Play/PlayScene.cs @@ -57,7 +57,7 @@ public static class PlayScene var gameControllerObject = GameObjectUtil.CreateGameObject(scene, [ new GameController(menuController), - new TimerController(), + new TimerController(5 * 60), plaTimerView, new ScoreController(), @@ -88,11 +88,10 @@ public static class PlayScene var monsterSpawnPoints = MeshToSpawnPoints( EngineUtil.AssetResourceManager.Load($"map/{parMapName}/monster_spawners.obj")); - var valuableSpawnPoints = MeshToSpawnPoints( - EngineUtil.AssetResourceManager.Load($"map/{parMapName}/valuable_spawners.obj")); - parScene.AddChild(mapObject, collidersObject); + #region Monster Objects + var monsterVector3ValueProvider = new RandomListValueProvider(monsterSpawnPoints.Select(ConstValueProvider.Create)); @@ -114,6 +113,12 @@ public static class PlayScene parScene.AddChild(parGameControllerObject, monsterSpawnerObject); + #endregion + + #region Consumable Objects + + var valuableSpawnPoints = MeshToSpawnPoints( + EngineUtil.AssetResourceManager.Load($"map/{parMapName}/valuable_spawners.obj")); var consumableVector3ValueProvider = new RandomListValueProvider(valuableSpawnPoints.Select(ConstValueProvider.Create)); @@ -157,6 +162,8 @@ public static class PlayScene parScene.AddChild(parGameControllerObject, consumableSpawnerObject); + #endregion + return mapObject; } diff --git a/DoomDeathmatch/src/Script/AnimationPlayer.cs b/DoomDeathmatch/src/Script/AnimationPlayer.cs index 754c62c..c4c3f64 100644 --- a/DoomDeathmatch/src/Script/AnimationPlayer.cs +++ b/DoomDeathmatch/src/Script/AnimationPlayer.cs @@ -3,17 +3,55 @@ using Engine.Util; namespace DoomDeathmatch.Script; -public class AnimationPlayer(float parInterval) : IUpdate +/// +/// Handles the playback of animations consisting of frames of type . +/// +/// The type of the animation frames. +public class AnimationPlayer : IUpdate { + /// + /// Occurs when the animation finishes playing. + /// public event Action? OnFinish; + + /// + /// Occurs when the animation advances to a new frame. + /// public event Action? OnFrameChanged; + /// + /// Indicates whether the animation is currently playing. + /// public bool IsPlaying { get; private set; } + + /// + /// The list of frames in the animation. + /// public List Frames { get; init; } = []; + + /// + /// The index of the next frame to be displayed. + /// public int NextFrame { get; private set; } - private readonly TickableTimer _timer = new(parInterval); + /// + /// The timer used to control frame intervals. + /// + private readonly TickableTimer _timer; + /// + /// Initializes a new instance of the class with a specified frame interval. + /// + /// The total time in seconds for whole animation. + public AnimationPlayer(float parInterval) + { + _timer = new TickableTimer(parInterval); + } + + /// + /// Updates the animation's playback state based on the elapsed time. + /// + /// The time in seconds since the last update. public void Update(double parDeltaTime) { if (!IsPlaying) @@ -37,12 +75,18 @@ public class AnimationPlayer(float parInterval) : IUpdate } } + /// + /// Starts the animation playback from the beginning. + /// public void Start() { Reset(); IsPlaying = true; } + /// + /// Resets the animation to its initial state. + /// public void Reset() { _timer.Reset(); diff --git a/DoomDeathmatch/src/Script/Collision/AABBCollider.cs b/DoomDeathmatch/src/Script/Collision/AABBCollider.cs index e72390c..3b2b011 100644 --- a/DoomDeathmatch/src/Script/Collision/AABBCollider.cs +++ b/DoomDeathmatch/src/Script/Collision/AABBCollider.cs @@ -2,14 +2,36 @@ namespace DoomDeathmatch.Script.Collision; +/// +/// Represents an Axis-Aligned Bounding Box (AABB) collider for 3D collision detection. +/// public class AABBCollider { + /// + /// The position of the collider's center in 3D space. + /// public Vector3 Position { get; set; } + + /// + /// The size (width, height, depth) of the collider. + /// public Vector3 Size { get; set; } + /// + /// The minimum point (corner) of the collider in 3D space. + /// public Vector3 Min => Position - (Size / 2); + + /// + /// The maximum point (corner) of the collider in 3D space. + /// public Vector3 Max => Position + (Size / 2); + /// + /// Checks if this collider intersects with another AABB collider. + /// + /// The other collider to check for intersection. + /// if the colliders intersect; otherwise, . public bool Intersects(AABBCollider parCollider) { var max = Max; @@ -21,6 +43,14 @@ public class AABBCollider max.Z >= otherMin.Z && min.Z <= otherMax.Z; } + /// + /// Calculates the collision normal between this collider and another collider. + /// + /// The other collider involved in the collision. + /// + /// A representing the normal of the collision surface. + /// This indicates the direction of the collision resolution. + /// public Vector3 GetCollisionNormal(AABBCollider parOther) { var normal = Vector3.Zero; diff --git a/DoomDeathmatch/src/Script/Collision/RaycastResult.cs b/DoomDeathmatch/src/Script/Collision/RaycastResult.cs index 437d38f..2e9dc8b 100644 --- a/DoomDeathmatch/src/Script/Collision/RaycastResult.cs +++ b/DoomDeathmatch/src/Script/Collision/RaycastResult.cs @@ -3,11 +3,28 @@ using OpenTK.Mathematics; namespace DoomDeathmatch.Script.Collision; +/// +/// Represents the result of a raycast in 3D space. +/// public class RaycastResult { + /// + /// The distance from the ray's origin to the hit point. + /// public float Distance { get; init; } + + /// + /// The point in 3D space where the ray hit an object. + /// public Vector3 HitPoint { get; init; } + + /// + /// The normal vector at the surface of the hit object. + /// public Vector3 Normal { get; init; } + /// + /// The game object that was hit by the ray. + /// public GameObject HitObject { get; init; } } \ No newline at end of file diff --git a/DoomDeathmatch/src/Script/Condition/ICondition.cs b/DoomDeathmatch/src/Script/Condition/ICondition.cs index 93e0e5d..a9b81ef 100644 --- a/DoomDeathmatch/src/Script/Condition/ICondition.cs +++ b/DoomDeathmatch/src/Script/Condition/ICondition.cs @@ -2,11 +2,23 @@ namespace DoomDeathmatch.Script.Condition; +/// +/// Represents a condition that can be updated and triggers an event when it becomes true. +/// public interface ICondition : IUpdate { + /// + /// Occurs when the condition evaluates to true. + /// public event Action? OnTrue; + /// + /// Indicates whether the condition is currently true. + /// public bool IsTrue { get; } + /// + /// Resets the condition to its initial state. + /// public void Reset(); } \ No newline at end of file diff --git a/DoomDeathmatch/src/Script/Condition/TickableTimerCondition.cs b/DoomDeathmatch/src/Script/Condition/TickableTimerCondition.cs index 6ca051f..6a54999 100644 --- a/DoomDeathmatch/src/Script/Condition/TickableTimerCondition.cs +++ b/DoomDeathmatch/src/Script/Condition/TickableTimerCondition.cs @@ -2,25 +2,49 @@ namespace DoomDeathmatch.Script.Condition; +/// +/// Represents a condition based on a timer that can be updated over time. +/// The condition becomes true when the timer finishes. +/// public class TickableTimerCondition : ICondition { + /// + /// Occurs when the timer finishes, causing the condition to become true. + /// public event Action? OnTrue; + /// + /// Indicates whether the timer has finished, making the condition true. + /// public bool IsTrue => _timer.IsFinished; + /// + /// The timer used to evaluate the condition's state. + /// private readonly TickableTimer _timer; + /// + /// Initializes a new instance of the class with a specified interval. + /// + /// The interval in seconds for the timer. public TickableTimerCondition(float parInterval) { _timer = new TickableTimer(parInterval); _timer.OnFinished += () => OnTrue?.Invoke(); } + /// + /// Updates the timer with the elapsed time, progressing the condition's state. + /// + /// The time in seconds since the last update. public void Update(double parDeltaTime) { _timer.Update(parDeltaTime); } + /// + /// Resets the timer and the condition to their initial state. + /// public void Reset() { _timer.Reset(); diff --git a/DoomDeathmatch/src/Script/Consumable/HealthPackConsumable.cs b/DoomDeathmatch/src/Script/Consumable/HealthPackConsumable.cs index f3c6b79..b34f6da 100644 --- a/DoomDeathmatch/src/Script/Consumable/HealthPackConsumable.cs +++ b/DoomDeathmatch/src/Script/Consumable/HealthPackConsumable.cs @@ -2,12 +2,32 @@ namespace DoomDeathmatch.Script.Consumable; -public class HealthPackConsumable(float parHealth) : IConsumable +/// +/// Represents a health pack consumable that restores health to the player. +/// +/// The amount of health restored by the consumable. +public class HealthPackConsumable : IConsumable { + /// public string Icon => "texture/health_pack.png"; + /// + /// The amount of health restored by the consumable. + /// + private readonly float _health; + + /// + /// Initializes a new instance of the class with a specified health restoration value. + /// + /// The amount of health this consumable restores. + public HealthPackConsumable(float parHealth) + { + _health = parHealth; + } + + /// public void Consume(PlayerController parPlayerController) { - parPlayerController.HealthController.Heal(parHealth); + parPlayerController.HealthController.Heal(_health); } } \ No newline at end of file diff --git a/DoomDeathmatch/src/Script/Consumable/IConsumable.cs b/DoomDeathmatch/src/Script/Consumable/IConsumable.cs index 442e11e..40365ad 100644 --- a/DoomDeathmatch/src/Script/Consumable/IConsumable.cs +++ b/DoomDeathmatch/src/Script/Consumable/IConsumable.cs @@ -2,9 +2,21 @@ namespace DoomDeathmatch.Script.Consumable; +/// +/// Represents a consumable item in the game. +/// Consumables can be used by a player to apply specific effects, such as healing or adding a weapon. +/// public interface IConsumable { + /// + /// The icon path associated with the consumable. + /// Used for displaying the consumable in the user interface. + /// public string Icon { get; } + /// + /// Consumes the item and applies its effect to the specified player. + /// + /// The player controller to apply the consumable effect to. public void Consume(PlayerController parPlayerController); } \ No newline at end of file diff --git a/DoomDeathmatch/src/Script/Consumable/WeaponConsumable.cs b/DoomDeathmatch/src/Script/Consumable/WeaponConsumable.cs index 98893f9..e9b118e 100644 --- a/DoomDeathmatch/src/Script/Consumable/WeaponConsumable.cs +++ b/DoomDeathmatch/src/Script/Consumable/WeaponConsumable.cs @@ -3,12 +3,31 @@ using DoomDeathmatch.Script.Model.Weapon; namespace DoomDeathmatch.Script.Consumable; -public class WeaponConsumable(WeaponData parWeaponData) : IConsumable +/// +/// Represents a weapon consumable that grants weapon or reloads ammo to the player. +/// +public class WeaponConsumable : IConsumable { - public string Icon => parWeaponData.IdleTexture; + /// + public string Icon => _weaponData.IdleTexture; + /// + /// The weapon data associated with this consumable. + /// + private readonly WeaponData _weaponData; + + /// + /// Initializes a new instance of the class with the specified weapon data. + /// + /// The weapon data associated with this consumable. + public WeaponConsumable(WeaponData parWeaponData) + { + _weaponData = parWeaponData; + } + + /// public void Consume(PlayerController parPlayerController) { - parPlayerController.WeaponController.AddOrMergeWeapon(parWeaponData); + parPlayerController.WeaponController.AddOrMergeWeapon(_weaponData); } } \ No newline at end of file diff --git a/DoomDeathmatch/src/Script/MVC/IView.cs b/DoomDeathmatch/src/Script/MVC/IView.cs new file mode 100644 index 0000000..a4b6851 --- /dev/null +++ b/DoomDeathmatch/src/Script/MVC/IView.cs @@ -0,0 +1,14 @@ +namespace DoomDeathmatch.Script.MVC; + +/// +/// Interface for view components. +/// +/// The type of the model to display. +public interface IView +{ + /// + /// Updates the view based on the specified model. + /// + /// The model to display. + public void UpdateView(T parModel); +} \ No newline at end of file diff --git a/DoomDeathmatch/src/Script/Model/Enemy/Attack/AttackBehavior.cs b/DoomDeathmatch/src/Script/Model/Enemy/Attack/AttackBehavior.cs index 8e9d173..7819c1d 100644 --- a/DoomDeathmatch/src/Script/Model/Enemy/Attack/AttackBehavior.cs +++ b/DoomDeathmatch/src/Script/Model/Enemy/Attack/AttackBehavior.cs @@ -3,10 +3,36 @@ using DoomDeathmatch.Component.MVC.Health; namespace DoomDeathmatch.Script.Model.Enemy.Attack; -public abstract class AttackBehavior(EnemyController parEnemyController, HealthController parHealthController) +/// +/// Represents the base class for defining enemy attack behaviors. +/// +public abstract class AttackBehavior { - protected readonly EnemyController _enemyController = parEnemyController; - protected readonly HealthController _healthController = parHealthController; + /// + /// The controller for the enemy performing the attack. + /// + protected readonly EnemyController _enemyController; + /// + /// The controller for the health of the target being attacked. + /// + protected readonly HealthController _healthController; + + /// + /// Initializes a new instance of the class. + /// + /// The enemy controller. + /// The health controller of the target. + protected AttackBehavior(EnemyController parEnemyController, HealthController parHealthController) + { + _enemyController = parEnemyController; + _healthController = parHealthController; + } + + /// + /// Performs the attack behavior. + /// + /// The time elapsed since the last update. + /// True if the attack was successful; otherwise, false. public abstract bool Attack(double parDeltaTime); } \ No newline at end of file diff --git a/DoomDeathmatch/src/Script/Model/Enemy/Attack/CloseContinuousAttackBehavior.cs b/DoomDeathmatch/src/Script/Model/Enemy/Attack/CloseContinuousAttackBehavior.cs index 932822c..2dc1487 100644 --- a/DoomDeathmatch/src/Script/Model/Enemy/Attack/CloseContinuousAttackBehavior.cs +++ b/DoomDeathmatch/src/Script/Model/Enemy/Attack/CloseContinuousAttackBehavior.cs @@ -3,22 +3,47 @@ using DoomDeathmatch.Component.MVC.Health; namespace DoomDeathmatch.Script.Model.Enemy.Attack; -public class CloseContinuousAttackBehavior( - EnemyController parEnemyController, - HealthController parHealthController, - float parRadius, - float parDamage) - : AttackBehavior(parEnemyController, parHealthController) +/// +/// Represents a behavior where the enemy continuously attacks when in close proximity. +/// +public class CloseContinuousAttackBehavior : AttackBehavior { + /// + /// The attack radius within which the attack is triggered. + /// + private readonly float _radius; + + /// + /// The amount of damage dealt per second. + /// + private readonly float _damage; + + /// + /// Initializes a new instance of the class. + /// + /// The enemy controller. + /// The health controller of the target. + /// The attack radius. + /// The damage dealt per second. + public CloseContinuousAttackBehavior(EnemyController parEnemyController, + HealthController parHealthController, + float parRadius, + float parDamage) : base(parEnemyController, parHealthController) + { + _radius = parRadius; + _damage = parDamage; + } + + /// public override bool Attack(double parDeltaTime) { var distanceSquared = (_enemyController.GameObject.Transform.Translation - _healthController.GameObject.Transform.Translation) .LengthSquared; - if (distanceSquared <= parRadius * parRadius) + if (distanceSquared <= _radius * _radius) { - _healthController.TakeDamage(parDamage * (float)parDeltaTime); + _healthController.TakeDamage(_damage * (float)parDeltaTime); return true; } diff --git a/DoomDeathmatch/src/Script/Model/Enemy/Attack/CloseCooldownAttackBehavior.cs b/DoomDeathmatch/src/Script/Model/Enemy/Attack/CloseCooldownAttackBehavior.cs index cded27a..88e15e8 100644 --- a/DoomDeathmatch/src/Script/Model/Enemy/Attack/CloseCooldownAttackBehavior.cs +++ b/DoomDeathmatch/src/Script/Model/Enemy/Attack/CloseCooldownAttackBehavior.cs @@ -3,26 +3,53 @@ using DoomDeathmatch.Component.MVC.Health; namespace DoomDeathmatch.Script.Model.Enemy.Attack; -public class CloseCooldownAttackBehavior( - EnemyController parEnemyController, - HealthController parHealthController, - float parRadius, - float parCooldown, - float parDamage) - : CooldownAttackBehavior(parEnemyController, parHealthController, parCooldown) +/// +/// Represents a behavior where the enemy performs a cooldown-based close-range attack. +/// +public class CloseCooldownAttackBehavior : CooldownAttackBehavior { + /// + /// The attack radius within which the attack is triggered. + /// + private readonly float _radius; + + /// + /// The damage dealt by the attack. + /// + private readonly float _damage; + + /// + /// Initializes a new instance of the class. + /// + /// The enemy controller. + /// The health controller of the target. + /// The attack radius. + /// The cooldown duration. + /// The damage dealt by the attack. + public CloseCooldownAttackBehavior(EnemyController parEnemyController, + HealthController parHealthController, + float parRadius, + float parCooldown, + float parDamage) : base(parEnemyController, parHealthController, parCooldown) + { + _radius = parRadius; + _damage = parDamage; + } + + /// protected override bool CanAttack() { var distanceSquared = (_enemyController.GameObject.Transform.Translation - _healthController.GameObject.Transform.Translation) .LengthSquared; - return distanceSquared <= parRadius * parRadius; + return distanceSquared <= _radius * _radius; } + /// protected override bool ActivateAttack() { - _healthController.TakeDamage(parDamage); + _healthController.TakeDamage(_damage); return true; } diff --git a/DoomDeathmatch/src/Script/Model/Enemy/Attack/CompositeAttackBehavior.cs b/DoomDeathmatch/src/Script/Model/Enemy/Attack/CompositeAttackBehavior.cs index 3a8d6ec..093fca6 100644 --- a/DoomDeathmatch/src/Script/Model/Enemy/Attack/CompositeAttackBehavior.cs +++ b/DoomDeathmatch/src/Script/Model/Enemy/Attack/CompositeAttackBehavior.cs @@ -3,17 +3,35 @@ using DoomDeathmatch.Component.MVC.Health; namespace DoomDeathmatch.Script.Model.Enemy.Attack; -public class CompositeAttackBehavior( - EnemyController parEnemyController, - HealthController parHealthController, - List parBehaviors) - : AttackBehavior(parEnemyController, parHealthController) +/// +/// Represents a composite attack behavior combining multiple attack behaviors. +/// +public class CompositeAttackBehavior : AttackBehavior { + /// + /// A collection of individual attack behaviors. + /// + private readonly List _behaviors; + + /// + /// Initializes a new instance of the class. + /// + /// The enemy controller. + /// The health controller of the target. + /// The collection of attack behaviors. + public CompositeAttackBehavior(EnemyController parEnemyController, + HealthController parHealthController, + IEnumerable parBehaviors) : base(parEnemyController, parHealthController) + { + _behaviors = parBehaviors.ToList(); + } + + /// public override bool Attack(double parDeltaTime) { var result = false; - foreach (var behavior in parBehaviors) + foreach (var behavior in _behaviors) { result |= behavior.Attack(parDeltaTime); } diff --git a/DoomDeathmatch/src/Script/Model/Enemy/Attack/CooldownAttackBehavior.cs b/DoomDeathmatch/src/Script/Model/Enemy/Attack/CooldownAttackBehavior.cs index b803b09..1cbfffe 100644 --- a/DoomDeathmatch/src/Script/Model/Enemy/Attack/CooldownAttackBehavior.cs +++ b/DoomDeathmatch/src/Script/Model/Enemy/Attack/CooldownAttackBehavior.cs @@ -4,14 +4,30 @@ using Engine.Util; namespace DoomDeathmatch.Script.Model.Enemy.Attack; -public abstract class CooldownAttackBehavior( - EnemyController parEnemyController, - HealthController parHealthController, - float parCooldown) - : AttackBehavior(parEnemyController, parHealthController) +/// +/// Represents a base class for attacks with cooldown functionality. +/// +public abstract class CooldownAttackBehavior : AttackBehavior { - private readonly TickableTimer _tickableTimer = new(parCooldown); + /// + /// Timer that tracks the cooldown duration between attacks. + /// + private readonly TickableTimer _tickableTimer; + /// + /// Initializes a new instance of the class. + /// + /// The enemy controller. + /// The health controller of the target. + /// The cooldown duration. + protected CooldownAttackBehavior(EnemyController parEnemyController, + HealthController parHealthController, + float parCooldown) : base(parEnemyController, parHealthController) + { + _tickableTimer = new TickableTimer(parCooldown); + } + + /// public sealed override bool Attack(double parDeltaTime) { _tickableTimer.Update(parDeltaTime); @@ -33,6 +49,15 @@ public abstract class CooldownAttackBehavior( return false; } + /// + /// Determines whether the attack can be performed. + /// + /// True if the attack can be performed; otherwise, false. protected abstract bool CanAttack(); + + /// + /// Executes the attack logic. + /// + /// True if the attack was successful; otherwise, false. protected abstract bool ActivateAttack(); } \ No newline at end of file diff --git a/DoomDeathmatch/src/Script/Model/Enemy/Attack/FuncAttackBehaviorCreator.cs b/DoomDeathmatch/src/Script/Model/Enemy/Attack/FuncAttackBehaviorCreator.cs index 26de5b6..27c981c 100644 --- a/DoomDeathmatch/src/Script/Model/Enemy/Attack/FuncAttackBehaviorCreator.cs +++ b/DoomDeathmatch/src/Script/Model/Enemy/Attack/FuncAttackBehaviorCreator.cs @@ -3,11 +3,31 @@ using DoomDeathmatch.Component.MVC.Health; namespace DoomDeathmatch.Script.Model.Enemy.Attack; -public class FuncAttackBehaviorCreator(Func parFunc) - : IAttackBehaviorCreator +/// +/// A creator that uses a function to generate instances of . +/// +public class FuncAttackBehaviorCreator : IAttackBehaviorCreator { + /// + /// The function used to create instances. + /// + private readonly Func _func; + + /// + /// Initializes a new instance of the class. + /// + /// + /// A function that takes an and + /// and returns a new instance of . + /// + public FuncAttackBehaviorCreator(Func parFunc) + { + _func = parFunc; + } + + /// public AttackBehavior Create(EnemyController parEnemyController, HealthController parHealthController) { - return parFunc(parEnemyController, parHealthController); + return _func(parEnemyController, parHealthController); } } \ No newline at end of file diff --git a/DoomDeathmatch/src/Script/Model/Enemy/Attack/IAttackBehaviorCreator.cs b/DoomDeathmatch/src/Script/Model/Enemy/Attack/IAttackBehaviorCreator.cs index 81e96f2..cace4aa 100644 --- a/DoomDeathmatch/src/Script/Model/Enemy/Attack/IAttackBehaviorCreator.cs +++ b/DoomDeathmatch/src/Script/Model/Enemy/Attack/IAttackBehaviorCreator.cs @@ -3,7 +3,16 @@ using DoomDeathmatch.Component.MVC.Health; namespace DoomDeathmatch.Script.Model.Enemy.Attack; +/// +/// Interface for creating instances of . +/// public interface IAttackBehaviorCreator { + /// + /// Creates a new instance of an . + /// + /// The enemy controller for the behavior. + /// The health controller for the target. + /// An instance of . public AttackBehavior Create(EnemyController parEnemyController, HealthController parHealthController); } \ No newline at end of file diff --git a/DoomDeathmatch/src/Script/Model/Enemy/Attack/ObjectSpawnAttackBehavior.cs b/DoomDeathmatch/src/Script/Model/Enemy/Attack/ObjectSpawnAttackBehavior.cs index cc93840..4e22678 100644 --- a/DoomDeathmatch/src/Script/Model/Enemy/Attack/ObjectSpawnAttackBehavior.cs +++ b/DoomDeathmatch/src/Script/Model/Enemy/Attack/ObjectSpawnAttackBehavior.cs @@ -5,22 +5,42 @@ using Engine.Util; namespace DoomDeathmatch.Script.Model.Enemy.Attack; -public class ObjectSpawnAttackBehavior( - EnemyController parEnemyController, - HealthController parHealthController, - float parCooldown, - Func parObjectSpawnFunc -) - : CooldownAttackBehavior(parEnemyController, parHealthController, parCooldown) +/// +/// Represents a behavior where an object is spawned as part of the attack. +/// +public class ObjectSpawnAttackBehavior : CooldownAttackBehavior { + /// + /// The function used to spawn an object based on the enemy and target controllers. + /// + private readonly Func _objectSpawnFunc; + + /// + /// Initializes a new instance of the class. + /// + /// The enemy controller. + /// The health controller of the target. + /// The cooldown duration between spawns. + /// The function used to create the spawned object. + public ObjectSpawnAttackBehavior(EnemyController parEnemyController, + HealthController parHealthController, + float parCooldown, + Func parObjectSpawnFunc) : base(parEnemyController, + parHealthController, parCooldown) + { + _objectSpawnFunc = parObjectSpawnFunc; + } + + /// protected override bool CanAttack() { return true; } + /// protected override bool ActivateAttack() { - var enemyObject = parObjectSpawnFunc(_enemyController, _healthController); + var enemyObject = _objectSpawnFunc(_enemyController, _healthController); EngineUtil.SceneManager.CurrentScene!.Add(enemyObject); return true; diff --git a/DoomDeathmatch/src/Script/Model/Enemy/EnemyData.cs b/DoomDeathmatch/src/Script/Model/Enemy/EnemyData.cs index 1f7bf55..466391e 100644 --- a/DoomDeathmatch/src/Script/Model/Enemy/EnemyData.cs +++ b/DoomDeathmatch/src/Script/Model/Enemy/EnemyData.cs @@ -6,8 +6,14 @@ using OpenTK.Mathematics; namespace DoomDeathmatch.Script.Model.Enemy; +/// +/// Represents data associated with an enemy type, including its stats, behavior, and appearance. +/// public class EnemyData { + /// + /// Data for the "Demon" enemy type. + /// public static EnemyData Demon => new() { @@ -24,6 +30,9 @@ public class EnemyData ) }; + /// + /// Data for the "Imp" enemy type. + /// public static EnemyData Imp => new() { @@ -61,25 +70,58 @@ public class EnemyData ) }; + /// + /// Unique identifier for the enemy type. + /// public string Id { get; private init; } = ""; + + /// + /// Display name for the enemy type. + /// public string Name { get; private init; } = ""; + /// + /// Path to the texture file used to render the enemy. + /// public string Texture { get; private init; } = ""; + /// + /// The base health of the enemy. + /// public float BaseHealth { get; private init; } + + /// + /// The base score awarded for defeating this enemy. + /// public int BaseScore { get; private init; } + + /// + /// The base speed of the enemy. + /// public float BaseSpeed { get; private init; } + /// + /// The movement behavior defining how the enemy moves. + /// public IMovementBehavior MovementBehavior { get; private init; } + + /// + /// The creator for the enemy's attack behavior. + /// public IAttackBehaviorCreator AttackBehaviorCreator { get; private init; } + /// + /// Private constructor to prevent direct instantiation. Use the predefined static properties instead. + /// private EnemyData() { } + /// public override bool Equals(object? parObj) { return parObj is EnemyData enemyData && Id == enemyData.Id; } + /// public override int GetHashCode() { return HashCode.Combine(Id); diff --git a/DoomDeathmatch/src/Script/Model/Enemy/Movement/FollowPlayerMovementBehavior.cs b/DoomDeathmatch/src/Script/Model/Enemy/Movement/FollowPlayerMovementBehavior.cs index 74bb2f2..d3374ca 100644 --- a/DoomDeathmatch/src/Script/Model/Enemy/Movement/FollowPlayerMovementBehavior.cs +++ b/DoomDeathmatch/src/Script/Model/Enemy/Movement/FollowPlayerMovementBehavior.cs @@ -2,11 +2,34 @@ namespace DoomDeathmatch.Script.Model.Enemy.Movement; -public class FollowPlayerMovementBehavior(float parRadius) : IMovementBehavior +/// +/// Movement behavior where the enemy follows the player while maintaining a specific radius. +/// +public class FollowPlayerMovementBehavior : IMovementBehavior { + /// + /// The radius to maintain between the enemy and the player. + /// + private readonly float _radius; + + /// + /// Initializes a new instance of the class. + /// + /// The radius to maintain between the enemy and the player. + public FollowPlayerMovementBehavior(float parRadius) + { + _radius = parRadius; + } + + /// + /// Calculates the next position for the enemy, keeping it at the specified radius from the player. + /// + /// The current position of the enemy. + /// The current position of the player. + /// The next position of the enemy, maintaining the specified radius from the player. public Vector3 GetNextPosition(Vector3 parPosition, Vector3 parPlayerPosition) { var direction = (parPosition - parPlayerPosition).Normalized(); - return parPlayerPosition + (parRadius * direction); + return parPlayerPosition + (_radius * direction); } } \ No newline at end of file diff --git a/DoomDeathmatch/src/Script/Model/Enemy/Movement/IMovementBehavior.cs b/DoomDeathmatch/src/Script/Model/Enemy/Movement/IMovementBehavior.cs index 41bbde1..c75fd7c 100644 --- a/DoomDeathmatch/src/Script/Model/Enemy/Movement/IMovementBehavior.cs +++ b/DoomDeathmatch/src/Script/Model/Enemy/Movement/IMovementBehavior.cs @@ -2,7 +2,16 @@ namespace DoomDeathmatch.Script.Model.Enemy.Movement; +/// +/// Interface for defining enemy movement behavior. +/// public interface IMovementBehavior { + /// + /// Calculates the next position for the enemy based on the current position and the player's position. + /// + /// The current position of the enemy. + /// The current position of the player. + /// The next position of the enemy. public Vector3 GetNextPosition(Vector3 parPosition, Vector3 parPlayerPosition); } \ No newline at end of file diff --git a/DoomDeathmatch/src/Script/Model/Enemy/Movement/StandingMovementBehavior.cs b/DoomDeathmatch/src/Script/Model/Enemy/Movement/StandingMovementBehavior.cs index cbbeaed..ffb68a3 100644 --- a/DoomDeathmatch/src/Script/Model/Enemy/Movement/StandingMovementBehavior.cs +++ b/DoomDeathmatch/src/Script/Model/Enemy/Movement/StandingMovementBehavior.cs @@ -2,8 +2,17 @@ namespace DoomDeathmatch.Script.Model.Enemy.Movement; +/// +/// Movement behavior where the enemy remains stationary. +/// public class StandingMovementBehavior : IMovementBehavior { + /// + /// Returns the current position of the enemy without any change, as the enemy does not move. + /// + /// The current position of the enemy. + /// The current position of the player (ignored). + /// The current position of the enemy. public Vector3 GetNextPosition(Vector3 parPosition, Vector3 parPlayerPosition) { return parPosition; diff --git a/DoomDeathmatch/src/Script/Model/HealthModel.cs b/DoomDeathmatch/src/Script/Model/HealthModel.cs index 6e64d4a..c7bf5f2 100644 --- a/DoomDeathmatch/src/Script/Model/HealthModel.cs +++ b/DoomDeathmatch/src/Script/Model/HealthModel.cs @@ -1,9 +1,20 @@ namespace DoomDeathmatch.Script.Model; +/// +/// Represents the health state of a player or entity in the game. +/// Provides functionality to track and modify health, and notify listeners when health changes. +/// public class HealthModel { + /// + /// Occurs when the health value changes. + /// public event Action? HealthChanged; + /// + /// The current health value of the entity. + /// Health is clamped between 0 and . + /// public float Health { get => _health; @@ -20,6 +31,10 @@ public class HealthModel } } + /// + /// The maximum health value of the entity. + /// When modified, the current health is clamped between 0 and the new . + /// public float MaxHealth { get => _maxHealth; @@ -31,9 +46,21 @@ public class HealthModel } } + /// + /// The current health value of the entity. + /// private float _health; + + /// + /// The maximum health value of the entity. + /// private float _maxHealth; + /// + /// Initializes a new instance of the class with a specified maximum health. + /// Sets the initial health to the maximum health. + /// + /// The maximum health value. public HealthModel(float parMaxHealth) { MaxHealth = parMaxHealth; diff --git a/DoomDeathmatch/src/Script/Model/ScoreModel.cs b/DoomDeathmatch/src/Script/Model/ScoreModel.cs index edfa21d..6e36708 100644 --- a/DoomDeathmatch/src/Script/Model/ScoreModel.cs +++ b/DoomDeathmatch/src/Script/Model/ScoreModel.cs @@ -1,9 +1,20 @@ namespace DoomDeathmatch.Script.Model; +/// +/// Represents the score state of a player or entity in the game. +/// Provides functionality to track and modify the score, and notify listeners when the score changes. +/// public class ScoreModel { + /// + /// Occurs when the score value changes. + /// public event Action? ScoreChanged; + /// + /// The current score of the player or entity. + /// The score cannot be negative. + /// public int Score { get => _score; @@ -14,5 +25,8 @@ public class ScoreModel } } + /// + /// The current score of the player or entity. + /// private int _score; } \ No newline at end of file diff --git a/DoomDeathmatch/src/Script/Model/Weapon/IShootPattern.cs b/DoomDeathmatch/src/Script/Model/Weapon/IShootPattern.cs index 74dc1c2..21f30d9 100644 --- a/DoomDeathmatch/src/Script/Model/Weapon/IShootPattern.cs +++ b/DoomDeathmatch/src/Script/Model/Weapon/IShootPattern.cs @@ -2,7 +2,17 @@ namespace DoomDeathmatch.Script.Model.Weapon; +/// +/// Defines a shooting pattern for a weapon. +/// public interface IShootPattern { + /// + /// Computes the shooting pattern based on directional vectors. + /// + /// The forward direction of the weapon. + /// The upward direction of the weapon. + /// The rightward direction of the weapon. + /// A collection of directional offsets representing the shoot pattern. public IEnumerable GetShootPattern(Vector3 parForward, Vector3 parUp, Vector3 parRight); } \ No newline at end of file diff --git a/DoomDeathmatch/src/Script/Model/Weapon/LineShootPattern.cs b/DoomDeathmatch/src/Script/Model/Weapon/LineShootPattern.cs index 8b23a26..c37262c 100644 --- a/DoomDeathmatch/src/Script/Model/Weapon/LineShootPattern.cs +++ b/DoomDeathmatch/src/Script/Model/Weapon/LineShootPattern.cs @@ -2,8 +2,18 @@ namespace DoomDeathmatch.Script.Model.Weapon; +/// +/// Represents a simple linear shooting pattern. +/// public class LineShootPattern : IShootPattern { + /// + /// Computes a linear shooting pattern, which always shoots straight ahead. + /// + /// The forward direction of the weapon. + /// The upward direction of the weapon. + /// The rightward direction of the weapon. + /// A collection containing a single zero vector, indicating no offset. public IEnumerable GetShootPattern(Vector3 parForward, Vector3 parUp, Vector3 parRight) { return [Vector3.Zero]; diff --git a/DoomDeathmatch/src/Script/Model/Weapon/RandomFlatSpreadShootPattern.cs b/DoomDeathmatch/src/Script/Model/Weapon/RandomFlatSpreadShootPattern.cs index 147ca26..b87c8bb 100644 --- a/DoomDeathmatch/src/Script/Model/Weapon/RandomFlatSpreadShootPattern.cs +++ b/DoomDeathmatch/src/Script/Model/Weapon/RandomFlatSpreadShootPattern.cs @@ -2,15 +2,49 @@ namespace DoomDeathmatch.Script.Model.Weapon; -public class RandomFlatSpreadShootPattern(float parAngle, uint parCount) : IShootPattern +/// +/// Represents a shooting pattern with a random flat spread. +/// +public class RandomFlatSpreadShootPattern : IShootPattern { + /// + /// The random number generator for calculating spread angles. + /// private readonly Random _random = new(); + /// + /// The maximum angle of spread in radians. + /// + private readonly float _angle; + + /// + /// The number of projectiles in the spread. + /// + private readonly uint _count; + + /// + /// Initializes a new instance of the class. + /// + /// The maximum angle of spread in radians. + /// The number of projectiles in the spread. + public RandomFlatSpreadShootPattern(float parAngle, uint parCount) + { + _angle = parAngle; + _count = parCount; + } + + /// + /// Computes a random flat spread shooting pattern. + /// + /// The forward direction of the weapon. + /// The upward direction of the weapon. + /// The rightward direction of the weapon. + /// A collection of directional offsets representing the spread pattern. public IEnumerable GetShootPattern(Vector3 parForward, Vector3 parUp, Vector3 parRight) { - for (var i = 0; i < parCount; i++) + for (var i = 0; i < _count; i++) { - var angle = parAngle * (((float)_random.NextDouble() * 2) - 1); + var angle = _angle * (((float)_random.NextDouble() * 2) - 1); var delta = MathF.Tan(angle); var offset = parRight * delta; diff --git a/DoomDeathmatch/src/Script/Model/Weapon/WeaponData.cs b/DoomDeathmatch/src/Script/Model/Weapon/WeaponData.cs index 3137bbf..8bc34d9 100644 --- a/DoomDeathmatch/src/Script/Model/Weapon/WeaponData.cs +++ b/DoomDeathmatch/src/Script/Model/Weapon/WeaponData.cs @@ -2,8 +2,14 @@ namespace DoomDeathmatch.Script.Model.Weapon; +/// +/// Represents data associated with a weapon, including its stats, animations, and shooting pattern. +/// public class WeaponData { + /// + /// Data for the "Pistol" weapon. + /// public static WeaponData Pistol => new(30) { @@ -19,6 +25,9 @@ public class WeaponData ShootPattern = new LineShootPattern() }; + /// + /// Data for the "Shotgun" weapon. + /// public static WeaponData Shotgun => new(10) { @@ -31,12 +40,24 @@ public class WeaponData ShootPattern = new RandomFlatSpreadShootPattern(MathHelper.DegreesToRadians(10), 40) }; + /// + /// Triggered when the ammo of the weapon changes. + /// public event Action? OnAmmoChanged; - + /// + /// A unique identifier for the weapon. + /// public string Id { get; private init; } = ""; + + /// + /// The display name of the weapon. + /// public string Name { get; private init; } = ""; + /// + /// The current ammo count of the weapon. Updates trigger the event. + /// public int Ammo { get => _ammo; @@ -62,29 +83,58 @@ public class WeaponData } } + /// + /// The maximum ammo capacity of the weapon. + /// public int MaxAmmo { get; } + /// + /// The base damage dealt by the weapon. + /// public int Damage { get; private init; } + /// + /// The path to the texture used when the weapon is idle. + /// public string IdleTexture { get; private init; } = ""; + + /// + /// The duration of the weapon's firing animation, in seconds. + /// public float FireAnimationDuration { get; private init; } + + /// + /// A list of textures used for the weapon's firing animation. + /// public List FireAnimation { get; } = []; + /// + /// The shooting pattern associated with the weapon. + /// public IShootPattern ShootPattern { get; private init; } + /// + ///The current ammo count of the weapon. + /// private int _ammo; + /// + /// Initializes a new instance of the class with the specified maximum ammo. + /// + /// The maximum ammo capacity for the weapon. private WeaponData(int parMaxAmmo) { MaxAmmo = parMaxAmmo; Ammo = MaxAmmo; } + /// public override bool Equals(object? parObj) { return parObj is WeaponData weaponModel && Id == weaponModel.Id; } + /// public override int GetHashCode() { return HashCode.Combine(Id); diff --git a/DoomDeathmatch/src/Script/Model/WeaponModel.cs b/DoomDeathmatch/src/Script/Model/WeaponModel.cs index f8917c1..9726e2e 100644 --- a/DoomDeathmatch/src/Script/Model/WeaponModel.cs +++ b/DoomDeathmatch/src/Script/Model/WeaponModel.cs @@ -2,13 +2,31 @@ namespace DoomDeathmatch.Script.Model; +/// +/// Represents the weapon state of a player or entity in the game. +/// Provides functionality to track available weapons, the selected weapon, and notify listeners when the selected weapon changes. +/// public class WeaponModel { + /// + /// Occurs when the selected weapon changes. + /// public event Action? OnWeaponSelected; + /// + /// The list of available weapons for the player or entity. + /// public IList Weapons => _weapons; + + /// + /// The currently selected weapon. + /// public WeaponData SelectedWeapon => _weapons[_selectedWeaponIndex]; + /// + /// The index of the currently selected weapon in the weapons list. + /// When modified, triggers the event with the old and new selected weapons. + /// public int SelectedWeaponIndex { get => _selectedWeaponIndex; @@ -27,7 +45,13 @@ public class WeaponModel } } + /// + /// The list of available weapons for the player or entity. + /// private readonly List _weapons = [WeaponData.Pistol]; + /// + /// The index of the currently selected weapon in the weapons list. + /// private int _selectedWeaponIndex; } \ No newline at end of file diff --git a/DoomDeathmatch/src/Script/ObjectSpawner.cs b/DoomDeathmatch/src/Script/ObjectSpawner.cs index d1defbf..ffa9fec 100644 --- a/DoomDeathmatch/src/Script/ObjectSpawner.cs +++ b/DoomDeathmatch/src/Script/ObjectSpawner.cs @@ -5,14 +5,37 @@ using OpenTK.Mathematics; namespace DoomDeathmatch.Script; +/// +/// Handles the spawning of game objects based on a condition and provided position and object data. +/// public class ObjectSpawner : IUpdate { + /// + /// Occurs when a new game object is spawned. + /// public event Action? OnSpawned; + /// + /// Provides the game object to spawn. + /// private readonly IValueProvider _gameObjectProvider; + + /// + /// Provides the position where the game object will be spawned. + /// private readonly IValueProvider _positionProvider; + + /// + /// Determines when the spawner should spawn a new object. + /// private readonly ICondition _condition; + /// + /// Initializes a new instance of the class. + /// + /// The provider for the game object to spawn. + /// The provider for the position of the spawned object. + /// The condition that triggers the spawning. public ObjectSpawner(IValueProvider parGameObjectProvider, IValueProvider parPositionProvider, ICondition parCondition) { @@ -23,11 +46,18 @@ public class ObjectSpawner : IUpdate _condition.OnTrue += Spawn; } + /// + /// Updates the state of the spawner, evaluating the condition to determine if a new object should spawn. + /// + /// The time elapsed since the last update, in seconds. public void Update(double parDeltaTime) { _condition.Update(parDeltaTime); } + /// + /// Spawns a new game object at the position provided by the position provider. + /// private void Spawn() { var gameObject = _gameObjectProvider.GetValue(); diff --git a/DoomDeathmatch/src/Script/Provider/ConstValueProvider.cs b/DoomDeathmatch/src/Script/Provider/ConstValueProvider.cs index db61517..3f962e5 100644 --- a/DoomDeathmatch/src/Script/Provider/ConstValueProvider.cs +++ b/DoomDeathmatch/src/Script/Provider/ConstValueProvider.cs @@ -1,9 +1,38 @@ namespace DoomDeathmatch.Script.Provider; -public class ConstValueProvider(T parValue) : IValueProvider +/// +/// Provides a constant value of type . +/// +/// The type of value provided. +public class ConstValueProvider : IValueProvider { - public T GetValue() => parValue; + /// + /// The value to provide. + /// + private readonly T _value; + /// + /// Initializes a new instance of . + /// + /// The value to store. + public ConstValueProvider(T parValue) + { + _value = parValue; + } + + /// + public T GetValue() => _value; + + /// + /// Creates a new instance of with the specified value. + /// + /// The constant value to provide. + /// A new instance of . public static ConstValueProvider Create(T parValue) => new(parValue); + + /// + /// Implicitly converts a value of type to a . + /// + /// The value to convert. public static implicit operator ConstValueProvider(T parValue) => new(parValue); } \ No newline at end of file diff --git a/DoomDeathmatch/src/Script/Provider/GeneratorValueProvider.cs b/DoomDeathmatch/src/Script/Provider/GeneratorValueProvider.cs index 09dc744..836db62 100644 --- a/DoomDeathmatch/src/Script/Provider/GeneratorValueProvider.cs +++ b/DoomDeathmatch/src/Script/Provider/GeneratorValueProvider.cs @@ -1,9 +1,38 @@ namespace DoomDeathmatch.Script.Provider; -public class GeneratorValueProvider(Func parGenerator) : IValueProvider +/// +/// Provides a value generated by a function. +/// +/// The type of value provided. +public class GeneratorValueProvider : IValueProvider { - public T GetValue() => parGenerator(); + /// + /// A function used to generate values. + /// + private readonly Func _generator; + /// + /// Initializes a new instance of . + /// + /// The function used to generate values. + public GeneratorValueProvider(Func parGenerator) + { + _generator = parGenerator; + } + + /// + public T GetValue() => _generator(); + + /// + /// Creates a new instance of with the specified generator function. + /// + /// The function that generates values. + /// A new instance of . public static GeneratorValueProvider Create(Func parGenerator) => new(parGenerator); + + /// + /// Implicitly converts a function of type to a . + /// + /// The function to convert. public static implicit operator GeneratorValueProvider(Func parGenerator) => new(parGenerator); } \ No newline at end of file diff --git a/DoomDeathmatch/src/Script/Provider/IValueProvider.cs b/DoomDeathmatch/src/Script/Provider/IValueProvider.cs index 6e0824b..3f63481 100644 --- a/DoomDeathmatch/src/Script/Provider/IValueProvider.cs +++ b/DoomDeathmatch/src/Script/Provider/IValueProvider.cs @@ -1,6 +1,14 @@ namespace DoomDeathmatch.Script.Provider; +/// +/// Represents a provider that supplies a value of type . +/// +/// The type of value provided. public interface IValueProvider { + /// + /// Retrieves the value provided by this instance. + /// + /// The value of type . public T GetValue(); } \ No newline at end of file diff --git a/DoomDeathmatch/src/Script/Provider/RandomListValueProvider.cs b/DoomDeathmatch/src/Script/Provider/RandomListValueProvider.cs index a165112..d5ce1ea 100644 --- a/DoomDeathmatch/src/Script/Provider/RandomListValueProvider.cs +++ b/DoomDeathmatch/src/Script/Provider/RandomListValueProvider.cs @@ -1,10 +1,25 @@ namespace DoomDeathmatch.Script.Provider; +/// +/// Provides a value randomly selected from a list of value providers. +/// +/// The type of value provided. public class RandomListValueProvider : IValueProvider { + /// + /// A collection of value providers to choose from. + /// private readonly List> _providers = []; + + /// + /// The random number generator for selecting providers. + /// private readonly Random _random = new(); + /// + /// Initializes a new instance of with the specified providers. + /// + /// The collection of value providers to select from. public RandomListValueProvider(IEnumerable> parProviders) { foreach (var provider in parProviders) @@ -13,6 +28,7 @@ public class RandomListValueProvider : IValueProvider } } + /// public T GetValue() { return _providers[_random.Next(_providers.Count)].GetValue(); diff --git a/DoomDeathmatch/src/Script/Provider/WeightedRandomValueProvider.cs b/DoomDeathmatch/src/Script/Provider/WeightedRandomValueProvider.cs index baf56c4..bf971e4 100644 --- a/DoomDeathmatch/src/Script/Provider/WeightedRandomValueProvider.cs +++ b/DoomDeathmatch/src/Script/Provider/WeightedRandomValueProvider.cs @@ -1,11 +1,31 @@ namespace DoomDeathmatch.Script.Provider; +/// +/// Provides a value randomly selected from weighted value providers. +/// +/// The type of value provided. public class WeightedRandomValueProvider : IValueProvider { + /// + /// A collection of weighted value providers. + /// private readonly List<(int, IValueProvider)> _providers = []; + + /// + /// The random number generator used to select a provider. + /// private readonly Random _random = new(); + + /// + /// The total weight of all providers. + /// private readonly int _totalWeight; + /// + /// Initializes a new instance of with the specified providers. + /// + /// The collection of weighted value providers. + /// Thrown if the total weight is less than or equal to 0. public WeightedRandomValueProvider(IEnumerable<(int, IValueProvider)> parProviders) { foreach (var (weight, provider) in parProviders) @@ -20,6 +40,7 @@ public class WeightedRandomValueProvider : IValueProvider } } + /// public T GetValue() { var random = _random.Next(_totalWeight); diff --git a/DoomDeathmatch/src/Script/Score/ScoreTable.cs b/DoomDeathmatch/src/Script/Score/ScoreTable.cs index f09d0ce..58e9e50 100644 --- a/DoomDeathmatch/src/Script/Score/ScoreTable.cs +++ b/DoomDeathmatch/src/Script/Score/ScoreTable.cs @@ -7,7 +7,7 @@ namespace DoomDeathmatch.Script.Score; [JsonSerializable(typeof(ScoreTable))] public class ScoreTable { - public List Rows { get; } = new(); + public List Rows { get; } = []; private static readonly JsonSerializerOptions OPTIONS = new() { Converters = { new ScoreTableJsonConverter() } }; diff --git a/DoomDeathmatch/src/Script/UI/Align.cs b/DoomDeathmatch/src/Script/UI/Align.cs index 0e22101..800d360 100644 --- a/DoomDeathmatch/src/Script/UI/Align.cs +++ b/DoomDeathmatch/src/Script/UI/Align.cs @@ -1,8 +1,22 @@ namespace DoomDeathmatch.Script.UI; +/// +/// Specifies horizontal alignment options. +/// public enum Align { + /// + /// Align content to the left. + /// Left, + + /// + /// Align content to the center. + /// Center, + + /// + /// Align content to the right. + /// Right -} \ No newline at end of file +} diff --git a/DoomDeathmatch/src/Script/UI/Anchor.cs b/DoomDeathmatch/src/Script/UI/Anchor.cs index 619c7b3..de5fcb8 100644 --- a/DoomDeathmatch/src/Script/UI/Anchor.cs +++ b/DoomDeathmatch/src/Script/UI/Anchor.cs @@ -1,14 +1,90 @@ -namespace DoomDeathmatch.Script.UI; +using OpenTK.Mathematics; +namespace DoomDeathmatch.Script.UI; + +/// +/// Specifies anchoring options for positioning elements. +/// public enum Anchor { + /// + /// Anchor to the top-left corner. + /// TopLeft, + + /// + /// Anchor to the top-center. + /// TopCenter, + + /// + /// Anchor to the top-right corner. + /// TopRight, + + /// + /// Anchor to the center-left. + /// CenterLeft, + + /// + /// Anchor to the center. + /// Center, + + /// + /// Anchor to the center-right. + /// CenterRight, + + /// + /// Anchor to the bottom-left corner. + /// BottomLeft, + + /// + /// Anchor to the bottom-center. + /// BottomCenter, + + /// + /// Anchor to the bottom-right corner. + /// BottomRight +} + +public static class AnchorHelper +{ + /// + /// Retrieves the ratio of an anchor. + /// + /// The anchor to retrieve the ratio of. + /// The ratio of the anchor. + public static Vector2 GetRatio(this Anchor parAnchor) + { + return parAnchor switch + { + Anchor.TopLeft => new Vector2(-0.5f, 0.5f), + Anchor.TopCenter => new Vector2(0, 0.5f), + Anchor.TopRight => new Vector2(0.5f, 0.5f), + Anchor.CenterLeft => new Vector2(-0.5f, 0), + Anchor.Center => new Vector2(0, 0), + Anchor.CenterRight => new Vector2(0.5f, 0), + Anchor.BottomLeft => new Vector2(-0.5f, -0.5f), + Anchor.BottomCenter => new Vector2(0, -0.5f), + Anchor.BottomRight => new Vector2(0.5f, -0.5f), + _ => throw new ArgumentOutOfRangeException(nameof(parAnchor), parAnchor, null) + }; + } + + /// + /// Retrieves the position of an anchor relative to a size. + /// + /// The anchor to retrieve the position of. + /// The size to use for calculations. + /// The position of the anchor relative to the size. + public static Vector2 GetPosition(this Anchor parAnchor, Vector2 parSize) + { + return parSize * parAnchor.GetRatio(); + } } \ No newline at end of file diff --git a/DoomDeathmatch/src/Script/UI/Orientation.cs b/DoomDeathmatch/src/Script/UI/Orientation.cs index cc35fe5..8d52c09 100644 --- a/DoomDeathmatch/src/Script/UI/Orientation.cs +++ b/DoomDeathmatch/src/Script/UI/Orientation.cs @@ -1,7 +1,17 @@ namespace DoomDeathmatch.Script.UI; +/// +/// Specifies the orientation of UI elements. +/// public enum Orientation { + /// + /// Arrange elements horizontally. + /// Horizontal, + + /// + /// Arrange elements vertically. + /// Vertical } \ No newline at end of file diff --git a/Engine/src/Resource/Loader/ProgramLoader.cs b/Engine/src/Resource/Loader/ProgramLoader.cs index 274f6a7..bad426a 100644 --- a/Engine/src/Resource/Loader/ProgramLoader.cs +++ b/Engine/src/Resource/Loader/ProgramLoader.cs @@ -17,7 +17,7 @@ public partial class ProgramLoader : IResourceLoader /// The loaded shader program. public object Load(string parPath, IResourceStreamProvider parStreamProvider) { - var textReader = new StreamReader(parStreamProvider.GetStream(parPath)); + using var textReader = new StreamReader(parStreamProvider.GetStream(parPath)); var vertexSource = new StringBuilder(); var fragmentSource = new StringBuilder(); diff --git a/Engine/src/Scene/Component/Component.cs b/Engine/src/Scene/Component/Component.cs index 0caf776..1cc49cc 100644 --- a/Engine/src/Scene/Component/Component.cs +++ b/Engine/src/Scene/Component/Component.cs @@ -45,6 +45,14 @@ public abstract class Component : IUpdate, IRender { } + /// + /// Called after the main update loop. + /// + /// Time elapsed since the last frame. + public virtual void PostUpdate(double parDeltaTime) + { + } + /// /// Called during the main render loop. /// diff --git a/Engine/src/Scene/GameObject.cs b/Engine/src/Scene/GameObject.cs index 452c845..a3b557e 100644 --- a/Engine/src/Scene/GameObject.cs +++ b/Engine/src/Scene/GameObject.cs @@ -151,6 +151,18 @@ public sealed class GameObject : IUpdate, IRender } } + /// + /// Performs post-update operations for all components. + /// + /// The time delta since the last update. + public void PostUpdate(double parDeltaTime) + { + foreach (var component in _components) + { + component.PostUpdate(parDeltaTime); + } + } + /// public void Render() { @@ -181,9 +193,9 @@ public sealed class GameObject : IUpdate, IRender /// /// The type of the component to retrieve. /// The component if found, otherwise null. - public T? GetComponent() where T : Component.Component + public T? GetComponent() where T : class { - if (!HasComponent()) + if (!typeof(T).IsInterface && !HasComponent()) { return null; } @@ -204,7 +216,7 @@ public sealed class GameObject : IUpdate, IRender /// /// The type of the component to retrieve. /// The component if found, otherwise null. - public T? GetComponentAny() where T : Component.Component + public T? GetComponentAny() where T : class { var component = GetComponent(); if (component != null) @@ -221,7 +233,7 @@ public sealed class GameObject : IUpdate, IRender /// /// The type of the component to retrieve. /// The component if found, otherwise null. - public T? GetComponentInChildren() where T : Component.Component + public T? GetComponentInChildren() where T : class { var children = Scene!.Hierarchy.GetChildren(this); @@ -334,7 +346,7 @@ public sealed class GameObject : IUpdate, IRender /// /// The type of the component to check for. /// True if the component exists, otherwise false. - public bool HasComponent() where T : Component.Component + public bool HasComponent() { var baseType = typeof(T).GetComponentBaseType(); return _addedComponentTypes.Contains(baseType); diff --git a/Engine/src/Scene/Scene.cs b/Engine/src/Scene/Scene.cs index 3c0bd19..7b79b32 100644 --- a/Engine/src/Scene/Scene.cs +++ b/Engine/src/Scene/Scene.cs @@ -61,6 +61,11 @@ public class Scene : IUpdate, IRender { gameObject.Update(parDeltaTime * TimeScale); } + + foreach (var gameObject in hierarchyObjects) + { + gameObject.PostUpdate(parDeltaTime * TimeScale); + } } /// diff --git a/Engine/src/Util/Math.cs b/Engine/src/Util/MathUtil.cs similarity index 96% rename from Engine/src/Util/Math.cs rename to Engine/src/Util/MathUtil.cs index 6c32f38..300e9ac 100644 --- a/Engine/src/Util/Math.cs +++ b/Engine/src/Util/MathUtil.cs @@ -5,7 +5,7 @@ namespace Engine.Util; /// /// Contains mathematical utility methods. /// -public static class Math +public static class MathUtil { /// /// Multiplies a by a and performs projective division. diff --git a/PresenterConsole/src/ConsolePresenter.cs b/PresenterConsole/src/ConsolePresenter.cs index ea0f6aa..3e0d61f 100644 --- a/PresenterConsole/src/ConsolePresenter.cs +++ b/PresenterConsole/src/ConsolePresenter.cs @@ -14,10 +14,16 @@ namespace PresenterConsole; public class ConsolePresenter : IPresenter { + /// public event Action? OnResize; + /// public int Width { get; private set; } = 2; + + /// public int Height { get; private set; } = 1; + + /// public bool IsExiting { get; private set; } private static readonly char[] LIGHTMAP = " .,:;=*#%@".Reverse().ToArray(); @@ -27,8 +33,8 @@ public class ConsolePresenter : IPresenter private readonly Engine.Graphics.Shader.Program _asciiProgram; private readonly Framebuffer _framebuffer; private readonly IndexBuffer _indexBuffer; - private readonly VertexArray _vertexArray; private readonly VertexBuffer _vertexBuffer; + private readonly VertexArray _vertexArray; private Image? _asciiImage; @@ -62,8 +68,6 @@ public class ConsolePresenter : IPresenter { var openglTexture = (Texture)parTexture; - // GL.Viewport(0, 0, Width / 2, Height); - _framebuffer.Bind(); openglTexture.BindUnit(); diff --git a/PresenterConsole/src/WindowsFFI.cs b/PresenterConsole/src/WindowsFFI.cs index 2c7e2e6..d169418 100644 --- a/PresenterConsole/src/WindowsFFI.cs +++ b/PresenterConsole/src/WindowsFFI.cs @@ -8,38 +8,101 @@ using Microsoft.Win32.SafeHandles; namespace PresenterConsole; +/// +/// Provides Windows FFI (Foreign Function Interface) functionality to interact with the Windows API +/// for various operations such as file handling, console operations, keyboard state management, and window handling. +/// public static partial class WindowsFFI { + /// + /// Represents a coordinate with X and Y values, typically used for positioning or grid-like structures. + /// [StructLayout(LayoutKind.Sequential)] public struct Coord(short parX, short parY) { + /// + /// The X coordinate. + /// public short X = parX; + + /// + /// The Y coordinate. + /// public short Y = parY; } + /// + /// A union structure that can hold either a Unicode or an ASCII character at the same memory location. + /// [StructLayout(LayoutKind.Explicit)] public struct CharUnion { + /// + /// The Unicode character value. + /// [FieldOffset(0)] public char UnicodeChar; + + /// + /// The ASCII character value. + /// [FieldOffset(0)] public byte AsciiChar; } + /// + /// Represents a character and its associated attributes in the console output, including the character's color. + /// [StructLayout(LayoutKind.Explicit)] public struct CharInfo { + /// + /// The character. + /// [FieldOffset(0)] public CharUnion Char; + + /// + /// The attributes associated with the character (e.g., color). + /// [FieldOffset(2)] public short Attributes; } + /// + /// Represents a rectangle in terms of its left, top, right, and bottom coordinates, used for defining the bounds of a console screen area. + /// [StructLayout(LayoutKind.Sequential)] public struct SmallRect { + /// + /// The left coordinate of the rectangle. + /// public short Left; + + /// + /// The top coordinate of the rectangle. + /// public short Top; + + /// + /// The right coordinate of the rectangle. + /// public short Right; + + /// + /// The bottom coordinate of the rectangle. + /// public short Bottom; } + /// + /// Uses the kernel32.dll's CreateFileW function to open or create a file with the specified parameters. + /// + /// The name of the file to open or create. + /// The requested access to the file. + /// The sharing mode for the file. + /// Security attributes for the file. + /// Action to take when opening the file. + /// File-specific flags. + /// An optional template file. + /// A handle to the opened or created file. [LibraryImport("kernel32.dll", SetLastError = true, EntryPoint = "CreateFileW")] public static partial SafeFileHandle CreateFile( [MarshalAs(UnmanagedType.LPWStr)] string parFileName, @@ -51,6 +114,15 @@ public static partial class WindowsFFI IntPtr parTemplate ); + /// + /// Uses the kernel32.dll's WriteConsoleOutputW function to write characters to the console output at the specified coordinates. + /// + /// A handle to the console output. + /// The buffer containing the characters and their attributes to be written. + /// The size of the buffer. + /// The coordinates of the starting position in the console buffer. + /// The region of the console buffer to write to. + /// True if the operation was successful, otherwise false. [LibraryImport("kernel32.dll", SetLastError = true, EntryPoint = "WriteConsoleOutputW")] [return: MarshalAs(UnmanagedType.Bool)] public static partial bool WriteConsoleOutput( @@ -62,32 +134,61 @@ public static partial class WindowsFFI ref SmallRect parLpWriteRegion ); + /// + /// Uses the user32.dll's GetKeyboardState function to retrieve the state of the keyboard keys. + /// + /// An array to receive the current state of the keyboard keys. + /// True if the operation was successful, otherwise false. [LibraryImport("user32.dll", SetLastError = true, EntryPoint = "GetKeyboardState")] [return: MarshalAs(UnmanagedType.Bool)] public static partial bool GetKeyboardState(byte[] parKeyboardState); + /// + /// Uses the user32.dll's GetAsyncKeyState function to retrieve the state of a specific key. + /// + /// The key code for the key whose state is to be retrieved. + /// A short value representing the key's state. [LibraryImport("user32.dll", SetLastError = true, EntryPoint = "GetAsyncKeyState")] public static partial short GetAsyncKeyState(int parKeyCode); + /// + /// Uses the user32.dll's GetForegroundWindow function to retrieve the handle of the foreground window. + /// + /// The handle of the foreground window. [LibraryImport("user32.dll", SetLastError = true, EntryPoint = "GetForegroundWindow")] public static partial IntPtr GetForegroundWindow(); + /// + /// Uses the user32.dll's GetWindowThreadProcessId function to retrieve the process ID of the thread that owns a specified window. + /// + /// The handle to the window. + /// An optional pointer to receive the process ID of the window's thread. + /// The thread ID of the window. [LibraryImport("user32.dll", SetLastError = true, EntryPoint = "GetWindowThreadProcessId")] public static partial uint GetWindowThreadProcessId(IntPtr parHwnd, IntPtr parLpdwProcessId); + /// + /// Uses the user32.dll's GetKeyboardLayout function to retrieve the input locale identifier for the current thread. + /// + /// The ID of the thread for which to retrieve the keyboard layout. + /// The handle to the keyboard layout for the thread. [LibraryImport("user32.dll", SetLastError = true, EntryPoint = "GetKeyboardLayout")] public static partial IntPtr GetKeyboardLayout(uint parThreadId); + /// + /// Retrieves the current keyboard layout for the foreground window's process. Assumes English (1033) if an error occurs. + /// + /// A object representing the current keyboard layout. public static CultureInfo GetCurrentKeyboardLayout() { try { - IntPtr foregroundWindow = GetForegroundWindow(); - uint foregroundProcess = GetWindowThreadProcessId(foregroundWindow, IntPtr.Zero); - int keyboardLayout = GetKeyboardLayout(foregroundProcess).ToInt32() & 0xFFFF; + var foregroundWindow = GetForegroundWindow(); + var foregroundProcess = GetWindowThreadProcessId(foregroundWindow, IntPtr.Zero); + var keyboardLayout = GetKeyboardLayout(foregroundProcess).ToInt32() & 0xFFFF; return new CultureInfo(keyboardLayout); } - catch (Exception _) + catch (Exception) { return new CultureInfo(1033); // Assume English if something went wrong. }