Compare commits
14 Commits
master
...
9c74a1ffb5
Author | SHA1 | Date | |
---|---|---|---|
9c74a1ffb5 | |||
94f0f02b3e | |||
4075231939 | |||
359a32aaf0 | |||
56da6f36a7 | |||
2dc4306be8 | |||
a859ff9fe7 | |||
0e41c811e4 | |||
cd3143bbad | |||
aeb024cbac | |||
3066f0c7b4 | |||
bdd5ce04da | |||
b54d886145 | |||
4326ca850d |
27
Autoloads/ConfigFileHandler.cs
Normal file
27
Autoloads/ConfigFileHandler.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace Mr.BrickAdventures.Autoloads;
|
||||||
|
|
||||||
|
public partial class ConfigFileHandler : Node
|
||||||
|
{
|
||||||
|
private ConfigFile _settingsConfig = new();
|
||||||
|
public const string SettingsPath = "user://settings.ini";
|
||||||
|
|
||||||
|
public ConfigFile SettingsConfig => _settingsConfig;
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
if (!FileAccess.FileExists(SettingsPath))
|
||||||
|
{
|
||||||
|
var err = _settingsConfig.Save(SettingsPath);
|
||||||
|
if (err != Error.Ok)
|
||||||
|
GD.PushError($"Failed to create settings file at {SettingsPath}: {err}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var err = _settingsConfig.Load(SettingsPath);
|
||||||
|
if (err != Error.Ok)
|
||||||
|
GD.PushError($"Failed to load settings file at {SettingsPath}: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
Autoloads/ConfigFileHandler.cs.uid
Normal file
1
Autoloads/ConfigFileHandler.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://8cyvbeyd13cj
|
195
Autoloads/GameManager.cs
Normal file
195
Autoloads/GameManager.cs
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
using Godot;
|
||||||
|
using Godot.Collections;
|
||||||
|
using Mr.BrickAdventures.scripts.Resources;
|
||||||
|
|
||||||
|
namespace Mr.BrickAdventures.Autoloads;
|
||||||
|
|
||||||
|
public partial class GameManager : Node
|
||||||
|
{
|
||||||
|
[Export] public Array<PackedScene> LevelScenes { get; set; } = new();
|
||||||
|
|
||||||
|
public Dictionary PlayerState { get; set; } = new()
|
||||||
|
{
|
||||||
|
{ "coins", 0 },
|
||||||
|
{ "lives", 3 },
|
||||||
|
{ "current_level", 0 },
|
||||||
|
{ "completed_levels", new Array<int>() },
|
||||||
|
{ "unlocked_levels", new Array<int>() {0}},
|
||||||
|
{ "unlocked_skills", new Array<SkillData>() }
|
||||||
|
};
|
||||||
|
|
||||||
|
public Dictionary CurrentSessionState { get; private set; } = new()
|
||||||
|
{
|
||||||
|
{ "coins_collected", 0 },
|
||||||
|
{ "skills_unlocked", new Array<SkillData>() }
|
||||||
|
};
|
||||||
|
|
||||||
|
public void AddCoins(int amount)
|
||||||
|
{
|
||||||
|
PlayerState["coins"] = Mathf.Max(0, (int)PlayerState["coins"] + amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetCoins(int amount) => PlayerState["coins"] = Mathf.Max(0, amount);
|
||||||
|
|
||||||
|
public int GetCoins() => (int)PlayerState["coins"] + (int)CurrentSessionState["coins_collected"];
|
||||||
|
|
||||||
|
public void RemoveCoins(int amount)
|
||||||
|
{
|
||||||
|
var sessionCoins = (int)CurrentSessionState["coins_collected"];
|
||||||
|
if (amount <= sessionCoins)
|
||||||
|
{
|
||||||
|
CurrentSessionState["coins_collected"] = sessionCoins - amount;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var remaining = amount - sessionCoins;
|
||||||
|
CurrentSessionState["coins_collected"] = 0;
|
||||||
|
PlayerState["coins"] = Mathf.Max(0, (int)PlayerState["coins"] - remaining);
|
||||||
|
}
|
||||||
|
PlayerState["coins"] = Mathf.Max(0, (int)PlayerState["coins"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddLives(int amount) => PlayerState["lives"] = (int)PlayerState["lives"] + amount;
|
||||||
|
public void RemoveLives(int amount) => PlayerState["lives"] = (int)PlayerState["lives"] - amount;
|
||||||
|
public void SetLives(int amount) => PlayerState["lives"] = amount;
|
||||||
|
public int GetLives() => (int)PlayerState["lives"];
|
||||||
|
|
||||||
|
public bool IsSkillUnlocked(SkillData skill)
|
||||||
|
{
|
||||||
|
return ((Array)PlayerState["unlocked_skills"]).Contains(skill)
|
||||||
|
|| ((Array)CurrentSessionState["skills_unlocked"]).Contains(skill);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnlockSkill(SkillData skill)
|
||||||
|
{
|
||||||
|
if (!IsSkillUnlocked(skill))
|
||||||
|
((Array)PlayerState["unlocked_skills"]).Add(skill);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveSkill(string skillName)
|
||||||
|
{
|
||||||
|
var arr = (Array)PlayerState["unlocked_skills"];
|
||||||
|
foreach (SkillData s in arr)
|
||||||
|
{
|
||||||
|
if (s.Name != skillName) continue;
|
||||||
|
|
||||||
|
arr.Remove(s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnlockSkills(Array<SkillData> skills)
|
||||||
|
{
|
||||||
|
foreach (var s in skills)
|
||||||
|
UnlockSkill(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetPlayerState()
|
||||||
|
{
|
||||||
|
PlayerState = new Dictionary
|
||||||
|
{
|
||||||
|
{ "coins", 0 },
|
||||||
|
{ "lives", 3 },
|
||||||
|
{ "current_level", 0 },
|
||||||
|
{ "completed_levels", new Array<int>() },
|
||||||
|
{ "unlocked_levels", new Array<int>() {0}},
|
||||||
|
{ "unlocked_skills", new Array<SkillData>() }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnlockLevel(int levelIndex)
|
||||||
|
{
|
||||||
|
var unlocked = (Array)PlayerState["unlocked_levels"];
|
||||||
|
if (!unlocked.Contains(levelIndex)) unlocked.Add(levelIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TryToGoToNextLevel()
|
||||||
|
{
|
||||||
|
var next = (int)PlayerState["current_level"] + 1;
|
||||||
|
var unlocked = (Array)PlayerState["unlocked_levels"];
|
||||||
|
if (next < LevelScenes.Count && unlocked.Contains(next))
|
||||||
|
{
|
||||||
|
PlayerState["current_level"] = next;
|
||||||
|
GetTree().ChangeSceneToPacked(LevelScenes[next]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void MarkLevelComplete(int levelIndex)
|
||||||
|
{
|
||||||
|
UnlockLevel(levelIndex + 1);
|
||||||
|
var completed = (Array)PlayerState["completed_levels"];
|
||||||
|
if (!completed.Contains(levelIndex)) completed.Add(levelIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetCurrentSessionState()
|
||||||
|
{
|
||||||
|
CurrentSessionState = new Dictionary
|
||||||
|
{
|
||||||
|
{ "coins_collected", 0 },
|
||||||
|
{ "skills_unlocked", new Array<SkillData>() }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RestartGame()
|
||||||
|
{
|
||||||
|
ResetPlayerState();
|
||||||
|
ResetCurrentSessionState();
|
||||||
|
GetTree().ChangeSceneToPacked(LevelScenes[0]);
|
||||||
|
GetNode<SaveSystem>("/root/SaveSystem").SaveGame();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void QuitGame() => GetTree().Quit();
|
||||||
|
|
||||||
|
public void PauseGame() => Engine.TimeScale = 0;
|
||||||
|
public void ResumeGame() => Engine.TimeScale = 1;
|
||||||
|
|
||||||
|
public void StartNewGame()
|
||||||
|
{
|
||||||
|
ResetPlayerState();
|
||||||
|
ResetCurrentSessionState();
|
||||||
|
GetTree().ChangeSceneToPacked(LevelScenes[0]);
|
||||||
|
GetNode<SaveSystem>("/root/SaveSystem").SaveGame();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ContinueGame()
|
||||||
|
{
|
||||||
|
var save = GetNode<SaveSystem>("/root/SaveSystem");
|
||||||
|
if (!save.LoadGame())
|
||||||
|
{
|
||||||
|
GD.PrintErr("Failed to load game. Starting a new game instead.");
|
||||||
|
StartNewGame();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var idx = (int)PlayerState["current_level"];
|
||||||
|
if (idx < LevelScenes.Count)
|
||||||
|
GetTree().ChangeSceneToPacked(LevelScenes[idx]);
|
||||||
|
else
|
||||||
|
GD.PrintErr("No levels unlocked to continue.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnLevelComplete()
|
||||||
|
{
|
||||||
|
var levelIndex = (int)PlayerState["current_level"];
|
||||||
|
MarkLevelComplete(levelIndex);
|
||||||
|
AddCoins((int)CurrentSessionState["coins_collected"]);
|
||||||
|
foreach (var s in (Array)CurrentSessionState["skills_unlocked"])
|
||||||
|
UnlockSkill((SkillData)s);
|
||||||
|
|
||||||
|
ResetCurrentSessionState();
|
||||||
|
TryToGoToNextLevel();
|
||||||
|
GetNode<SaveSystem>("/root/SaveSystem").SaveGame();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array GetUnlockedSkills()
|
||||||
|
{
|
||||||
|
var unlocked = (Array<SkillData>)PlayerState["unlocked_skills"];
|
||||||
|
var session = (Array<SkillData>)CurrentSessionState["skills_unlocked"];
|
||||||
|
if ((((Array)session)!).Count == 0) return (Array)unlocked;
|
||||||
|
if ((((Array)unlocked)!).Count == 0) return (Array)session;
|
||||||
|
var joined = new Array();
|
||||||
|
joined.AddRange((Array)unlocked ?? new Array());
|
||||||
|
joined.AddRange((Array)session ?? new Array());
|
||||||
|
return joined;
|
||||||
|
}
|
||||||
|
}
|
1
Autoloads/GameManager.cs.uid
Normal file
1
Autoloads/GameManager.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://c6eoi3ymefc0x
|
61
Autoloads/SaveSystem.cs
Normal file
61
Autoloads/SaveSystem.cs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
using Godot;
|
||||||
|
using Godot.Collections;
|
||||||
|
using Mr.BrickAdventures.scripts.Resources;
|
||||||
|
|
||||||
|
namespace Mr.BrickAdventures.Autoloads;
|
||||||
|
|
||||||
|
public partial class SaveSystem : Node
|
||||||
|
{
|
||||||
|
[Export] public string SavePath { get; set; } = "user://savegame.save";
|
||||||
|
[Export] public int Version { get; set; } = 1;
|
||||||
|
|
||||||
|
private GameManager _gameManager;
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
_gameManager = GetNode<GameManager>("/root/GameManager");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveGame()
|
||||||
|
{
|
||||||
|
var saveData = new Dictionary
|
||||||
|
{
|
||||||
|
{ "player_state", _gameManager.PlayerState},
|
||||||
|
{ "version", Version}
|
||||||
|
};
|
||||||
|
|
||||||
|
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Write);
|
||||||
|
file.StoreVar(saveData);
|
||||||
|
GD.Print("Game state saved to: ", SavePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool LoadGame()
|
||||||
|
{
|
||||||
|
if (!FileAccess.FileExists(SavePath))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
using var file = FileAccess.Open(SavePath, FileAccess.ModeFlags.Read);
|
||||||
|
var saveDataObj = (Dictionary)file.GetVar();
|
||||||
|
|
||||||
|
if (saveDataObj.ContainsKey("version") && (int)saveDataObj["version"] != Version)
|
||||||
|
{
|
||||||
|
GD.Print($"Save file version mismatch. Expected: {Version}, Found: {saveDataObj["version"]}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GD.Print("Game state loaded from: ", SavePath);
|
||||||
|
GD.Print("Player state: ", saveDataObj["player_state"]);
|
||||||
|
_gameManager.PlayerState = (Dictionary)saveDataObj["player_state"];
|
||||||
|
|
||||||
|
var skills = new Array<SkillData>();
|
||||||
|
foreach (var skill in (Array<SkillData>)_gameManager.PlayerState["unlocked_skills"])
|
||||||
|
{
|
||||||
|
skills.Add(skill);
|
||||||
|
}
|
||||||
|
|
||||||
|
_gameManager.UnlockSkills(skills);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CheckSaveExists() => FileAccess.FileExists(SavePath);
|
||||||
|
}
|
1
Autoloads/SaveSystem.cs.uid
Normal file
1
Autoloads/SaveSystem.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bh20fqbyifidc
|
65
Autoloads/UIManager.cs
Normal file
65
Autoloads/UIManager.cs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
using Godot;
|
||||||
|
using Godot.Collections;
|
||||||
|
|
||||||
|
namespace Mr.BrickAdventures.Autoloads;
|
||||||
|
|
||||||
|
public partial class UIManager : Node
|
||||||
|
{
|
||||||
|
[Export] public Array<Control> UiStack { get; set; } = new();
|
||||||
|
|
||||||
|
[Signal] public delegate void ScreenPushedEventHandler(Control screen);
|
||||||
|
[Signal] public delegate void ScreenPoppedEventHandler(Control screen);
|
||||||
|
|
||||||
|
public void PushScreen(Control screen)
|
||||||
|
{
|
||||||
|
if (screen == null)
|
||||||
|
{
|
||||||
|
GD.PushError($"Cannot push a null screen.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UiStack.Add(screen);
|
||||||
|
screen.Show();
|
||||||
|
screen.SetProcessInput(true);
|
||||||
|
screen.SetFocusMode(Control.FocusModeEnum.All);
|
||||||
|
screen.GrabFocus();
|
||||||
|
EmitSignalScreenPushed(screen);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PopScreen()
|
||||||
|
{
|
||||||
|
if (UiStack.Count == 0)
|
||||||
|
{
|
||||||
|
GD.PushError($"Cannot pop screen from an empty stack.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var top = (Control)UiStack[^1];
|
||||||
|
UiStack.RemoveAt(UiStack.Count - 1);
|
||||||
|
top.Hide();
|
||||||
|
top.SetProcessInput(false);
|
||||||
|
EmitSignalScreenPopped(top);
|
||||||
|
top.AcceptEvent();
|
||||||
|
|
||||||
|
if (UiStack.Count > 0) ((Control)UiStack[^1]).GrabFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Control TopScreen() => UiStack.Count > 0 ? (Control)UiStack[^1] : null;
|
||||||
|
|
||||||
|
public bool IsScreenOnTop(Control screen) => UiStack.Count > 0 && (Control)UiStack[^1] == screen;
|
||||||
|
|
||||||
|
public bool IsVisibleOnStack(Control screen) => UiStack.Contains(screen) && screen.Visible;
|
||||||
|
|
||||||
|
public void CloseAll()
|
||||||
|
{
|
||||||
|
while (UiStack.Count > 0)
|
||||||
|
PopScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void HideAndDisable(Control screen)
|
||||||
|
{
|
||||||
|
screen.Hide();
|
||||||
|
screen.SetProcessInput(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
1
Autoloads/UIManager.cs.uid
Normal file
1
Autoloads/UIManager.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://c3ldmnrwperr4
|
143
Mr. Brick Adventures.csproj
Normal file
143
Mr. Brick Adventures.csproj
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
<Project Sdk="Godot.NET.Sdk/4.4.1">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||||
|
<RootNamespace>Mr.BrickAdventures</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Include="README.md" />
|
||||||
|
<Content Include="scripts\components\.idea\.gitignore" />
|
||||||
|
<Content Include="scripts\components\.idea\encodings.xml" />
|
||||||
|
<Content Include="scripts\components\.idea\indexLayout.xml" />
|
||||||
|
<Content Include="scripts\components\.idea\inspectionProfiles\Project_Default.xml" />
|
||||||
|
<Content Include="scripts\components\.idea\projectSettingsUpdater.xml" />
|
||||||
|
<Content Include="scripts\components\.idea\vcs.xml" />
|
||||||
|
<Content Include="scripts\components\.idea\workspace.xml" />
|
||||||
|
<Content Include="scripts\components\BeamComponent.cs.uid" />
|
||||||
|
<Content Include="scripts\components\beam_component.gd" />
|
||||||
|
<Content Include="scripts\components\beam_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\brick_throw.gd" />
|
||||||
|
<Content Include="scripts\components\brick_throw.gd.uid" />
|
||||||
|
<Content Include="scripts\components\bullet_component.gd" />
|
||||||
|
<Content Include="scripts\components\bullet_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\cage_component.gd" />
|
||||||
|
<Content Include="scripts\components\cage_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\cannot_stomp_component.gd" />
|
||||||
|
<Content Include="scripts\components\cannot_stomp_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\can_be_launched_component.gd" />
|
||||||
|
<Content Include="scripts\components\can_be_launched_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\can_pickup.gd" />
|
||||||
|
<Content Include="scripts\components\can_pickup.gd.uid" />
|
||||||
|
<Content Include="scripts\components\charge_throw_component.gd" />
|
||||||
|
<Content Include="scripts\components\charge_throw_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\chase_level_component.gd" />
|
||||||
|
<Content Include="scripts\components\chase_level_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\cleanup_component.gd" />
|
||||||
|
<Content Include="scripts\components\cleanup_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\collapsable.gd" />
|
||||||
|
<Content Include="scripts\components\collapsable.gd.uid" />
|
||||||
|
<Content Include="scripts\components\collectable.gd" />
|
||||||
|
<Content Include="scripts\components\collectable.gd.uid" />
|
||||||
|
<Content Include="scripts\components\damage_component.gd" />
|
||||||
|
<Content Include="scripts\components\damage_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\destroyable_component.gd" />
|
||||||
|
<Content Include="scripts\components\destroyable_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\effect_inflictor_component.gd" />
|
||||||
|
<Content Include="scripts\components\effect_inflictor_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\enemy_death.gd" />
|
||||||
|
<Content Include="scripts\components\enemy_death.gd.uid" />
|
||||||
|
<Content Include="scripts\components\enemy_wave_trigger.gd" />
|
||||||
|
<Content Include="scripts\components\enemy_wave_trigger.gd.uid" />
|
||||||
|
<Content Include="scripts\components\exit_door_component.gd" />
|
||||||
|
<Content Include="scripts\components\exit_door_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\explosive_component.gd" />
|
||||||
|
<Content Include="scripts\components\explosive_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\fade_away.gd" />
|
||||||
|
<Content Include="scripts\components\fade_away.gd.uid" />
|
||||||
|
<Content Include="scripts\components\fire_effect_component.gd" />
|
||||||
|
<Content Include="scripts\components\fire_effect_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\flashing_component.gd" />
|
||||||
|
<Content Include="scripts\components\flashing_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\flip_player.gd" />
|
||||||
|
<Content Include="scripts\components\flip_player.gd.uid" />
|
||||||
|
<Content Include="scripts\components\gravity_motion_component.gd" />
|
||||||
|
<Content Include="scripts\components\gravity_motion_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\health.gd" />
|
||||||
|
<Content Include="scripts\components\health.gd.uid" />
|
||||||
|
<Content Include="scripts\components\heal_component.gd" />
|
||||||
|
<Content Include="scripts\components\heal_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\hit_component.gd" />
|
||||||
|
<Content Include="scripts\components\hit_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\homing_missile_motion.gd" />
|
||||||
|
<Content Include="scripts\components\homing_missile_motion.gd.uid" />
|
||||||
|
<Content Include="scripts\components\ice_effect_component.gd" />
|
||||||
|
<Content Include="scripts\components\ice_effect_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\invulnerability_component.gd" />
|
||||||
|
<Content Include="scripts\components\invulnerability_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\jump_pad_component.gd" />
|
||||||
|
<Content Include="scripts\components\jump_pad_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\kill_player_out_of_screen.gd" />
|
||||||
|
<Content Include="scripts\components\kill_player_out_of_screen.gd.uid" />
|
||||||
|
<Content Include="scripts\components\knockback.gd" />
|
||||||
|
<Content Include="scripts\components\knockback.gd.uid" />
|
||||||
|
<Content Include="scripts\components\launch_component.gd" />
|
||||||
|
<Content Include="scripts\components\launch_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\lever_component.gd" />
|
||||||
|
<Content Include="scripts\components\lever_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\lifetime_component.gd" />
|
||||||
|
<Content Include="scripts\components\lifetime_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\magnetic_skill.gd" />
|
||||||
|
<Content Include="scripts\components\magnetic_skill.gd.uid" />
|
||||||
|
<Content Include="scripts\components\out_of_screen_component.gd" />
|
||||||
|
<Content Include="scripts\components\out_of_screen_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\periodic_shooting.gd" />
|
||||||
|
<Content Include="scripts\components\periodic_shooting.gd.uid" />
|
||||||
|
<Content Include="scripts\components\PlatformMovementComponent.cs.uid" />
|
||||||
|
<Content Include="scripts\components\platform_movement.gd" />
|
||||||
|
<Content Include="scripts\components\PlayerController.cs.uid" />
|
||||||
|
<Content Include="scripts\components\player_death.gd" />
|
||||||
|
<Content Include="scripts\components\player_death.gd.uid" />
|
||||||
|
<Content Include="scripts\components\player_movement.gd" />
|
||||||
|
<Content Include="scripts\components\player_movement.gd.uid" />
|
||||||
|
<Content Include="scripts\components\progressive_damage_component.gd" />
|
||||||
|
<Content Include="scripts\components\progressive_damage_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\projectile_component.gd" />
|
||||||
|
<Content Include="scripts\components\projectile_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\projectile_init_component.gd" />
|
||||||
|
<Content Include="scripts\components\projectile_init_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\requirement_component.gd" />
|
||||||
|
<Content Include="scripts\components\requirement_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\score.gd" />
|
||||||
|
<Content Include="scripts\components\score.gd.uid" />
|
||||||
|
<Content Include="scripts\components\ship_movement.gd" />
|
||||||
|
<Content Include="scripts\components\ship_movement.gd.uid" />
|
||||||
|
<Content Include="scripts\components\ship_shooter.gd" />
|
||||||
|
<Content Include="scripts\components\ship_shooter.gd.uid" />
|
||||||
|
<Content Include="scripts\components\side_to_side_movement.gd" />
|
||||||
|
<Content Include="scripts\components\side_to_side_movement.gd.uid" />
|
||||||
|
<Content Include="scripts\components\skill_unlocker_component.gd" />
|
||||||
|
<Content Include="scripts\components\skill_unlocker_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\spaceship_enter_component.gd" />
|
||||||
|
<Content Include="scripts\components\spaceship_enter_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\spaceship_exit_component.gd" />
|
||||||
|
<Content Include="scripts\components\spaceship_exit_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\spin_component.gd" />
|
||||||
|
<Content Include="scripts\components\spin_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\status_effect_component.gd" />
|
||||||
|
<Content Include="scripts\components\status_effect_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\stomp_damage_component.gd" />
|
||||||
|
<Content Include="scripts\components\stomp_damage_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\straight_motion_component.gd" />
|
||||||
|
<Content Include="scripts\components\straight_motion_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\terrain_hit_fx.gd" />
|
||||||
|
<Content Include="scripts\components\terrain_hit_fx.gd.uid" />
|
||||||
|
<Content Include="scripts\components\tooltip_component.gd" />
|
||||||
|
<Content Include="scripts\components\tooltip_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\trail_component.gd" />
|
||||||
|
<Content Include="scripts\components\trail_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\trigger_lever_component.gd" />
|
||||||
|
<Content Include="scripts\components\trigger_lever_component.gd.uid" />
|
||||||
|
<Content Include="scripts\components\unlock_on_requirement_component.gd" />
|
||||||
|
<Content Include="scripts\components\unlock_on_requirement_component.gd.uid" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
19
Mr. Brick Adventures.sln
Normal file
19
Mr. Brick Adventures.sln
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 2012
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mr. Brick Adventures", "Mr. Brick Adventures.csproj", "{A1D482B9-207B-4D6C-A0A0-D9E6D1AE2356}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
ExportDebug|Any CPU = ExportDebug|Any CPU
|
||||||
|
ExportRelease|Any CPU = ExportRelease|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{A1D482B9-207B-4D6C-A0A0-D9E6D1AE2356}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{A1D482B9-207B-4D6C-A0A0-D9E6D1AE2356}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{A1D482B9-207B-4D6C-A0A0-D9E6D1AE2356}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU
|
||||||
|
{A1D482B9-207B-4D6C-A0A0-D9E6D1AE2356}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU
|
||||||
|
{A1D482B9-207B-4D6C-A0A0-D9E6D1AE2356}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU
|
||||||
|
{A1D482B9-207B-4D6C-A0A0-D9E6D1AE2356}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
6
Mr. Brick Adventures.sln.DotSettings.user
Normal file
6
Mr. Brick Adventures.sln.DotSettings.user
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACanvasItem_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fef7b819b226fab796d1dfe66d415dd7510bcac87675020ddb8f03a828e763_003FCanvasItem_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACollisionShape2D_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F2ca9b7334678f5c97c7c2a9fbe4837be71cae11b6a30408dd4791b18f997e4a_003FCollisionShape2D_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANode2D_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F86db9cd834346aad02d74c1b66dd9c64d6ef3147435dd9c9c9477b48f7_003FNode2D_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ARectangleShape2D_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fa1cc98873548652da0c14ecefa4737431426fcbb24a7f0641e3d9c266c3_003FRectangleShape2D_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AShape2D_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F3671dbbd9b17cdf2bf9075b468b6bd7e3ab13fc3be7a116484085d3b6cc9fe_003FShape2D_002Ecs/@EntryIndexedValue">ForceIncluded</s:String></wpf:ResourceDictionary>
|
@@ -51,4 +51,4 @@ func close_all() -> void:
|
|||||||
|
|
||||||
func hide_and_disable(screen: Control) -> void:
|
func hide_and_disable(screen: Control) -> void:
|
||||||
screen.hide()
|
screen.hide()
|
||||||
screen.set_process_input(false)
|
screen.set_process_input(false)
|
||||||
|
@@ -19,7 +19,7 @@ config/version="in-dev"
|
|||||||
run/main_scene="uid://cl00e2ocomk3m"
|
run/main_scene="uid://cl00e2ocomk3m"
|
||||||
config/use_custom_user_dir=true
|
config/use_custom_user_dir=true
|
||||||
config/custom_user_dir_name="MrBrickAdventures"
|
config/custom_user_dir_name="MrBrickAdventures"
|
||||||
config/features=PackedStringArray("4.4", "GL Compatibility")
|
config/features=PackedStringArray("4.4", "C#", "GL Compatibility")
|
||||||
run/max_fps=180
|
run/max_fps=180
|
||||||
boot_splash/bg_color=Color(0, 0, 0, 1)
|
boot_splash/bg_color=Color(0, 0, 0, 1)
|
||||||
boot_splash/show_image=false
|
boot_splash/show_image=false
|
||||||
|
70
scripts/Resources/ChargeThrowInputResource.cs
Normal file
70
scripts/Resources/ChargeThrowInputResource.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
1
scripts/Resources/ChargeThrowInputResource.cs.uid
Normal file
1
scripts/Resources/ChargeThrowInputResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://dtpdh4jp51jis
|
9
scripts/Resources/CollectableResource.cs
Normal file
9
scripts/Resources/CollectableResource.cs
Normal 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; }
|
||||||
|
}
|
1
scripts/Resources/CollectableResource.cs.uid
Normal file
1
scripts/Resources/CollectableResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://gptsgaw3agkf
|
8
scripts/Resources/CollectableType.cs
Normal file
8
scripts/Resources/CollectableType.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Mr.BrickAdventures.scripts.Resources;
|
||||||
|
|
||||||
|
public enum CollectableType
|
||||||
|
{
|
||||||
|
Coin,
|
||||||
|
Kid,
|
||||||
|
Health,
|
||||||
|
}
|
1
scripts/Resources/CollectableType.cs.uid
Normal file
1
scripts/Resources/CollectableType.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://2ql8wj3vfeke
|
19
scripts/Resources/SkillData.cs
Normal file
19
scripts/Resources/SkillData.cs
Normal 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; }
|
||||||
|
}
|
1
scripts/Resources/SkillData.cs.uid
Normal file
1
scripts/Resources/SkillData.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://d4crrfmbgxnqf
|
8
scripts/Resources/SkillType.cs
Normal file
8
scripts/Resources/SkillType.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Mr.BrickAdventures.scripts.Resources;
|
||||||
|
|
||||||
|
public enum SkillType
|
||||||
|
{
|
||||||
|
Attack,
|
||||||
|
Throw,
|
||||||
|
Misc,
|
||||||
|
}
|
1
scripts/Resources/SkillType.cs.uid
Normal file
1
scripts/Resources/SkillType.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://sma20qug2r0q
|
10
scripts/Resources/StatusEffectDataResource.cs
Normal file
10
scripts/Resources/StatusEffectDataResource.cs
Normal 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; }
|
||||||
|
}
|
1
scripts/Resources/StatusEffectDataResource.cs.uid
Normal file
1
scripts/Resources/StatusEffectDataResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://pw0pu6gb21y2
|
8
scripts/Resources/StatusEffectType.cs
Normal file
8
scripts/Resources/StatusEffectType.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Mr.BrickAdventures.scripts.Resources;
|
||||||
|
|
||||||
|
public enum StatusEffectType
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Fire,
|
||||||
|
Ice
|
||||||
|
}
|
1
scripts/Resources/StatusEffectType.cs.uid
Normal file
1
scripts/Resources/StatusEffectType.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://b0a7k7mse3l68
|
20
scripts/Resources/TapThrowInputResource.cs
Normal file
20
scripts/Resources/TapThrowInputResource.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
1
scripts/Resources/TapThrowInputResource.cs.uid
Normal file
1
scripts/Resources/TapThrowInputResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cx7ryqxemgs56
|
24
scripts/Resources/ThrowInputResource.cs
Normal file
24
scripts/Resources/ThrowInputResource.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
1
scripts/Resources/ThrowInputResource.cs.uid
Normal file
1
scripts/Resources/ThrowInputResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://mnn5wy5cyr4m
|
15
scripts/Screenshot.cs
Normal file
15
scripts/Screenshot.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
1
scripts/Screenshot.cs.uid
Normal file
1
scripts/Screenshot.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://chrhjch4ymfvr
|
144
scripts/SkillManager.cs
Normal file
144
scripts/SkillManager.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
1
scripts/SkillManager.cs.uid
Normal file
1
scripts/SkillManager.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://di572axt0c3s8
|
102
scripts/UI/AudioSettings.cs
Normal file
102
scripts/UI/AudioSettings.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
1
scripts/UI/AudioSettings.cs.uid
Normal file
1
scripts/UI/AudioSettings.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://g61qqsymqfxd
|
81
scripts/UI/ChargeProgressBar.cs
Normal file
81
scripts/UI/ChargeProgressBar.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
23
scripts/UI/Credits.cs
Normal file
23
scripts/UI/Credits.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
scripts/UI/GameOverScreen.cs
Normal file
39
scripts/UI/GameOverScreen.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
109
scripts/components/BeamComponent.cs
Normal file
109
scripts/components/BeamComponent.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/BeamComponent.cs.uid
Normal file
1
scripts/components/BeamComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://df1llrbm80e02
|
70
scripts/components/BrickThrowComponent.cs
Normal file
70
scripts/components/BrickThrowComponent.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/BrickThrowComponent.cs.uid
Normal file
1
scripts/components/BrickThrowComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://b0bv8kw5w5037
|
45
scripts/components/BulletComponent.cs
Normal file
45
scripts/components/BulletComponent.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/BulletComponent.cs.uid
Normal file
1
scripts/components/BulletComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cfw8nbrarex0i
|
47
scripts/components/CageComponent.cs
Normal file
47
scripts/components/CageComponent.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/CageComponent.cs.uid
Normal file
1
scripts/components/CageComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://dojn0gw8hsv02
|
8
scripts/components/CanBeLaunchedComponent.cs
Normal file
8
scripts/components/CanBeLaunchedComponent.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace Mr.BrickAdventures.scripts.components;
|
||||||
|
|
||||||
|
public partial class CanBeLaunchedComponent : Node
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
1
scripts/components/CanBeLaunchedComponent.cs.uid
Normal file
1
scripts/components/CanBeLaunchedComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cjcc7fia15wu3
|
8
scripts/components/CanPickUpComponent.cs
Normal file
8
scripts/components/CanPickUpComponent.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace Mr.BrickAdventures.scripts.components;
|
||||||
|
|
||||||
|
public partial class CanPickUpComponent : Node
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
1
scripts/components/CanPickUpComponent.cs.uid
Normal file
1
scripts/components/CanPickUpComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://mnjg3p0aw1ow
|
8
scripts/components/CannotStompComponent.cs
Normal file
8
scripts/components/CannotStompComponent.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace Mr.BrickAdventures.scripts.components;
|
||||||
|
|
||||||
|
public partial class CannotStompComponent : Node
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
1
scripts/components/CannotStompComponent.cs.uid
Normal file
1
scripts/components/CannotStompComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://dh67n16bnl838
|
79
scripts/components/ChaseLevelComponent.cs
Normal file
79
scripts/components/ChaseLevelComponent.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/ChaseLevelComponent.cs.uid
Normal file
1
scripts/components/ChaseLevelComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://dnpj72mfi1ywl
|
11
scripts/components/CleanupComponent.cs
Normal file
11
scripts/components/CleanupComponent.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace Mr.BrickAdventures.scripts.components;
|
||||||
|
|
||||||
|
public partial class CleanupComponent : Node
|
||||||
|
{
|
||||||
|
public void CleanUp()
|
||||||
|
{
|
||||||
|
Owner.QueueFree();
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/CleanupComponent.cs.uid
Normal file
1
scripts/components/CleanupComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://v7tt4w6bejux
|
81
scripts/components/CollapsableComponent.cs
Normal file
81
scripts/components/CollapsableComponent.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/CollapsableComponent.cs.uid
Normal file
1
scripts/components/CollapsableComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://xqhrb1c7f6y4
|
50
scripts/components/CollectableComponent.cs
Normal file
50
scripts/components/CollectableComponent.cs
Normal 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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/CollectableComponent.cs.uid
Normal file
1
scripts/components/CollectableComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://r4jybneigfcn
|
105
scripts/components/DamageComponent.cs
Normal file
105
scripts/components/DamageComponent.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/DamageComponent.cs.uid
Normal file
1
scripts/components/DamageComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://2i7p7v135u7c
|
35
scripts/components/DestroyableComponent.cs
Normal file
35
scripts/components/DestroyableComponent.cs
Normal 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
1
scripts/components/DestroyableComponent.cs.uid
Normal file
1
scripts/components/DestroyableComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://ctfrbj52ejay4
|
27
scripts/components/EffectInflictorComponent.cs
Normal file
27
scripts/components/EffectInflictorComponent.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/EffectInflictorComponent.cs.uid
Normal file
1
scripts/components/EffectInflictorComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://xjq33vj0rol0
|
42
scripts/components/EnemyDeathComponent.cs
Normal file
42
scripts/components/EnemyDeathComponent.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/EnemyDeathComponent.cs.uid
Normal file
1
scripts/components/EnemyDeathComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cfdugoeduudar
|
51
scripts/components/EnemyWaveTriggerComponent.cs
Normal file
51
scripts/components/EnemyWaveTriggerComponent.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/EnemyWaveTriggerComponent.cs.uid
Normal file
1
scripts/components/EnemyWaveTriggerComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://d3fpwddc2j41x
|
52
scripts/components/ExitDoorComponent.cs
Normal file
52
scripts/components/ExitDoorComponent.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/ExitDoorComponent.cs.uid
Normal file
1
scripts/components/ExitDoorComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://c5mhwlyvfuaip
|
81
scripts/components/ExplosiveComponent.cs
Normal file
81
scripts/components/ExplosiveComponent.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/ExplosiveComponent.cs.uid
Normal file
1
scripts/components/ExplosiveComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://7uttgdr6cr5y
|
32
scripts/components/FadeAwayComponent.cs
Normal file
32
scripts/components/FadeAwayComponent.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/FadeAwayComponent.cs.uid
Normal file
1
scripts/components/FadeAwayComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bjln6jb1sigx2
|
71
scripts/components/FireEffectComponent.cs
Normal file
71
scripts/components/FireEffectComponent.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/FireEffectComponent.cs.uid
Normal file
1
scripts/components/FireEffectComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cxuig4xh8nfov
|
83
scripts/components/FlashingComponent.cs
Normal file
83
scripts/components/FlashingComponent.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/FlashingComponent.cs.uid
Normal file
1
scripts/components/FlashingComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://dvyd26ricriql
|
36
scripts/components/FlipComponent.cs
Normal file
36
scripts/components/FlipComponent.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/FlipComponent.cs.uid
Normal file
1
scripts/components/FlipComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://dy78ak8eykw6e
|
37
scripts/components/GravityMotionComponent.cs
Normal file
37
scripts/components/GravityMotionComponent.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/GravityMotionComponent.cs.uid
Normal file
1
scripts/components/GravityMotionComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cwi5qashdag1g
|
48
scripts/components/HealComponent.cs
Normal file
48
scripts/components/HealComponent.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/HealComponent.cs.uid
Normal file
1
scripts/components/HealComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bmx6rk281yim2
|
65
scripts/components/HealthComponent.cs
Normal file
65
scripts/components/HealthComponent.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/HealthComponent.cs.uid
Normal file
1
scripts/components/HealthComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://dgb8bqcri7nsj
|
75
scripts/components/HitComponent.cs
Normal file
75
scripts/components/HitComponent.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/HitComponent.cs.uid
Normal file
1
scripts/components/HitComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bo506l4x0808e
|
64
scripts/components/HomingMissileMotionComponent.cs
Normal file
64
scripts/components/HomingMissileMotionComponent.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/HomingMissileMotionComponent.cs.uid
Normal file
1
scripts/components/HomingMissileMotionComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://c2hplha6af74q
|
70
scripts/components/IceEffectComponent.cs
Normal file
70
scripts/components/IceEffectComponent.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/IceEffectComponent.cs.uid
Normal file
1
scripts/components/IceEffectComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://d1388lhp2gpgr
|
31
scripts/components/InvulnerabilityComponent.cs
Normal file
31
scripts/components/InvulnerabilityComponent.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace Mr.BrickAdventures.scripts.components;
|
||||||
|
|
||||||
|
public partial class InvulnerabilityComponent : Node
|
||||||
|
{
|
||||||
|
[Export] public float Duration { get; set; } = 1f;
|
||||||
|
[Export] public FlashingComponent FlashingComponent { get; set; }
|
||||||
|
|
||||||
|
private bool _isInvulnerable = false;
|
||||||
|
|
||||||
|
public void Activate()
|
||||||
|
{
|
||||||
|
if (_isInvulnerable)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_isInvulnerable = true;
|
||||||
|
FlashingComponent?.StartFlashing();
|
||||||
|
|
||||||
|
var timer = GetTree().CreateTimer(Duration);
|
||||||
|
timer.Timeout += Deactivate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Deactivate()
|
||||||
|
{
|
||||||
|
_isInvulnerable = false;
|
||||||
|
FlashingComponent?.StopFlashing();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsInvulnerable() => _isInvulnerable;
|
||||||
|
}
|
1
scripts/components/InvulnerabilityComponent.cs.uid
Normal file
1
scripts/components/InvulnerabilityComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://cecelixl41t3j
|
39
scripts/components/JumpPadComponent.cs
Normal file
39
scripts/components/JumpPadComponent.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
namespace Mr.BrickAdventures.scripts.components;
|
||||||
|
|
||||||
|
public partial class JumpPadComponent : Node
|
||||||
|
{
|
||||||
|
[Export] public float JumpForce { get; set; } = 10f;
|
||||||
|
[Export] public Area2D Area { get; set; }
|
||||||
|
[Export] public Sprite2D Sprite { get; set; }
|
||||||
|
[Export] public int StartAnimationIndex { get; set; } = 0;
|
||||||
|
[Export] public float AnimationDuration { get; set; } = 0.5f;
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
Area.BodyEntered += OnBodyEntered;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBodyEntered(Node2D body)
|
||||||
|
{
|
||||||
|
var canBeLaunched = body.GetNodeOrNull<CanBeLaunchedComponent>("CanBeLaunchedComponent");
|
||||||
|
if (canBeLaunched == null) return;
|
||||||
|
|
||||||
|
if (body is not PlayerController { CurrentMovement: PlatformMovementComponent movement }) return;
|
||||||
|
_ = HandleLaunchPadAnimation();
|
||||||
|
movement.Body.Velocity = new Vector2(movement.Body.Velocity.X, -JumpForce);
|
||||||
|
movement.JumpSfx?.Play();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleLaunchPadAnimation()
|
||||||
|
{
|
||||||
|
if (Sprite == null) return;
|
||||||
|
|
||||||
|
var timer = GetTree().CreateTimer(AnimationDuration);
|
||||||
|
Sprite.Frame = StartAnimationIndex + 1;
|
||||||
|
await ToSignal(timer, Timer.SignalName.Timeout);
|
||||||
|
Sprite.Frame = StartAnimationIndex;
|
||||||
|
}
|
||||||
|
}
|
1
scripts/components/JumpPadComponent.cs.uid
Normal file
1
scripts/components/JumpPadComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
|||||||
|
uid://bgbnof7aeydmq
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user