Csharp rewrite (#4)

* Implement BeamComponent in C# and enhance marketplace button functionality

* Add core game components including ConfigFileHandler, GameManager, SaveSystem, and UIManager

* cleanup

* Add new components: CanPickUpComponent, CollapsableComponent, DestroyableComponent, EffectInflictorComponent, StatusEffectComponent, and StatusEffectDataResource

* Add new components: EnemyDeathComponent, EnemyWaveTriggerComponent, and ExitDoorComponent

* Add new components: ExplosiveComponent, FadeAwayComponent, FireEffectComponent, FlipComponent, GravityMotionComponent, LaunchComponent, and update PlatformMovement with LastDirection property

* Add new components: HealComponent, HitComponent, HomingMissileMotionComponent, LeverComponent, and TriggerLeverComponent

* Refactor GameManager session state handling and add new components: CanBeLaunchedComponent, IceEffectComponent, JumpPadComponent, KillPlayerOutOfScreenComponent, KnockbackComponent, LifetimeComponent, MagneticSkillComponent, OutOfScreenComponent, PeriodicShootingComponent, PlayerDeathComponent, ProgressiveDamageComponent, ProjectileComponent, ProjectileInitComponent, RequirementComponent, ScoreComponent, ShipMovementComponent, ShipShooterComponent, and SideToSideMovementComponent

* Add new components: CannotStompComponent, SkillUnlockedComponent, SpaceshipEnterComponent, SpaceshipExitComponent, SpinComponent, StompDamageComponent, StraightMotionComponent, TerrainHitFx, TooltipComponent, TrailComponent, and UnlockOnRequirementComponent

* Add new components: BrickThrowComponent, BulletComponent, CageComponent, ChaseLevelComponent, CleanupComponent, and ThrowInputResource classes; implement game saving and loading logic in SaveSystem

* Add audio settings management and platform movement component

* Add ChargeProgressBar, Credits, and GameOverScreen components for UI management

* Add UID files for ConfigFileHandler, GameManager, SaveSystem, and UIManager components

* Add README.md file with game description and features; include project license and contribution guidelines

* Add Hud component for UI management; display health, coins, and lives

* Add MainMenu and Marketplace components; implement game management and skill unlocking features

* Add PauseMenu, SettingsMenu, and SkillButton components; enhance game management and UI functionality
This commit is contained in:
2025-08-15 00:45:57 +02:00
committed by GitHub
parent 2ad0fe26d2
commit d84f7d1740
188 changed files with 4774 additions and 18 deletions

View File

@@ -0,0 +1,70 @@
using Godot;
namespace Mr.BrickAdventures.scripts.Resources;
public partial class ChargeThrowInputResource : ThrowInputResource
{
[Export] public float MinPower { get; set; } = 0.5f;
[Export] public float MaxPower { get; set; } = 2.0f;
[Export] public float MaxChargeTime { get; set; } = 2.0f;
[Export] public float MinChargeDuration { get; set; } = 0.1f;
private bool _isCharging = false;
private float _chargeStartTime = 0f;
[Signal] public delegate void ChargeStartedEventHandler();
[Signal] public delegate void ChargeUpdatedEventHandler(float chargeRatio);
[Signal] public delegate void ChargeStoppedEventHandler();
public override void ProcessInput(InputEvent @event)
{
if (@event.IsActionPressed("attack"))
{
_isCharging = true;
_chargeStartTime = Time.GetTicksMsec() / 1000f;
EmitSignalChargeStarted();
}
if (@event.IsActionReleased("attack") && _isCharging)
{
var power = CalculatePower();
_isCharging = false;
EmitSignalThrowRequested(power);
EmitSignalChargeStopped();
}
}
public override void Update(double delta)
{
if (!_isCharging) return;
var t = Mathf.Clamp(GetChargeRatio(), MinPower, MaxPower);
EmitSignalChargeUpdated(t);
}
public override bool SupportsCharging()
{
return true;
}
private float CalculatePower()
{
var now = Time.GetTicksMsec() / 1000f;
var heldTime = now - _chargeStartTime;
if (heldTime < MinChargeDuration)
return MinPower;
var t = Mathf.Clamp(heldTime / MaxChargeTime, 0f, 1f);
return Mathf.Lerp(MinPower, MaxPower, t);
}
private float GetChargeRatio()
{
if (!_isCharging) return MinPower;
var now = Time.GetTicksMsec() / 1000f;
var heldTime = now - _chargeStartTime;
var t = Mathf.Clamp(heldTime / MaxChargeTime, 0f, 1f);
return Mathf.Lerp(MinPower, MaxPower, t);
}
}

View File

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

View File

@@ -0,0 +1,9 @@
using Godot;
namespace Mr.BrickAdventures.scripts.Resources;
public partial class CollectableResource : Resource
{
[Export] public Variant Amount { get; set; } = 0.0;
[Export] public CollectableType Type { get; set; }
}

View File

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

View File

@@ -0,0 +1,8 @@
namespace Mr.BrickAdventures.scripts.Resources;
public enum CollectableType
{
Coin,
Kid,
Health,
}

View File

@@ -0,0 +1 @@
uid://2ql8wj3vfeke

View File

@@ -0,0 +1,19 @@
using System;
using Godot;
using Godot.Collections;
namespace Mr.BrickAdventures.scripts.Resources;
public partial class SkillData : Resource
{
[Export] public string Name { get; set; } = "New Skill";
[Export] public string Description { get; set; } = "New Skill";
[Export] public Dictionary<string, Variant> Config { get; set; } = new();
[Export] public int Cost { get; set; } = 0;
[Export] public Texture2D Icon { get; set; }
[Export] public bool IsActive { get; set; } = false;
[Export] public int Level { get; set; } = 1;
[Export] public int MaxLevel { get; set; } = 1;
[Export] public SkillType Type { get; set; } = SkillType.Throw;
[Export] public PackedScene Node { get; set; }
}

View File

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

View File

@@ -0,0 +1,8 @@
namespace Mr.BrickAdventures.scripts.Resources;
public enum SkillType
{
Attack,
Throw,
Misc,
}

View File

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

View File

@@ -0,0 +1,10 @@
using Godot;
namespace Mr.BrickAdventures.scripts.Resources;
public partial class StatusEffectDataResource : Resource
{
[Export] public float Duration { get; set; } = 1f;
[Export] public float DamagePerSecond { get; set; } = 0.25f;
[Export] public StatusEffectType Type { get; set; }
}

View File

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

View File

@@ -0,0 +1,8 @@
namespace Mr.BrickAdventures.scripts.Resources;
public enum StatusEffectType
{
None,
Fire,
Ice
}

View File

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

View File

@@ -0,0 +1,20 @@
using Godot;
using Mr.BrickAdventures.scripts.interfaces;
namespace Mr.BrickAdventures.scripts.Resources;
public partial class TapThrowInputResource : ThrowInputResource
{
public override void Update(double delta)
{
if (Input.IsActionPressed("attack"))
{
EmitSignalThrowRequested(1f);
}
}
public override bool SupportsCharging()
{
return false;
}
}

View File

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

View File

@@ -0,0 +1,24 @@
using Godot;
using Mr.BrickAdventures.scripts.interfaces;
namespace Mr.BrickAdventures.scripts.Resources;
public abstract partial class ThrowInputResource : Resource, IThrowInput
{
[Signal] public delegate void ThrowRequestedEventHandler(float powerMultiplier = 1f);
public virtual void ProcessInput(InputEvent @event)
{
throw new System.NotImplementedException();
}
public virtual void Update(double delta)
{
throw new System.NotImplementedException();
}
public virtual bool SupportsCharging()
{
throw new System.NotImplementedException();
}
}

View File

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

15
scripts/Screenshot.cs Normal file
View File

@@ -0,0 +1,15 @@
using Godot;
namespace Mr.BrickAdventures.scripts;
public partial class Screenshot : Node
{
public override void _Process(double delta)
{
if (!OS.IsDebugBuild() || !Input.IsActionJustPressed("screenshot")) return;
var img = GetViewport().GetTexture().GetImage();
var id = OS.GetUniqueId() + "_" + Time.GetDatetimeStringFromSystem();
var path = "user://screenshots/screenshot_" + id + ".png";
img.SavePng(path);
}
}

View File

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

144
scripts/SkillManager.cs Normal file
View File

@@ -0,0 +1,144 @@
using Godot;
using Godot.Collections;
using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts;
public partial class SkillManager : Node
{
private GameManager _gameManager;
[Export] public Array<SkillData> AvailableSkills { get; set; } = [];
public Dictionary ActiveComponents { get; private set; } = new();
public override void _Ready()
{
_gameManager = GetNode<GameManager>("/root/GameManager");
ApplyUnlockedSkills();
}
public void AddSkill(SkillData skillData)
{
if (ActiveComponents.ContainsKey(skillData.Name))
return;
if (skillData.Type == SkillType.Throw)
{
var unlocked = _gameManager.GetUnlockedSkills();
foreach (var skill in unlocked)
{
SkillData data = null;
foreach (var s in AvailableSkills)
{
if (s == (SkillData)skill)
{
data = s;
break;
}
}
if (data != null && data.Type == SkillType.Throw)
RemoveSkill(data.Name);
}
}
var instance = skillData.Node.Instantiate();
foreach (var key in skillData.Config.Keys)
{
if (instance.HasMethod("get")) // rough presence check
{
var value = skillData.Config[key];
var parent = GetParent();
if (value.VariantType == Variant.Type.NodePath)
{
var np = (NodePath)value;
if (parent.HasNode(np))
value = parent.GetNode(np);
else if (instance.HasNode(np))
value = instance.GetNode(np);
else
continue;
}
// Set via property if exists
instance.Set(key, value);
}
}
Owner.AddChild(instance);
ActiveComponents[skillData.Name] = instance;
}
public void RemoveSkill(string skillName)
{
if (!ActiveComponents.TryGetValue(skillName, out var component))
return;
var inst = (Node)component;
if (IsInstanceValid(inst))
inst.QueueFree();
var skills = _gameManager.GetUnlockedSkills();
foreach (SkillData s in skills)
{
if (s.Name == skillName)
{
s.IsActive = false;
break;
}
}
ActiveComponents.Remove(skillName);
}
public void ApplyUnlockedSkills()
{
foreach (var sd in AvailableSkills)
{
if (_gameManager.IsSkillUnlocked(sd))
{
GD.Print("Applying skill: ", sd.Name);
CallDeferred(MethodName.AddSkill, sd);
}
else
{
RemoveSkill(sd.Name);
}
}
}
public SkillData GetSkillByName(string skillName)
{
foreach (var sd in AvailableSkills)
if (sd.Name == skillName) return sd;
return null;
}
public void ActivateSkill(SkillData skill)
{
if (!ActiveComponents.ContainsKey(skill.Name))
{
AddSkill(skill);
skill.IsActive = true;
}
}
public void DeactivateSkill(SkillData skill)
{
if (ActiveComponents.ContainsKey(skill.Name))
{
RemoveSkill(skill.Name);
skill.IsActive = false;
}
}
public void ToggleSkillActivation(SkillData skill)
{
if (skill == null) return;
if (ActiveComponents.ContainsKey(skill.Name))
DeactivateSkill(skill);
else
ActivateSkill(skill);
}
}

View File

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

102
scripts/UI/AudioSettings.cs Normal file
View File

@@ -0,0 +1,102 @@
using Godot;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.UI;
public partial class AudioSettings : Node
{
[Export] public Slider MasterVolumeSlider { get; set; }
[Export] public Slider MusicVolumeSlider { get; set; }
[Export] public Slider SfxVolumeSlider { get; set; }
[Export] public Control AudioSettingsControl { get; set; }
[Export] public float MuteThreshold { get; set; } = -20f;
private UIManager _uiManager;
private ConfigFileHandler _configFileHandler;
public override void _Ready()
{
_uiManager = GetNode<UIManager>("/root/UIManager");
_configFileHandler = GetNode<ConfigFileHandler>("/root/ConfigFileHandler");
Initialize();
MasterVolumeSlider.ValueChanged += OnMasterVolumeChanged;
MusicVolumeSlider.ValueChanged += OnMusicVolumeChanged;
SfxVolumeSlider.ValueChanged += OnSfxVolumeChanged;
}
public override void _UnhandledInput(InputEvent @event)
{
if (!@event.IsActionReleased("ui_cancel")) return;
if (!_uiManager.IsScreenOnTop(AudioSettingsControl)) return;
SaveSettings();
_uiManager.PopScreen();
}
private void OnSfxVolumeChanged(double value)
{
AudioServer.SetBusVolumeDb(AudioServer.GetBusIndex("sfx"), (float)value);
HandleMute(AudioServer.GetBusIndex("sfx"), (float)value);
}
private void OnMusicVolumeChanged(double value)
{
AudioServer.SetBusVolumeDb(AudioServer.GetBusIndex("music"), (float)value);
HandleMute(AudioServer.GetBusIndex("music"), (float)value);
}
private void OnMasterVolumeChanged(double value)
{
AudioServer.SetBusVolumeDb(AudioServer.GetBusIndex("Master"), (float)value);
HandleMute(AudioServer.GetBusIndex("Master"), (float)value);
}
private void Initialize()
{
var volumeDb = AudioServer.GetBusVolumeDb(AudioServer.GetBusIndex("Master"));
MasterVolumeSlider.Value = volumeDb;
MasterVolumeSlider.MinValue = MuteThreshold;
MasterVolumeSlider.MaxValue = 0f;
var musicVolumeDb = AudioServer.GetBusVolumeDb(AudioServer.GetBusIndex("music"));
MusicVolumeSlider.Value = musicVolumeDb;
MusicVolumeSlider.MinValue = MuteThreshold;
MusicVolumeSlider.MaxValue = 0f;
var sfxVolumeDb = AudioServer.GetBusVolumeDb(AudioServer.GetBusIndex("sfx"));
SfxVolumeSlider.Value = sfxVolumeDb;
SfxVolumeSlider.MinValue = MuteThreshold;
SfxVolumeSlider.MaxValue = 0f;
}
private void HandleMute(int busIndex, float value)
{
AudioServer.SetBusMute(busIndex, value <= MuteThreshold);
}
private void SaveSettings()
{
var settingsConfig = _configFileHandler.SettingsConfig;
settingsConfig.SetValue("audio_settings", "master_volume", MasterVolumeSlider.Value);
settingsConfig.SetValue("audio_settings", "music_volume", MusicVolumeSlider.Value);
settingsConfig.SetValue("audio_settings", "sfx_volume", SfxVolumeSlider.Value);
settingsConfig.SetValue("audio_settings", "mute_threshold", MuteThreshold);
settingsConfig.Save(ConfigFileHandler.SettingsPath);
}
private void LoadSettings()
{
var settingsConfig = _configFileHandler.SettingsConfig;
if (!settingsConfig.HasSection("audio_settings")) return;
var masterVolume = (float)settingsConfig.GetValue("audio_settings", "master_volume", MasterVolumeSlider.Value);
var musicVolume = (float)settingsConfig.GetValue("audio_settings", "music_volume", MusicVolumeSlider.Value);
var sfxVolume = (float)settingsConfig.GetValue("audio_settings", "sfx_volume", SfxVolumeSlider.Value);
var muteThreshold = (float)settingsConfig.GetValue("audio_settings", "mute_threshold", MuteThreshold);
MasterVolumeSlider.Value = masterVolume;
MusicVolumeSlider.Value = musicVolume;
SfxVolumeSlider.Value = sfxVolume;
MuteThreshold = muteThreshold;
}
}

View File

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

View File

@@ -0,0 +1,81 @@
using Godot;
using Mr.BrickAdventures.scripts.components;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.UI;
public partial class ChargeProgressBar : Node
{
[Export] public ProgressBar ProgressBar { get; set; }
[Export] public BrickThrowComponent ThrowComponent { get; set; }
private ChargeThrowInputResource _throwInput;
public override void _Ready()
{
Owner.ChildEnteredTree += OnNodeEntered;
ProgressBar.Hide();
SetupDependencies();
}
private void OnNodeEntered(Node node)
{
if (node is not BrickThrowComponent throwComponent || ThrowComponent != null) return;
ThrowComponent = throwComponent;
SetupDependencies();
}
private void SetupDependencies()
{
if (ThrowComponent.ThrowInputBehavior is ChargeThrowInputResource throwInput)
{
_throwInput = throwInput;
}
else
{
_throwInput = null;
}
if (_throwInput == null)
{
return;
}
if (!_throwInput.SupportsCharging())
{
ProgressBar.Hide();
return;
}
SetupProgressBar();
_throwInput.ChargeStarted += OnChargeStarted;
_throwInput.ChargeStopped += OnChargeStopped;
_throwInput.ChargeUpdated += OnChargeUpdated;
}
private void SetupProgressBar()
{
ProgressBar.MinValue = _throwInput.MinPower;
ProgressBar.MaxValue = _throwInput.MaxPower;
ProgressBar.Value = _throwInput.MinPower;
ProgressBar.Step = 0.01f;
ProgressBar.Hide();
}
private void OnChargeStarted()
{
ProgressBar.Show();
}
private void OnChargeStopped()
{
ProgressBar.Hide();
}
private void OnChargeUpdated(float chargeRatio)
{
ProgressBar.Value = chargeRatio;
ProgressBar.Show();
}
}

View File

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

23
scripts/UI/Credits.cs Normal file
View File

@@ -0,0 +1,23 @@
using Godot;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.UI;
public partial class Credits : Control
{
private UIManager _uiManager;
public override void _Ready()
{
_uiManager = GetNode<UIManager>("/root/UIManager");
}
public override void _UnhandledInput(InputEvent @event)
{
if (!@event.IsActionPressed("ui_cancel")) return;
if (_uiManager != null && _uiManager.IsScreenOnTop(this))
{
_uiManager.PopScreen();
}
}
}

View File

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

View File

@@ -0,0 +1,39 @@
using Godot;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.UI;
public partial class GameOverScreen : Node
{
[Export] public Control GameOverPanel { get; set; }
[Export] public Button RestartButton { get; set; }
[Export] public Button MainMenuButton { get; set; }
[Export] public PackedScene MainMenuScene { get; set; }
private GameManager _gameManager;
public override void _Ready()
{
_gameManager = GetNode<GameManager>("/root/GameManager");
RestartButton.Pressed += OnRestartClicked;
MainMenuButton.Pressed += OnMainMenuClicked;
}
private void OnMainMenuClicked()
{
_gameManager.ResetPlayerState();
GetTree().ChangeSceneToPacked(MainMenuScene);
}
private void OnRestartClicked()
{
_gameManager.RestartGame();
}
public void OnPlayerDeath()
{
if (_gameManager == null || _gameManager.GetLives() != 0) return;
GameOverPanel.Show();
}
}

View File

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

43
scripts/UI/Hud.cs Normal file
View File

@@ -0,0 +1,43 @@
using Godot;
using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.components;
namespace Mr.BrickAdventures.scripts.UI;
public partial class Hud : Node
{
[Export] public HealthComponent Health { get; set; }
[Export] public Label CoinsLabel { get; set; }
[Export] public ProgressBar HealthBar { get; set; }
[Export] public Label LivesLabel { get; set; }
private GameManager _gameManager;
public override void _Ready()
{
_gameManager = GetNode<GameManager>("/root/GameManager");
}
public override void _Process(double delta)
{
SetHealthBar();
SetLivesLabel();
SetCoinsLabel();
}
private void SetCoinsLabel()
{
CoinsLabel.Text = Tr("COINS_LABEL") + ": " + _gameManager.GetCoins();
}
private void SetLivesLabel()
{
LivesLabel.Text = Tr("LIVES_LABEL") + ": " + _gameManager.GetLives();
}
private void SetHealthBar()
{
HealthBar.Value = Health.Health;
HealthBar.MaxValue = Health.MaxHealth;
}
}

1
scripts/UI/Hud.cs.uid Normal file
View File

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

67
scripts/UI/MainMenu.cs Normal file
View File

@@ -0,0 +1,67 @@
using Godot;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.UI;
public partial class MainMenu : Node
{
[Export] public Control MainMenuControl { get; set; }
[Export] public Button NewGameButton { get; set; }
[Export] public Button ContinueButton { get; set; }
[Export] public Button SettingsButton { get; set; }
[Export] public Button CreditsButton { get; set; }
[Export] public Button ExitButton { get; set; }
[Export] public Label VersionLabel { get; set; }
[Export] public Control SettingsControl { get; set; }
[Export] public Control CreditsControl { get; set; }
private SaveSystem _saveSystem;
private GameManager _gameManager;
private UIManager _uiManager;
public override void _Ready()
{
_saveSystem = GetNode<SaveSystem>("/root/SaveSystem");
_gameManager = GetNode<GameManager>("/root/GameManager");
_uiManager = GetNode<UIManager>("/root/UIManager");
NewGameButton.Pressed += OnNewGamePressed;
ContinueButton.Pressed += OnContinuePressed;
SettingsButton.Pressed += OnSettingsPressed;
CreditsButton.Pressed += OnCreditsPressed;
ExitButton.Pressed += OnExitPressed;
VersionLabel.Text = $"v. {ProjectSettings.GetSetting("application/config/version")}";
ContinueButton.Disabled = !_saveSystem.CheckSaveExists();
if (_saveSystem.CheckSaveExists())
ContinueButton.GrabFocus();
else
NewGameButton.GrabFocus();
}
private void OnExitPressed()
{
_gameManager.QuitGame();
}
private void OnCreditsPressed()
{
_uiManager.PushScreen(CreditsControl);
}
private void OnSettingsPressed()
{
_uiManager.PushScreen(SettingsControl);
}
private void OnContinuePressed()
{
_gameManager.ContinueGame();
}
private void OnNewGamePressed()
{
_gameManager.StartNewGame();
}
}

View File

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

142
scripts/UI/Marketplace.cs Normal file
View File

@@ -0,0 +1,142 @@
using System.Collections.Generic;
using Godot;
using Godot.Collections;
using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.components;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.UI;
public partial class Marketplace : Node
{
[Export] public Array<SkillData> Skills { get; set; } = [];
[Export] public GridContainer ToUnlockGrid { get; set; }
[Export] public GridContainer UnlockedGrid { get; set; }
[Export] public Font Font { get; set; }
[Export] public SkillUnlockedComponent SkillUnlockedComponent { get; set; }
[Export] public Array<Node> ComponentsToDisable { get; set; } = [];
[Export] public PackedScene MarketplaceButtonScene { get; set; }
[Export] public PackedScene SkillButtonScene { get; set; }
private GameManager _gameManager;
private readonly List<Button> _unlockButtons = [];
private readonly List<SkillButton> _skillButtons = [];
public override void _Ready()
{
var skillsToUnlock = new List<SkillData>();
foreach (var skill in Skills) skillsToUnlock.Add(skill);
foreach (var skill in skillsToUnlock) CreateUpgradeButton(skill);
var unlockedSkills = _gameManager.GetUnlockedSkills();
foreach (var skill in unlockedSkills) CreateSkillButton(skill);
SkillUnlockedComponent.SkillUnlocked += OnSkillUnlocked;
}
public override void _ExitTree()
{
SkillUnlockedComponent.SkillUnlocked -= OnSkillUnlocked;
}
public override void _Input(InputEvent @event)
{
var root = Owner as Control;
if (!@event.IsActionPressed("show_marketplace")) return;
if (root != null && root.IsVisible())
{
root.Hide();
foreach (var c in ComponentsToDisable) c.ProcessMode = ProcessModeEnum.Inherit;
}
else
{
root?.Show();
foreach (var c in ComponentsToDisable) c.ProcessMode = ProcessModeEnum.Disabled;
}
}
private string GetButtonText(SkillData skill)
{
return $"{Tr(skill.Name)} {skill.Cost}";
}
private void OnSkillUnlocked(SkillData skill)
{
if (_skillButtons.Count == 0) CreateSkillButton(skill);
foreach (var btn in _skillButtons)
{
if (btn.Data.IsActive)
btn.Activate();
else
btn.Deactivate();
}
}
private void CreateSkillButton(SkillData skill)
{
var button = SkillButtonScene.Instantiate<SkillButton>();
button.Data = skill;
button.Setup();
button.Pressed += () => OnSkillButtonPressed(button);
button.Activate();
_skillButtons.Add(button);
UnlockedGrid.AddChild(button);
UnlockedGrid.QueueSort();
}
private void CreateUpgradeButton(SkillData skill)
{
var button = MarketplaceButtonScene.Instantiate<MarketplaceButton>();
button.Text = GetButtonText(skill);
button.Data = skill;
button.Icon = skill.Icon;
button.Pressed += () => OnUpgradeButtonPressed(skill);
_unlockButtons.Add(button);
UnlockedGrid.AddChild(button);
UnlockedGrid.QueueSort();
}
private void OnUpgradeButtonPressed(SkillData skill)
{
if (_gameManager.IsSkillUnlocked(skill))
{
if (skill.Level < skill.MaxLevel)
{
SkillUnlockedComponent.TryUpgradeSkill(skill);
if (!skill.IsActive) SkillUnlockedComponent.SkillManager.ToggleSkillActivation(skill);
}
else
{
SkillUnlockedComponent.SkillManager.ToggleSkillActivation(skill);
}
}
else
{
SkillUnlockedComponent.TryUnlockSkill(skill);
}
}
private void RemoveButton(SkillData skill)
{
foreach (var node in ToUnlockGrid.GetChildren())
{
var child = (Button)node;
if (child.Text != GetButtonText(skill)) continue;
child.QueueFree();
break;
}
}
private void OnSkillButtonPressed(SkillButton button)
{
SkillUnlockedComponent.SkillManager.ToggleSkillActivation(button.Data);
}
}

View File

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

View File

@@ -0,0 +1,62 @@
using Godot;
using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.components;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.UI;
public partial class MarketplaceButton : Button
{
[Export] public SkillData Data { get; set; }
[Export] public Texture2D UnlockedSkillIcon { get; set; }
[Export] public Texture2D LockedSkillIcon { get; set; }
[Export] public Container SkillLevelContainer { get; set; }
private GameManager _gameManager;
private SkillUnlockedComponent _skillUnlockedComponent;
public override void _Ready()
{
_gameManager = GetNode<GameManager>("/root/GameManager");
Setup();
var player = _gameManager.Player;
var skillUnlockerComponent = player?.GetNodeOrNull<SkillUnlockedComponent>("SkillUnlockerComponent");
if (skillUnlockerComponent == null) return;
skillUnlockerComponent.SkillUnlocked += OnSkillUnlock;
}
public override void _ExitTree()
{
_skillUnlockedComponent.SkillUnlocked -= OnSkillUnlock;
}
private void Setup()
{
if (Data == null) return;
for (var i = 0; i < Data.MaxLevel; i++)
{
var icon = new TextureRect()
{
Texture = i < Data.Level ? UnlockedSkillIcon : LockedSkillIcon,
};
SkillLevelContainer.AddChild(icon);
}
}
private void OnSkillUnlock(SkillData skill)
{
if (skill.Name != Data.Name) return;
for (var i = 0; i < Data.MaxLevel; i++)
{
var icon = SkillLevelContainer.GetChildOrNull<TextureRect>(i);
if (icon == null) continue;
icon.Texture = i < Data.Level ? UnlockedSkillIcon : LockedSkillIcon;
Disabled = i >= Data.Level;
}
}
}

View File

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

67
scripts/UI/PauseMenu.cs Normal file
View File

@@ -0,0 +1,67 @@
using Godot;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.UI;
public partial class PauseMenu : Node
{
[Export] public Control PauseMenuControl { get; set; }
[Export] public Control SettingsControl { get; set; }
[Export] public Button ResumeButton { get; set; }
[Export] public Button MainMenuButton { get; set; }
[Export] public Button QuitButton { get; set; }
[Export] public Button SettingsButton { get; set; }
[Export] public PackedScene MainMenuScene { get; set; }
private GameManager _gameManager;
private UIManager _uiManager;
public override void _Ready()
{
_gameManager = GetNode<GameManager>("/root/GameManager");
_uiManager = GetNode<UIManager>("/root/UIManager");
ResumeButton.Pressed += OnResumePressed;
MainMenuButton.Pressed += OnMainMenuPressed;
QuitButton.Pressed += OnQuitPressed;
SettingsButton.Pressed += OnSettingsPressed;
PauseMenuControl.Hide();
}
public override void _UnhandledInput(InputEvent @event)
{
if (!@event.IsActionPressed("pause")) return;
if (_uiManager.IsVisibleOnStack(PauseMenuControl))
OnResumePressed();
else
{
_uiManager.PushScreen(PauseMenuControl);
_gameManager.PauseGame();
}
}
private void OnSettingsPressed()
{
_uiManager.PushScreen(SettingsControl);
_gameManager.PauseGame();
}
private void OnQuitPressed()
{
_gameManager.QuitGame();
}
private void OnMainMenuPressed()
{
_gameManager.ResumeGame();
_gameManager.ResetCurrentSessionState();
GetTree().ChangeSceneToPacked(MainMenuScene);
}
private void OnResumePressed()
{
_uiManager.PopScreen();
_gameManager.ResumeGame();
}
}

View File

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

View File

@@ -0,0 +1,60 @@
using Godot;
using Mr.BrickAdventures.Autoloads;
namespace Mr.BrickAdventures.scripts.UI;
public partial class SettingsMenu : Node
{
[Export] public Control InputSettingsControl { get; set; }
[Export] public Control AudioSettingsControl { get; set; }
[Export] public Control DisplaySettingsControl { get; set; }
[Export] public Control GameplaySettingsControl { get; set; }
[Export] public Control SettingsMenuControl { get; set; }
[Export] public Button InputSettingsButton { get; set; }
[Export] public Button AudioSettingsButton { get; set; }
[Export] public Button DisplaySettingsButton { get; set; }
[Export] public Button GameplaySettingsButton { get; set; }
private UIManager _uiManager;
public override void _Ready()
{
_uiManager = GetNode<UIManager>("/root/UIManager");
InputSettingsButton.Pressed += OnInputSettingsPressed;
AudioSettingsButton.Pressed += OnAudioSettingsPressed;
DisplaySettingsButton.Pressed += OnDisplaySettingsPressed;
GameplaySettingsButton.Pressed += OnGameplaySettingsPressed;
InputSettingsControl.Hide();
AudioSettingsControl.Hide();
DisplaySettingsControl.Hide();
GameplaySettingsControl.Hide();
}
public override void _UnhandledInput(InputEvent @event)
{
if (!@event.IsActionPressed("ui_cancel")) return;
if (_uiManager.IsScreenOnTop(SettingsMenuControl)) _uiManager.PopScreen();
}
private void OnInputSettingsPressed()
{
_uiManager.PushScreen(InputSettingsControl);
}
private void OnAudioSettingsPressed()
{
_uiManager.PushScreen(AudioSettingsControl);
}
private void OnDisplaySettingsPressed()
{
_uiManager.PushScreen(DisplaySettingsControl);
}
private void OnGameplaySettingsPressed()
{
_uiManager.PushScreen(GameplaySettingsControl);
}
}

View File

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

25
scripts/UI/SkillButton.cs Normal file
View File

@@ -0,0 +1,25 @@
using Godot;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.UI;
public partial class SkillButton : Button
{
[Export] public SkillData Data { get; set; }
public void Setup()
{
Icon = Data?.Icon;
Text = Tr(Data?.Name) ?? string.Empty;
}
public void Activate()
{
Set("theme_override_colors/font_color", new Color("#49aa10"));
}
public void Deactivate()
{
Set("theme_override_colors/font_color", new Color("#ffffff"));
}
}

View File

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

View File

@@ -0,0 +1,109 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
[GlobalClass]
public partial class BeamComponent : Node2D
{
private float _currentLength = 0.0f;
private const float PixelSize = 16.0f; // Assuming 16 pixels per unit for scaling
[Export]
public float ExpansionSpeed { get; set; } = 100.0f;
[Export]
public float MaxLength { get; set; } = 512.0f;
[Export]
public Vector2 Direction { get; set; } = Vector2.Down;
[Export]
public Node2D Root { get; set; }
[Export]
public Sprite2D Sprite { get; set; }
[Export]
public CollisionShape2D CollisionShape { get; set; }
public override void _Ready()
{
if (Root == null)
{
GD.PrintErr("Root node is not set for BeamComponent.");
}
if (Sprite == null)
{
GD.PrintErr("Sprite node is not set for BeamComponent.");
}
if (CollisionShape == null)
{
GD.PrintErr("CollisionShape node is not set for BeamComponent.");
}
var shape = CollisionShape?.Shape as RectangleShape2D;
shape?.SetSize(new Vector2(_currentLength / 2.0f, _currentLength / 2.0f));
Sprite?.SetScale(new Vector2(1f, 1f));
CollisionShape?.SetPosition(Vector2.Zero);
}
public override void _Process(double delta)
{
var newLength = _currentLength + ExpansionSpeed * (float)delta;
if (newLength > MaxLength) newLength = MaxLength;
if (!CheckForObstacle(newLength)) ExpandBeam(newLength);
}
private void ExpandBeam(float newLength)
{
_currentLength = newLength;
if (Direction == Vector2.Up)
{
var pos = Sprite.Position;
var scale = Sprite.Scale;
var shape = CollisionShape?.Shape as RectangleShape2D;
Sprite.SetScale(new Vector2(scale.X, _currentLength / PixelSize));
Sprite.SetPosition(new Vector2(pos.X, -_currentLength / 2.0f));
shape?.SetSize(new Vector2(PixelSize / 2f, _currentLength / 2.0f));
CollisionShape?.SetPosition(new Vector2(CollisionShape.Position.X, -_currentLength / 2.0f));
} else if (Direction == Vector2.Down)
{
var pos = Sprite.Position;
var scale = Sprite.Scale;
var shape = CollisionShape?.Shape as RectangleShape2D;
Sprite.SetScale(new Vector2(scale.X, _currentLength / PixelSize));
Sprite.SetPosition(new Vector2(pos.X, _currentLength / 2.0f));
shape?.SetSize(new Vector2(PixelSize / 2f, _currentLength / 2.0f));
CollisionShape?.SetPosition(new Vector2(CollisionShape.Position.X, _currentLength / 2.0f));
} else if (Direction == Vector2.Left)
{
var pos = Sprite.Position;
var scale = Sprite.Scale;
var shape = CollisionShape?.Shape as RectangleShape2D;
Sprite.SetScale(new Vector2(_currentLength / PixelSize, scale.Y));
Sprite.SetPosition(new Vector2(-_currentLength / 2.0f, pos.Y));
shape?.SetSize(new Vector2(_currentLength / 2.0f, PixelSize / 2f));
CollisionShape?.SetPosition(new Vector2(-_currentLength / 2.0f, CollisionShape.Position.Y));
} else if (Direction == Vector2.Right)
{
var pos = Sprite.Position;
var scale = Sprite.Scale;
var shape = CollisionShape?.Shape as RectangleShape2D;
Sprite.SetScale(new Vector2(_currentLength / PixelSize, scale.Y));
Sprite.SetPosition(new Vector2(_currentLength / 2.0f, pos.Y));
shape?.SetSize(new Vector2(_currentLength / 2.0f, PixelSize / 2f));
CollisionShape?.SetPosition(new Vector2(_currentLength / 2.0f, CollisionShape.Position.Y));
}
}
private bool CheckForObstacle(float newLength)
{
var spaceState = GetWorld2D().DirectSpaceState;
var queryStart = GlobalPosition;
var queryEnd = queryStart + Direction.Normalized() * newLength;
var query = PhysicsRayQueryParameters2D.Create(queryStart, queryEnd);
query.CollideWithAreas = false;
query.CollideWithBodies = true;
var result = spaceState.IntersectRay(query);
return result.Count > 0;
}
}

View File

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

View File

@@ -0,0 +1,70 @@
using Godot;
using Mr.BrickAdventures.scripts.interfaces;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components;
public partial class BrickThrowComponent : Node
{
[Export] public PackedScene BrickScene { get; set; }
[Export] public float FireRate { get; set; } = 1.0f;
[Export] public PlayerController PlayerController { get; set; }
[Export] public ThrowInputResource ThrowInputBehavior { get; set; }
private bool _canThrow = true;
private Timer _timer;
public override void _Ready()
{
SetupTimer();
_canThrow = true;
if (ThrowInputBehavior != null) ThrowInputBehavior.ThrowRequested += ThrowBrick;
}
public override void _Input(InputEvent @event)
{
ThrowInputBehavior?.ProcessInput(@event);
}
public override void _Process(double delta)
{
ThrowInputBehavior?.Update(delta);
}
private void SetupTimer()
{
_timer.WaitTime = FireRate;
_timer.OneShot = false;
_timer.Autostart = false;
_timer.Timeout += OnTimerTimeout;
}
private void OnTimerTimeout()
{
_canThrow = true;
}
private void ThrowBrick(float powerMultiplier = 1f)
{
if (!_canThrow || PlayerController == null || BrickScene == null)
return;
var instance = BrickScene.Instantiate<Node2D>();
var init = instance.GetNodeOrNull<ProjectileInitComponent>("ProjectileInitComponent");
if (init != null && PlayerController.CurrentMovement is PlatformMovementComponent)
{
init.Initialize(new ProjectileInitParams()
{
Position = PlayerController.GlobalPosition,
Rotation = PlayerController.Rotation,
Direction = PlayerController.CurrentMovement.LastDirection,
PowerMultiplier = powerMultiplier,
});
}
GetTree().CurrentScene.AddChild(instance);
_canThrow = false;
_timer.Start();
}
}

View File

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

View File

@@ -0,0 +1,45 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class BulletComponent : Node
{
[Export] public Area2D Area { get; set; }
[Export] public TerrainHitFx TerrainHitFx { get; set; }
[Export] public Sprite2D BulletSprite { get; set; }
public override void _Ready()
{
Area.BodyEntered += OnBodyEntered;
}
private void OnBodyEntered(Node2D body)
{
if (body is TileMapLayer)
{
if (BulletSprite != null)
{
BulletSprite.Visible = false;
}
PlayTerrainHitFx();
return;
}
Owner.QueueFree();
}
private void OnAreaEntered(Area2D area)
{
Owner.QueueFree();
}
private void PlayTerrainHitFx()
{
if (TerrainHitFx == null) return;
TerrainHitFx.TriggerFx();
Owner.QueueFree();
}
}

View File

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

View File

@@ -0,0 +1,47 @@
using System.Threading.Tasks;
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class CageComponent : Node
{
[Export] public LeverComponent Lever { get; set; }
[Export] public Vector2 MoveValue { get; set; } = new(0, -100f);
[Export] public float TweenDuration { get; set; } = 0.5f;
[Export] public bool ShouldFree { get; set; } = true;
private const string LeverGroupName = "levers";
public override async void _Ready()
{
await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
if (Lever == null)
{
var leverNodes = GetTree().GetNodesInGroup(LeverGroupName);
foreach (var leverNode in leverNodes)
{
var lever = leverNode.GetNodeOrNull<LeverComponent>("LeverComponent");
if (lever != null) lever.Activated += OnLeverActivated;
}
}
}
private void OnLeverActivated()
{
var tween = CreateTween();
if (Owner is Node2D root)
{
var endPosition = root.Position + MoveValue;
tween.TweenProperty(root, "position", endPosition, TweenDuration);
}
tween.TweenCallback(Callable.From(OnTweenCompleted));
}
private void OnTweenCompleted()
{
if (!ShouldFree) return;
Owner.QueueFree();
}
}

View File

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

View File

@@ -0,0 +1,8 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class CanBeLaunchedComponent : Node
{
}

View File

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

View File

@@ -0,0 +1,8 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class CanPickUpComponent : Node
{
}

View File

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

View File

@@ -0,0 +1,8 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class CannotStompComponent : Node
{
}

View File

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

View File

@@ -0,0 +1,79 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
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]
public delegate void ChaseStartedEventHandler();
[Signal]
public delegate void ChaseStoppedEventHandler();
private bool _isChasing = false;
private Node2D _previousCameraFollowTarget = null;
public override void _Process(double delta)
{
if (!_isChasing) return;
if (ChaseTarget == null) return;
if (CheckIfReachedTarget())
{
StopChasing();
return;
}
var targetPosition = ChaseTarget.GlobalPosition;
if (Owner is not Node2D root) return;
var direction = (targetPosition - root.GlobalPosition).Normalized();
root.GlobalPosition += direction * ChaseSpeed * (float)delta;
}
public void OnShipEntered()
{
if (ChaseTarget == null || PhantomCamera == null)
return;
if (_isChasing) return;
_previousCameraFollowTarget = (Node2D)PhantomCamera.Call("get_follow_target");
PhantomCamera.Call("set_follow_target", Owner as Node2D);
EmitSignalChaseStarted();
_isChasing = true;
}
public void OnShipExited()
{
StopChasing();
}
private bool CheckIfReachedTarget()
{
if (ChaseTarget == null)
return false;
if (Owner is not Node2D root) return false;
var targetPosition = ChaseTarget.GlobalPosition;
var currentPosition = root.GlobalPosition;
return currentPosition.DistanceTo(targetPosition) <= MinimumDistance;
}
private void StopChasing()
{
if (PhantomCamera == null) return;
PhantomCamera.Call("set_follow_target", _previousCameraFollowTarget);
EmitSignalChaseStopped();
_isChasing = false;
}
}

