Audio system and rich text

This commit is contained in:
Rendo 2025-06-29 14:28:51 +05:00
commit 68cafff083
161 changed files with 1605 additions and 255 deletions

View file

@ -29,6 +29,7 @@ public partial class Sun : Area2D
_fade.Stop();
scoring = true;
GetNode<ChannelPlayer>("SunPlayer").Play();
var tween = CreateTween();
tween.TweenInterval(0.1);
tween.TweenProperty(this, "global_position", GetCanvasTransform().AffineInverse() * (Counter.GlobalPosition + Counter.PivotOffset), 0.5).SetTrans(Tween.TransitionType.Cubic).SetEase(Tween.EaseType.Out);

View file

@ -14,9 +14,9 @@ public class Utility
//
#region Enums
public enum EffectSlots {FREEZE, STUN, POISON, GARLIC};
public enum DamageTypes {PHYSICAL, ICE};
public enum EffectSlots { FREEZE, STUN, POISON, GARLIC };
public enum DamageTypes { PHYSICAL, ICE };
#endregion
@ -25,9 +25,12 @@ public class Utility
public const int TileWidth = 50;
public const int TileHeight = 60;
public const int LayersCount = 3;
public static Vector2I LeftFieldBoundary = new(305,76);
public static Vector2I RightFieldBoundary = new(755,376);
public static Vector2I LeftFieldBoundary = new(305, 76);
public static Vector2I RightFieldBoundary = new(755, 376);
public static readonly Vector2 Tile = new(TileWidth, TileHeight);
public static double SFX = 1.0f;
public static double Music = 1.0f;
}

View file

@ -0,0 +1,85 @@
using Godot;
using Godot.Collections;
public partial class AudioSequencer : Node
{
private static AudioSequencer instance;
private Dictionary<string, AudioStreamPlayer> channels = [];
private Dictionary<string, bool> channelProcess = [];
private Dictionary<string, ChannelSettings> channelSettings = [];
[Export]
private ChannelSettings standardSettings;
public override void _Ready()
{
instance = this;
}
public static bool IsChannelPlaying(string id)
{
if (instance.channels.ContainsKey(id) == false)
{
instance.InitiateChannel(id);
return false;
}
if (instance.channelSettings[id].restartTreshold == 0)
return false;
if (instance.channelSettings[id].restartTreshold < 0)
return instance.channelProcess[id];
return instance.channelProcess[id] && instance.channels[id].GetPlaybackPosition() < instance.channelSettings[id].restartTreshold / Engine.TimeScale;
}
public static void Play(string id, AudioStream what)
{
if (IsChannelPlaying(id)) return;
instance.PlayAtChannel(id, what);
}
public static void ChangeSettings(string id, ChannelSettings settings)
{
if (instance.channels.ContainsKey(id) == false)
{
instance.InitiateChannel(id, settings);
return;
}
instance.channelSettings[id] = settings;
}
private AudioStreamPlayer InitiateChannel(string id, ChannelSettings settings = null)
{
AudioStreamPlayer player = new();
AddChild(player);
channels.Add(id, player);
channelProcess.Add(id, false);
player.Name = id;
player.Finished += () => { MarkChannel(id, false); };
if (settings != null)
{
channelSettings.Add(id, settings);
}
else
{
channelSettings.Add(id, standardSettings);
}
return player;
}
private void PlayAtChannel(string id, AudioStream what)
{
channels[id].Stream = what;
channels[id].Play();
MarkChannel(id, true);
}
private void MarkChannel(string id, bool how)
{
channelProcess[id] = how;
}
}

View file

@ -0,0 +1 @@
uid://cauc1ieq84fwg

View file

