Complete C# rewrite with working game in Editor (#6)
* Refactor collectable components to C# and update resource scripts for consistency * Update resource paths and refactor properties for consistency * Refactor UI components to inherit from Control and update node paths for consistency * Update node paths and group assignments for consistency across scenes * Refactor GameManager and PlayerDeathComponent for improved state management and logging; update scene connections for player death handling * Add PhantomCamera components and UI elements for improved scene management; refactor existing components for better integration * Refactor skill components and update resource paths for consistency; enhance skill management in scenes * Add new UID files and update scene configurations for dialogue components; refactor skill management and input handling * Add next level command and refactor player retrieval in GameManager; update scene files for consistency * Add skill upgrade system and refactor skill components for enhanced functionality; update resource paths and configurations * Enhance ChargeProgressBar and Marketplace functionality; add owner exit handling and update skill button states * Refactor ChargeProgressBar and SkillManager; update skill handling and improve component interactions * Refactor player and level configurations; streamline FlipPlayerComponent and reposition Spaceship Enter
This commit is contained in:
@@ -4,7 +4,7 @@ using Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
public partial class BrickThrowComponent : Node
|
||||
public partial class BrickThrowComponent : Node, ISkill
|
||||
{
|
||||
[Export] public PackedScene BrickScene { get; set; }
|
||||
[Export] public float FireRate { get; set; } = 1.0f;
|
||||
@@ -13,13 +13,22 @@ public partial class BrickThrowComponent : Node
|
||||
|
||||
private bool _canThrow = true;
|
||||
private Timer _timer;
|
||||
private SkillData _skillData;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
SetupTimer();
|
||||
_canThrow = true;
|
||||
}
|
||||
|
||||
if (ThrowInputBehavior != null) ThrowInputBehavior.ThrowRequested += ThrowBrick;
|
||||
public override void _ExitTree()
|
||||
{
|
||||
if (ThrowInputBehavior != null) ThrowInputBehavior.ThrowRequested -= ThrowBrick;
|
||||
if (_timer != null)
|
||||
{
|
||||
_timer.Timeout -= OnTimerTimeout;
|
||||
_timer.QueueFree();
|
||||
}
|
||||
}
|
||||
|
||||
public override void _Input(InputEvent @event)
|
||||
@@ -34,10 +43,12 @@ public partial class BrickThrowComponent : Node
|
||||
|
||||
private void SetupTimer()
|
||||
{
|
||||
_timer = new Timer();
|
||||
_timer.WaitTime = FireRate;
|
||||
_timer.OneShot = false;
|
||||
_timer.Autostart = false;
|
||||
_timer.Timeout += OnTimerTimeout;
|
||||
AddChild(_timer);
|
||||
}
|
||||
|
||||
private void OnTimerTimeout()
|
||||
@@ -52,19 +63,59 @@ public partial class BrickThrowComponent : Node
|
||||
|
||||
var instance = BrickScene.Instantiate<Node2D>();
|
||||
var init = instance.GetNodeOrNull<ProjectileInitComponent>("ProjectileInitComponent");
|
||||
|
||||
if (init != null && PlayerController.CurrentMovement is PlatformMovementComponent)
|
||||
{
|
||||
init.Initialize(new ProjectileInitParams()
|
||||
var @params = new ProjectileInitParams()
|
||||
{
|
||||
Position = PlayerController.GlobalPosition,
|
||||
Rotation = PlayerController.Rotation,
|
||||
Direction = PlayerController.CurrentMovement.LastDirection,
|
||||
PowerMultiplier = powerMultiplier,
|
||||
});
|
||||
};
|
||||
|
||||
init.Initialize(@params);
|
||||
}
|
||||
|
||||
GetTree().CurrentScene.AddChild(instance);
|
||||
_canThrow = false;
|
||||
_timer.Start();
|
||||
}
|
||||
|
||||
public void Initialize(Node owner, SkillData data)
|
||||
{
|
||||
PlayerController = owner as PlayerController;
|
||||
_skillData = data;
|
||||
|
||||
ThrowInputBehavior = (ThrowInputResource)ThrowInputBehavior?.Duplicate();
|
||||
|
||||
if (PlayerController == null)
|
||||
{
|
||||
GD.PushError("BrickThrowComponent: Owner is not a PlayerController.");
|
||||
}
|
||||
|
||||
if (_skillData.Level > 0 && _skillData.Upgrades.Count >= _skillData.Level)
|
||||
{
|
||||
ApplyUpgrade(_skillData.Upgrades[_skillData.Level - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
if (ThrowInputBehavior != null) ThrowInputBehavior.ThrowRequested += ThrowBrick;
|
||||
SetProcessInput(true);
|
||||
}
|
||||
|
||||
public void Deactivate()
|
||||
{
|
||||
if (ThrowInputBehavior != null) ThrowInputBehavior.ThrowRequested -= ThrowBrick;
|
||||
}
|
||||
|
||||
public void ApplyUpgrade(SkillUpgrade upgrade)
|
||||
{
|
||||
foreach (var property in upgrade.Properties)
|
||||
{
|
||||
Set(property.Key, property.Value);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
using Godot;
|
||||
using PhantomCamera;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
@@ -6,7 +7,6 @@ public partial class ChaseLevelComponent : Node
|
||||
{
|
||||
[Export] public float ChaseSpeed { get; set; } = 200.0f;
|
||||
[Export] public Marker2D ChaseTarget { get; set; }
|
||||
[Export] public GodotObject PhantomCamera { get; set; }
|
||||
[Export] public float MinimumDistance { get; set; } = 10f;
|
||||
|
||||
[Signal]
|
||||
@@ -17,8 +17,17 @@ public partial class ChaseLevelComponent : Node
|
||||
|
||||
private bool _isChasing = false;
|
||||
private Node2D _previousCameraFollowTarget = null;
|
||||
private PhantomCamera2D _phantomCamera = null;
|
||||
private Node2D _root = null;
|
||||
|
||||
|
||||
public override void _Process(double delta)
|
||||
public override void _Ready()
|
||||
{
|
||||
_phantomCamera = GetNode<Node2D>("../../%PhantomCamera").AsPhantomCamera2D();
|
||||
_root = Owner as Node2D;
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
if (!_isChasing) return;
|
||||
if (ChaseTarget == null) return;
|
||||
@@ -31,21 +40,22 @@ public partial class ChaseLevelComponent : Node
|
||||
|
||||
var targetPosition = ChaseTarget.GlobalPosition;
|
||||
|
||||
if (Owner is not Node2D root) return;
|
||||
if (_root == null) return;
|
||||
|
||||
var direction = (targetPosition - root.GlobalPosition).Normalized();
|
||||
root.GlobalPosition += direction * ChaseSpeed * (float)delta;
|
||||
var direction = (targetPosition - _root.GlobalPosition).Normalized();
|
||||
var speed = direction * ChaseSpeed * (float)delta;
|
||||
_root.GlobalPosition += speed;
|
||||
}
|
||||
|
||||
public void OnShipEntered()
|
||||
{
|
||||
if (ChaseTarget == null || PhantomCamera == null)
|
||||
if (ChaseTarget == null || _phantomCamera == null)
|
||||
return;
|
||||
|
||||
if (_isChasing) return;
|
||||
|
||||
_previousCameraFollowTarget = (Node2D)PhantomCamera.Call("get_follow_target");
|
||||
PhantomCamera.Call("set_follow_target", Owner as Node2D);
|
||||
_previousCameraFollowTarget = _phantomCamera.FollowTarget;
|
||||
_phantomCamera.FollowTarget = _root;
|
||||
EmitSignalChaseStarted();
|
||||
_isChasing = true;
|
||||
}
|
||||
@@ -70,9 +80,9 @@ public partial class ChaseLevelComponent : Node
|
||||
|
||||
private void StopChasing()
|
||||
{
|
||||
if (PhantomCamera == null) return;
|
||||
if (_phantomCamera == null) return;
|
||||
|
||||
PhantomCamera.Call("set_follow_target", _previousCameraFollowTarget);
|
||||
_phantomCamera.FollowTarget = _previousCameraFollowTarget;
|
||||
EmitSignalChaseStopped();
|
||||
_isChasing = false;
|
||||
}
|
||||
|
@@ -13,7 +13,7 @@ public partial class CollectableComponent : Node
|
||||
[Export] public CollectableResource Data { get; set; }
|
||||
[Export] public AudioStreamPlayer2D Sfx {get; set; }
|
||||
|
||||
[Signal] public delegate void CollectedEventHandler(Variant amount, CollectableType type, Node2D body);
|
||||
[Signal] public delegate void CollectedEventHandler(float amount, CollectableType type, Node2D body);
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
|
@@ -16,16 +16,13 @@ public partial class DamageComponent : Node
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
if (Area == null)
|
||||
if (Area != null)
|
||||
{
|
||||
GD.PushError($"DamageComponent: Area2D node is not set.");
|
||||
return;
|
||||
Area.BodyEntered += OnAreaBodyEntered;
|
||||
Area.BodyExited += OnAreaBodyExited;
|
||||
Area.AreaEntered += OnAreaAreaEntered;
|
||||
}
|
||||
|
||||
Area.BodyEntered += OnAreaBodyEntered;
|
||||
Area.BodyExited += OnAreaBodyExited;
|
||||
Area.AreaEntered += OnAreaAreaEntered;
|
||||
|
||||
if (DamageTimer != null)
|
||||
{
|
||||
DamageTimer.Timeout += OnDamageTimerTimeout;
|
||||
|
@@ -28,7 +28,7 @@ public partial class EnemyDeathComponent : Node
|
||||
|
||||
private void OnDeath()
|
||||
{
|
||||
CallDeferred(nameof(Die));
|
||||
_ = Die();
|
||||
}
|
||||
|
||||
private async Task Die()
|
||||
|
@@ -18,6 +18,8 @@ public partial class ExitDoorComponent : Node, IUnlockable
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||
|
||||
if (ExitArea == null)
|
||||
{
|
||||
GD.PushError("ExitDoorComponent: ExitArea is not set.");
|
||||
@@ -26,12 +28,15 @@ public partial class ExitDoorComponent : Node, IUnlockable
|
||||
|
||||
ExitArea.BodyEntered += OnExitAreaBodyEntered;
|
||||
|
||||
_gameManager = GetNode<GameManager>("/root/gameManager");
|
||||
}
|
||||
|
||||
private void OnExitAreaBodyEntered(Node2D body)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
if (Locked) return;
|
||||
|
||||
EmitSignalExitTriggered();
|
||||
_gameManager.UnlockLevel((int)_gameManager.PlayerState["current_level"] + 1);
|
||||
CallDeferred(nameof(GoToNextLevel));
|
||||
}
|
||||
|
||||
public void Unlock()
|
||||
|
@@ -16,13 +16,13 @@ public partial class ExplosiveComponent : Node2D
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
if (Damage != null)
|
||||
if (Damage == null)
|
||||
{
|
||||
GD.PushError("ExplosiveComponent: DamageComponent is not set.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ExplodeArea != null)
|
||||
if (ExplodeArea == null)
|
||||
{
|
||||
GD.PushError("ExplosiveComponent: ExplodeArea is not set.");
|
||||
return;
|
||||
@@ -30,6 +30,8 @@ public partial class ExplosiveComponent : Node2D
|
||||
|
||||
Area.BodyEntered += OnAreaBodyEntered;
|
||||
Area.AreaEntered += OnAreaAreaEntered;
|
||||
|
||||
PrepareTimer();
|
||||
}
|
||||
|
||||
private void OnAreaAreaEntered(Area2D area)
|
||||
|
@@ -30,11 +30,10 @@ public partial class FlashingComponent : Node
|
||||
public void StartFlashing()
|
||||
{
|
||||
if (Sprite == null) return;
|
||||
|
||||
_tween?.Kill();
|
||||
|
||||
if (_tween != null && _tween.IsRunning()) return;
|
||||
|
||||
_tween = CreateTween();
|
||||
_tween.SetParallel(true);
|
||||
_tween.SetParallel(false);
|
||||
|
||||
var flashes = (int)(FlashDuration / FlashTime);
|
||||
for (var i = 0; i < flashes; i++)
|
||||
|
@@ -15,9 +15,9 @@ public partial class GravityMotionComponent : Node2D
|
||||
{
|
||||
if (LaunchComponent == null) return;
|
||||
|
||||
var direction = LaunchComponent.InitialDirection.X > 0f ? TargetDirection : new Vector2(-TargetDirection.X, TargetDirection.Y);
|
||||
direction = direction.Normalized();
|
||||
_velocity = direction * LaunchComponent.Speed;
|
||||
var horizontalDirection = LaunchComponent.InitialDirection.Normalized();
|
||||
var combinedDirection = (horizontalDirection + TargetDirection).Normalized();
|
||||
_velocity = combinedDirection * LaunchComponent.Speed;
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
|
@@ -19,7 +19,7 @@ public partial class HealComponent : Node
|
||||
Collectable.Collected += OnCollected;
|
||||
}
|
||||
|
||||
private void OnCollected(Variant amount, CollectableType type, Node2D body)
|
||||
private void OnCollected(float amount, CollectableType type, Node2D body)
|
||||
{
|
||||
if (type != CollectableType.Health) return;
|
||||
|
||||
@@ -28,8 +28,7 @@ public partial class HealComponent : Node
|
||||
var healthComponent = body.GetNodeOrNull<HealthComponent>("HealthComponent");
|
||||
if (healthComponent == null) return;
|
||||
|
||||
var value = amount.AsSingle();
|
||||
healthComponent.IncreaseHealth(value);
|
||||
healthComponent.IncreaseHealth(amount);
|
||||
if (HealFx != null)
|
||||
{
|
||||
PlayHealFx();
|
||||
|
@@ -1,21 +1,19 @@
|
||||
using System;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using Mr.BrickAdventures.scripts.interfaces;
|
||||
using Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
public partial class MagneticSkillComponent : Node
|
||||
public partial class MagneticSkillComponent : Node, ISkill
|
||||
{
|
||||
[Export] public Area2D MagneticArea { get; set; }
|
||||
[Export] public float MagneticMoveDuration { get; set; } = 1.25f;
|
||||
|
||||
private Array<Node2D> _collectablesToPickUp = [];
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
MagneticArea.AreaEntered += OnAreaEntered;
|
||||
MagneticArea.BodyEntered += OnBodyEntered;
|
||||
}
|
||||
private Node2D _owner;
|
||||
private SkillData _skillData;
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
@@ -33,7 +31,7 @@ public partial class MagneticSkillComponent : Node
|
||||
|
||||
private void OnBodyEntered(Node2D body)
|
||||
{
|
||||
if (!HasComponentInChildren(body, "Collectable")) return;
|
||||
if (!HasComponentInChildren(body, "CollectableComponent")) return;
|
||||
|
||||
if (_collectablesToPickUp.Contains(body)) return;
|
||||
_collectablesToPickUp.Add(body);
|
||||
@@ -41,7 +39,7 @@ public partial class MagneticSkillComponent : Node
|
||||
|
||||
private void OnAreaEntered(Area2D area)
|
||||
{
|
||||
if (!HasComponentInChildren(area, "Collectable")) return;
|
||||
if (!HasComponentInChildren(area, "CollectableComponent")) return;
|
||||
|
||||
if (_collectablesToPickUp.Contains(area)) return;
|
||||
_collectablesToPickUp.Add(area);
|
||||
@@ -66,13 +64,69 @@ public partial class MagneticSkillComponent : Node
|
||||
|
||||
private void MoveCollectableToOwner(Node2D collectable)
|
||||
{
|
||||
if (!IsInstanceValid(collectable)) return;
|
||||
|
||||
if (Owner is not Node2D root) return;
|
||||
if (!IsInstanceValid(collectable) || !IsInstanceValid(_owner)) return;
|
||||
|
||||
var direction = (root.GlobalPosition - collectable.GlobalPosition).Normalized();
|
||||
var direction = (_owner.GlobalPosition - collectable.GlobalPosition).Normalized();
|
||||
var speed = direction.Length() / MagneticMoveDuration;
|
||||
|
||||
collectable.GlobalPosition += direction.Normalized() * speed;
|
||||
}
|
||||
|
||||
public void Initialize(Node owner, SkillData data)
|
||||
{
|
||||
_owner = owner as Node2D;
|
||||
_skillData = data;
|
||||
|
||||
if (_owner == null)
|
||||
{
|
||||
GD.PushWarning("MagneticSkillComponent: Owner is not a Node2D.");
|
||||
}
|
||||
|
||||
if (MagneticArea == null)
|
||||
{
|
||||
if (owner is Area2D area2D) MagneticArea = area2D;
|
||||
else
|
||||
{
|
||||
MagneticArea = owner.GetNodeOrNull<Area2D>("MagneticArea");
|
||||
if (MagneticArea == null)
|
||||
{
|
||||
GD.PushError("MagneticSkillComponent: MagneticArea is not set.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_skillData.Level > 0 && _skillData.Upgrades.Count >= _skillData.Level)
|
||||
{
|
||||
ApplyUpgrade(_skillData.Upgrades[_skillData.Level - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
if (MagneticArea == null)
|
||||
{
|
||||
GD.PushError("MagneticSkillComponent: MagneticArea is not set.");
|
||||
return;
|
||||
}
|
||||
|
||||
MagneticArea.BodyEntered += OnBodyEntered;
|
||||
MagneticArea.AreaEntered += OnAreaEntered;
|
||||
}
|
||||
|
||||
public void Deactivate()
|
||||
{
|
||||
if (MagneticArea == null) return;
|
||||
|
||||
MagneticArea.BodyEntered -= OnBodyEntered;
|
||||
MagneticArea.AreaEntered -= OnAreaEntered;
|
||||
}
|
||||
|
||||
public void ApplyUpgrade(SkillUpgrade upgrade)
|
||||
{
|
||||
foreach (var property in upgrade.Properties)
|
||||
{
|
||||
Set(property.Key, property.Value);
|
||||
}
|
||||
}
|
||||
}
|
@@ -123,7 +123,8 @@ public partial class PlatformMovementComponent : Node2D, IMovement
|
||||
Body.Velocity = new Vector2(direction * Speed, Body.Velocity.Y);
|
||||
else
|
||||
Body.Velocity = new Vector2(Mathf.MoveToward(Body.Velocity.X, 0, Speed), Body.Velocity.Y);
|
||||
|
||||
|
||||
PreviousVelocity = Body.Velocity;
|
||||
Body.MoveAndSlide();
|
||||
}
|
||||
|
||||
|
@@ -4,7 +4,7 @@ using Mr.BrickAdventures.scripts.interfaces;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
public partial class PlayerController : Node2D
|
||||
public partial class PlayerController : CharacterBody2D
|
||||
{
|
||||
[Export]
|
||||
public string DefaultMovementType { get; set; } = "platform";
|
||||
|
@@ -14,7 +14,7 @@ public partial class PlayerDeathComponent : Node2D
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_gameManager = GetNode<GameManager>("/root/gameManager");
|
||||
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||
HealthComponent.Death += OnDeath;
|
||||
}
|
||||
|
||||
|
@@ -2,6 +2,7 @@ using Godot;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class ProjectileComponent : Node2D
|
||||
{
|
||||
[Export] public float Speed { get; set; } = 16f;
|
||||
|
@@ -2,7 +2,7 @@ using Godot;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
public partial class ProjectileInitParams
|
||||
public class ProjectileInitParams
|
||||
{
|
||||
public Vector2 Position { get; set; } = Vector2.Zero;
|
||||
public Vector2 Direction { get; set; } = Vector2.Right;
|
||||
@@ -20,7 +20,7 @@ public partial class ProjectileInitComponent : Node
|
||||
var direction = p.Direction;
|
||||
var rotation = p.Rotation;
|
||||
var power = p.PowerMultiplier;
|
||||
|
||||
|
||||
if (Owner is Node2D root)
|
||||
{
|
||||
root.GlobalPosition = position;
|
||||
|
@@ -28,9 +28,9 @@ public partial class RequirementComponent : Node
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCollected(Variant amount, CollectableType type, Node2D body)
|
||||
private void OnCollected(float amount, CollectableType type, Node2D body)
|
||||
{
|
||||
AddProgress(amount.As<int>());
|
||||
AddProgress((int)amount);
|
||||
}
|
||||
|
||||
private void AddProgress(int amount = 1)
|
||||
|
@@ -7,7 +7,7 @@ namespace Mr.BrickAdventures.scripts.components;
|
||||
public partial class ScoreComponent : Node
|
||||
{
|
||||
private GameManager _gameManager;
|
||||
private const string CoinsGroupName = "Coins";
|
||||
private const string CoinsGroupName = "coins";
|
||||
|
||||
public override async void _Ready()
|
||||
{
|
||||
@@ -20,7 +20,7 @@ public partial class ScoreComponent : Node
|
||||
return;
|
||||
}
|
||||
|
||||
var coins = GetTree().GetNodesInGroup("Coins");
|
||||
var coins = GetTree().GetNodesInGroup(CoinsGroupName);
|
||||
foreach (var coin in coins)
|
||||
{
|
||||
var c = coin.GetNodeOrNull<CollectableComponent>("CollectableComponent");
|
||||
@@ -30,12 +30,12 @@ public partial class ScoreComponent : Node
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCollected(Variant amount, CollectableType type, Node2D body)
|
||||
|
||||
private void OnCollected(float amount, CollectableType type, Node2D body)
|
||||
{
|
||||
if (type != CollectableType.Coin) return;
|
||||
|
||||
var coinAmount = amount.As<int>();
|
||||
var coinAmount = (int)amount;
|
||||
var currentCoins = (int)_gameManager.CurrentSessionState["coins_collected"];
|
||||
_gameManager.CurrentSessionState["coins_collected"] = currentCoins + coinAmount;
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@ public partial class ShipShooterComponent : Node
|
||||
[Export] public Marker2D BulletSpawn { get; set; }
|
||||
[Export] public AudioStreamPlayer2D ShootSfx { get; set; }
|
||||
|
||||
private bool _canShoot = false;
|
||||
private bool _canShoot = true;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
|
@@ -1,11 +1,12 @@
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
using Mr.BrickAdventures.Autoloads;
|
||||
using Mr.BrickAdventures.scripts.interfaces;
|
||||
using Mr.BrickAdventures.scripts.Resources;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
public partial class SkillUnlockedComponent : Node
|
||||
public partial class SkillUnlockerComponent : Node
|
||||
{
|
||||
[Export] public SkillManager SkillManager { get; set; }
|
||||
|
||||
@@ -28,11 +29,11 @@ public partial class SkillUnlockedComponent : Node
|
||||
{
|
||||
if (_gameManager == null) return false;
|
||||
if (_gameManager.IsSkillUnlocked(skill)) return false;
|
||||
if (!HasEnoughCoins(skill.Cost)) return false;
|
||||
if (!HasEnoughCoins(skill.Upgrades[0].Cost)) return false;
|
||||
|
||||
skill.Level = 1;
|
||||
skill.IsActive = true;
|
||||
_gameManager.RemoveCoins(skill.Cost);
|
||||
_gameManager.RemoveCoins(skill.Upgrades[0].Cost);
|
||||
|
||||
var skillsUnlocked = (Array<SkillData>)_gameManager.CurrentSessionState["skills_unlocked"];
|
||||
skillsUnlocked.Add(skill);
|
||||
@@ -59,11 +60,19 @@ public partial class SkillUnlockedComponent : Node
|
||||
{
|
||||
if (_gameManager == null) return false;
|
||||
if (!_gameManager.IsSkillUnlocked(skill)) return false;
|
||||
if (!HasEnoughCoins(skill.Cost)) return false;
|
||||
if (skill.Level >= skill.MaxLevel) return false;
|
||||
if (!HasEnoughCoins(skill.Upgrades[skill.Level].Cost)) return false;
|
||||
|
||||
_gameManager.RemoveCoins(skill.Cost);
|
||||
_gameManager.RemoveCoins(skill.Upgrades[skill.Level].Cost);
|
||||
skill.Level++;
|
||||
if (SkillManager.ActiveComponents.TryGetValue(skill.Name, out Variant componentVariant))
|
||||
{
|
||||
var component = componentVariant.AsGodotObject();
|
||||
if (component is ISkill skillInstance)
|
||||
{
|
||||
skillInstance.ApplyUpgrade(skill.Upgrades[skill.Level - 1]);
|
||||
}
|
||||
}
|
||||
EmitSignalSkillUnlocked(skill);
|
||||
return true;
|
||||
}
|
@@ -2,20 +2,19 @@ using Godot;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
public partial class SpaceshipEnterComponent : Node
|
||||
public partial class SpaceshipEnterComponent : Area2D
|
||||
{
|
||||
[Export] public Area2D Area { get; set; }
|
||||
[Signal] public delegate void SpaceshipEnteredEventHandler();
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
Area.BodyEntered += OnBodyEntered;
|
||||
BodyEntered += OnBodyEntered;
|
||||
}
|
||||
|
||||
private void OnBodyEntered(Node2D body)
|
||||
{
|
||||
if (body is not PlayerController) return;
|
||||
EmitSignalSpaceshipEntered();
|
||||
Owner.QueueFree();
|
||||
QueueFree();
|
||||
}
|
||||
}
|
@@ -2,14 +2,13 @@ using Godot;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
public partial class SpaceshipExitComponent : Node
|
||||
public partial class SpaceshipExitComponent : Area2D
|
||||
{
|
||||
[Export] public Area2D Area { get; set; }
|
||||
[Signal] public delegate void SpaceshipExitEventHandler();
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
Area.BodyEntered += OnBodyEntered;
|
||||
BodyEntered += OnBodyEntered;
|
||||
}
|
||||
|
||||
private void OnBodyEntered(Node2D body)
|
||||
|
@@ -2,9 +2,8 @@ using Godot;
|
||||
|
||||
namespace Mr.BrickAdventures.scripts.components;
|
||||
|
||||
public partial class TooltipComponent : Node
|
||||
public partial class TooltipComponent : Area2D
|
||||
{
|
||||
[Export] public Area2D Area { get; set; }
|
||||
[Export] public Control UiRoot { get; set; }
|
||||
[Export] public string Text { get; set; } = string.Empty;
|
||||
[Export] public Label TooltipLabel { get; set; }
|
||||
@@ -13,8 +12,8 @@ public partial class TooltipComponent : Node
|
||||
{
|
||||
TooltipLabel.Text = Text;
|
||||
UiRoot.Visible = false;
|
||||
Area.BodyEntered += OnBodyEntered;
|
||||
Area.BodyExited += OnBodyExited;
|
||||
BodyEntered += OnBodyEntered;
|
||||
BodyExited += OnBodyExited;
|
||||
}
|
||||
|
||||
private void OnBodyEntered(Node2D body)
|
||||
|
@@ -1,64 +0,0 @@
|
||||
# class_name BeamComponent
|
||||
extends Node2D
|
||||
|
||||
@export var expansion_speed: float = 16.0
|
||||
@export var max_length: float = 512.0
|
||||
@export var direction: Vector2 = Vector2.DOWN
|
||||
|
||||
var current_length: float = 16.0
|
||||
|
||||
@export var root: Node2D
|
||||
@export var sprite2d: Sprite2D
|
||||
@export var collision_shape: CollisionShape2D
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
collision_shape.shape.extents = Vector2(current_length / 2.0, current_length / 2.0)
|
||||
sprite2d.scale = Vector2(1, 1)
|
||||
collision_shape.position = Vector2.ZERO
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
var new_length = current_length + expansion_speed * delta
|
||||
if new_length > max_length:
|
||||
new_length = max_length
|
||||
|
||||
if not check_for_obstacle(new_length):
|
||||
expand_beam(new_length)
|
||||
|
||||
|
||||
func expand_beam(new_length: float) -> void:
|
||||
current_length = new_length
|
||||
|
||||
if direction == Vector2.UP:
|
||||
sprite2d.scale.y = current_length / 16.0
|
||||
sprite2d.position.y = -current_length / 2.0
|
||||
collision_shape.shape.extents = Vector2(8.0, current_length / 2.0)
|
||||
collision_shape.position.y = -current_length / 2.0
|
||||
elif direction == Vector2.DOWN:
|
||||
sprite2d.scale.y = current_length / 16.0
|
||||
sprite2d.position.y = current_length / 2.0
|
||||
collision_shape.shape.extents = Vector2(8.0, current_length / 2.0)
|
||||
collision_shape.position.y = current_length / 2.0
|
||||
elif direction == Vector2.LEFT:
|
||||
sprite2d.scale.y = current_length / 16.0
|
||||
sprite2d.position.x = -current_length / 2.0
|
||||
collision_shape.shape.extents = Vector2(current_length / 2.0, 8.0)
|
||||
collision_shape.position.x = -current_length / 2.0
|
||||
elif direction == Vector2.RIGHT:
|
||||
sprite2d.scale.y = current_length / 16.0
|
||||
sprite2d.position.x = current_length / 2.0
|
||||
collision_shape.shape.extents = Vector2(current_length / 2.0, 8.0)
|
||||
collision_shape.position.x = current_length / 2.0
|
||||
|
||||
|
||||
func check_for_obstacle(new_length: float) -> bool:
|
||||
var space_state: PhysicsDirectSpaceState2D = get_world_2d().direct_space_state
|
||||
var query_start: Vector2 = global_position
|
||||
var query_end: Vector2 = global_position + direction * new_length
|
||||
var query: PhysicsRayQueryParameters2D = PhysicsRayQueryParameters2D.create(query_start, query_end)
|
||||
query.collide_with_areas = false
|
||||
query.collide_with_bodies = true
|
||||
|
||||
var result: Dictionary = space_state.intersect_ray(query)
|
||||
return result.size() > 0
|
@@ -1 +0,0 @@
|
||||
uid://bejv75mi8npj0
|
@@ -1,59 +0,0 @@
|
||||
class_name BrickThrowComponent
|
||||
extends Node
|
||||
|
||||
@export var brick_scene: PackedScene
|
||||
@export var fire_rate: float = 1.0
|
||||
@export var player_controller: PlayerController
|
||||
@export var timer: Timer
|
||||
@export var throw_input_behavior: ThrowInputResource
|
||||
|
||||
var can_throw: bool = true
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
setup_timer()
|
||||
can_throw = true
|
||||
|
||||
if throw_input_behavior:
|
||||
throw_input_behavior.throw_requested.connect(throw_brick)
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if throw_input_behavior:
|
||||
throw_input_behavior.process_input(event)
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if throw_input_behavior:
|
||||
throw_input_behavior.update(delta)
|
||||
|
||||
|
||||
func setup_timer() -> void:
|
||||
timer.wait_time = fire_rate
|
||||
timer.one_shot = false
|
||||
timer.autostart = false
|
||||
timer.timeout.connect(on_timer_timeout)
|
||||
|
||||
|
||||
func on_timer_timeout() -> void:
|
||||
can_throw = true
|
||||
|
||||
|
||||
func throw_brick(power_multiplier: float = 1.0) -> void:
|
||||
if not can_throw:
|
||||
return
|
||||
|
||||
var instance: Node2D = brick_scene.instantiate()
|
||||
var init := instance.get_node_or_null("ProjectileInitComponent") as ProjectileInitComponent
|
||||
if init and player_controller.current_movement is PlatformMovement:
|
||||
init.initialize({
|
||||
"position": player_controller.global_position,
|
||||
"rotation": player_controller.rotation,
|
||||
"direction": player_controller.current_movement.last_direction,
|
||||
"power_multiplier": power_multiplier
|
||||
})
|
||||
|
||||
get_tree().current_scene.add_child(instance)
|
||||
|
||||
can_throw = false
|
||||
timer.start()
|
@@ -1 +0,0 @@
|
||||
uid://cm06xg1l3xtw5
|
@@ -1,37 +0,0 @@
|
||||
class_name BulletComponent
|
||||
extends Node
|
||||
|
||||
@export var root: Node2D
|
||||
@export var area2d: Area2D
|
||||
@export var hit_terrain_fx: TerrainHitFx
|
||||
@export var bullet_sprite: Sprite2D
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
area2d.body_entered.connect(on_area2d_body_entered)
|
||||
area2d.area_entered.connect(on_area2d_area_entered)
|
||||
|
||||
|
||||
|
||||
func on_area2d_body_entered(body: Node2D) -> void:
|
||||
if body is TileMapLayer:
|
||||
if bullet_sprite:
|
||||
bullet_sprite.visible = false
|
||||
play_terrain_hit_fx()
|
||||
return
|
||||
|
||||
root.queue_free()
|
||||
|
||||
|
||||
func on_area2d_area_entered(_area: Area2D) -> void:
|
||||
root.queue_free()
|
||||
|
||||
|
||||
|
||||
func play_terrain_hit_fx() -> void:
|
||||
if not hit_terrain_fx:
|
||||
return
|
||||
|
||||
await hit_terrain_fx.trigger_fx()
|
||||
|
||||
root.queue_free()
|
@@ -1 +0,0 @@
|
||||
uid://cdnwrn8v05qhi
|
@@ -1,37 +0,0 @@
|
||||
class_name CageComponent
|
||||
extends Node
|
||||
|
||||
@export var lever: LeverComponent
|
||||
@export var root: Node2D
|
||||
@export var move_value: Vector2 = Vector2(0, -100)
|
||||
@export var tween_duration: float = 0.5
|
||||
@export var should_free: bool = true
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
await get_tree().process_frame
|
||||
if not lever:
|
||||
var levers_nodes := get_tree().get_nodes_in_group("levers")
|
||||
for lever_node in levers_nodes:
|
||||
var lever_component: LeverComponent = lever_node.get_node_or_null("LeverComponent")
|
||||
if lever_component:
|
||||
lever_component.activated.connect(on_lever_activated)
|
||||
else:
|
||||
lever.activated.connect(on_lever_activated)
|
||||
|
||||
if not root:
|
||||
printerr("CageComponent: root is not set.")
|
||||
return
|
||||
|
||||
|
||||
func on_lever_activated() -> void:
|
||||
var tween: Tween = create_tween()
|
||||
var end_position: Vector2 = root.position + move_value
|
||||
tween.tween_property(root, "position", end_position, tween_duration)
|
||||
tween.tween_callback(_on_tween_completed)
|
||||
|
||||
|
||||
func _on_tween_completed() -> void:
|
||||
if not should_free:
|
||||
return
|
||||
root.queue_free()
|
@@ -1 +0,0 @@
|
||||
uid://ddplvyjqguxtl
|
@@ -1,2 +0,0 @@
|
||||
class_name CanBeLaunchedComponent
|
||||
extends Node
|
@@ -1 +0,0 @@
|
||||
uid://6ffxsx3gknhr
|
@@ -1,2 +0,0 @@
|
||||
class_name CanPickUpComponent
|
||||
extends Node
|
@@ -1 +0,0 @@
|
||||
uid://dcvjvjy2nhf8s
|
@@ -1,2 +0,0 @@
|
||||
class_name CannotStompComponent
|
||||
extends Node
|
@@ -1 +0,0 @@
|
||||
uid://ct7lsuwjvmwxu
|
@@ -1,59 +0,0 @@
|
||||
class_name ChargeThrowComponent
|
||||
extends Node
|
||||
|
||||
@export var min_charge_duration: float = 0.1
|
||||
@export var min_power: float = 0.5
|
||||
@export var max_power: float = 2.0
|
||||
@export var max_charge_time: float = 1.5
|
||||
|
||||
var charge_start_time: float = 0.0
|
||||
var is_charging: bool = false
|
||||
signal charge_started
|
||||
signal charge_updated(charge_ratio: float)
|
||||
signal charge_stopped
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if not is_charging:
|
||||
return
|
||||
|
||||
var charge_ratio := get_charge_ratio()
|
||||
charge_updated.emit(charge_ratio)
|
||||
|
||||
|
||||
func start_charging() -> void:
|
||||
is_charging = true
|
||||
charge_start_time = Time.get_ticks_msec() / 1000.0
|
||||
call_deferred("emit_charge_started")
|
||||
|
||||
|
||||
func get_charge_ratio() -> float:
|
||||
if not is_charging:
|
||||
return 0.0
|
||||
|
||||
var held_time := (Time.get_ticks_msec() / 1000.0) - charge_start_time
|
||||
var t = clamp(held_time / max_charge_time, 0.0, 1.0)
|
||||
|
||||
return lerp(min_power, max_power, t)
|
||||
|
||||
|
||||
func stop_charging() -> float:
|
||||
if not is_charging:
|
||||
return min_power
|
||||
|
||||
var held_time := (Time.get_ticks_msec() / 1000.0) - charge_start_time
|
||||
is_charging = false
|
||||
charge_start_time = 0.0
|
||||
charge_stopped.emit()
|
||||
|
||||
if held_time < min_charge_duration:
|
||||
return min_power
|
||||
|
||||
var t = clamp(held_time / max_charge_time, 0.0, 1.0)
|
||||
return lerp(min_power, max_power, t)
|
||||
|
||||
|
||||
func emit_charge_started() -> void:
|
||||
if not is_charging:
|
||||
return
|
||||
charge_started.emit()
|
@@ -1 +0,0 @@
|
||||
uid://c6fclevp3peuo
|
@@ -1,69 +0,0 @@
|
||||
class_name ChaseLevelComponent
|
||||
extends Node
|
||||
|
||||
@export var chase_speed: float = 200.0
|
||||
@export var chase_target: Marker2D
|
||||
@export var phantom_camera: PhantomCamera2D
|
||||
@export var minimum_distance: float = 10.0
|
||||
signal chase_started
|
||||
signal chase_stopped
|
||||
var is_chasing: bool = false
|
||||
var previous_camera_follow_target: Node2D = null
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if not is_chasing:
|
||||
return
|
||||
|
||||
if not chase_target:
|
||||
printerr("ChaseLevelComponent: chase_target is not set.")
|
||||
return
|
||||
|
||||
if check_if_reached_target():
|
||||
stop_chasing()
|
||||
return
|
||||
|
||||
var target_position: Vector2 = chase_target.global_position
|
||||
var current_position: Vector2 = owner.global_position
|
||||
var direction: Vector2 = (target_position - current_position).normalized()
|
||||
|
||||
owner.global_position += direction * chase_speed * delta
|
||||
|
||||
|
||||
func on_ship_entered() -> void:
|
||||
if not chase_target:
|
||||
printerr("ChaseLevelComponent: chase_target is not set.")
|
||||
return
|
||||
|
||||
if not phantom_camera:
|
||||
printerr("ChaseLevelComponent: phantom_camera is not set.")
|
||||
return
|
||||
|
||||
previous_camera_follow_target = phantom_camera.get_follow_target()
|
||||
phantom_camera.set_follow_target(owner as Node2D)
|
||||
chase_started.emit()
|
||||
is_chasing = true
|
||||
|
||||
|
||||
func on_ship_exited() -> void:
|
||||
stop_chasing()
|
||||
|
||||
|
||||
func check_if_reached_target() -> bool:
|
||||
if not chase_target:
|
||||
printerr("ChaseLevelComponent: chase_target is not set.")
|
||||
return false
|
||||
|
||||
var target_position: Vector2 = chase_target.global_position
|
||||
var current_position: Vector2 = owner.global_position
|
||||
return current_position.distance_to(target_position) < minimum_distance
|
||||
|
||||
|
||||
func stop_chasing() -> void:
|
||||
if not phantom_camera:
|
||||
printerr("ChaseLevelComponent: phantom_camera is not set.")
|
||||
return
|
||||
|
||||
phantom_camera.set_follow_target(previous_camera_follow_target)
|
||||
chase_stopped.emit()
|
||||
is_chasing = false
|
@@ -1 +0,0 @@
|
||||
uid://cf4li7whw5old
|
@@ -1,8 +0,0 @@
|
||||
class_name CleanUpComponent
|
||||
extends Node
|
||||
|
||||
@export var root: Node
|
||||
|
||||
|
||||
func clean_up() -> void:
|
||||
root.queue_free()
|
@@ -1 +0,0 @@
|
||||
uid://f74xpfg7624d
|
@@ -1,55 +0,0 @@
|
||||
class_name CollapsableComponent
|
||||
extends Node
|
||||
|
||||
@export var to_collapse_timer: Timer
|
||||
@export var reset_timer: Timer
|
||||
@export var sprite2d: Sprite2D
|
||||
@export var collision_shape: CollisionShape2D
|
||||
|
||||
@export var collapse_time: float = 0.5
|
||||
@export var reset_time: float = 1.0
|
||||
|
||||
@export var anim_time: float = 0.25
|
||||
|
||||
func _ready() -> void:
|
||||
reset_timers()
|
||||
|
||||
func _on_to_collapse_timer_timeout() -> void:
|
||||
collapse_bridge()
|
||||
|
||||
func _on_reset_timer_timeout() -> void:
|
||||
reactivate_bridge()
|
||||
|
||||
func collapse_bridge():
|
||||
to_collapse_timer.stop()
|
||||
to_collapse_timer.wait_time = collapse_time
|
||||
|
||||
var bridge_tween = create_tween()
|
||||
bridge_tween.tween_property(sprite2d, "modulate:a", 0, anim_time)
|
||||
await bridge_tween.finished
|
||||
|
||||
collision_shape.disabled = true
|
||||
reset_timer.start()
|
||||
|
||||
func reactivate_bridge():
|
||||
reset_timer.stop()
|
||||
reset_timer.wait_time = reset_time
|
||||
|
||||
var bridge_tween = create_tween()
|
||||
bridge_tween.tween_property(sprite2d, "modulate:a", 1, anim_time)
|
||||
await bridge_tween.finished
|
||||
|
||||
collision_shape.disabled = false
|
||||
|
||||
func _on_collapsable_detector_body_entered(_body: Node2D) -> void:
|
||||
to_collapse_timer.start()
|
||||
|
||||
func reset_timers():
|
||||
to_collapse_timer.stop()
|
||||
to_collapse_timer.wait_time = collapse_time
|
||||
|
||||
|
||||
func _on_collapsable_detector_body_exited(_body: Node2D) -> void:
|
||||
var collapse_time_left: float = abs(to_collapse_timer.time_left - collapse_time)
|
||||
if collapse_time_left < (0.1 * collapse_time):
|
||||
reset_timers()
|
@@ -1 +0,0 @@
|
||||
uid://r0a6xjicrh74
|
@@ -1,38 +0,0 @@
|
||||
class_name CollectableComponent
|
||||
extends Node
|
||||
|
||||
var root: Node
|
||||
var has_fade_away: bool = false
|
||||
|
||||
@export var area2d: Area2D
|
||||
@export var collision_shape: CollisionShape2D
|
||||
@export var collectable_data: CollectableResource
|
||||
@export var sfx: AudioStreamPlayer2D
|
||||
signal collected(amount: Variant, type: CollectableResource.CollectableType, body: Node2D)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if area2d:
|
||||
area2d.body_entered.connect(_on_area2d_body_entered)
|
||||
else:
|
||||
printerr("Collectable node missing Area2D child.")
|
||||
|
||||
root = get_parent()
|
||||
|
||||
if root.has_node("FadeAwayComponent"):
|
||||
has_fade_away = true
|
||||
|
||||
|
||||
func _on_area2d_body_entered(body: Node2D) -> void:
|
||||
if body.has_node("CanPickUpComponent"):
|
||||
collected.emit(collectable_data.amount, collectable_data.type, body)
|
||||
if collision_shape:
|
||||
collision_shape.call_deferred("set_disabled", true)
|
||||
if sfx:
|
||||
sfx.play()
|
||||
if not has_fade_away and sfx:
|
||||
await sfx.finished
|
||||
root.queue_free()
|
||||
elif not has_fade_away:
|
||||
root.queue_free()
|
||||
|
@@ -1 +0,0 @@
|
||||
uid://pa1bwc4no08q
|
@@ -1,94 +0,0 @@
|
||||
class_name DamageComponent
|
||||
extends Node
|
||||
|
||||
@export var damage: float = 0.25
|
||||
@export var area2d: Area2D
|
||||
@export var status_effect_data: StatusEffectDataResource
|
||||
@export var damage_timer: Timer
|
||||
|
||||
var current_target: Node = null
|
||||
signal effect_inflicted(target: Node2D, effect: StatusEffectDataResource)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not area2d:
|
||||
printerr("No area2d assigned!")
|
||||
return
|
||||
|
||||
area2d.body_entered.connect(on_area2d_body_entered)
|
||||
area2d.body_exited.connect(on_area2d_body_exited)
|
||||
area2d.area_entered.connect(on_area2d_area_entered)
|
||||
|
||||
if damage_timer:
|
||||
damage_timer.timeout.connect(on_damage_timer_timeout)
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if not current_target:
|
||||
return
|
||||
if damage_timer:
|
||||
return
|
||||
process_entity_and_apply_damage(current_target)
|
||||
|
||||
|
||||
func deal_damage(target: HealthComponent) -> void:
|
||||
target.decrease_health(damage)
|
||||
|
||||
|
||||
func on_damage_timer_timeout() -> void:
|
||||
if not current_target:
|
||||
return
|
||||
|
||||
process_entity_and_apply_damage(current_target)
|
||||
|
||||
|
||||
func process_entity_and_apply_damage(body: Node2D) -> void:
|
||||
if body.has_node("HealthComponent"):
|
||||
var health_component: HealthComponent = body.get_node("HealthComponent")
|
||||
var invulnerability_component: InvulnerabilityComponent = body.get_node_or_null("InvulnerabilityComponent")
|
||||
|
||||
if invulnerability_component and invulnerability_component.is_invulnerable():
|
||||
return
|
||||
|
||||
if status_effect_data and status_effect_data.effect_type != StatusEffectComponent.EffectType.NONE:
|
||||
effect_inflicted.emit(body, status_effect_data)
|
||||
|
||||
deal_damage(health_component)
|
||||
|
||||
if invulnerability_component:
|
||||
invulnerability_component.activate()
|
||||
|
||||
|
||||
func on_area2d_body_entered(body: Node2D) -> void:
|
||||
current_target = body
|
||||
|
||||
if not check_if_processing_is_on():
|
||||
return
|
||||
|
||||
if damage_timer:
|
||||
damage_timer.start()
|
||||
|
||||
process_entity_and_apply_damage(body)
|
||||
|
||||
|
||||
func on_area2d_body_exited(body: Node2D) -> void:
|
||||
if body == current_target:
|
||||
current_target = null
|
||||
if damage_timer:
|
||||
damage_timer.stop()
|
||||
|
||||
|
||||
func on_area2d_area_entered(area: Area2D) -> void:
|
||||
if not check_if_processing_is_on():
|
||||
return
|
||||
|
||||
if area == area2d:
|
||||
return
|
||||
|
||||
var parent := area.get_parent()
|
||||
if parent.has_node("DamageComponent"):
|
||||
process_entity_and_apply_damage(parent)
|
||||
|
||||
|
||||
func check_if_processing_is_on() -> bool:
|
||||
return self.process_mode == PROCESS_MODE_INHERIT or self.process_mode == PROCESS_MODE_ALWAYS
|
@@ -1 +0,0 @@
|
||||
uid://dkmxhjtmu5xlb
|
@@ -1,23 +0,0 @@
|
||||
class_name DestroyableComponent
|
||||
extends Node
|
||||
|
||||
@export var root: Node
|
||||
@export var health_component: HealthComponent
|
||||
@export var destroy_effect: PackedScene
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not health_component:
|
||||
printerr("No health component assigned!")
|
||||
return
|
||||
|
||||
health_component.on_death.connect(on_health_component_death)
|
||||
|
||||
|
||||
func on_health_component_death() -> void:
|
||||
if destroy_effect:
|
||||
var effect: Node2D = destroy_effect.instantiate()
|
||||
health_component.get_parent().add_child(effect)
|
||||
effect.global_position = health_component.global_position
|
||||
|
||||
root.queue_free()
|
@@ -1 +0,0 @@
|
||||
uid://d01dmoafptl2p
|
@@ -1,20 +0,0 @@
|
||||
class_name EffectInflictorComponent
|
||||
extends Node
|
||||
|
||||
@export var damage: DamageComponent
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not damage:
|
||||
printerr("No damage component assigned!")
|
||||
return
|
||||
|
||||
damage.effect_inflicted.connect(on_effect_inflicted)
|
||||
|
||||
|
||||
func on_effect_inflicted(target: Node2D, effect: StatusEffectDataResource) -> void:
|
||||
var status_effect_component: StatusEffectComponent = target.get_node_or_null("StatusEffectComponent")
|
||||
if not status_effect_component:
|
||||
return
|
||||
|
||||
status_effect_component.apply_effect(effect)
|
@@ -1 +0,0 @@
|
||||
uid://d3brcje121krs
|
@@ -1,28 +0,0 @@
|
||||
class_name EnemyDeathComponent
|
||||
extends Node
|
||||
|
||||
@export var root: Node2D
|
||||
@export var tween_duration: float = 0.5
|
||||
@export var collision_shape_2d: CollisionShape2D
|
||||
@export var health_component: HealthComponent
|
||||
|
||||
func _ready() -> void:
|
||||
if not collision_shape_2d:
|
||||
printerr("No CollisionShape2D assigned!")
|
||||
return
|
||||
if not health_component:
|
||||
printerr("No HealthComponent assigned!")
|
||||
return
|
||||
|
||||
health_component.on_death.connect(_on_health_component_on_death)
|
||||
|
||||
func _on_health_component_on_death() -> void:
|
||||
call_deferred("die")
|
||||
|
||||
|
||||
func die() -> void:
|
||||
collision_shape_2d.disabled = true
|
||||
var tween := create_tween()
|
||||
tween.tween_property(root, "scale", Vector2(0, 0), tween_duration)
|
||||
await (tween.finished)
|
||||
root.queue_free()
|
@@ -1 +0,0 @@
|
||||
uid://dqm371fysuk7i
|
@@ -1,37 +0,0 @@
|
||||
class_name EnemyWaveTrigger
|
||||
extends Node
|
||||
|
||||
@export var area2d: Area2D
|
||||
@export var path_follow_node: PathFollow2D
|
||||
@export var speed: float = 100.0
|
||||
@export var loop: bool = false
|
||||
@export var activate_on_enter: bool = true
|
||||
|
||||
var active: bool = false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
area2d.body_entered.connect(_on_body_entered)
|
||||
if path_follow_node:
|
||||
path_follow_node.progress = 0
|
||||
path_follow_node.set_process(false)
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if not active or not path_follow_node:
|
||||
return
|
||||
path_follow_node.progress += speed * delta
|
||||
if path_follow_node.progress_ratio >= 1.0 and not loop:
|
||||
active = false
|
||||
path_follow_node.set_process(false)
|
||||
|
||||
|
||||
func _on_body_entered(body: Node2D) -> void:
|
||||
if activate_on_enter:
|
||||
start_wave()
|
||||
|
||||
|
||||
func start_wave() -> void:
|
||||
if path_follow_node:
|
||||
path_follow_node.set_process(true)
|
||||
active = true
|
@@ -1 +0,0 @@
|
||||
uid://tmahwsvpkrbv
|
@@ -1,39 +0,0 @@
|
||||
class_name ExitDoorComponent
|
||||
extends Node
|
||||
|
||||
@export var locked: bool = true
|
||||
@export var exit_area: Area2D
|
||||
@export var door_sprite: Sprite2D
|
||||
@export var opened_door_sfx: AudioStreamPlayer2D
|
||||
@export var opened_door_frame: int = 0
|
||||
signal exit_triggered
|
||||
@onready var gm: GM = $"/root/GameManager"
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not exit_area:
|
||||
printerr("ExitDoorComponent: exit_area is not set.")
|
||||
return
|
||||
|
||||
exit_area.body_entered.connect(on_exit_area_body_entered)
|
||||
|
||||
|
||||
func unlock() -> void:
|
||||
locked = false
|
||||
if door_sprite:
|
||||
door_sprite.frame = opened_door_frame
|
||||
if opened_door_sfx:
|
||||
opened_door_sfx.play()
|
||||
|
||||
|
||||
func on_exit_area_body_entered(_body: Node2D) -> void:
|
||||
if locked:
|
||||
return
|
||||
|
||||
exit_triggered.emit()
|
||||
gm.unlock_level(gm.player_state["current_level"] + 1)
|
||||
call_deferred("go_to_next_level")
|
||||
|
||||
|
||||
func go_to_next_level() -> void:
|
||||
gm.on_level_complete()
|
@@ -1 +0,0 @@
|
||||
uid://bwamqffvpa452
|
@@ -1,64 +0,0 @@
|
||||
class_name ExplosiveComponent
|
||||
extends Node
|
||||
|
||||
@export var root: Node2D
|
||||
@export var damage: DamageComponent
|
||||
@export var area2d: Area2D
|
||||
@export var explosion_area2d: Area2D
|
||||
@export var explosion_effect: PackedScene
|
||||
@export var time_to_explode: float = 9.0
|
||||
signal on_explosion(body: Node2D)
|
||||
var timer: Timer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not damage:
|
||||
printerr("No damage component assigned!")
|
||||
return
|
||||
|
||||
if not explosion_area2d:
|
||||
printerr("No area2d assigned!")
|
||||
return
|
||||
|
||||
area2d.body_entered.connect(on_area2d_body_entered)
|
||||
area2d.area_entered.connect(on_area2d_area_entered)
|
||||
|
||||
prepare_timer()
|
||||
|
||||
|
||||
func prepare_timer() -> void:
|
||||
timer = Timer.new()
|
||||
timer.set_wait_time(time_to_explode)
|
||||
timer.set_one_shot(true)
|
||||
timer.autostart = true
|
||||
timer.timeout.connect(explode)
|
||||
add_child(timer)
|
||||
|
||||
|
||||
func explode() -> void:
|
||||
timer.stop()
|
||||
|
||||
if explosion_effect:
|
||||
var explosion_instance: GPUParticles2D = explosion_effect.instantiate()
|
||||
explosion_instance.global_position = root.global_position
|
||||
get_tree().current_scene.add_child(explosion_instance)
|
||||
explosion_instance.emitting = true
|
||||
|
||||
var bodies: Array = explosion_area2d.get_overlapping_bodies()
|
||||
for body in bodies:
|
||||
var health_component: HealthComponent = body.get_node_or_null("HealthComponent")
|
||||
|
||||
if damage and health_component:
|
||||
damage.deal_damage(health_component)
|
||||
|
||||
on_explosion.emit(body)
|
||||
|
||||
root.queue_free()
|
||||
|
||||
|
||||
func on_area2d_body_entered(_body: Node2D) -> void:
|
||||
explode()
|
||||
|
||||
|
||||
func on_area2d_area_entered(_area: Area2D) -> void:
|
||||
explode()
|
@@ -1 +0,0 @@
|
||||
uid://beg4dk7d5pvhp
|
@@ -1,27 +0,0 @@
|
||||
class_name FadeAwayComponent
|
||||
extends Node
|
||||
|
||||
@export var sprite2d: Sprite2D
|
||||
@export var fade_duration: float = 1.0
|
||||
@export var speed: float = 10.0
|
||||
@export var direction: Vector2 = Vector2.UP
|
||||
@export var root: Node2D
|
||||
@export var area2d: Area2D
|
||||
|
||||
|
||||
func _ready():
|
||||
root = get_parent()
|
||||
if area2d:
|
||||
area2d.body_entered.connect(on_area2d_body_entered)
|
||||
|
||||
|
||||
func fade_away() -> void:
|
||||
var fade_tween := create_tween().set_parallel(true)
|
||||
fade_tween.tween_property(sprite2d, "modulate:a", 0, fade_duration)
|
||||
fade_tween.tween_property(sprite2d, "position", sprite2d.position + (direction * speed), fade_duration)
|
||||
await (fade_tween.finished)
|
||||
root.queue_free()
|
||||
|
||||
|
||||
func on_area2d_body_entered(_body: Node2D) -> void:
|
||||
fade_away()
|
@@ -1 +0,0 @@
|
||||
uid://bg75hnr3q6grk
|
@@ -1,54 +0,0 @@
|
||||
class_name FireEffectComponent
|
||||
extends Node
|
||||
|
||||
@export var health_component: HealthComponent
|
||||
@export var status_effect_component: StatusEffectComponent
|
||||
@export var root: Node2D
|
||||
@export var fire_fx: GPUParticles2D
|
||||
|
||||
var data: StatusEffectDataResource = null
|
||||
var should_deal_damage: bool = false
|
||||
var time_elapsed: float = 0.0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not health_component:
|
||||
health_component = root.get_node("HealthComponent")
|
||||
if not status_effect_component:
|
||||
status_effect_component = root.get_node("StatusEffectComponent")
|
||||
|
||||
if not health_component:
|
||||
printerr("No HealthComponent assigned!")
|
||||
return
|
||||
if not status_effect_component:
|
||||
printerr("No StatusEffectComponent assigned!")
|
||||
return
|
||||
|
||||
status_effect_component.effect_applied.connect(on_effect_applied)
|
||||
status_effect_component.effect_removed.connect(on_effect_removed)
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
if not should_deal_damage or not health_component or not data:
|
||||
return
|
||||
|
||||
time_elapsed += delta
|
||||
if time_elapsed >= 1.0:
|
||||
health_component.decrease_health(data.damage_per_second)
|
||||
time_elapsed = 0.0
|
||||
|
||||
|
||||
func on_effect_applied(effect_data: StatusEffectDataResource) -> void:
|
||||
if effect_data.effect_type == StatusEffectComponent.EffectType.FIRE:
|
||||
data = effect_data
|
||||
should_deal_damage = true
|
||||
if fire_fx:
|
||||
fire_fx.emitting = true
|
||||
|
||||
|
||||
func on_effect_removed(effect_type: StatusEffectComponent.EffectType) -> void:
|
||||
if effect_type == StatusEffectComponent.EffectType.FIRE:
|
||||
data = null
|
||||
should_deal_damage = false
|
||||
if fire_fx:
|
||||
fire_fx.emitting = false
|
@@ -1 +0,0 @@
|
||||
uid://qi2irprbqru2
|
@@ -1,55 +0,0 @@
|
||||
class_name FlashingComponent
|
||||
extends Node
|
||||
|
||||
@export var sprite: Node2D
|
||||
@export var flash_duration: float = 0.5
|
||||
@export var flash_time: float = 0.1
|
||||
@export var use_modulate: bool = true
|
||||
@export var health_component: HealthComponent
|
||||
|
||||
var tween: Tween
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if health_component:
|
||||
health_component.on_health_change.connect(on_health_change)
|
||||
health_component.on_death.connect(on_death)
|
||||
|
||||
if not sprite:
|
||||
printerr("No sprite assigned!")
|
||||
return
|
||||
|
||||
|
||||
func start_flashing() -> void:
|
||||
if not sprite:
|
||||
return
|
||||
|
||||
if tween:
|
||||
tween.kill()
|
||||
|
||||
tween = create_tween()
|
||||
tween.set_parallel(false)
|
||||
|
||||
var flashes: int = int(flash_duration / flash_time)
|
||||
for i in range(flashes):
|
||||
var opacity: float = 0.3 if i % 2 == 0 else 1.0
|
||||
tween.tween_property(sprite, "modulate:a" if use_modulate else "visible", opacity if use_modulate else float(i % 2 == 0), flash_time)
|
||||
|
||||
tween.tween_callback(stop_flashing)
|
||||
|
||||
|
||||
func stop_flashing() -> void:
|
||||
if use_modulate:
|
||||
sprite.modulate.a = 1.0
|
||||
else:
|
||||
sprite.visible = true
|
||||
|
||||
|
||||
func on_health_change(delta: float, _total_health: float) -> void:
|
||||
if delta < 0:
|
||||
start_flashing()
|
||||
|
||||
|
||||
func on_death() -> void:
|
||||
stop_flashing()
|
||||
|
@@ -1 +0,0 @@
|
||||
uid://dqmbvuutd5c3c
|
@@ -1,27 +0,0 @@
|
||||
class_name FlipPlayerComponent
|
||||
extends Node2D
|
||||
|
||||
@export var eye_left: Sprite2D
|
||||
@export var eye_right: Sprite2D
|
||||
@export var platform_movement: PlatformMovement
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if not platform_movement:
|
||||
return
|
||||
|
||||
var velocity := platform_movement.last_direction
|
||||
if velocity.x < 0:
|
||||
eye_left.frame = 1
|
||||
eye_right.frame = 1
|
||||
eye_left.flip_h = true
|
||||
eye_right.flip_h = true
|
||||
elif velocity.x > 0:
|
||||
eye_left.frame = 1
|
||||
eye_right.frame = 1
|
||||
eye_left.flip_h = false
|
||||
eye_right.flip_h = false
|
||||
else:
|
||||
eye_left.frame = 0
|
||||
eye_right.frame = 0
|
||||
|
@@ -1 +0,0 @@
|
||||
uid://oxeqvxkgj87j
|
@@ -1,30 +0,0 @@
|
||||
class_name GravityMotionComponent
|
||||
extends Node2D
|
||||
|
||||
@export var character_body: CharacterBody2D
|
||||
@export var launch_component: LaunchComponent
|
||||
@export var gravity: Vector2 = Vector2(0, 980.0)
|
||||
@export var target_direction: Vector2 = Vector2(1.0, -1.0)
|
||||
|
||||
var velocity: Vector2 = Vector2.ZERO
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not launch_component:
|
||||
return
|
||||
var direction := target_direction if launch_component.initial_direction.x > 0 else Vector2(-target_direction.x, target_direction.y)
|
||||
direction = direction.normalized()
|
||||
velocity = direction * launch_component.speed
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if not character_body:
|
||||
return
|
||||
|
||||
velocity += gravity * delta
|
||||
character_body.velocity = velocity
|
||||
|
||||
character_body.move_and_slide()
|
||||
|
||||
if velocity.length_squared() > 0.01:
|
||||
character_body.rotation = velocity.angle()
|
@@ -1 +0,0 @@
|
||||
uid://c2gbumw4x4t1v
|
@@ -1,40 +0,0 @@
|
||||
class_name HealComponent
|
||||
extends Node
|
||||
|
||||
@export var heal_fx: GPUParticles2D
|
||||
@export var collectable: CollectableComponent
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not collectable:
|
||||
printerr("HealComponent: No CollectableComponent assigned.")
|
||||
return
|
||||
|
||||
collectable.collected.connect(on_collected)
|
||||
|
||||
|
||||
func on_collected(amount: float, type: CollectableResource.CollectableType, _body: Node2D) -> void:
|
||||
if type != CollectableResource.CollectableType.HEALTH:
|
||||
return
|
||||
|
||||
if not collectable:
|
||||
printerr("HealComponent: No CollectableComponent assigned.")
|
||||
return
|
||||
|
||||
var health_component := _body.get_node_or_null("HealthComponent") as HealthComponent
|
||||
if not health_component or not health_component is HealthComponent:
|
||||
printerr("HealComponent: No HealthComponent found on collected body.")
|
||||
return
|
||||
health_component.increase_health(amount)
|
||||
if heal_fx:
|
||||
play_heal_fx()
|
||||
else:
|
||||
owner.queue_free()
|
||||
|
||||
|
||||
func play_heal_fx() -> void:
|
||||
if not heal_fx:
|
||||
return
|
||||
|
||||
heal_fx.restart()
|
||||
heal_fx.emitting = true
|
@@ -1 +0,0 @@
|
||||
uid://cegdd1sravi5m
|
@@ -1,48 +0,0 @@
|
||||
class_name HealthComponent
|
||||
extends Node
|
||||
|
||||
@export var health: float = 1.0
|
||||
@export var max_health: float = 1.0
|
||||
@export var hurt_fx: AudioStreamPlayer2D
|
||||
@export var heal_fx: AudioStreamPlayer2D
|
||||
signal on_health_change(delta: float, total_health: float)
|
||||
signal on_death
|
||||
|
||||
|
||||
|
||||
func _get_delta(new_value: float) -> float:
|
||||
return new_value - health
|
||||
|
||||
|
||||
func set_health(new_value: float):
|
||||
_apply_health_change(new_value)
|
||||
|
||||
|
||||
func decrease_health(value: float):
|
||||
_apply_health_change(health - value)
|
||||
|
||||
|
||||
func increase_health(value: float):
|
||||
_apply_health_change(health + value)
|
||||
|
||||
|
||||
func _apply_health_change(new_health: float, play_fx: bool = true) -> void:
|
||||
new_health = clamp(new_health, 0.0, max_health)
|
||||
var delta := new_health - health
|
||||
|
||||
if delta == 0.0:
|
||||
return # No change
|
||||
|
||||
if play_fx:
|
||||
if delta > 0 and heal_fx:
|
||||
heal_fx.play()
|
||||
elif delta < 0 and hurt_fx:
|
||||
hurt_fx.play()
|
||||
await hurt_fx.finished
|
||||
|
||||
health = new_health
|
||||
|
||||
if health <= 0:
|
||||
on_death.emit()
|
||||
else:
|
||||
on_health_change.emit(delta, health)
|
@@ -1 +0,0 @@
|
||||
uid://btfsq0bvtrx3t
|
@@ -1,57 +0,0 @@
|
||||
class_name HitComponent
|
||||
extends Node
|
||||
|
||||
@export var sprite: Sprite2D
|
||||
@export var health_component: HealthComponent
|
||||
@export var hit_duration: float = 0.1
|
||||
@export var hit_fx: GPUParticles2D
|
||||
@export var flash_mode: bool = true
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if health_component:
|
||||
health_component.on_health_change.connect(on_health_change)
|
||||
health_component.on_death.connect(on_death)
|
||||
|
||||
if not sprite:
|
||||
printerr("No sprite assigned!")
|
||||
return
|
||||
|
||||
if sprite.material and flash_mode:
|
||||
sprite.material = sprite.material.duplicate()
|
||||
|
||||
|
||||
func activate() -> void:
|
||||
if not flash_mode:
|
||||
return
|
||||
sprite.material.set_shader_parameter("enabled", true)
|
||||
|
||||
|
||||
func deactivate() -> void:
|
||||
if not flash_mode:
|
||||
return
|
||||
sprite.material.set_shader_parameter("enabled", false)
|
||||
|
||||
|
||||
func on_health_change(delta: float, total_health: float) -> void:
|
||||
if delta < 0:
|
||||
activate()
|
||||
await get_tree().create_timer(hit_duration).timeout
|
||||
deactivate()
|
||||
|
||||
if total_health > 0 and delta < 0:
|
||||
handle_hit_fx()
|
||||
|
||||
|
||||
func on_death() -> void:
|
||||
activate()
|
||||
await get_tree().create_timer(hit_duration).timeout
|
||||
deactivate()
|
||||
|
||||
|
||||
func handle_hit_fx() -> void:
|
||||
if not hit_fx:
|
||||
return
|
||||
|
||||
hit_fx.restart()
|
||||
hit_fx.emitting = true
|
@@ -1 +0,0 @@
|
||||
uid://ceq8n7yw7qxpi
|
@@ -1,71 +0,0 @@
|
||||
class_name HomingMissileMotion
|
||||
extends Node
|
||||
|
||||
@export var launch_component: LaunchComponent
|
||||
@export var root: Node2D
|
||||
@export var max_speed: float = 16.0
|
||||
@export var acceleration: float = 8.0
|
||||
@export var detection_area: Area2D
|
||||
@export var max_turn_rate: float = 180.0
|
||||
@export var wobble_strength := 5.0 # degrees
|
||||
@export var drag := 0.98
|
||||
@export var steering_lerp := 0.05 # low = sluggish
|
||||
|
||||
var steering_direction: Vector2 = velocity.normalized()
|
||||
var target: Node2D = null
|
||||
var velocity: Vector2 = Vector2.ZERO
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not detection_area:
|
||||
printerr("No detection area assigned!")
|
||||
return
|
||||
|
||||
if not launch_component:
|
||||
printerr("No launch component assigned!")
|
||||
return
|
||||
|
||||
detection_area.body_entered.connect(on_detection_area_body_entered)
|
||||
velocity = launch_component.get_initial_velocity()
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if not launch_component or not root:
|
||||
return
|
||||
|
||||
if not target:
|
||||
root.position += velocity * delta
|
||||
return
|
||||
|
||||
var to_target := (target.global_position - root.global_position).normalized()
|
||||
steering_direction = steering_direction.lerp(to_target, steering_lerp)
|
||||
|
||||
var angle_to_target := velocity.angle_to(steering_direction)
|
||||
var max_angle := deg_to_rad(max_turn_rate) * delta
|
||||
var clamped_angle = clamp(angle_to_target, - max_angle, max_angle)
|
||||
|
||||
var wobble := deg_to_rad(randf_range(-wobble_strength, wobble_strength))
|
||||
clamped_angle += wobble
|
||||
|
||||
velocity = velocity.rotated(clamped_angle)
|
||||
|
||||
velocity *= drag
|
||||
|
||||
var desired_speed = min(max_speed, velocity.length() + acceleration * delta)
|
||||
velocity = velocity.normalized() * desired_speed
|
||||
|
||||
root.position += velocity * delta
|
||||
root.rotation = velocity.angle()
|
||||
|
||||
|
||||
func on_detection_area_body_entered(body: Node) -> void:
|
||||
if target != null:
|
||||
return
|
||||
|
||||
if body == null:
|
||||
return
|
||||
|
||||
target = body
|
||||
|
||||
|
||||
|
@@ -1 +0,0 @@
|
||||
uid://be8jhvb8t3kif
|
@@ -1,62 +0,0 @@
|
||||
class_name IceEffectComponent
|
||||
extends Node
|
||||
|
||||
@export var components_to_disable: Array = []
|
||||
@export var status_effect_component: StatusEffectComponent
|
||||
@export var ice_fx: Node2D
|
||||
|
||||
var data: StatusEffectDataResource = null
|
||||
var ice_effects_applied: int = 0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not status_effect_component:
|
||||
status_effect_component = get_node("StatusEffectComponent")
|
||||
|
||||
if not status_effect_component:
|
||||
printerr("No StatusEffectComponent assigned!")
|
||||
return
|
||||
|
||||
status_effect_component.effect_applied.connect(on_effect_applied)
|
||||
status_effect_component.effect_removed.connect(on_effect_removed)
|
||||
|
||||
|
||||
func on_effect_applied(effect_data: StatusEffectDataResource) -> void:
|
||||
if effect_data.effect_type == StatusEffectComponent.EffectType.ICE:
|
||||
data = effect_data
|
||||
ice_effects_applied += 1
|
||||
apply_freeze()
|
||||
|
||||
|
||||
func on_effect_removed(effect_type: StatusEffectComponent.EffectType) -> void:
|
||||
if effect_type == StatusEffectComponent.EffectType.ICE:
|
||||
data = null
|
||||
ice_effects_applied -= 1
|
||||
remove_freeze()
|
||||
|
||||
|
||||
func apply_freeze() -> void:
|
||||
if ice_fx:
|
||||
ice_fx.visible = true
|
||||
|
||||
for component_path in components_to_disable:
|
||||
var component: Node = get_node_or_null(component_path)
|
||||
if not component or ice_effects_applied == 0:
|
||||
continue
|
||||
|
||||
component.process_mode = PROCESS_MODE_DISABLED
|
||||
|
||||
|
||||
func remove_freeze() -> void:
|
||||
if ice_effects_applied > 0:
|
||||
return
|
||||
|
||||
if ice_fx:
|
||||
ice_fx.visible = false
|
||||
|
||||
for component_path in components_to_disable:
|
||||
var component: Node = get_node_or_null(component_path)
|
||||
if not component:
|
||||
continue
|
||||
|
||||
component.process_mode = PROCESS_MODE_ALWAYS
|
@@ -1 +0,0 @@
|
||||
uid://dhj4qtwcqmqkj
|
@@ -1,30 +0,0 @@
|
||||
class_name InvulnerabilityComponent
|
||||
extends Node
|
||||
|
||||
@export var duration: float = 1.0
|
||||
@export var flashing_component: FlashingComponent
|
||||
|
||||
var invulnerable: bool = false
|
||||
|
||||
|
||||
func activate() -> void:
|
||||
if invulnerable:
|
||||
return
|
||||
|
||||
invulnerable = true
|
||||
if flashing_component:
|
||||
flashing_component.start_flashing()
|
||||
|
||||
var timer = get_tree().create_timer(duration)
|
||||
timer.timeout.connect(deactivate)
|
||||
|
||||
|
||||
func deactivate() -> void:
|
||||
invulnerable = false
|
||||
if flashing_component:
|
||||
flashing_component.stop_flashing()
|
||||
|
||||
|
||||
func is_invulnerable() -> bool:
|
||||
return invulnerable
|
||||
|
@@ -1 +0,0 @@
|
||||
uid://ijrli0x8ij8v
|
@@ -1,42 +0,0 @@
|
||||
class_name JumpPadComponent
|
||||
extends Node
|
||||
|
||||
@export var jump_force: float = 10.0
|
||||
@export var area2d: Area2D
|
||||
@export var sprite2d: Sprite2D
|
||||
@export var start_animation_index: int = 0
|
||||
@export var animation_duration: float = 0.5
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not area2d:
|
||||
printerr("JumpPadComponent: area2d is not set.")
|
||||
return
|
||||
|
||||
if not sprite2d:
|
||||
printerr("JumpPadComponent: sprite2d is not set.")
|
||||
return
|
||||
|
||||
area2d.body_entered.connect(_on_body_entered)
|
||||
|
||||
|
||||
func _on_body_entered(body: Node2D) -> void:
|
||||
var can_be_launched: CanBeLaunchedComponent = body.get_node_or_null("CanBeLaunchedComponent")
|
||||
if not can_be_launched:
|
||||
return
|
||||
|
||||
if body is PlayerController and body.current_movement is PlatformMovement:
|
||||
handle_launchpad_animation()
|
||||
body.velocity.y = -jump_force
|
||||
if body.current_movement.jump_sfx:
|
||||
body.current_movement.jump_sfx.play()
|
||||
|
||||
|
||||
func handle_launchpad_animation() -> void:
|
||||
if not sprite2d:
|
||||
return
|
||||
|
||||
var timer := get_tree().create_timer(animation_duration)
|
||||
sprite2d.frame = start_animation_index + 1
|
||||
await timer.timeout
|
||||
sprite2d.frame = start_animation_index
|
@@ -1 +0,0 @@
|
||||
uid://dwok2qx4wpkey
|
@@ -1,24 +0,0 @@
|
||||
class_name KillPlayerOutOfScreen
|
||||
extends Node
|
||||
|
||||
@export var screen_notifier: VisibleOnScreenNotifier2D
|
||||
@export var health_component: HealthComponent
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not screen_notifier:
|
||||
printerr("KillPlayerOutOfScreen: screen_notifier is not set.")
|
||||
return
|
||||
|
||||
if not health_component:
|
||||
printerr("KillPlayerOutOfScreen: health_component is not set.")
|
||||
return
|
||||
|
||||
screen_notifier.screen_exited.connect(out_of_screen)
|
||||
|
||||
|
||||
func out_of_screen() -> void:
|
||||
if not health_component:
|
||||
return
|
||||
|
||||
health_component.decrease_health(6000)
|
@@ -1 +0,0 @@
|
||||
uid://cfeoalic0mu2j
|
@@ -1,32 +0,0 @@
|
||||
class_name KnockbackComponent
|
||||
extends Node
|
||||
|
||||
@export var character_body: CharacterBody2D
|
||||
@export var knockback_force: float = 25.0
|
||||
|
||||
var knockback_mode: bool = false
|
||||
var knockback_frames: int = 0
|
||||
|
||||
func apply_knockback(force: float, delta: float) -> void:
|
||||
var velocity = character_body.velocity.normalized()
|
||||
var knockback_dir = Vector2(sign(velocity.x) * 1.0, 0.4)
|
||||
var knockback_vector = -knockback_dir * force * delta
|
||||
character_body.velocity += knockback_vector
|
||||
|
||||
func _on_health_component_on_health_change(delta: float, total_health: float) -> void:
|
||||
if total_health <= 0.0 and delta < 0.0:
|
||||
return
|
||||
knockback_mode = true
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if knockback_mode:
|
||||
knockback_frames += 1
|
||||
if knockback_frames > 1:
|
||||
knockback_mode = false
|
||||
knockback_frames = 0
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if knockback_mode:
|
||||
apply_knockback(knockback_force, delta)
|
||||
|
||||
|
@@ -1 +0,0 @@
|
||||
uid://nogmyshjrv57
|
@@ -1,17 +0,0 @@
|
||||
class_name LaunchComponent
|
||||
extends Node2D
|
||||
|
||||
@export var root: Node2D
|
||||
@export var initial_direction: Vector2 = Vector2.RIGHT
|
||||
@export var speed: float = 16.0
|
||||
@export var spawn_position: Vector2 = Vector2.ZERO
|
||||
@export var spawn_rotation: float = 0.0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
root.global_position = spawn_position
|
||||
root.global_rotation = spawn_rotation
|
||||
|
||||
|
||||
func get_initial_velocity() -> Vector2:
|
||||
return initial_direction.normalized() * speed
|
@@ -1 +0,0 @@
|
||||
uid://873un8agkyja
|
@@ -1,49 +0,0 @@
|
||||
class_name LeverComponent
|
||||
extends Node
|
||||
|
||||
@export var area2d: Area2D
|
||||
@export var sprite2d: Sprite2D
|
||||
@export var start_animation_index: int = 0
|
||||
@export var animation_duration: float = 0.5
|
||||
@export var sfx: AudioStreamPlayer2D
|
||||
signal activated
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not area2d:
|
||||
printerr("LeverComponent: area2d is not set.")
|
||||
return
|
||||
|
||||
if not sprite2d:
|
||||
printerr("LeverComponent: sprite2d is not set.")
|
||||
return
|
||||
|
||||
area2d.body_entered.connect(_on_body_entered)
|
||||
area2d.area_entered.connect(_on_area_entered)
|
||||
|
||||
|
||||
func _on_body_entered(body: Node2D) -> void:
|
||||
var trigger_lever: TriggerLeverComponent = body.get_node_or_null("TriggerLeverComponent")
|
||||
if not trigger_lever:
|
||||
return
|
||||
|
||||
activate()
|
||||
|
||||
|
||||
|
||||
func _on_area_entered(area: Area2D) -> void:
|
||||
var trigger_lever: TriggerLeverComponent = area.get_node_or_null("TriggerLeverComponent")
|
||||
if not trigger_lever:
|
||||
return
|
||||
|
||||
activate()
|
||||
|
||||
|
||||
func activate() -> void:
|
||||
activated.emit()
|
||||
if sfx:
|
||||
sfx.play()
|
||||
sprite2d.frame = start_animation_index + 1
|
||||
var timer := get_tree().create_timer(animation_duration)
|
||||
await timer.timeout
|
||||
sprite2d.frame = start_animation_index
|
@@ -1 +0,0 @@
|
||||
uid://hyuwsp1b336a
|
@@ -1,22 +0,0 @@
|
||||
class_name LifetimeComponent
|
||||
extends Node
|
||||
|
||||
@export var root: Node
|
||||
@export var life_time: float = 5.0
|
||||
@export var timer: Timer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not root:
|
||||
printerr("Root node not set.")
|
||||
return
|
||||
|
||||
if not timer:
|
||||
printerr("Timer node not set.")
|
||||
return
|
||||
|
||||
timer.timeout.connect(on_timer_timeout)
|
||||
|
||||
|
||||
func on_timer_timeout() -> void:
|
||||
root.queue_free()
|
@@ -1 +0,0 @@
|
||||
uid://bvsgg8lu0a8m6
|
@@ -1,63 +0,0 @@
|
||||
class_name MagneticSkillComponent
|
||||
extends Node
|
||||
|
||||
@export var magnetic_area: Area2D
|
||||
@export var root: Node2D
|
||||
@export var magnetic_move_duration: float = 1.25
|
||||
|
||||
var collectables_to_pickup: Array = []
|
||||
|
||||
|
||||
func has_component_in_children(node: Node, component_name: String) -> bool:
|
||||
if not node:
|
||||
return false
|
||||
|
||||
if node.has_node(component_name):
|
||||
return true
|
||||
|
||||
for child in node.get_children():
|
||||
if has_component_in_children(child, component_name):
|
||||
return true
|
||||
|
||||
return false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not magnetic_area:
|
||||
printerr("No magnetic_area assigned!")
|
||||
return
|
||||
|
||||
magnetic_area.body_entered.connect(on_magnetic_area_body_entered)
|
||||
magnetic_area.area_entered.connect(on_magnetic_area_area_entered)
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
for collectable in collectables_to_pickup:
|
||||
if not is_instance_valid(collectable):
|
||||
collectables_to_pickup.erase(collectable)
|
||||
continue
|
||||
|
||||
move_collectable_to_root(collectable)
|
||||
|
||||
|
||||
func on_magnetic_area_body_entered(_body: Node2D) -> void:
|
||||
pass
|
||||
|
||||
|
||||
func on_magnetic_area_area_entered(area: Area2D) -> void:
|
||||
if not has_component_in_children(area, "Collectable"):
|
||||
return
|
||||
|
||||
if not collectables_to_pickup.has(area):
|
||||
collectables_to_pickup.append(area)
|
||||
|
||||
|
||||
func move_collectable_to_root(collectable: Node2D) -> void:
|
||||
if not is_instance_valid(collectable):
|
||||
return
|
||||
|
||||
var direction: Vector2 = (root.global_position - collectable.global_position).normalized()
|
||||
var speed: float = direction.length() / magnetic_move_duration
|
||||
|
||||
collectable.global_position += direction.normalized() * speed
|
||||
|
@@ -1 +0,0 @@
|
||||
uid://ce8w71vgv37pt
|
@@ -1,21 +0,0 @@
|
||||
class_name OutOfScreenComponent
|
||||
extends Node
|
||||
|
||||
@export var visibility_notifier: VisibleOnScreenNotifier2D
|
||||
@export var root: Node
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if not visibility_notifier:
|
||||
printerr("Visibility notifier not set.")
|
||||
return
|
||||
|
||||
visibility_notifier.screen_exited.connect(_on_screen_exited)
|
||||
|
||||
|
||||
func _on_screen_exited() -> void:
|
||||
if not root:
|
||||
printerr("Root node not set.")
|
||||
return
|
||||
|
||||
root.queue_free()
|
@@ -1 +0,0 @@
|
||||
uid://1tnr46o1ib4u
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user