View File

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

View File

@@ -0,0 +1,11 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class CleanupComponent : Node
{
public void CleanUp()
{
Owner.QueueFree();
}
}

View File

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

View File

@@ -0,0 +1,81 @@
using System.Threading.Tasks;
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class CollapsableComponent : Node
{
[Export] public Timer ToCollapseTimer { get; set; }
[Export] public Timer ResetTimer { get; set; }
[Export] public Sprite2D Sprite2D { get; set; }
[Export] public CollisionShape2D CollisionShape { get; set; }
[Export] public float CollapseTime { get; set; } = 0.5f;
[Export] public float ResetTime { get; set; } = 0.5f;
[Export] public float AnimationTime { get; set; } = 0.25f;
public override void _Ready()
{
ResetTimers();
ToCollapseTimer.Timeout += OnToCollapseTimerTimeout;
ResetTimer.Timeout += OnResetTimerTimeout;
}
public void OnCollapsableDetectorBodyEntered(Node2D body)
{
ToCollapseTimer.Start();
}
public void OnCollapsableDetectorBodyExited(Node2D body)
{
var collapseTimeLeft = Mathf.Abs(ToCollapseTimer.TimeLeft - CollapseTime);
if (collapseTimeLeft < (0.1f * CollapseTime))
{
ResetTimers();
}
}
private void OnToCollapseTimerTimeout()
{
_ = Collapse();
}
private void OnResetTimerTimeout()
{
_ = Reactivate();
}
private async Task Collapse()
{
ToCollapseTimer.Stop();
ToCollapseTimer.SetWaitTime(CollapseTime);
var tween = CreateTween();
tween.TweenProperty(Sprite2D, "modulate:a", 0f, AnimationTime);
await ToSignal(tween, Tween.SignalName.Finished);
CollisionShape?.CallDeferred("set_disabled", true);
ResetTimer.Start();
}
private async Task Reactivate()
{
ResetTimer.Stop();
ResetTimer.SetWaitTime(ResetTime);
var tween = CreateTween();
tween.TweenProperty(Sprite2D, "modulate:a", 1f, AnimationTime);
await ToSignal(tween, Tween.SignalName.Finished);
CollisionShape?.CallDeferred("set_disabled", false);
}
private void ResetTimers()
{
ToCollapseTimer.Stop();
ToCollapseTimer.SetWaitTime(CollapseTime);
ResetTimer.Stop();
ResetTimer.SetWaitTime(ResetTime);
}
}