@ -0,0 +1,43 @@
using Godot;
using Newlon;
public partial class AudioSlider : HSlider
{
enum TYPE
{
SFX,
MUSIC
}
[Export] private TYPE affects;
public override void _Ready()
{
DragEnded += OnDragEnded;
if (affects == TYPE.SFX)
{
SetValueNoSignal(Utility.SFX);
}
else
{
SetValueNoSignal(Utility.Music);
}
}
private void OnDragEnded(bool hasChanged)
{
if (hasChanged)
{
if (affects == TYPE.SFX)
{
Utility.SFX = Value;
AudioServer.SetBusVolumeDb(0, Mathf.LinearToDb((float)Value));
}
else
{
Utility.Music = Value;
AudioServer.SetBusVolumeDb(1, Mathf.LinearToDb((float)Value));
}
}
}
}

View file

@ -0,0 +1 @@
uid://ciccaxqo70s13

View file

@ -0,0 +1,16 @@
using Godot;
[GlobalClass]
public partial class ChannelPlayer : Node
{
[Export] private ChannelSettings settings;
[Export] private AudioStream audioStream;
[Export] private string channel;
public void Play()
{
AudioSequencer.Play(channel, audioStream);
if (settings != null)
AudioSequencer.ChangeSettings(channel, settings);
}
}

View file

@ -0,0 +1 @@
uid://c36bj8u7jghc7

View file

@ -0,0 +1,35 @@
using Godot;
using Godot.Collections;
[GlobalClass]
public partial class ChannelPlaylist : Node
{
[Export] private Array<AudioStream> playlist;
[Export] private Array<string> channels;
[Export] private ChannelSettings channelSettings;
private int internal_index = 0;
public void Play()
{
AudioSequencer.Play(channels[internal_index], playlist[internal_index]);
}
public void SetIndex(int index)
{
internal_index = index;
if (internal_index >= playlist.Count)
{
internal_index = 0;
}
else if (internal_index < 0)
{
internal_index = playlist.Count - 1;
}
}
public void Next()
{
SetIndex(internal_index + 1);
}
}

View file

@ -0,0 +1 @@
uid://cnn0ymuhypdff

View file

@ -0,0 +1,7 @@
using Godot;
[GlobalClass]
public partial class ChannelSettings : Resource
{
[Export] public float restartTreshold;
}

View file

@ -0,0 +1 @@
uid://c1x4n4nqyq72f

View file

