add initial project files and configurations, including EventBus, systems, and resources
This commit is contained in:
66
Code/Systems/BaseLiftSystem.cs
Normal file
66
Code/Systems/BaseLiftSystem.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using Godot;
|
||||
using MaxEffort.Code.Core;
|
||||
using MaxEffort.Code.Data;
|
||||
|
||||
namespace MaxEffort.Code.Systems;
|
||||
|
||||
public abstract partial class BaseLiftSystem : Node
|
||||
{
|
||||
[Export] protected float PowerPerClick = 5f;
|
||||
[Export] protected float Gravity = 2f;
|
||||
[Export] protected float TargetValue = 100f; // Distance OR Time
|
||||
|
||||
protected float CurrentProgress = 0f;
|
||||
protected bool IsLiftComplete = false;
|
||||
protected int ActiveHazardCount = 0;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
EventBus.OnLiftEffortApplied += ApplyPower;
|
||||
EventBus.OnHazardSpawned += OnHazardSpawned;
|
||||
EventBus.OnHazardResolved += OnHazardResolved;
|
||||
EventBus.OnLiftCompleted += OnLiftCompleted;
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
EventBus.OnLiftEffortApplied -= ApplyPower;
|
||||
EventBus.OnHazardSpawned -= OnHazardSpawned;
|
||||
EventBus.OnHazardResolved -= OnHazardResolved;
|
||||
EventBus.OnLiftCompleted -= OnLiftCompleted;
|
||||
}
|
||||
|
||||
public virtual void Initialize(float target, float gravity)
|
||||
{
|
||||
TargetValue = target;
|
||||
Gravity = gravity;
|
||||
CurrentProgress = 0f;
|
||||
IsLiftComplete = false;
|
||||
ActiveHazardCount = 0;
|
||||
}
|
||||
|
||||
private void OnLiftCompleted(bool success)
|
||||
{
|
||||
IsLiftComplete = true;
|
||||
}
|
||||
|
||||
private void OnHazardResolved(HazardType _)
|
||||
{
|
||||
ActiveHazardCount--;
|
||||
if (ActiveHazardCount < 0) ActiveHazardCount = 0;
|
||||
}
|
||||
|
||||
private void OnHazardSpawned(HazardType _)
|
||||
{
|
||||
ActiveHazardCount++;
|
||||
}
|
||||
|
||||
private void ApplyPower(float effortDelta)
|
||||
{
|
||||
if (IsLiftComplete || ActiveHazardCount > 0) return;
|
||||
HandleEffort(effortDelta);
|
||||
}
|
||||
|
||||
protected abstract void HandleEffort(float effortDelta);
|
||||
public abstract override void _Process(double delta);
|
||||
}
|
||||
1
Code/Systems/BaseLiftSystem.cs.uid
Normal file
1
Code/Systems/BaseLiftSystem.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://whef7x61oogl
|
||||
38
Code/Systems/BenchPressSystem.cs
Normal file
38
Code/Systems/BenchPressSystem.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using Godot;
|
||||
using MaxEffort.Code.Core;
|
||||
|
||||
namespace MaxEffort.Code.Systems;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class BenchPressSystem : BaseLiftSystem
|
||||
{
|
||||
protected override void HandleEffort(float effortDelta)
|
||||
{
|
||||
CurrentProgress += PowerPerClick * effortDelta;
|
||||
CheckWin();
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
if (IsLiftComplete) return;
|
||||
|
||||
if (CurrentProgress > 0)
|
||||
{
|
||||
CurrentProgress -= Gravity * (float)delta;
|
||||
CurrentProgress = Mathf.Max(0, CurrentProgress);
|
||||
}
|
||||
|
||||
var ratio = CurrentProgress / TargetValue;
|
||||
|
||||
EventBus.PublishLiftProgress(ratio);
|
||||
EventBus.PublishLiftVisualHeight(ratio);
|
||||
}
|
||||
|
||||
private void CheckWin()
|
||||
{
|
||||
if (CurrentProgress >= TargetValue)
|
||||
{
|
||||
EventBus.PublishLiftCompleted(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
1
Code/Systems/BenchPressSystem.cs.uid
Normal file
1
Code/Systems/BenchPressSystem.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dg5h252kyse6b
|
||||
72
Code/Systems/CameraShakeSystem.cs
Normal file
72
Code/Systems/CameraShakeSystem.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using Godot;
|
||||
using MaxEffort.Code.Core;
|
||||
|
||||
namespace MaxEffort.Code.Systems;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class CameraShakeSystem : Node
|
||||
{
|
||||
[Export] private Camera2D _camera;
|
||||
[Export] private float _decayRate = 0.8f; // How fast shaking stops
|
||||
[Export] private float _maxOffset = 20.0f; // Max pixels to shake
|
||||
[Export] private float _maxRoll = 0.1f; // Max rotation (radians)
|
||||
|
||||
private float _trauma = 0f; // Current shake intensity (0 to 1)
|
||||
private float _currentFocus = 0f;
|
||||
private RandomNumberGenerator _rng = new();
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
EventBus.OnCameraTrauma += AddTrauma;
|
||||
EventBus.OnFocusChanged += OnFocusChanged;
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
EventBus.OnCameraTrauma -= AddTrauma;
|
||||
EventBus.OnFocusChanged -= OnFocusChanged;
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
if (_camera == null) return;
|
||||
|
||||
if (_currentFocus > 0.75f)
|
||||
{
|
||||
AddTrauma(1.5f * (float)delta);
|
||||
}
|
||||
|
||||
if (_trauma > 0)
|
||||
{
|
||||
_trauma -= _decayRate * (float)delta;
|
||||
_trauma = Mathf.Max(0, _trauma);
|
||||
|
||||
var shake = _trauma * _trauma;
|
||||
|
||||
var offsetX = _maxOffset * shake * _rng.RandfRange(-1, 1);
|
||||
var offsetY = _maxOffset * shake * _rng.RandfRange(-1, 1);
|
||||
var rotation = _maxRoll * shake * _rng.RandfRange(-1, 1);
|
||||
|
||||
_camera.Offset = new Vector2(offsetX, offsetY);
|
||||
_camera.Rotation = rotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_camera.Offset != Vector2.Zero)
|
||||
{
|
||||
_camera.Offset = _camera.Offset.Lerp(Vector2.Zero, (float)delta * 5f);
|
||||
_camera.Rotation = Mathf.Lerp(_camera.Rotation, 0, (float)delta * 5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFocusChanged(float focus)
|
||||
{
|
||||
_currentFocus = focus;
|
||||
}
|
||||
|
||||
private void AddTrauma(float amount)
|
||||
{
|
||||
_trauma = Mathf.Clamp(_trauma + amount, 0f, 1f);
|
||||
}
|
||||
}
|
||||
1
Code/Systems/CameraShakeSystem.cs.uid
Normal file
1
Code/Systems/CameraShakeSystem.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cvrn2apa0djla
|
||||
64
Code/Systems/DeadliftSystem.cs
Normal file
64
Code/Systems/DeadliftSystem.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using Godot;
|
||||
using MaxEffort.Code.Core;
|
||||
|
||||
namespace MaxEffort.Code.Systems;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class DeadliftSystem : BaseLiftSystem
|
||||
{
|
||||
[Export] private float _barHeight = 100f; // Visual height of the lift
|
||||
[Export] private float _holdZoneThreshold = 0.8f; // Must be above 80% to count
|
||||
[Export] private Node2D _barVisual;
|
||||
[Export] private Vector2 _startPos;
|
||||
[Export] private Vector2 _endPos;
|
||||
|
||||
private float _currentBarHeight = 0f;
|
||||
private float _holdTimer = 0f;
|
||||
|
||||
public override void Initialize(float targetTime, float gravity)
|
||||
{
|
||||
base.Initialize(targetTime, gravity);
|
||||
_holdTimer = 0f;
|
||||
}
|
||||
|
||||
protected override void HandleEffort(float effortDelta)
|
||||
{
|
||||
_currentBarHeight += PowerPerClick * effortDelta;
|
||||
_currentBarHeight = Mathf.Min(_currentBarHeight, _barHeight);
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
if (IsLiftComplete) return;
|
||||
|
||||
var dt = (float)delta;
|
||||
|
||||
if (_currentBarHeight > 0)
|
||||
{
|
||||
_currentBarHeight -= Gravity * dt;
|
||||
_currentBarHeight = Mathf.Max(0, _currentBarHeight);
|
||||
}
|
||||
|
||||
var visualRatio = _currentBarHeight / _barHeight; // 0.0 to 1.0 (Height)
|
||||
|
||||
if (visualRatio >= _holdZoneThreshold && ActiveHazardCount == 0)
|
||||
{
|
||||
_holdTimer += dt;
|
||||
EventBus.PublishCameraTrauma(0.15f * dt);
|
||||
}
|
||||
|
||||
EventBus.PublishLiftVisualHeight(visualRatio);
|
||||
EventBus.PublishLiftProgress(_holdTimer / TargetValue);
|
||||
|
||||
if (_barVisual != null)
|
||||
{
|
||||
_barVisual.Position = _startPos.Lerp(_endPos, visualRatio);
|
||||
}
|
||||
|
||||
if (_holdTimer >= TargetValue)
|
||||
{
|
||||
EventBus.PublishCameraTrauma(1.0f);
|
||||
EventBus.PublishLiftCompleted(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
1
Code/Systems/DeadliftSystem.cs.uid
Normal file
1
Code/Systems/DeadliftSystem.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dpes80ppxqppv
|
||||
96
Code/Systems/GameManager.cs
Normal file
96
Code/Systems/GameManager.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
using Godot;
|
||||
using MaxEffort.Code.Core;
|
||||
using MaxEffort.Code.Data;
|
||||
|
||||
namespace MaxEffort.Code.Systems;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class GameManager : Node
|
||||
{
|
||||
[Export] private Godot.Collections.Array<DayConfig> _days;
|
||||
[Export] private HazardSystem _hazardSystem;
|
||||
[Export] private Node _minigameContainer;
|
||||
|
||||
[Export] private Control _winScreen;
|
||||
[Export] private Control _loseScreen;
|
||||
[Export] private Label _dayLabel;
|
||||
|
||||
private int _currentDayIndex = 0;
|
||||
private Node _currentMiniGame;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
EventBus.OnLiftCompleted += HandleLiftResult;
|
||||
StartDay(_currentDayIndex);
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
EventBus.OnLiftCompleted -= HandleLiftResult;
|
||||
}
|
||||
|
||||
private void StartDay(int index)
|
||||
{
|
||||
if (index >= _days.Count)
|
||||
{
|
||||
GD.Print("YOU BEAT THE GYM! ALL DAYS COMPLETE.");
|
||||
// TODO: Show "Game Complete" screen
|
||||
return;
|
||||
}
|
||||
|
||||
var config = _days[index];
|
||||
|
||||
_currentMiniGame?.QueueFree();
|
||||
_hazardSystem.ClearHazards();
|
||||
|
||||
if (config.MiniGameScene != null)
|
||||
{
|
||||
_currentMiniGame = config.MiniGameScene.Instantiate();
|
||||
_minigameContainer.AddChild(_currentMiniGame);
|
||||
|
||||
var liftSystem = _currentMiniGame.GetNode<BaseLiftSystem>("System");
|
||||
liftSystem?.Initialize(config.TargetWeight, config.Gravity);
|
||||
}
|
||||
|
||||
_hazardSystem.SetAvailableHazards(config.AvailableHazards);
|
||||
|
||||
if (_dayLabel != null) _dayLabel.Text = config.DayTitle;
|
||||
|
||||
if (_winScreen != null) _winScreen.Visible = false;
|
||||
if (_loseScreen != null) _loseScreen.Visible = false;
|
||||
|
||||
GD.Print($"Started {config.DayTitle}");
|
||||
}
|
||||
|
||||
private void HandleLiftResult(bool success)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
GD.Print("Day Complete! Next day loading...");
|
||||
if (_winScreen != null) _winScreen.Visible = true;
|
||||
|
||||
// Wait for player input to proceed, or auto-load after delay
|
||||
// For now, let's auto-advance after 3 seconds for the prototype
|
||||
GetTree().CreateTimer(3.0f).Timeout += () => NextDay();
|
||||
}
|
||||
else
|
||||
{
|
||||
GD.Print("FAIL! Go home.");
|
||||
if (_loseScreen != null) _loseScreen.Visible = true;
|
||||
|
||||
// Restart day after delay
|
||||
GetTree().CreateTimer(3.0f).Timeout += () => RestartDay();
|
||||
}
|
||||
}
|
||||
|
||||
private void NextDay()
|
||||
{
|
||||
_currentDayIndex++;
|
||||
StartDay(_currentDayIndex);
|
||||
}
|
||||
|
||||
private void RestartDay()
|
||||
{
|
||||
GetTree().ReloadCurrentScene();
|
||||
}
|
||||
}
|
||||
1
Code/Systems/GameManager.cs.uid
Normal file
1
Code/Systems/GameManager.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cmiktfrffb3pc
|
||||
115
Code/Systems/HazardController.cs
Normal file
115
Code/Systems/HazardController.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using Godot;
|
||||
using MaxEffort.Code.Core;
|
||||
using MaxEffort.Code.Data;
|
||||
|
||||
namespace MaxEffort.Code.Systems;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class HazardController : Node2D
|
||||
{
|
||||
[Export] private AnimatedSprite2D _animSprite;
|
||||
[Export] private Area2D _clickArea;
|
||||
[Export] private CollisionShape2D _clickShape; // Reference to resize it
|
||||
[Export] private Label _nameLabel; // Can still use UI nodes inside Node2D
|
||||
|
||||
private HazardDef _data;
|
||||
private float _timeActive = 0f;
|
||||
private bool _isResolved = false;
|
||||
private int _currentHealth = 1;
|
||||
|
||||
public void Initialize(HazardDef data)
|
||||
{
|
||||
_data = data;
|
||||
_currentHealth = data.ClicksToResolve;
|
||||
|
||||
if (_animSprite != null && data.Animations != null)
|
||||
{
|
||||
_animSprite.SpriteFrames = data.Animations;
|
||||
|
||||
var texture = data.Animations.GetFrameTexture(data.IdleAnimName, 0);
|
||||
if (texture != null && _clickShape != null)
|
||||
{
|
||||
var rect = new RectangleShape2D();
|
||||
rect.Size = texture.GetSize();
|
||||
_clickShape.Shape = rect;
|
||||
}
|
||||
|
||||
_animSprite.Play(data.IdleAnimName);
|
||||
}
|
||||
|
||||
if (_nameLabel != null) _nameLabel.Text = data.DisplayName;
|
||||
|
||||
if (_clickArea != null)
|
||||
{
|
||||
_clickArea.InputEvent += OnInputEvent;
|
||||
}
|
||||
|
||||
Scale = Vector2.Zero;
|
||||
var tween = CreateTween();
|
||||
tween.SetTrans(Tween.TransitionType.Back).SetEase(Tween.EaseType.Out);
|
||||
tween.TweenProperty(this, "scale", Vector2.One, 0.4f);
|
||||
|
||||
if (!string.IsNullOrEmpty(data.WalkAnimName))
|
||||
{
|
||||
_animSprite?.Play(data.WalkAnimName);
|
||||
tween.TweenCallback(Callable.From(() => _animSprite?.Play(data.IdleAnimName)));
|
||||
}
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
if (_isResolved) return;
|
||||
_timeActive += (float)delta;
|
||||
|
||||
if (_timeActive >= _data.TimeToFail)
|
||||
{
|
||||
EventBus.PublishLiftCompleted(false);
|
||||
QueueFree();
|
||||
}
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
if (_clickArea != null) _clickArea.InputEvent -= OnInputEvent;
|
||||
}
|
||||
|
||||
private void OnInputEvent(Node viewport, InputEvent @event, long shapeIdx)
|
||||
{
|
||||
if (_isResolved) return;
|
||||
|
||||
if (@event is InputEventMouseButton mouseEvent && mouseEvent.Pressed && mouseEvent.ButtonIndex == MouseButton.Left)
|
||||
{
|
||||
TakeDamage();
|
||||
}
|
||||
}
|
||||
|
||||
private void TakeDamage()
|
||||
{
|
||||
_currentHealth--;
|
||||
|
||||
var tween = CreateTween();
|
||||
tween.TweenProperty(this, "scale", new Vector2(1.2f, 0.8f), 0.05f);
|
||||
tween.TweenProperty(this, "scale", Vector2.One, 0.05f);
|
||||
|
||||
if (_animSprite != null)
|
||||
{
|
||||
_animSprite.Modulate = Colors.Red;
|
||||
tween.Parallel().TweenProperty(_animSprite, "modulate", Colors.White, 0.1f);
|
||||
}
|
||||
|
||||
if (_currentHealth <= 0)
|
||||
{
|
||||
Resolve();
|
||||
}
|
||||
}
|
||||
|
||||
private void Resolve()
|
||||
{
|
||||
_isResolved = true;
|
||||
EventBus.PublishHazardResolved(_data.Type);
|
||||
|
||||
var tween = CreateTween();
|
||||
tween.TweenProperty(this, "scale", Vector2.Zero, 0.2f);
|
||||
tween.TweenCallback(Callable.From(QueueFree));
|
||||
}
|
||||
}
|
||||
1
Code/Systems/HazardController.cs.uid
Normal file
1
Code/Systems/HazardController.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cfpdfb8fnym2y
|
||||
114
Code/Systems/HazardSystem.cs
Normal file
114
Code/Systems/HazardSystem.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using System.Linq;
|
||||
using Godot;
|
||||
using MaxEffort.Code.Core;
|
||||
using MaxEffort.Code.Data;
|
||||
|
||||
namespace MaxEffort.Code.Systems;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class HazardSystem : Node
|
||||
{
|
||||
[Export] private Godot.Collections.Array<HazardDef> _possibleHazards;
|
||||
[Export] private Godot.Collections.Array<Node2D> _spawnLocations;
|
||||
[Export] private PackedScene _hazardPrefab; // The visual object to spawn
|
||||
[Export] private float _checkInterval = 1.0f; // How often to try spawning
|
||||
|
||||
private float _currentFocus = 0f;
|
||||
private double _timer = 0;
|
||||
private RandomNumberGenerator _rng = new();
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
EventBus.OnFocusChanged += OnFocusChanged;
|
||||
EventBus.OnHazardResolved += OnHazardResolved;
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
EventBus.OnFocusChanged -= OnFocusChanged;
|
||||
EventBus.OnHazardResolved -= OnHazardResolved;
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
_timer += delta;
|
||||
if (_timer >= _checkInterval)
|
||||
{
|
||||
_timer = 0;
|
||||
TrySpawnHazard();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetAvailableHazards(Godot.Collections.Array<HazardDef> hazards)
|
||||
{
|
||||
if (_possibleHazards == null) _possibleHazards = [];
|
||||
|
||||
_possibleHazards.Clear();
|
||||
foreach(var h in hazards) _possibleHazards.Add(h);
|
||||
}
|
||||
|
||||
public void ClearHazards()
|
||||
{
|
||||
if (_spawnLocations == null) return;
|
||||
|
||||
// Loop through all locations and destroy active hazards
|
||||
foreach (var loc in _spawnLocations)
|
||||
{
|
||||
foreach (Node child in loc.GetChildren())
|
||||
{
|
||||
child.QueueFree();
|
||||
}
|
||||
}
|
||||
_timer = 0;
|
||||
}
|
||||
|
||||
private void OnFocusChanged(float focus)
|
||||
{
|
||||
_currentFocus = focus;
|
||||
}
|
||||
|
||||
private void TrySpawnHazard()
|
||||
{
|
||||
if (_currentFocus < 0.2f) return;
|
||||
|
||||
var spawnChance = _currentFocus * 0.5f;
|
||||
|
||||
if (_rng.Randf() < spawnChance)
|
||||
{
|
||||
SpawnRandomHazard();
|
||||
}
|
||||
}
|
||||
|
||||
private void SpawnRandomHazard()
|
||||
{
|
||||
if (_possibleHazards.Count == 0 || _hazardPrefab == null || _spawnLocations.Count == 0) return;
|
||||
|
||||
var emptyLocations = _spawnLocations.Where(loc => loc.GetChildCount() == 0).ToArray();
|
||||
|
||||
if (emptyLocations.Length == 0) return;
|
||||
|
||||
var targetLoc = emptyLocations[_rng.Randi() % emptyLocations.Length];
|
||||
|
||||
var validHazards = _possibleHazards
|
||||
.Where(h => h.MinFocusToSpawn <= _currentFocus)
|
||||
.ToArray();
|
||||
|
||||
if (validHazards.Length == 0) return;
|
||||
|
||||
var selected = validHazards[_rng.Randi() % validHazards.Length];
|
||||
|
||||
if (_hazardPrefab.Instantiate() is HazardController instance)
|
||||
{
|
||||
instance.Position = Vector2.Zero;
|
||||
targetLoc.AddChild(instance);
|
||||
|
||||
instance.Initialize(selected);
|
||||
EventBus.PublishHazardSpawned(selected.Type);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnHazardResolved(HazardType type)
|
||||
{
|
||||
GD.Print($"Resolved {type}!");
|
||||
}
|
||||
}
|
||||
1
Code/Systems/HazardSystem.cs.uid
Normal file
1
Code/Systems/HazardSystem.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bwtppr4djfdwf
|
||||
23
Code/Systems/PlayerInputSystem.cs
Normal file
23
Code/Systems/PlayerInputSystem.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Godot;
|
||||
using MaxEffort.Code.Core;
|
||||
|
||||
namespace MaxEffort.Code.Systems;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class PlayerInputSystem : Node
|
||||
{
|
||||
private const string LiftAction = "lift_action";
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
if (Input.IsActionPressed(LiftAction))
|
||||
{
|
||||
EventBus.PublishLiftEffortApplied((float)delta);
|
||||
}
|
||||
|
||||
if (Input.IsActionJustReleased(LiftAction))
|
||||
{
|
||||
EventBus.PublishFocusRelease();
|
||||
}
|
||||
}
|
||||
}
|
||||
1
Code/Systems/PlayerInputSystem.cs.uid
Normal file
1
Code/Systems/PlayerInputSystem.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dorbq313ggc7e
|
||||
195
Code/Systems/SoundManager.cs
Normal file
195
Code/Systems/SoundManager.cs
Normal file
@@ -0,0 +1,195 @@
|
||||
using Godot;
|
||||
using MaxEffort.Code.Core;
|
||||
using MaxEffort.Code.Data;
|
||||
|
||||
namespace MaxEffort.Code.Systems;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class SoundManager : Node
|
||||
{
|
||||
[Export] private SoundBank _bank;
|
||||
[Export] private int _poolSize = 8;
|
||||
|
||||
private AudioStreamPlayer[] _sfxPool;
|
||||
private AudioStreamPlayer _strainPlayer;
|
||||
private AudioStreamPlayer _heartbeatPlayer;
|
||||
private AudioStreamPlayer _musicPlayer;
|
||||
private bool _isLifting = false;
|
||||
private float _currentLiftProgress = 0f;
|
||||
|
||||
private int _masterBus;
|
||||
private int _musicBus;
|
||||
private int _sfxBus;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_masterBus = AudioServer.GetBusIndex("Master");
|
||||
_musicBus = AudioServer.GetBusIndex("Music");
|
||||
_sfxBus = AudioServer.GetBusIndex("Sfx");
|
||||
|
||||
InitializePool();
|
||||
|
||||
EventBus.OnLiftEffortApplied += HandleLiftEffort;
|
||||
EventBus.OnFocusRelease += HandleFocusRelease;
|
||||
EventBus.OnFocusChanged += OnFocusChanged;
|
||||
EventBus.OnLiftCompleted += HandleLiftComplete;
|
||||
EventBus.OnLiftProgress += OnLiftProgress;
|
||||
EventBus.OnHazardSpawned += OnOnHazardSpawned;
|
||||
EventBus.OnHazardResolved += OnHazardResolved;
|
||||
EventBus.OnCameraTrauma += OnCameraTrauma;
|
||||
|
||||
PlayMusic(_bank.GameMusic);
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
EventBus.OnLiftEffortApplied -= HandleLiftEffort;
|
||||
EventBus.OnFocusRelease -= HandleFocusRelease;
|
||||
EventBus.OnLiftCompleted -= HandleLiftComplete;
|
||||
EventBus.OnLiftProgress -= OnLiftProgress;
|
||||
EventBus.OnHazardSpawned -= OnOnHazardSpawned;
|
||||
EventBus.OnHazardResolved -= OnHazardResolved;
|
||||
EventBus.OnCameraTrauma -= OnCameraTrauma;
|
||||
}
|
||||
|
||||
public void PlayMusic(AudioStream clip)
|
||||
{
|
||||
if (clip == null || _musicPlayer.Stream == clip) return;
|
||||
_musicPlayer.Stream = clip;
|
||||
_musicPlayer.Play();
|
||||
}
|
||||
|
||||
public void ToggleMasterMute(bool isMuted)
|
||||
{
|
||||
AudioServer.SetBusMute(_masterBus, isMuted);
|
||||
}
|
||||
|
||||
public void ToggleMusicMute(bool isMuted)
|
||||
{
|
||||
AudioServer.SetBusMute(_musicBus, isMuted);
|
||||
}
|
||||
|
||||
private void InitializePool()
|
||||
{
|
||||
_sfxPool = new AudioStreamPlayer[_poolSize];
|
||||
for (var i = 0; i < _poolSize; i++)
|
||||
{
|
||||
var p = new AudioStreamPlayer();
|
||||
p.Bus = "Sfx";
|
||||
AddChild(p);
|
||||
_sfxPool[i] = p;
|
||||
}
|
||||
|
||||
_strainPlayer = new AudioStreamPlayer { Bus = "Sfx" };
|
||||
AddChild(_strainPlayer);
|
||||
|
||||
_heartbeatPlayer = new AudioStreamPlayer { Bus = "Sfx" };
|
||||
AddChild(_heartbeatPlayer);
|
||||
|
||||
_musicPlayer = new AudioStreamPlayer { Bus = "Music" };
|
||||
AddChild(_musicPlayer);
|
||||
|
||||
if (_bank != null && _bank.HeartbeatLoop != null)
|
||||
{
|
||||
_heartbeatPlayer.Stream = _bank.HeartbeatLoop;
|
||||
_heartbeatPlayer.VolumeDb = -80f;
|
||||
_heartbeatPlayer.Play();
|
||||
}
|
||||
}
|
||||
|
||||
private void PlaySfx(AudioStream clip, float pitch = 1.0f)
|
||||
{
|
||||
if (clip == null) return;
|
||||
|
||||
AudioStreamPlayer bestCandidate = null;
|
||||
var longestPlaybackPosition = -1f;
|
||||
|
||||
foreach (var player in _sfxPool)
|
||||
{
|
||||
if (!player.Playing)
|
||||
{
|
||||
bestCandidate = player;
|
||||
break;
|
||||
}
|
||||
|
||||
if (player.GetPlaybackPosition() > longestPlaybackPosition)
|
||||
{
|
||||
longestPlaybackPosition = player.GetPlaybackPosition();
|
||||
bestCandidate = player;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestCandidate != null)
|
||||
{
|
||||
bestCandidate.Stream = clip;
|
||||
bestCandidate.PitchScale = pitch;
|
||||
bestCandidate.Play();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleLiftEffort(float _)
|
||||
{
|
||||
if (!_isLifting)
|
||||
{
|
||||
_isLifting = true;
|
||||
if (_bank.StrainLoop != null)
|
||||
{
|
||||
_strainPlayer.Stream = _bank.StrainLoop;
|
||||
_strainPlayer.Play();
|
||||
}
|
||||
}
|
||||
|
||||
_strainPlayer.PitchScale = 1.0f + (_currentLiftProgress * 0.3f);
|
||||
}
|
||||
|
||||
private void HandleFocusRelease()
|
||||
{
|
||||
if (_isLifting)
|
||||
{
|
||||
_isLifting = false;
|
||||
_strainPlayer.Stop();
|
||||
PlaySfx(_bank.EffortExhale);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleLiftComplete(bool success)
|
||||
{
|
||||
_strainPlayer.Stop();
|
||||
PlaySfx(success ? _bank.WinStinger : _bank.FailStinger);
|
||||
}
|
||||
|
||||
private void OnCameraTrauma(float amount)
|
||||
{
|
||||
if (amount > 0.5f) PlaySfx(_bank.CameraTrauma);
|
||||
}
|
||||
|
||||
private void OnHazardResolved(HazardType _)
|
||||
{
|
||||
PlaySfx(_bank.HazardClear);
|
||||
}
|
||||
|
||||
private void OnOnHazardSpawned(HazardType _)
|
||||
{
|
||||
PlaySfx(_bank.HazardSpawn);
|
||||
}
|
||||
|
||||
private void OnLiftProgress(float progress)
|
||||
{
|
||||
_currentLiftProgress = progress;
|
||||
}
|
||||
|
||||
private void OnFocusChanged(float focus)
|
||||
{
|
||||
if (focus < 0.1f)
|
||||
{
|
||||
_heartbeatPlayer.VolumeDb = -80f;
|
||||
}
|
||||
else
|
||||
{
|
||||
var t = (focus - 0.1f) / 0.9f;
|
||||
_heartbeatPlayer.VolumeDb = Mathf.Lerp(-30f, 5f, t);
|
||||
|
||||
_heartbeatPlayer.PitchScale = Mathf.Lerp(1.0f, 1.4f, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
1
Code/Systems/SoundManager.cs.uid
Normal file
1
Code/Systems/SoundManager.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://khh8yy66oq36
|
||||
65
Code/Systems/TunnelSystem.cs
Normal file
65
Code/Systems/TunnelSystem.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using Godot;
|
||||
using MaxEffort.Code.Core;
|
||||
using MaxEffort.Code.Data;
|
||||
|
||||
namespace MaxEffort.Code.Systems;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class TunnelSystem : Node
|
||||
{
|
||||
[Export] private TunnelConfig _config;
|
||||
[Export] private ColorRect _vignetteOverlay;
|
||||
|
||||
private float _currentFocus = 0f;
|
||||
private bool _isEfforting = false;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
EventBus.OnLiftEffortApplied += HandleEffort;
|
||||
EventBus.OnFocusRelease += HandleRelease;
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
EventBus.OnLiftEffortApplied -= HandleEffort;
|
||||
EventBus.OnFocusRelease -= HandleRelease;
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
var dt = (float)delta;
|
||||
|
||||
if (_isEfforting)
|
||||
{
|
||||
_currentFocus += _config.VisionNarrowRate * dt;
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentFocus -= _config.VisionRecoverRate * dt;
|
||||
}
|
||||
|
||||
_currentFocus = Mathf.Clamp(_currentFocus, 0f, _config.MaxTunnelIntensity);
|
||||
|
||||
var visualValue = _config.VisionCurve?.Sample(_currentFocus) ?? _currentFocus;
|
||||
|
||||
if (_vignetteOverlay != null)
|
||||
{
|
||||
var mat = _vignetteOverlay.Material as ShaderMaterial;
|
||||
mat?.SetShaderParameter("vignette_intensity", visualValue);
|
||||
}
|
||||
|
||||
EventBus.PublishFocusChanged(_currentFocus);
|
||||
|
||||
_isEfforting = false;
|
||||
}
|
||||
|
||||
private void HandleRelease()
|
||||
{
|
||||
_isEfforting = false;
|
||||
}
|
||||
|
||||
private void HandleEffort(float effortDelta)
|
||||
{
|
||||
_isEfforting = true;
|
||||
}
|
||||
}
|
||||
1
Code/Systems/TunnelSystem.cs.uid
Normal file
1
Code/Systems/TunnelSystem.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dnmsuw3knxhp0
|
||||
Reference in New Issue
Block a user