View File

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

View File

@@ -0,0 +1,50 @@
using System;
using Godot;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components;
public partial class CollectableComponent : Node
{
private bool _hasFadeAway = false;
[Export] public Area2D Area2D { get; set; }
[Export] public CollisionShape2D CollisionShape { get; set; }
[Export] public CollectableResource Data { get; set; }
[Export] public AudioStreamPlayer2D Sfx {get; set; }
[Signal] public delegate void CollectedEventHandler(Variant amount, CollectableType type, Node2D body);
public override void _Ready()
{
if (Area2D != null)
Area2D.BodyEntered += OnArea2DBodyEntered;
else
GD.PushError("Collectable node missing Area2D node.");
if (Owner.HasNode("FadeAwayComponent"))
_hasFadeAway = true;
}
private async void OnArea2DBodyEntered(Node2D body)
{
try
{
if (!body.HasNode("CanPickUpComponent")) return;
EmitSignalCollected(Data.Amount, Data.Type, body);
CollisionShape?.CallDeferred("set_disabled", true);
Sfx?.Play();
if (_hasFadeAway) return;
if (Sfx != null)
await ToSignal(Sfx, AudioStreamPlayer2D.SignalName.Finished);
Owner.QueueFree();
}
catch (Exception e)
{
GD.PushError($"Error in CollectableComponent.OnArea2DBodyEntered: {e.Message}");
}
}
}