@ -8,7 +8,7 @@ public partial class Previewport : SubViewport
private RuntimePlantData current_display;
[Export] private Label title;
[Export] private Label description;
[Export] private RichTextLabel description;
public override void _Ready()
{

View file

@ -8,7 +8,7 @@ public class ChoosableHandler : SeedpacketHandler, ISeedpacketPress
public void Pressed()
{
if(LevelGUIElements.Instance.SeedpacketsHotbar.GetChildCount() > 9) return;
if (LevelGUIElements.Instance.SeedpacketsHotbar.GetChildCount() > 9) return;
_owner.disablePacket = true;
var hotbarSeedpacket = Seedpacket.Prefab.Instantiate<Seedpacket>();
@ -18,6 +18,8 @@ public class ChoosableHandler : SeedpacketHandler, ISeedpacketPress
var pregameHandler = new HotbarPregameHandler(hotbarSeedpacket);
hotbarSeedpacket.SetHandler(pregameHandler);
pregameHandler.Clicked += OnHotbarClicked;
AudioSequencer.Play("tap", Seedpacket.TapStream);
}
public void OnHotbarClicked()

View file

@ -10,7 +10,8 @@ public class HotbarHandler : SeedpacketHandler, ISeedpacketPress, ISeedpacketPro
public void Pressed()
{
PlantField.Instance.SetPlant(_owner,_owner.GetPlantResource());
PlantField.Instance.SetPlant(_owner, _owner.GetPlantResource());
AudioSequencer.Play("lift_seed", Seedpacket.LiftStream);
}
public void Process()

View file

@ -16,6 +16,7 @@ public class HotbarPregameHandler : SeedpacketHandler, ISeedpacketPress
{
if (Clicked != null) Clicked();
_owner.QueueFree();
AudioSequencer.Play("tap", Seedpacket.UntapStream);
}
public void OnLevelStateChanged(RuntimeLevelData.LevelStates state)

View file

@ -4,6 +4,9 @@ namespace Newlon.Components.GUI.Seedpackets;
public partial class Seedpacket : TextureButton
{
public static AudioStream TapStream;
public static AudioStream UntapStream;
public static AudioStream LiftStream;
private const string PATH_TO_PACKED_SCENE = "res://scenes/gui/seedpacket.tscn";
private PlantResource _resource;
private Label _cost;
@ -18,8 +21,14 @@ public partial class Seedpacket : TextureButton
// Node overrides
public override void _Ready()
{
if (TapStream == null)
{
TapStream = ResourceLoader.Load<AudioStream>("res://assets/audio/gui/tap.mp3");
UntapStream = ResourceLoader.Load<AudioStream>("res://assets/audio/gui/tap2.mp3");
LiftStream = ResourceLoader.Load<AudioStream>("res://assets/audio/gui/seedlift.mp3");
}
if (_resource != null)
UpdateContents();
UpdateContents();
if (Prefab == null)
{
Prefab = ResourceLoader.Load<PackedScene>(PATH_TO_PACKED_SCENE);

View file

@ -10,6 +10,7 @@ public partial class PlantField : Node2D
private PlantResource _resource;
private Seedpacket _slot;
private bool _previousCanPlace;
private ChannelPlayer player;
[Export] private PackedScene particles;
public static PlantField Instance {get; private set;}
@ -17,6 +18,7 @@ public partial class PlantField : Node2D
{
Instance = this;
_plantSetter = GetChild<Node2D>(0);
player = GetNode<ChannelPlayer>("PlantPlayer");
}
public void SetPlant(Seedpacket slot, PlantResource plant)
@ -99,6 +101,8 @@ public partial class PlantField : Node2D
PoolContainer.Instance.SpawnParticles(particles, plant.GlobalPosition + Vector2.Down * Utility.TileHeight/2.0f);
player.Play();
// Unfocusing and recharging slot
_slot.Recharge();
}

View file

@ -5,11 +5,10 @@ using Newlon.Components.Zombies;
public partial class FallParticle : RigidBody2D
{
[Export] private RuntimeZombieData data;
[Export] private Timer deathTimer;
[Export] private Vector2 falloffImpulseMin = Vector2.Zero;
[Export] private Vector2 falloffImpulseMax = Vector2.Zero;
[Export] private Vector2 falloffOffsetMin = Vector2.Zero;
[Export] private Vector2 falloffOffsetMax = Vector2.Zero;
[Export] private float minAngle;
[Export] private float maxAngle;
[Export] private float minTorque;
[Export] private float maxTorque;
[Export] private float Impulse;
public void FallOff()
{
@ -21,14 +20,14 @@ public partial class FallParticle : RigidBody2D
Callable.From(() =>
{
Reparent(PoolContainer.Instance.Zombies);
ApplyImpulse(RandomVector(falloffImpulseMin, falloffImpulseMax).Normalized() * Impulse, RandomVector(falloffOffsetMin, falloffOffsetMax));
float rng_angle = Mathf.DegToRad((float)GD.RandRange(minAngle, maxAngle));
float rng_torque = Mathf.DegToRad((float)GD.RandRange(minTorque, maxTorque));
ApplyImpulse(new Vector2(Mathf.Sin(rng_angle) * Impulse, Mathf.Cos(rng_angle) * Impulse));
ApplyTorqueImpulse(rng_torque);
}).CallDeferred();
deathTimer.Start();
}
private Vector2 RandomVector(Vector2 min, Vector2 max)
{
return new Vector2((float)GD.RandRange(min.X,max.X),(float)GD.RandRange(min.Y,max.Y));
var tween = CreateTween();
tween.TweenInterval(4.0);
tween.TweenProperty(this, "modulate", new Color(Modulate.R, Modulate.G, Modulate.B, 0f), 1.0);
tween.TweenCallback(Callable.From(QueueFree));
}
}

View file

@ -19,6 +19,7 @@ public partial class ExplosionComponent : Area2D
PoolContainer.Instance.SpawnParticles(particles, GetParent<RuntimePlantData>().GlobalPosition);
GetNode<ChannelPlayer>("ExplosionPlayer").Play();
GetParent<RuntimePlantData>().Kill();
}

View file

@ -17,12 +17,15 @@ public partial class RuntimePlantData : Node2D, IEntity
public int Line { get; set; }
public PlantResource Resource;
private AudioStream eatenSound;
[Signal]
public delegate void OnHPChangedEventHandler(int amount, Node origin);
public override void _Ready()
{
_hp = _maxHP;
eatenSound = ResourceLoader.Load<AudioStream>("res://assets/audio/sfx/gulp.mp3");
}
public virtual void Heal(int amount, Node origin)
@ -46,6 +49,7 @@ public partial class RuntimePlantData : Node2D, IEntity
if (_hp <= 0)
{
Kill();
AudioSequencer.Play("plant_eaten", eatenSound);
}
}
public virtual void Kill()

View file

@ -8,6 +8,8 @@ public partial class RuntimeZombieData : Node2D, IEntity, ILocalTimescale, IEffe
{
[Signal]
public delegate void OnHPChangedEventHandler(int deltaHP, Node origin);
[Signal]
public delegate void OnDamagedEventHandler();
[Signal]
public delegate void OnLocalTimescaleChangedEventHandler(int currentTimescale);
@ -18,6 +20,11 @@ public partial class RuntimeZombieData : Node2D, IEntity, ILocalTimescale, IEffe
private int _maxHP;
[Export]
private Armor _armor;
[Export]
private AudioStream garlicSound;
[Export]
private AudioStream freezeSound;
private float _localTimescale = 1.0f;
@ -61,7 +68,6 @@ public partial class RuntimeZombieData : Node2D, IEntity, ILocalTimescale, IEffe
else
_hp += amount;
EmitSignal(SignalName.OnHPChanged,amount,origin);
if (MaxHp > 0)
{
@ -78,6 +84,7 @@ public partial class RuntimeZombieData : Node2D, IEntity, ILocalTimescale, IEffe
else
_hp -= amount;
EmitSignal(SignalName.OnHPChanged,-amount, origin);
EmitSignal(SignalName.OnDamaged);
if (_hp <= 0)
{
@ -94,11 +101,27 @@ public partial class RuntimeZombieData : Node2D, IEntity, ILocalTimescale, IEffe
public void GiveEffect(Effect what)
{
int slot = (int)what.Slot;
if(_activeEffectSlots[slot] != null)
if (_activeEffectSlots[slot] == null)
{
_effectSlotTimers[slot].Stop();
_activeEffectSlots[slot].Exit(this);
switch (what.Slot)
{
case Utility.EffectSlots.FREEZE:
AudioSequencer.Play("zombie_freeze", freezeSound);
var settings = new ChannelSettings();
settings.restartTreshold = -1;
AudioSequencer.ChangeSettings("zombie_freeze",settings);
break;
case Utility.EffectSlots.GARLIC:
AudioSequencer.Play("zombie_garlic", garlicSound);
break;
}
}
if (_activeEffectSlots[slot] != null)
{
_effectSlotTimers[slot].Stop();
_activeEffectSlots[slot].Exit(this);
}
_effectSlotTimers[slot].WaitTime = what.Duration;
_effectSlotTimers[slot].Start();

View file

@ -11,11 +11,11 @@ public partial class SlownessEffect : Effect
public override void Enter(Node target)
{
if(target is IEffectHandler handler)
if (target is IEffectHandler handler)
{
if(target is ILocalTimescale timescalable)
if (target is ILocalTimescale timescalable)
timescalable.LocalTimescale *= Multiplier;
if(target is CanvasItem canvasItem)
if (target is CanvasItem canvasItem)
canvasItem.Modulate = ColorOverride;
}
}