✨ New sfx and vfx and features in building
This commit is contained in:
@@ -19,6 +19,11 @@ public partial class PlacementManager : Node2D
|
|||||||
private static readonly List<string> BuildableTiles = ["wall", "miner"];
|
private static readonly List<string> BuildableTiles = ["wall", "miner"];
|
||||||
private readonly Dictionary<Node2D, BuildTask> _buildTasks = new();
|
private readonly Dictionary<Node2D, BuildTask> _buildTasks = new();
|
||||||
private AudioStreamPlayer _completionSound;
|
private AudioStreamPlayer _completionSound;
|
||||||
|
private AudioStreamPlayer _buildingSound;
|
||||||
|
private AudioStreamPlayer _canceledSound;
|
||||||
|
private AudioStreamPlayer _cannotDeploySound;
|
||||||
|
private AudioStreamPlayer _notReadySound;
|
||||||
|
private AudioStreamPlayer _insufficientFundsSound;
|
||||||
private Node2D _currentGhost; // Keep track of the current ghost building
|
private Node2D _currentGhost; // Keep track of the current ghost building
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
@@ -26,13 +31,25 @@ public partial class PlacementManager : Node2D
|
|||||||
base._Ready();
|
base._Ready();
|
||||||
|
|
||||||
// Setup completion sound
|
// Setup completion sound
|
||||||
_completionSound = new AudioStreamPlayer();
|
_completionSound = CreateAudioPlayer("res://Sounds/Events/ConstructionComplete.wav");
|
||||||
AddChild(_completionSound);
|
_buildingSound = CreateAudioPlayer("res://Sounds/Events/Building.wav");
|
||||||
var sound = GD.Load<AudioStream>("res://Sounds/Events/ConstructionComplete.wav");
|
_canceledSound = CreateAudioPlayer("res://Sounds/Events/Canceled.wav");
|
||||||
|
_cannotDeploySound = CreateAudioPlayer("res://Sounds/Events/CannotDeployHere.wav");
|
||||||
|
_notReadySound = CreateAudioPlayer("res://Sounds/Events/NotReady.wav");
|
||||||
|
_insufficientFundsSound = CreateAudioPlayer("res://Sounds/Events/InsufficientFunds.wav");
|
||||||
|
}
|
||||||
|
|
||||||
|
private AudioStreamPlayer CreateAudioPlayer(string path)
|
||||||
|
{
|
||||||
|
var player = new AudioStreamPlayer();
|
||||||
|
AddChild(player);
|
||||||
|
var sound = GD.Load<AudioStream>(path);
|
||||||
if (sound != null)
|
if (sound != null)
|
||||||
{
|
{
|
||||||
_completionSound.Stream = sound;
|
player.Stream = sound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return player;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBuildCompleted()
|
private void OnBuildCompleted()
|
||||||
@@ -194,24 +211,25 @@ public partial class PlacementManager : Node2D
|
|||||||
_ghostBuilding.SetGhostMode(canPlace);
|
_ghostBuilding.SetGhostMode(canPlace);
|
||||||
|
|
||||||
// Left click to place
|
// Left click to place
|
||||||
if (Input.IsActionPressed("build_tile") && canPlace)
|
if (Input.IsActionPressed("build_tile"))
|
||||||
{
|
{
|
||||||
var building = Registry.GetBuilding(_currentBuildingId);
|
var building = Registry.GetBuilding(_currentBuildingId);
|
||||||
if (building == null) return;
|
if (building == null) return;
|
||||||
|
|
||||||
if (!CanStartNewBuild())
|
if (!CanStartNewBuild())
|
||||||
{
|
{
|
||||||
// Optionally show feedback to player that build queue is full
|
_notReadySound.Play();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Consume resources first
|
// Consume resources first
|
||||||
if (!ConsumeBuildingResources(_currentBuildingId))
|
if (!ConsumeBuildingResources(_currentBuildingId))
|
||||||
{
|
{
|
||||||
// Optionally show feedback to player that they can't afford this building
|
_insufficientFundsSound.Play();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create the building instance first
|
||||||
var scene = building.Scene;
|
var scene = building.Scene;
|
||||||
var buildingInstance = (BaseTile)scene.Instantiate();
|
var buildingInstance = (BaseTile)scene.Instantiate();
|
||||||
buildingInstance.RotationDegrees = _currentRotation;
|
buildingInstance.RotationDegrees = _currentRotation;
|
||||||
@@ -219,23 +237,30 @@ public partial class PlacementManager : Node2D
|
|||||||
buildingInstance.Position = _ghostBuilding.Position;
|
buildingInstance.Position = _ghostBuilding.Position;
|
||||||
AddChild(buildingInstance);
|
AddChild(buildingInstance);
|
||||||
|
|
||||||
// Check if area is free before placing
|
// First check if area is free
|
||||||
if (!IsAreaFree(_hoveredCell, building.Size, _currentRotation, building.Layer))
|
if (!IsAreaFree(_hoveredCell, building.Size, _currentRotation, building.Layer))
|
||||||
{
|
{
|
||||||
|
_cannotDeploySound.Play();
|
||||||
RefundBuildingResources(_currentBuildingId);
|
RefundBuildingResources(_currentBuildingId);
|
||||||
buildingInstance.QueueFree();
|
buildingInstance.QueueFree();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Occupy the area
|
// If we get here, area is free, so we can safely occupy it
|
||||||
Grid.OccupyArea(_hoveredCell, buildingInstance, building.Size, _currentRotation, building.Layer);
|
Grid.OccupyArea(_hoveredCell, buildingInstance, building.Size, _currentRotation, building.Layer);
|
||||||
|
|
||||||
if (building.BuildTime > 0f)
|
if (building.BuildTime > 0f)
|
||||||
{
|
{
|
||||||
|
var wasQueueEmpty = _buildTasks.Count == 0;
|
||||||
var buildTask = new BuildTask(OnBuildCompleted);
|
var buildTask = new BuildTask(OnBuildCompleted);
|
||||||
_buildTasks[buildingInstance] = buildTask;
|
_buildTasks[buildingInstance] = buildTask;
|
||||||
|
|
||||||
buildingInstance.StartConstruction(building.BuildTime, () => {
|
// Play building sound only when adding to an empty queue
|
||||||
|
if (wasQueueEmpty)
|
||||||
|
_buildingSound.Play();
|
||||||
|
|
||||||
|
buildingInstance.StartConstruction(building.BuildTime, () =>
|
||||||
|
{
|
||||||
// On construction complete
|
// On construction complete
|
||||||
if (_buildTasks.TryGetValue(buildingInstance, out var task))
|
if (_buildTasks.TryGetValue(buildingInstance, out var task))
|
||||||
{
|
{
|
||||||
@@ -245,6 +270,7 @@ public partial class PlacementManager : Node2D
|
|||||||
Grid.FreeArea(_hoveredCell, building.Size, _currentRotation, building.Layer);
|
Grid.FreeArea(_hoveredCell, building.Size, _currentRotation, building.Layer);
|
||||||
buildingInstance.QueueFree();
|
buildingInstance.QueueFree();
|
||||||
}
|
}
|
||||||
|
|
||||||
task.Complete();
|
task.Complete();
|
||||||
_buildTasks.Remove(buildingInstance);
|
_buildTasks.Remove(buildingInstance);
|
||||||
}
|
}
|
||||||
@@ -258,9 +284,28 @@ public partial class PlacementManager : Node2D
|
|||||||
// Right click to destroy from current layer
|
// Right click to destroy from current layer
|
||||||
var building = Grid.GetBuildingAtCell(_hoveredCell);
|
var building = Grid.GetBuildingAtCell(_hoveredCell);
|
||||||
if (building == null) return;
|
if (building == null) return;
|
||||||
|
|
||||||
// Find all cells occupied by this building
|
// Find all cells occupied by this building
|
||||||
var buildingInfo = Grid.GetBuildingInfoAtCell(_hoveredCell, GridLayer.Building);
|
var buildingInfo = Grid.GetBuildingInfoAtCell(_hoveredCell, GridLayer.Building);
|
||||||
if (buildingInfo == null) return;
|
if (buildingInfo == null) return;
|
||||||
|
|
||||||
|
// Check if this building is in the build tasks (under construction)
|
||||||
|
if (_buildTasks.TryGetValue(building, out var buildTask))
|
||||||
|
{
|
||||||
|
var buildingTile = building as BaseTile;
|
||||||
|
// Cancel the build task
|
||||||
|
buildTask.Complete(true); // Mark as cancelled
|
||||||
|
_buildTasks.Remove(building);
|
||||||
|
_canceledSound.Play();
|
||||||
|
if (buildingTile == null) return;
|
||||||
|
|
||||||
|
// Refund resources for canceled build
|
||||||
|
var buildingData = Registry.GetBuilding(buildingTile.TileId);
|
||||||
|
if (buildingData != null)
|
||||||
|
RefundBuildingResources(buildingTile.TileId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up the building and grid
|
||||||
building.QueueFree();
|
building.QueueFree();
|
||||||
Grid.FreeArea(buildingInfo.Value.Position, buildingInfo.Value.Size, buildingInfo.Value.Rotation);
|
Grid.FreeArea(buildingInfo.Value.Position, buildingInfo.Value.Size, buildingInfo.Value.Rotation);
|
||||||
}
|
}
|
||||||
|
@@ -14,6 +14,7 @@ public partial class BaseTile : Node2D
|
|||||||
private Sprite2D _sprite;
|
private Sprite2D _sprite;
|
||||||
private ColorRect _progressOverlay;
|
private ColorRect _progressOverlay;
|
||||||
private Action _onConstructionComplete;
|
private Action _onConstructionComplete;
|
||||||
|
private bool _isConstructing = false;
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
@@ -26,6 +27,9 @@ public partial class BaseTile : Node2D
|
|||||||
|
|
||||||
public void SetGhostMode(bool canPlace)
|
public void SetGhostMode(bool canPlace)
|
||||||
{
|
{
|
||||||
|
// Don't modify collision for constructing buildings
|
||||||
|
if (_isConstructing) return;
|
||||||
|
|
||||||
if (_collisionShape != null)
|
if (_collisionShape != null)
|
||||||
_collisionShape.Disabled = true;
|
_collisionShape.Disabled = true;
|
||||||
|
|
||||||
@@ -46,8 +50,13 @@ public partial class BaseTile : Node2D
|
|||||||
// Building progress visualization
|
// Building progress visualization
|
||||||
public void StartConstruction(float buildTime, Action onComplete = null)
|
public void StartConstruction(float buildTime, Action onComplete = null)
|
||||||
{
|
{
|
||||||
|
_isConstructing = true;
|
||||||
|
if (_collisionShape != null)
|
||||||
|
_collisionShape.Disabled = true;
|
||||||
|
|
||||||
if (_progressOverlay == null || _sprite?.Texture == null)
|
if (_progressOverlay == null || _sprite?.Texture == null)
|
||||||
{
|
{
|
||||||
|
_isConstructing = false;
|
||||||
onComplete?.Invoke();
|
onComplete?.Invoke();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -55,6 +64,10 @@ public partial class BaseTile : Node2D
|
|||||||
_onConstructionComplete = onComplete;
|
_onConstructionComplete = onComplete;
|
||||||
var texSize = new Vector2(GridUtils.TileSize, GridUtils.TileSize);
|
var texSize = new Vector2(GridUtils.TileSize, GridUtils.TileSize);
|
||||||
|
|
||||||
|
// Set initial transparency for construction
|
||||||
|
if (_sprite != null)
|
||||||
|
_sprite.Modulate = new Color(1, 1, 1, 0.8f);
|
||||||
|
|
||||||
_progressOverlay.Visible = true;
|
_progressOverlay.Visible = true;
|
||||||
_progressOverlay.Modulate = Colors.White;
|
_progressOverlay.Modulate = Colors.White;
|
||||||
_progressOverlay.Color = new Color(0, 0, 1, 0.4f); // semi-transparent blue
|
_progressOverlay.Color = new Color(0, 0, 1, 0.4f); // semi-transparent blue
|
||||||
@@ -80,7 +93,14 @@ public partial class BaseTile : Node2D
|
|||||||
// Fade out the overlay
|
// Fade out the overlay
|
||||||
await FadeOutOverlay(0.5f);
|
await FadeOutOverlay(0.5f);
|
||||||
|
|
||||||
// Notify completion
|
// Construction complete - restore full opacity and enable collision
|
||||||
|
if (_sprite != null)
|
||||||
|
_sprite.Modulate = Colors.White;
|
||||||
|
|
||||||
|
_isConstructing = false;
|
||||||
|
if (_collisionShape != null)
|
||||||
|
_collisionShape.Disabled = false;
|
||||||
|
|
||||||
_onConstructionComplete?.Invoke();
|
_onConstructionComplete?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BIN
Sounds/Events/Building.wav
Normal file
BIN
Sounds/Events/Building.wav
Normal file
Binary file not shown.
24
Sounds/Events/Building.wav.import
Normal file
24
Sounds/Events/Building.wav.import
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://d1trbqrntmuij"
|
||||||
|
path="res://.godot/imported/Building.wav-b8766581fd25a206c63f47b13bd2e2f5.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Sounds/Events/Building.wav"
|
||||||
|
dest_files=["res://.godot/imported/Building.wav-b8766581fd25a206c63f47b13bd2e2f5.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
BIN
Sounds/Events/Canceled.wav
Normal file
BIN
Sounds/Events/Canceled.wav
Normal file
Binary file not shown.
24
Sounds/Events/Canceled.wav.import
Normal file
24
Sounds/Events/Canceled.wav.import
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://chn8ux4two1kd"
|
||||||
|
path="res://.godot/imported/Canceled.wav-6d441d9a898b5cd9be4c0664a1f489ed.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Sounds/Events/Canceled.wav"
|
||||||
|
dest_files=["res://.godot/imported/Canceled.wav-6d441d9a898b5cd9be4c0664a1f489ed.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
BIN
Sounds/Events/CannotDeployHere.wav
Normal file
BIN
Sounds/Events/CannotDeployHere.wav
Normal file
Binary file not shown.
24
Sounds/Events/CannotDeployHere.wav.import
Normal file
24
Sounds/Events/CannotDeployHere.wav.import
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://7u1gw7lt5xd1"
|
||||||
|
path="res://.godot/imported/CannotDeployHere.wav-a9ec27508654f74d03ac7b8c8037059c.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Sounds/Events/CannotDeployHere.wav"
|
||||||
|
dest_files=["res://.godot/imported/CannotDeployHere.wav-a9ec27508654f74d03ac7b8c8037059c.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
BIN
Sounds/Events/InsufficientFunds.wav
Normal file
BIN
Sounds/Events/InsufficientFunds.wav
Normal file
Binary file not shown.
24
Sounds/Events/InsufficientFunds.wav.import
Normal file
24
Sounds/Events/InsufficientFunds.wav.import
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://bemxvqcettqgp"
|
||||||
|
path="res://.godot/imported/InsufficientFunds.wav-7aba215cb1cd04a5285a5e9908999906.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Sounds/Events/InsufficientFunds.wav"
|
||||||
|
dest_files=["res://.godot/imported/InsufficientFunds.wav-7aba215cb1cd04a5285a5e9908999906.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
BIN
Sounds/Events/NotReady.wav
Normal file
BIN
Sounds/Events/NotReady.wav
Normal file
Binary file not shown.
24
Sounds/Events/NotReady.wav.import
Normal file
24
Sounds/Events/NotReady.wav.import
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="wav"
|
||||||
|
type="AudioStreamWAV"
|
||||||
|
uid="uid://dogbl6tfealwg"
|
||||||
|
path="res://.godot/imported/NotReady.wav-2cfa5f22feed110ca411d6478060fdea.sample"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://Sounds/Events/NotReady.wav"
|
||||||
|
dest_files=["res://.godot/imported/NotReady.wav-2cfa5f22feed110ca411d6478060fdea.sample"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
force/8_bit=false
|
||||||
|
force/mono=false
|
||||||
|
force/max_rate=false
|
||||||
|
force/max_rate_hz=44100
|
||||||
|
edit/trim=false
|
||||||
|
edit/normalize=false
|
||||||
|
edit/loop_mode=0
|
||||||
|
edit/loop_begin=0
|
||||||
|
edit/loop_end=-1
|
||||||
|
compress/mode=2
|
Reference in New Issue
Block a user