View File

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

View File

@@ -0,0 +1,105 @@
using Godot;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components;
public partial class DamageComponent : Node
{
[Export] public float Damage { get; set; } = 0.25f;
[Export] public Area2D Area { get; set; }
[Export] public StatusEffectDataResource StatusEffectData { get; set; }
[Export] public Timer DamageTimer { get; set; }
private Node _currentTarget = null;
[Signal] public delegate void EffectInflictedEventHandler(Node2D target, StatusEffectDataResource effect);
public override void _Ready()
{
if (Area == null)
{
GD.PushError($"DamageComponent: Area2D node is not set.");
return;
}
Area.BodyEntered += OnAreaBodyEntered;
Area.BodyExited += OnAreaBodyExited;
Area.AreaEntered += OnAreaAreaEntered;
if (DamageTimer != null)
{
DamageTimer.Timeout += OnDamageTimerTimeout;
}
}
public override void _Process(double delta)
{
if (_currentTarget == null) return;
if (DamageTimer != null) return;
ProcessEntityAndApplyDamage(_currentTarget as Node2D);
}
public void DealDamage(HealthComponent target) => target.DecreaseHealth(Damage);
private void OnAreaAreaEntered(Area2D area)
{
if (!CheckIfProcessingIsOn())
return;
if (area == Area) return;
var parent = area.GetParent();
if (parent.HasNode("DamageComponent"))
ProcessEntityAndApplyDamage(parent as Node2D);
}
private void OnAreaBodyExited(Node2D body)
{
if (body != _currentTarget) return;
_currentTarget = null;
DamageTimer?.Stop();
}
private void OnAreaBodyEntered(Node2D body)
{
_currentTarget = body;
if (!CheckIfProcessingIsOn())
return;
DamageTimer?.Start();
ProcessEntityAndApplyDamage(body);
}
private void OnDamageTimerTimeout()
{
if (_currentTarget == null) return;
ProcessEntityAndApplyDamage(_currentTarget as Node2D);
}
private void ProcessEntityAndApplyDamage(Node2D body)
{
if (body == null) return;
if (!body.HasNode("HealthComponent")) return;
var health = body.GetNode<HealthComponent>("HealthComponent");
var inv = body.GetNodeOrNull<InvulnerabilityComponent>("InvulnerabilityComponent");
if (inv != null && inv.IsInvulnerable())
return;
if (StatusEffectData != null && StatusEffectData.Type != StatusEffectType.None)
EmitSignalEffectInflicted(body, StatusEffectData);
DealDamage(health);
inv?.Activate();
}
private bool CheckIfProcessingIsOn()
{
return ProcessMode is ProcessModeEnum.Inherit or ProcessModeEnum.Always;
}
}

