Skip to content

Commit 75c717e

Browse files
Merge pull request #9 from jacuzzicoding/feature/crafting
Feature/crafting
2 parents ab7ef00 + f40af5b commit 75c717e

16 files changed

Lines changed: 3650 additions & 32 deletions

Source/Crafting/CraftingSystem.cs

Lines changed: 691 additions & 0 deletions
Large diffs are not rendered by default.

Source/Crafting/Recipe.cs

Lines changed: 600 additions & 0 deletions
Large diffs are not rendered by default.

Source/Crafting/RecipeManager.cs

Lines changed: 704 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using MyIslandGame.Crafting;
4+
5+
namespace MyIslandGame.ECS.Components
6+
{
7+
/// <summary>
8+
/// Component that allows an entity to perform crafting operations.
9+
/// </summary>
10+
public class CraftingComponent : Component
11+
{
12+
/// <summary>
13+
/// Gets the highest level crafting station available to this entity.
14+
/// </summary>
15+
public CraftingStationType HighestStationType { get; private set; }
16+
17+
/// <summary>
18+
/// Gets a list of unlocked recipe IDs.
19+
/// </summary>
20+
public HashSet<string> UnlockedRecipes { get; private set; }
21+
22+
/// <summary>
23+
/// Gets a value indicating whether the entity is currently crafting.
24+
/// </summary>
25+
public bool IsCrafting { get; private set; }
26+
27+
/// <summary>
28+
/// Event raised when a recipe is unlocked.
29+
/// </summary>
30+
public event EventHandler<RecipeUnlockedEventArgs> RecipeUnlocked;
31+
32+
/// <summary>
33+
/// Initializes a new instance of the <see cref="CraftingComponent"/> class.
34+
/// </summary>
35+
/// <param name="stationType">The highest crafting station type available.</param>
36+
public CraftingComponent(CraftingStationType stationType = CraftingStationType.None)
37+
{
38+
HighestStationType = stationType;
39+
UnlockedRecipes = new HashSet<string>();
40+
IsCrafting = false;
41+
}
42+
43+
/// <summary>
44+
/// Sets the crafting state.
45+
/// </summary>
46+
/// <param name="isCrafting">Whether the entity is crafting.</param>
47+
public void SetCraftingState(bool isCrafting)
48+
{
49+
IsCrafting = isCrafting;
50+
}
51+
52+
/// <summary>
53+
/// Upgrades the crafting station type.
54+
/// </summary>
55+
/// <param name="stationType">The new station type.</param>
56+
/// <returns>True if the station was upgraded, false if downgraded or unchanged.</returns>
57+
public bool UpgradeStation(CraftingStationType stationType)
58+
{
59+
if (stationType > HighestStationType)
60+
{
61+
HighestStationType = stationType;
62+
return true;
63+
}
64+
65+
return false;
66+
}
67+
68+
/// <summary>
69+
/// Unlocks a recipe for crafting.
70+
/// </summary>
71+
/// <param name="recipeId">The ID of the recipe to unlock.</param>
72+
/// <returns>True if the recipe was newly unlocked, false if already unlocked.</returns>
73+
public bool UnlockRecipe(string recipeId)
74+
{
75+
if (string.IsNullOrEmpty(recipeId))
76+
return false;
77+
78+
if (UnlockedRecipes.Add(recipeId))
79+
{
80+
OnRecipeUnlocked(recipeId);
81+
return true;
82+
}
83+
84+
return false;
85+
}
86+
87+
/// <summary>
88+
/// Checks if a recipe is unlocked.
89+
/// </summary>
90+
/// <param name="recipeId">The ID of the recipe to check.</param>
91+
/// <returns>True if the recipe is unlocked, otherwise false.</returns>
92+
public bool IsRecipeUnlocked(string recipeId)
93+
{
94+
if (string.IsNullOrEmpty(recipeId))
95+
return false;
96+
97+
return UnlockedRecipes.Contains(recipeId);
98+
}
99+
100+
/// <summary>
101+
/// Raises the RecipeUnlocked event.
102+
/// </summary>
103+
/// <param name="recipeId">The ID of the recipe that was unlocked.</param>
104+
protected virtual void OnRecipeUnlocked(string recipeId)
105+
{
106+
RecipeUnlocked?.Invoke(this, new RecipeUnlockedEventArgs(recipeId));
107+
}
108+
}
109+
110+
/// <summary>
111+
/// Event arguments for recipe unlocked events.
112+
/// </summary>
113+
public class RecipeUnlockedEventArgs : EventArgs
114+
{
115+
/// <summary>
116+
/// Gets the ID of the recipe that was unlocked.
117+
/// </summary>
118+
public string RecipeId { get; }
119+
120+
/// <summary>
121+
/// Initializes a new instance of the <see cref="RecipeUnlockedEventArgs"/> class.
122+
/// </summary>
123+
/// <param name="recipeId">The ID of the recipe that was unlocked.</param>
124+
public RecipeUnlockedEventArgs(string recipeId)
125+
{
126+
RecipeId = recipeId;
127+
}
128+
}
129+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
using System;
2+
using Microsoft.Xna.Framework;
3+
using MyIslandGame.Crafting;
4+
5+
namespace MyIslandGame.ECS.Components
6+
{
7+
/// <summary>
8+
/// Component for entities that function as crafting stations.
9+
/// </summary>
10+
public class CraftingStationComponent : Component
11+
{
12+
/// <summary>
13+
/// Gets the type of crafting station.
14+
/// </summary>
15+
public CraftingStationType StationType { get; }
16+
17+
/// <summary>
18+
/// Gets the interaction range for this crafting station.
19+
/// </summary>
20+
public float InteractionRange { get; }
21+
22+
/// <summary>
23+
/// Gets or sets the entity that is currently using this crafting station.
24+
/// </summary>
25+
public Entity CurrentUser { get; private set; }
26+
27+
/// <summary>
28+
/// Gets a value indicating whether the station is currently in use.
29+
/// </summary>
30+
public bool InUse => CurrentUser != null;
31+
32+
/// <summary>
33+
/// Event raised when the station is used.
34+
/// </summary>
35+
public event EventHandler<StationUsedEventArgs> StationUsed;
36+
37+
/// <summary>
38+
/// Initializes a new instance of the <see cref="CraftingStationComponent"/> class.
39+
/// </summary>
40+
/// <param name="stationType">The type of crafting station.</param>
41+
/// <param name="interactionRange">The interaction range for this station.</param>
42+
public CraftingStationComponent(CraftingStationType stationType, float interactionRange = 100f)
43+
{
44+
if (stationType == CraftingStationType.None)
45+
throw new ArgumentException("Crafting station type cannot be None", nameof(stationType));
46+
47+
StationType = stationType;
48+
InteractionRange = interactionRange;
49+
CurrentUser = null;
50+
}
51+
52+
/// <summary>
53+
/// Sets the current user of this crafting station.
54+
/// </summary>
55+
/// <param name="user">The entity using the station, or null to clear.</param>
56+
/// <returns>True if the user was set or cleared successfully, false if already in use.</returns>
57+
public bool SetUser(Entity user)
58+
{
59+
// If we're clearing the user, always allow it
60+
if (user == null)
61+
{
62+
CurrentUser = null;
63+
return true;
64+
}
65+
66+
// Don't allow if already in use by someone else
67+
if (InUse && CurrentUser != user)
68+
return false;
69+
70+
// Set the user
71+
CurrentUser = user;
72+
OnStationUsed(user);
73+
return true;
74+
}
75+
76+
/// <summary>
77+
/// Checks if an entity is within range to use this station.
78+
/// </summary>
79+
/// <param name="entity">The entity to check.</param>
80+
/// <returns>True if the entity is within range, otherwise false.</returns>
81+
public bool IsEntityInRange(Entity entity)
82+
{
83+
if (entity == null || Owner == null)
84+
return false;
85+
86+
// Both entities need transform components
87+
if (!entity.HasComponent<TransformComponent>() || !Owner.HasComponent<TransformComponent>())
88+
return false;
89+
90+
var entityTransform = entity.GetComponent<TransformComponent>();
91+
var stationTransform = Owner.GetComponent<TransformComponent>();
92+
93+
// Calculate distance
94+
float distance = Vector2.Distance(entityTransform.Position, stationTransform.Position);
95+
96+
return distance <= InteractionRange;
97+
}
98+
99+
/// <summary>
100+
/// Raises the StationUsed event.
101+
/// </summary>
102+
/// <param name="user">The entity using the station.</param>
103+
protected virtual void OnStationUsed(Entity user)
104+
{
105+
StationUsed?.Invoke(this, new StationUsedEventArgs(user));
106+
}
107+
}
108+
109+
/// <summary>
110+
/// Event arguments for station used events.
111+
/// </summary>
112+
public class StationUsedEventArgs : EventArgs
113+
{
114+
/// <summary>
115+
/// Gets the entity using the station.
116+
/// </summary>
117+
public Entity User { get; }
118+
119+
/// <summary>
120+
/// Initializes a new instance of the <see cref="StationUsedEventArgs"/> class.
121+
/// </summary>
122+
/// <param name="user">The entity using the station.</param>
123+
public StationUsedEventArgs(Entity user)
124+
{
125+
User = user;
126+
}
127+
}
128+
}

Source/ECS/EntityManager.cs.fixed

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Microsoft.Xna.Framework;
5+
6+
namespace MyIslandGame.ECS
7+
{
8+
/// <summary>
9+
/// Manages all entities and systems in the game.
10+
/// </summary>
11+
public class EntityManager
12+
{
13+
private readonly List<Entity> _entities = new();
14+
private readonly List<Entity> _entitiesToAdd = new();
15+
private readonly List<Entity> _entitiesToRemove = new();
16+
17+
private readonly List<System> _systems = new();
18+
private readonly List<System> _systemsToAdd = new();
19+
private readonly List<System> _systemsToRemove = new();
20+
21+
private readonly Dictionary<Type, List<Entity>> _componentEntityMap = new();
22+
23+
/// <summary>
24+
/// Event raised when a component is added to an entity.
25+
/// </summary>
26+
public event Action<Entity, Component> ComponentAdded;
27+
28+
/// <summary>
29+
/// Event raised when a component is removed from an entity.
30+
/// </summary>
31+
public event Action<Entity, Component> ComponentRemoved;
32+
33+
/// <summary>
34+
/// Event raised when an entity is added.
35+
/// </summary>
36+
public event Action<Entity> EntityAdded;
37+
38+
/// <summary>
39+
/// Event raised when an entity is removed.
40+
/// </summary>
41+
public event Action<Entity> EntityRemoved;
42+
43+
/// <summary>
44+
/// Creates a new entity.
45+
/// </summary>
46+
/// <returns>The created entity.</returns>
47+
public Entity CreateEntity()
48+
{
49+
var entity = new Entity();
50+
entity.EntityManager = this;
51+
_entitiesToAdd.Add(entity);
52+
return entity;
53+
}
54+
55+
/// <summary>
56+
/// Adds an existing entity to the manager.
57+
/// </summary>
58+
/// <param name="entity">The entity to add.</param>
59+
/// <exception cref="ArgumentException">Thrown if the entity is already managed by another manager.</exception>
60+
public void AddEntity(Entity entity)
61+
{
62+
if (entity.EntityManager != null && entity.EntityManager != this)
63+
{
64+
throw new ArgumentException("Entity is already managed by another EntityManager", nameof(entity));
65+
}
66+
67+
entity.EntityManager = this;
68+
_entitiesToAdd.Add(entity);
69+
}
70+
71+
/// <summary>
72+
/// Gets an entity by its unique identifier.
73+
/// </summary>
74+
/// <param name="id">The entity identifier.</param>
75+
/// <returns>The entity if found, otherwise null.</returns>
76+
public Entity GetEntityById(Guid id)
77+
{
78+
return _entities.FirstOrDefault(e => e.Id == id);
79+
}
80+
81+
/// <summary>
82+
/// Destroys an entity, removing it from management.
83+
/// </summary>
84+
/// <param name="entity">The entity to destroy.</param>
85+
public void DestroyEntity(Entity entity)
86+
{
87+
if (entity.EntityManager == this)
88+
{
89+
entity.Destroy();
90+
_entitiesToRemove.Add(entity);
91+
}
92+
}
93+
94+
/// <summary>
95+
/// Adds a system to the manager.
96+
/// </summary>
97+
/// <param name="system">The system to add.</param>
98+
public void AddSystem(System system)
99+
{
100+
_systemsToAdd.Add(system);
101+
}
102+
103+
/// <summary>
104+
/// Removes a system from the manager.
105+
/// </summary>
106+
/// <param name="system">The system to remove.</param>
107+
public void RemoveSystem(System system)
108+
{
109+
_systemsToRemove.Add(system);
110+
}
111+
112+
/// <summary>
113+
/// Gets all entities managed by this manager.
114+
/// </summary>
115+
/// <returns>An enumerable collection of entities.</returns>
116+
public IEnumerable<Entity> GetEntities()
117+
{
118+
return _entities;
119+
}
120+
121+
/// <summary>
122+
/// Gets entities that have all of the specified component types.
123+
/// </summary>
124+
/// <param name="componentTypes">The component types to filter by.</param>
125+
/// <returns>An enumerable collection of entities.</returns>
126+
public IEnumerable<Entity> GetEntitiesWithComponents(params Type[] componentTypes)
127+
{
128+
if (componentTypes.Length == 0)
129+
{
130+
return _entities;
131+
}
132+
133+
IEnumerable<Entity> result = null;
134+
135+
foreach (var type in componentTypes)
136+
{
137+
if (_componentEntityMap.TryGetValue(type, out var entities))
138+
{
139+
if (result ==

0 commit comments

Comments
 (0)