View File

@@ -0,0 +1 @@
uid://2i7p7v135u7c

View File

@@ -0,0 +1,35 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class DestroyableComponent : Node2D
{
[Export] public HealthComponent Health { get; set; }
[Export] public PackedScene DestroyEffect { get; set; }
public override void _Ready()
{
if (Health == null)
{
GD.PushError("DestroyableComponent: HealthComponent is not set.");
return;
}
Health.Death += OnHealthDeath;
}
private void OnHealthDeath()
{
if (DestroyEffect == null)
{
Owner.QueueFree();
return;
}
var effect = DestroyEffect.Instantiate<Node2D>();
Health.GetParent().AddChild(effect);
effect.SetGlobalPosition(Health.GlobalPosition);
Owner.QueueFree();
}
}

View File

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

View File

@@ -0,0 +1,27 @@
using Godot;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components;
public partial class EffectInflictorComponent : Node
{
[Export] public DamageComponent Damage { get; set; }
public override void _Ready()
{
if (Damage == null)
{
GD.PushError("EffectInflictorComponent requires a DamageComponent to be set.");
return;
}
Damage.EffectInflicted += OnEffectInflicted;
}
private void OnEffectInflicted(Node2D target, StatusEffectDataResource effect)
{
var statusEffect = target.GetNodeOrNull<StatusEffectComponent>("StatusEffectComponent");
statusEffect?.ApplyEffect(effect);
}
}

View File

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

View File

@@ -0,0 +1,42 @@
using System.Threading.Tasks;
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class EnemyDeathComponent : Node
{
[Export] public float TweenDuration { get; set; } = 0.5f;
[Export] public CollisionShape2D CollisionShape { get; set; }
[Export] public HealthComponent Health { get; set; }
public override void _Ready()
{
if (CollisionShape == null)
{
GD.PushError("EnemyDeathComponent: CollisionShape is not set.");
return;
}
if (Health == null)
{
GD.PushError("EnemyDeathComponent: Health is not set.");
return;
}
Health.Death += OnDeath;
}
private void OnDeath()
{
CallDeferred(nameof(Die));
}
private async Task Die()
{
CollisionShape.SetDisabled(true);
var tween = CreateTween();
tween.TweenProperty(Owner, "scale", Vector2.Zero, TweenDuration);
await ToSignal(tween, Tween.SignalName.Finished);
Owner.QueueFree();
}
}

View File

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

View File

@@ -0,0 +1,51 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class EnemyWaveTriggerComponent : Node
{
[Export] public Area2D Area2D { get; set; }
[Export] public PathFollow2D PathFollowNode { get; set; }
[Export] public float Speed { get; set; } = 100f;
[Export] public bool Loop { get; set; } = false;
[Export] public bool ActivateOnEnter { get; set; } = true;
private bool _isActive = false;
public override void _Ready()
{
Area2D.BodyEntered += OnBodyEntered;
if (PathFollowNode == null) return;
PathFollowNode.SetProgress(0f);
PathFollowNode.SetProcess(false);
}
public override void _Process(double delta)
{
if (!_isActive || PathFollowNode == null) return;
var progress = PathFollowNode.Progress;
progress += (float)(delta * Speed);
PathFollowNode.SetProgress(progress);
if (!(PathFollowNode.ProgressRatio >= 1f) || Loop) return;
_isActive = false;
PathFollowNode.SetProcess(false);
}
private void OnBodyEntered(Node2D body)
{
if (ActivateOnEnter) StartWave();
}
private void StartWave()
{
if (PathFollowNode == null) return;
PathFollowNode.SetProcess(true);
_isActive = true;
}
}

View File

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

View File

@@ -0,0 +1,52 @@
using Godot;
using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.interfaces;
namespace Mr.BrickAdventures.scripts.components;
public partial class ExitDoorComponent : Node, IUnlockable
{
[Export] public bool Locked { get; set; } = true;
[Export] public Area2D ExitArea { get; set; }
[Export] public Sprite2D DoorSprite { get; set; }
[Export] public AudioStreamPlayer2D OpenDoorSfx { get; set; }
[Export] public int OpenedDoorFrame { get; set; } = 0;
[Signal] public delegate void ExitTriggeredEventHandler();
private GameManager _gameManager;
public override void _Ready()
{
if (ExitArea == null)
{
GD.PushError("ExitDoorComponent: ExitArea is not set.");
return;
}
ExitArea.BodyEntered += OnExitAreaBodyEntered;
_gameManager = GetNode<GameManager>("/root/gameManager");
}
private void OnExitAreaBodyEntered(Node2D body)
{
throw new System.NotImplementedException();
}
public void Unlock()
{
Locked = false;
if (DoorSprite != null)
{
DoorSprite.Frame = OpenedDoorFrame;
}
OpenDoorSfx?.Play();
}
private void GoToNextLevel()
{
_gameManager.OnLevelComplete();
}
}

View File

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

View File

@@ -0,0 +1,81 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class ExplosiveComponent : Node2D
{
[Export] public DamageComponent Damage { get; set; }
[Export] public Area2D Area { get; set; }
[Export] public Area2D ExplodeArea { get; set; }
[Export] public PackedScene ExplosionEffect { get; set; }
[Export] public float TimeToExplode { get; set; } = 9f;
[Signal] public delegate void OnExplosionEventHandler(Node2D body);
private Timer _timer;
public override void _Ready()
{
if (Damage != null)
{
GD.PushError("ExplosiveComponent: DamageComponent is not set.");
return;
}
if (ExplodeArea != null)
{
GD.PushError("ExplosiveComponent: ExplodeArea is not set.");
return;
}
Area.BodyEntered += OnAreaBodyEntered;
Area.AreaEntered += OnAreaAreaEntered;
}
private void OnAreaAreaEntered(Area2D area)
{
Explode();
}
private void OnAreaBodyEntered(Node2D body)
{
Explode();
}
private void PrepareTimer()
{
_timer = new Timer();
_timer.SetWaitTime(TimeToExplode);
_timer.OneShot = true;
_timer.Autostart = true;
_timer.Timeout += Explode;
AddChild(_timer);
}
private void Explode()
{
_timer.Stop();
if (ExplosionEffect != null)
{
var explosionInstance = ExplosionEffect.Instantiate<GpuParticles2D>();
if (Owner is Node2D root) explosionInstance.SetGlobalPosition(root.GlobalPosition);
GetTree().CurrentScene.AddChild(explosionInstance);
explosionInstance.SetEmitting(true);
}
var bodies = ExplodeArea.GetOverlappingBodies();
foreach (var body in bodies)
{
var health = body.GetNodeOrNull<HealthComponent>("HealthComponent");
if (Damage != null && health != null)
{
Damage.DealDamage(health);
}
EmitSignalOnExplosion(body);
}
Owner.QueueFree();
}
}

View File

@@ -0,0 +1 @@
uid://7uttgdr6cr5y

View File

@@ -0,0 +1,32 @@
using System.Threading.Tasks;
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class FadeAwayComponent : Node
{
[Export] public Sprite2D Sprite { get; set; }
[Export] public float FadeDuration { get; set; } = 1f;
[Export] public float Speed { get; set; } = 10f;
[Export] public Vector2 Direction { get; set; } = Vector2.Up;
[Export] public Area2D Area { get; set; }
public override void _Ready()
{
Area.BodyEntered += OnBodyEntered;
}
private void OnBodyEntered(Node2D body)
{
_ = FadeAway();
}
private async Task FadeAway()
{
var tween = CreateTween().SetParallel(true);
tween.TweenProperty(Sprite, "modulate:a", 0f, FadeDuration);
tween.TweenProperty(Sprite, "position", Sprite.Position + (Direction * Speed), FadeDuration);
await ToSignal(tween, Tween.SignalName.Finished);
Owner.QueueFree();
}
}

View File

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

View File

@@ -0,0 +1,71 @@
using Godot;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components;
public partial class FireEffectComponent : Node
{
[Export] public HealthComponent Health { get; set; }
[Export] public StatusEffectComponent StatusEffectComponent { get; set; }
[Export] public GpuParticles2D FireFX { get; set; }
private StatusEffectDataResource _data = null;
private bool _shouldDealDamage = false;
private double _timeElapsed = 0f;
public override void _Ready()
{
if (Health == null)
{
Health = GetNode<HealthComponent>("HealthComponent");
}
if (StatusEffectComponent == null)
{
StatusEffectComponent = GetNode<StatusEffectComponent>("StatusEffectComponent");
}
if (Health == null)
{
GD.PushError("FireEffectComponent: HealthComponent is not set.");
return;
}
if (StatusEffectComponent == null)
{
GD.PushError("FireEffectComponent: StatusEffectComponent is not set.");
return;
}
StatusEffectComponent.EffectApplied += OnEffectApplied;
StatusEffectComponent.EffectRemoved += OnEffectRemoved;
}
public override void _Process(double delta)
{
if (!_shouldDealDamage || _data == null || Health == null) return;
_timeElapsed += delta;
if (_timeElapsed >= 1f)
{
Health.DecreaseHealth(_data.DamagePerSecond);
_timeElapsed = 0f;
}
}
private void OnEffectApplied(StatusEffect statusEffect)
{
if (statusEffect.EffectData.Type != StatusEffectType.Fire) return;
_data = statusEffect.EffectData;
_shouldDealDamage = true;
FireFX?.SetEmitting(true);
}
private void OnEffectRemoved(StatusEffectType type)
{
if (type != StatusEffectType.Fire) return;
_shouldDealDamage = false;
_data = null;
FireFX?.SetEmitting(false);
}
}

View File

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

View File

@@ -0,0 +1,83 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class FlashingComponent : Node
{
[Export] public Node2D Sprite { get; set; }
[Export] public float FlashDuration { get; set; } = 0.5f;
[Export] public float FlashTime { get; set; } = 0.1f;
[Export] public bool UseModulate { get; set; } = true;
[Export] public HealthComponent HealthComponent { get; set; }
private Tween _tween;
public override void _Ready()
{
if (HealthComponent != null)
{
HealthComponent.HealthChanged += OnHealthChanged;
HealthComponent.Death += OnDeath;
}
if (Sprite == null)
{
GD.PushError("FlashingComponent: Sprite node is not set.");
return;
}
}
public void StartFlashing()
{
if (Sprite == null) return;
_tween?.Kill();
_tween = CreateTween();
_tween.SetParallel(true);
var flashes = (int)(FlashDuration / FlashTime);
for (var i = 0; i < flashes; i++)
{
if (UseModulate)
{
var opacity = i % 2 == 0 ? 1.0f : 0.3f;
_tween.TweenProperty(Sprite, "modulate:a", opacity, FlashTime);
}
else
{
var visible = i % 2 == 0;
_tween.TweenProperty(Sprite, "visible", visible, FlashTime);
}
}
_tween.TweenCallback(Callable.From(StopFlashing));
}
public void StopFlashing()
{
if (UseModulate)
{
var modulateColor = Sprite.GetModulate();
modulateColor.A = 1.0f;
Sprite.SetModulate(modulateColor);
}
else
{
Sprite.SetVisible(true);
}
}
private void OnHealthChanged(float delta, float totalHealth)
{
if (delta < 0f)
{
StartFlashing();
}
}
private void OnDeath()
{
StopFlashing();
}
}

View File

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

View File

@@ -0,0 +1,36 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class FlipComponent : Node2D
{
[Export] public Sprite2D LeftEye { get; set; }
[Export] public Sprite2D RightEye { get; set; }
[Export] public PlatformMovementComponent PlatformMovement { get; set; }
public override void _Process(double delta)
{
if (PlatformMovement == null) return;
var velocity = PlatformMovement.LastDirection;
switch (velocity.X)
{
case < 0f:
LeftEye.Frame = 1;
RightEye.Frame = 1;
LeftEye.FlipH = true;
RightEye.FlipH = true;
break;
case > 0f:
LeftEye.Frame = 1;
RightEye.Frame = 1;
LeftEye.FlipH = false;
RightEye.FlipH = false;
break;
default:
LeftEye.Frame = 0;
RightEye.Frame = 0;
break;
}
}
}

View File

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

View File

@@ -0,0 +1,37 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class GravityMotionComponent : Node2D
{
[Export] public CharacterBody2D Body { get; set; }
[Export] public LaunchComponent LaunchComponent { get; set; }
[Export] public Vector2 Gravity { get; set; } = new Vector2(0, 1000f);
[Export] public Vector2 TargetDirection { get; set; } = Vector2.Up;
private Vector2 _velocity = Vector2.Zero;
public override void _Ready()
{
if (LaunchComponent == null) return;
var direction = LaunchComponent.InitialDirection.X > 0f ? TargetDirection : new Vector2(-TargetDirection.X, TargetDirection.Y);
direction = direction.Normalized();
_velocity = direction * LaunchComponent.Speed;
}
public override void _PhysicsProcess(double delta)
{
if (Body == null) return;
_velocity += Gravity * (float)delta;
Body.Velocity = _velocity;
Body.MoveAndSlide();
if (_velocity.LengthSquared() > 0.01f)
{
Body.Rotation = _velocity.Angle();
}
}
}

View File

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

View File

@@ -0,0 +1,48 @@
using Godot;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components;
public partial class HealComponent : Node
{
[Export] public GpuParticles2D HealFx { get; set; }
[Export] public CollectableComponent Collectable { get; set; }
public override void _Ready()
{
if (Collectable == null)
{
GD.PushError("HealComponent: Collectable is not set.");
return;
}
Collectable.Collected += OnCollected;
}
private void OnCollected(Variant amount, CollectableType type, Node2D body)
{
if (type != CollectableType.Health) return;
if (Collectable == null) return;
var healthComponent = body.GetNodeOrNull<HealthComponent>("HealthComponent");
if (healthComponent == null) return;
var value = amount.AsSingle();
healthComponent.IncreaseHealth(value);
if (HealFx != null)
{
PlayHealFx();
}
Owner.QueueFree();
}
private void PlayHealFx()
{
if (HealFx == null) return;
HealFx.Restart();
HealFx.Emitting = true;
}
}

View File

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

View File

@@ -0,0 +1,65 @@
using System.Threading.Tasks;
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class HealthComponent : Node2D
{
[Export] public float Health { get; set; } = 1.0f;
[Export] public float MaxHealth { get; set; } = 1.0f;
[Export] public AudioStreamPlayer2D HurtSfx { get; set; }
[Export] public AudioStreamPlayer2D HealSfx { get; set; }
[Signal] public delegate void HealthChangedEventHandler(float delta, float totalHealth);
[Signal] public delegate void DeathEventHandler();
public void SetHealth(float newValue)
{
_ = ApplyHealthChange(newValue);
}
public void IncreaseHealth(float delta)
{
_ = ApplyHealthChange(Health + delta);
}
public void DecreaseHealth(float delta)
{
_ = ApplyHealthChange(Health - delta);
}
public float GetDelta(float newValue) => newValue - Health;
private async Task ApplyHealthChange(float newHealth, bool playSfx = true)
{
newHealth = Mathf.Clamp(newHealth, 0.0f, MaxHealth);
var delta = newHealth - Health;
if (delta == 0.0f)
return;
if (playSfx)
{
if (delta > 0f && HealSfx != null)
{
HealSfx.Play();
}
else if (delta < 0f && HurtSfx != null)
{
HurtSfx.Play();
await HurtSfx.ToSignal(HurtSfx, AudioStreamPlayer2D.SignalName.Finished);
}
}
Health = newHealth;
if (Health <= 0f)
{
EmitSignalDeath();
}
else
{
EmitSignalHealthChanged(delta, Health);
}
}
}

View File

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

View File

@@ -0,0 +1,75 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class HitComponent : Node
{
[Export] public Sprite2D Sprite { get; set; }
[Export] public HealthComponent Health { get; set; }
[Export] public float HitDuration { get; set; } = 0.1f;
[Export] public GpuParticles2D HitFx { get; set; }
[Export] public bool FlashMode { get; set; } = true;
public override void _Ready()
{
if (Health != null)
{
Health.HealthChanged += OnHealthChange;
Health.Death += OnDeath;
}
if (Sprite == null)
{
GD.PushError("HitComponent: Sprite is null");
return;
}
if (Sprite.Material != null && FlashMode)
{
Sprite.Material = (Material)Sprite.Material.Duplicate();
}
}
private void Activate()
{
if (!FlashMode) return;
Sprite.SetInstanceShaderParameter("enabled", true);
}
private void Deactivate()
{
if (!FlashMode) return;
Sprite.SetInstanceShaderParameter("enabled", false);
}
private async void OnHealthChange(float delta, float totalHealth)
{
if (!(delta < 0f)) return;
Activate();
await ToSignal(GetTree().CreateTimer(HitDuration), Timer.SignalName.Timeout);
Deactivate();
if (totalHealth > 0f && delta < 0f)
{
HandleHitFx();
}
}
private async void OnDeath()
{
Activate();
await ToSignal(GetTree().CreateTimer(HitDuration), Timer.SignalName.Timeout);
Deactivate();
}
private void HandleHitFx()
{
if (HitFx == null) return;
HitFx.Restart();
HitFx.Emitting = true;
}
}

View File

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

View File

@@ -0,0 +1,64 @@
using Godot;
namespace Mr.BrickAdventures.scripts.components;
public partial class HomingMissileMotionComponent : Node
{
[Export] public LaunchComponent Launch { get; set; }
[Export] public float MaxSpeed { get; set; } = 16f;
[Export] public float Acceleration { get; set; } = 8f;
[Export] public float MaxTurnRate { get; set; } = 180f; // degrees per second
[Export] public float WobbleStrength { get; set; } = 5f; // degrees
[Export] public float Drag { get; set; } = 0.98f;
[Export] public float SteeringLerp { get; set; } = 0.05f; // low = sluggish, high = responsive
[Export] public Area2D DetectionArea { get; set; }
private Vector2 _steeringDirection = Vector2.Zero;
private Vector2 _velocity = Vector2.Zero;
private Node2D _target = null;
public override void _Ready()
{
DetectionArea.BodyEntered += OnBodyEntered;
_velocity = Launch.GetInitialVelocity();
}
public override void _PhysicsProcess(double delta)
{
if (Launch == null) return;
if (Owner is not Node2D owner) return;
if (_target == null)
{
owner.Position += _velocity * (float)delta;
return;
}
var toTarget = (_target.GlobalPosition - owner.GlobalPosition).Normalized();
_steeringDirection = _steeringDirection.Lerp(toTarget, SteeringLerp);
var angleToTarget = _velocity.AngleTo(_steeringDirection);
var maxAngle = Mathf.DegToRad(MaxTurnRate) * (float)delta;
var clampedAngle = Mathf.Clamp(angleToTarget, -maxAngle, maxAngle);
var rng = new RandomNumberGenerator();
var wobble = Mathf.DegToRad(rng.RandfRange(-WobbleStrength, WobbleStrength));
clampedAngle += wobble;
_velocity = _velocity.Rotated(clampedAngle);
_velocity *= Drag;
var desiredSpeed = Mathf.Min(MaxSpeed, _velocity.Length() + Acceleration * (float)delta);
_velocity = _velocity.Normalized() * desiredSpeed;
owner.Position += _velocity * (float)delta;
owner.Rotation = _velocity.Angle();
}
private void OnBodyEntered(Node2D body)
{
if (_target != null) return;
if (body == null) return;
_target = body;
}
}

View File

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

View File

@@ -0,0 +1,70 @@
using Godot;
using Godot.Collections;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components;
public partial class IceEffectComponent : Node
{
[Export] public Array<Node> ComponentsToDisable { get; set; } = [];
[Export] public StatusEffectComponent StatusEffectComponent { get; set; }
[Export] public Node2D IceFx { get; set; }
private StatusEffectDataResource _data = null;
private int _iceEffectsApplied = 0;
public override void _Ready()
{
StatusEffectComponent.EffectApplied += OnEffectApplied;
StatusEffectComponent.EffectRemoved += OnEffectRemoved;
}
private void OnEffectApplied(StatusEffect statusEffect)
{
if (statusEffect.EffectData.Type != StatusEffectType.Ice) return;
_data = statusEffect.EffectData;
_iceEffectsApplied++;
ApplyFreeze();
}
private void OnEffectRemoved(StatusEffectType type)
{
if (type != StatusEffectType.Ice) return;
_data = null;
_iceEffectsApplied--;
RemoveFreeze();
}
private void ApplyFreeze()
{
if (IceFx != null)
{
IceFx.Visible = true;
}
foreach (var component in ComponentsToDisable)
{
if (component == null || _iceEffectsApplied == 0) continue;
component.ProcessMode = ProcessModeEnum.Disabled;
}
}
private void RemoveFreeze()
{
if (_iceEffectsApplied > 0) return;
if (IceFx != null)
{
IceFx.Visible = false;
}
foreach (var component in ComponentsToDisable)
{
if (component == null) continue;
component.ProcessMode = ProcessModeEnum.Inherit;
}
}
}

View File

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

Some files were not shown because too many files have changed in this diff Show More