diff --git a/Assets/Resources/Data/Buildings.xml b/Assets/Resources/Data/Buildings.xml
index 43c757c..4ad8ac4 100644
--- a/Assets/Resources/Data/Buildings.xml
+++ b/Assets/Resources/Data/Buildings.xml
@@ -17,7 +17,7 @@
10
3
4
- 0
+ 0
BDFA02
@@ -33,7 +33,7 @@
10
3
4
- 0
+ 0
BDFA03
@@ -49,7 +49,7 @@
13
2
4
- 0
+ 0
BDFA04
@@ -65,7 +65,7 @@
0
5
5
- 0
+ 0
BDFA05
@@ -81,7 +81,7 @@
0
2
5
- 0
+ 0
BDFA06
@@ -97,7 +97,7 @@
10
4
2
- 1
+ 1
BDFA07
@@ -113,7 +113,7 @@
20
2
2
- 5
+ 5
@@ -133,7 +133,7 @@
4
800
0
- 0
+ 0
BDDF02
@@ -150,7 +150,7 @@
2
0
40
- 0
+ 0
BDDF03
@@ -167,7 +167,7 @@
5
2000
0
- 0
+ 0
BDDF04
@@ -184,7 +184,7 @@
1
0
0
- 3
+ 3
BDDF05
@@ -201,7 +201,7 @@
3
5000
0
- 4
+ 4
BDDF06
@@ -218,6 +218,6 @@
3
0
80
- 6
+ 6
diff --git a/Assets/Resources/Data/Events.xml b/Assets/Resources/Data/Events.xml
deleted file mode 100644
index 5487329..0000000
--- a/Assets/Resources/Data/Events.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/Assets/Resources/Data/GameEvents.xml b/Assets/Resources/Data/GameEvents.xml
new file mode 100644
index 0000000..9b8b4d2
--- /dev/null
+++ b/Assets/Resources/Data/GameEvents.xml
@@ -0,0 +1,52 @@
+
+
+
+
+ Luke Visits Yoda
+ LUKE_VISITS_YODA
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Luke Confronts Vader
+ LUKE_CONFRONTS_VADER
+ true
+
+
+
+ LUKE_SKYWALKER
+ DARTH_VADER
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Luke Discovers Heritage
+ LUKE_DISCOVERS_HERITAGE
+ false
+
+
+
+
+
+
diff --git a/Assets/Resources/Data/Officers.xml b/Assets/Resources/Data/Officers.xml
index ec60434..b4d8e51 100644
--- a/Assets/Resources/Data/Officers.xml
+++ b/Assets/Resources/Data/Officers.xml
@@ -9,6 +9,7 @@
Emperor Palpatine
FNEMP1
true
+ false
CORUSCANT
@@ -55,6 +56,7 @@
Darth Vader
FNEMP1
true
+ false
Diplomacy
@@ -102,6 +104,7 @@
FNEMP1
false
+ true
Diplomacy
@@ -149,6 +152,7 @@
FNEMP1
false
+ true
Diplomacy
@@ -196,6 +200,7 @@
FNEMP1
false
+ true
Diplomacy
@@ -243,6 +248,7 @@
FNEMP1
false
+ true
Diplomacy
@@ -290,6 +296,7 @@
FNEMP1
false
+ true
Diplomacy
@@ -337,6 +344,7 @@
FNEMP1
false
+ true
Diplomacy
@@ -384,6 +392,7 @@
FNEMP1
false
+ true
Diplomacy
@@ -431,6 +440,7 @@
FNEMP1
false
+ true
Diplomacy
@@ -478,6 +488,7 @@
FNEMP1
false
+ true
Diplomacy
@@ -525,6 +536,7 @@
FNEMP1
false
+ true
Diplomacy
@@ -572,6 +584,7 @@
FNEMP1
false
+ true
Diplomacy
@@ -619,6 +632,7 @@
FNEMP1
false
+ true
Diplomacy
@@ -666,6 +680,7 @@
FNEMP1
false
+ true
Diplomacy
@@ -713,6 +728,7 @@
FNEMP1
false
+ true
Diplomacy
@@ -760,6 +776,7 @@
FNEMP1
false
+ true
Diplomacy
@@ -807,6 +824,7 @@
FNEMP1
false
+ true
Diplomacy
@@ -854,6 +872,7 @@
FNEMP1
false
+ true
Diplomacy
@@ -901,6 +920,7 @@
FNEMP1
false
+ true
Diplomacy
@@ -948,6 +968,7 @@
FNEMP1
false
+ true
Diplomacy
@@ -996,6 +1017,7 @@
Mon Mothma
FNALL1
true
+ false
Diplomacy
@@ -1040,6 +1062,7 @@
Leia Organa
FNALL1
true
+ false
Diplomacy
@@ -1084,6 +1107,7 @@
Luke Skywalker
FNALL1
true
+ false
Diplomacy
@@ -1129,6 +1153,7 @@
Han Solo
FNALL1
true
+ false
Diplomacy
@@ -1176,6 +1201,7 @@
FNALL1
false
+ true
Diplomacy
@@ -1223,6 +1249,7 @@
FNALL1
false
+ true
Diplomacy
@@ -1270,6 +1297,7 @@
FNALL1
false
+ true
Diplomacy
@@ -1317,6 +1345,7 @@
FNALL1
false
+ true
Diplomacy
@@ -1364,6 +1393,7 @@
FNALL1
false
+ true
Diplomacy
@@ -1411,6 +1441,7 @@
FNALL1
false
+ true
Diplomacy
@@ -1458,6 +1489,7 @@
FNALL1
false
+ true
Diplomacy
@@ -1505,6 +1537,7 @@
FNALL1
false
+ true
Diplomacy
@@ -1552,6 +1585,7 @@
FNALL1
false
+ true
Diplomacy
@@ -1599,6 +1633,7 @@
FNALL1
false
+ true
Diplomacy
@@ -1646,6 +1681,7 @@
FNALL1
false
+ true
Diplomacy
@@ -1693,6 +1729,7 @@
FNALL1
false
+ true
Diplomacy
@@ -1740,6 +1777,7 @@
FNALL1
false
+ true
Diplomacy
@@ -1787,6 +1825,7 @@
FNALL1
false
+ true
Diplomacy
@@ -1834,6 +1873,7 @@
FNALL1
false
+ true
Diplomacy
@@ -1881,6 +1921,7 @@
FNALL1
false
+ true
Diplomacy
@@ -1928,6 +1969,7 @@
FNALL1
false
+ true
Diplomacy
@@ -1967,4 +2009,52 @@
40
None
+
+ OFAL022
+ YODA
+ Yoda
+
+ FNALL1
+
+ false
+ false
+
+
+ Diplomacy
+ 150
+
+
+ Espionage
+ 150
+
+
+ Combat
+ 150
+
+
+ Leadership
+ 150
+
+
+ 100
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+ false
+ false
+ false
+ true
+ 0
+ 100
+ 0
+ None
+
\ No newline at end of file
diff --git a/Assets/Resources/Data/PlanetSystems.xml b/Assets/Resources/Data/PlanetSystems.xml
index f2b77dc..2babc9a 100644
--- a/Assets/Resources/Data/PlanetSystems.xml
+++ b/Assets/Resources/Data/PlanetSystems.xml
@@ -9,8 +9,6 @@
Small
High
PSSEW
- 11648
- 20
469
642
@@ -92,8 +90,6 @@
Small
High
PSCOR
- 11649
- 21
272
96
@@ -175,8 +171,6 @@
Large
High
PSFAK
- 11655
- 27
385
525
@@ -258,8 +252,6 @@
Small
High
PLDOL
- 11653
- 25
472
253
@@ -341,8 +333,6 @@
Medium
High
PLFAR
- 11656
- 28
587
370
@@ -424,8 +414,6 @@
Large
High
PLSLU
- 11653
- 25
472
253
@@ -510,8 +498,6 @@
Large
Low
PLATR
- 11649
- 21
272
96
@@ -593,8 +579,6 @@
Small
Low
PLMOD
- 11661
- 33
191
148
@@ -676,8 +660,6 @@
Medium
Low
PLQUE
- 11663
- 35
113
277
@@ -759,8 +741,6 @@
Small
Medium
PLCHU
- 11650
- 22
132
417
@@ -843,8 +823,6 @@
Medium
Medium
PLKAN
- 11659
- 31
221
569
@@ -926,8 +904,6 @@
Small
Medium
PLDUF
- 11654
- 26
359
663
@@ -1009,8 +985,6 @@
Small
Medium
PLABR
- 11648
- 20
469
642
@@ -1092,8 +1066,6 @@
Large
Low
PLXAP
- 11667
- 39
575
755
@@ -1113,8 +1085,8 @@
PLXAP03
- NORULAC
- Norulac
+ MISSING_NAME
+ missing_name
585
790
@@ -1175,8 +1147,6 @@
Small
Low
PLORU
- 11662
- 34
695
684
@@ -1258,8 +1228,6 @@
Small
Low
PLJOS
- 11658
- 30
737
548
@@ -1341,8 +1309,6 @@
Small
Low
PLSUM
- 11666
- 38
717
438
@@ -1424,8 +1390,6 @@
Medium
Medium
PLGLY
- 11667
- 39
575
755
@@ -1507,8 +1471,6 @@
Small
Medium
PLMAY
- 11660
- 32
488
129
@@ -1590,8 +1552,6 @@
Small
Medium
PLCAL
- 11652
- 24
380
129
diff --git a/Assets/Resources/Data/Regiments.xml b/Assets/Resources/Data/Regiments.xml
index b9961b5..674ddcc 100644
--- a/Assets/Resources/Data/Regiments.xml
+++ b/Assets/Resources/Data/Regiments.xml
@@ -15,7 +15,7 @@
3
2
20
- 0
+ 0
REEM002
@@ -29,7 +29,7 @@
5
5
15
- 0
+ 0
REEM003
@@ -43,7 +43,7 @@
6
5
25
- 0
+ 0
REEM004
@@ -57,7 +57,7 @@
2
2
5
- 1
+ 1
REEM005
@@ -71,7 +71,7 @@
8
6
30
- 2
+ 2
@@ -88,7 +88,7 @@
3
5
15
- 0
+ 0
REAL002
@@ -102,7 +102,7 @@
5
5
15
- 0
+ 0
REAL003
@@ -116,7 +116,7 @@
4
2
35
- 1
+ 1
REAL004
@@ -130,7 +130,7 @@
8
9
20
- 2
+ 2
REAL005
@@ -144,7 +144,7 @@
4
4
20
- 3
+ 3
diff --git a/Assets/Resources/Data/Starfighters.xml b/Assets/Resources/Data/Starfighters.xml
index cd21441..50a92d4 100644
--- a/Assets/Resources/Data/Starfighters.xml
+++ b/Assets/Resources/Data/Starfighters.xml
@@ -24,7 +24,7 @@
12
0
0
- 0
+ 0
SFEM02
@@ -47,7 +47,7 @@
12
12
7
- 1
+ 1
SFEM03
@@ -70,7 +70,7 @@
12
0
0
- 3
+ 3
SFEM04
@@ -93,7 +93,7 @@
12
0
10
- 8
+ 8
@@ -119,7 +119,7 @@
12
12
7
- 0
+ 0
SFAL02
@@ -142,7 +142,7 @@
12
12
7
- 0
+ 0
SFAL03
@@ -165,7 +165,7 @@
12
0
0
- 3
+ 3
SFAL04
@@ -188,6 +188,6 @@
20
18
7
- 5
+ 5
diff --git a/Assets/Scenes/GameInitializer.unity b/Assets/Scenes/GameInitializer.unity
index 6d03ca7..cae0043 100644
--- a/Assets/Scenes/GameInitializer.unity
+++ b/Assets/Scenes/GameInitializer.unity
@@ -131,7 +131,7 @@ GameObject:
serializedVersion: 6
m_Component:
- component: {fileID: 82622251}
- - component: {fileID: 82622250}
+ - component: {fileID: 82622252}
m_Layer: 0
m_Name: GameInitializer
m_TagString: Untagged
@@ -139,18 +139,6 @@ GameObject:
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
---- !u!114 &82622250
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 82622249}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: ed248910eb46d4891a7f8e50d18abb29, type: 3}
- m_Name:
- m_EditorClassIdentifier:
--- !u!4 &82622251
Transform:
m_ObjectHideFlags: 0
@@ -166,6 +154,18 @@ Transform:
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &82622252
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 82622249}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: ed248910eb46d4891a7f8e50d18abb29, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
--- !u!1 &342021166
GameObject:
m_ObjectHideFlags: 0
diff --git a/Assets/Scripts/Configs/NewGameConfig.cs b/Assets/Scripts/Configs/NewGameConfig.cs
index 8448699..0bf761c 100644
--- a/Assets/Scripts/Configs/NewGameConfig.cs
+++ b/Assets/Scripts/Configs/NewGameConfig.cs
@@ -9,7 +9,7 @@
[Serializable]
public class NewGamePlanetSizeConfig : Config
{
- public int Snall;
+ public int Small;
public int Medium;
public int Large;
diff --git a/Assets/Scripts/Events/GameAction.cs b/Assets/Scripts/Events/GameAction.cs
index b45ad7e..6df6b45 100644
--- a/Assets/Scripts/Events/GameAction.cs
+++ b/Assets/Scripts/Events/GameAction.cs
@@ -18,32 +18,43 @@
public abstract class GameAction
{
[PersistableAttribute(Name = "Type")]
- public string ActionType { get; set; }
+ protected string ActionType { get; set; }
- public string Value { get; set; }
- public Dictionary Parameters { get; set; }
+ [PersistableAttribute(Name = "Value")]
+ protected string ActionValue { get; set; }
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
public GameAction() { }
///
/// Creates a new GameAction with a specific value (as an XML attribute).
///
+ /// The type of the action.
/// The value of the action.
- public GameAction(string value)
+ public GameAction(string type, string value)
{
- Value = value;
+ ActionType = type;
+ ActionValue = value;
}
///
- /// Creates a new GameAction with specific parameters.
+ ///
///
- /// The parameters of the action.
- public GameAction(Dictionary parameters)
+ ///
+ protected string GetActionType()
{
- Parameters = parameters;
+ return ActionType;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ protected string GetActionValue()
+ {
+ return ActionValue;
}
///
diff --git a/Assets/Scripts/Events/GameActions.cs b/Assets/Scripts/Events/GameActions.cs
index c5adc86..412d006 100644
--- a/Assets/Scripts/Events/GameActions.cs
+++ b/Assets/Scripts/Events/GameActions.cs
@@ -2,84 +2,84 @@
using System.Collections.Generic;
using System.Linq;
+[PersistableObject(Name = "MoveUnits")]
public class MoveUnitsAction : GameAction
{
+ public List UnitInstanceIDs { get; set; } = new List();
+ public string TargetInstanceID { get; set; }
+
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
public MoveUnitsAction()
: base() { }
- public MoveUnitsAction(List nodes, ISceneNode target)
- : base(
- new Dictionary
- {
- { "UnitInstanceIDs", nodes.Select(n => n.InstanceID).ToList() },
- { "TargetInstanceID", target.InstanceID },
- }
- ) { }
-
- public MoveUnitsAction(Dictionary parameters)
- : base(parameters) { }
-
public override void Execute(Game game)
{
// Get the parameters for the action.
- List unitInstanceIds = (List)Parameters["UnitInstanceIDs"];
- string targetInstanceId = (string)Parameters["TargetInstanceID"];
-
- IMovable movable = game.GetSceneNodeByInstanceID(unitInstanceIds[0]) as IMovable;
- ISceneNode target = game.GetSceneNodeByInstanceID(targetInstanceId);
+ IMovable movable = game.GetSceneNodeByInstanceID(UnitInstanceIDs[0]) as IMovable;
+ ISceneNode target = game.GetSceneNodeByInstanceID(TargetInstanceID);
movable.MoveTo(target);
}
}
+[PersistableObject(Name = "RandomOutcome")]
public class RandomOutcomeAction : GameAction
{
+ public List Actions { get; set; } = new List();
private Random random = new Random();
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
public RandomOutcomeAction()
: base() { }
- public RandomOutcomeAction(Dictionary parameters)
- : base(parameters) { }
-
public override void Execute(Game game)
{
- double probability = Convert.ToDouble(Value);
-
- // Get the parameters for the action.
- List actions = (List)Parameters["Actions"];
+ double probability = Convert.ToDouble(this.GetActionValue());
// Execute a random action.
if (random.NextDouble() < probability)
{
- actions[random.Next(actions.Count)].Execute(game);
+ Actions[random.Next(Actions.Count)].Execute(game);
}
}
}
+[PersistableObject(Name = "TriggerDuel")]
+public class TriggerDuelAction : GameAction
+{
+ public List AttackerInstanceIDs { get; set; }
+ public List DefenderInstanceIDs { get; set; }
+
+ ///
+ /// Default constructor used for deserialization.
+ ///
+ public TriggerDuelAction()
+ : base() { }
+
+ public override void Execute(Game game)
+ {
+ // @TODO: Implement
+ }
+}
+
+[PersistableObject(Name = "TriggerEvent")]
public class TriggerEventAction : GameAction
{
+ public string EventInstanceID { get; set; }
+
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
public TriggerEventAction()
: base() { }
- public TriggerEventAction(Dictionary parameters)
- : base(parameters) { }
-
public override void Execute(Game game)
{
- // Get the parameters for the action.
- string eventId = (string)Parameters["EventID"];
-
- GameEvent gameEvent = game.GetPoolEventByID(eventId);
+ GameEvent gameEvent = game.GetEventByInstanceID(EventInstanceID);
gameEvent.Execute(game);
}
diff --git a/Assets/Scripts/Events/GameConditional.cs b/Assets/Scripts/Events/GameConditional.cs
index 4ef658d..f52e96b 100644
--- a/Assets/Scripts/Events/GameConditional.cs
+++ b/Assets/Scripts/Events/GameConditional.cs
@@ -13,31 +13,42 @@
[PersistableObject]
public abstract class GameConditional : BaseGameEntity
{
- [PersistableMember]
- public string Value { get; set; }
- public Dictionary Parameters { get; set; }
+ [PersistableAttribute(Name = "Value")]
+ public string ConditionalValue { get; set; }
+
+ [PersistableAttribute(Name = "Type")]
+ public string ConditionalType { get; set; }
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
public GameConditional() { }
///
/// Creates a new GameConditional with a specific value (as an XML attribute).
///
- /// The value of the condition.
- public GameConditional(string value)
+ /// The value of the condition.
+ public GameConditional(string conditionalValue)
+ {
+ ConditionalValue = conditionalValue;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public string GetConditionalValue()
{
- Value = value;
+ return ConditionalValue;
}
///
- /// Creates a new GameConditional with specific parameters.
+ ///
///
- /// The parameters of the condition.
- public GameConditional(Dictionary parameters)
+ ///
+ public string GetConditionalType()
{
- Parameters = parameters;
+ return ConditionalType;
}
///
diff --git a/Assets/Scripts/Events/GameConditionals.cs b/Assets/Scripts/Events/GameConditionals.cs
index 3847fb0..d4a41d3 100644
--- a/Assets/Scripts/Events/GameConditionals.cs
+++ b/Assets/Scripts/Events/GameConditionals.cs
@@ -5,90 +5,90 @@
///
///
///
+[PersistableObject(Name = "And")]
public class AndConditional : GameConditional
{
+ [PersistableMember(Name = "Conditionals")]
+ public List Conditionals = new List();
+
public AndConditional()
: base() { }
- public AndConditional(Dictionary parameters)
- : base(parameters) { }
-
public override bool IsMet(Game game)
{
- List conditionals = (List)Parameters["Conditionals"];
- return conditionals.All(conditional => conditional.IsMet(game));
+ return Conditionals.All(conditional => conditional.IsMet(game));
}
}
///
///
///
+[PersistableObject(Name = "Or")]
public class OrConditional : GameConditional
{
+ [PersistableMember(Name = "Conditionals")]
+ public List Conditionals = new List();
+
public OrConditional()
: base() { }
- public OrConditional(Dictionary parameters)
- : base(parameters) { }
-
public override bool IsMet(Game game)
{
- List conditionals = (List)Parameters["Conditionals"];
- return conditionals.Any(conditional => conditional.IsMet(game));
+ return Conditionals.Any(conditional => conditional.IsMet(game));
}
}
///
///
///
+[PersistableObject(Name = "Not")]
public class NotConditional : GameConditional
{
+ [PersistableMember(Name = "Conditionals")]
+ public List Conditionals = new List();
+
public NotConditional()
: base() { }
- public NotConditional(Dictionary parameters)
- : base(parameters) { }
-
public override bool IsMet(Game game)
{
- List conditionals = (List)Parameters["Conditionals"];
- return conditionals.All(conditional => !conditional.IsMet(game));
+ return Conditionals.All(conditional => !conditional.IsMet(game));
}
}
///
///
///
+[PersistableObject(Name = "Xor")]
public class XorConditional : GameConditional
{
+ [PersistableMember(Name = "Conditionals")]
+ public List Conditionals = new List();
+
public XorConditional()
: base() { }
- public XorConditional(Dictionary parameters)
- : base(parameters) { }
-
public override bool IsMet(Game game)
{
- List conditionals = (List)Parameters["Conditionals"];
- return conditionals.Count(conditional => conditional.IsMet(game)) == 1;
+ return Conditionals.Count(conditional => conditional.IsMet(game)) == 1;
}
}
///
///
///
+[PersistableObject(Name = "AreOnSamePlanet")]
public class AreOnSamePlanetConditional : GameConditional
{
+ [PersistableMember(Name = "UnitInstanceIDs")]
+ public List UnitInstanceIDs { get; set; }
+
public AreOnSamePlanetConditional()
: base() { }
- public AreOnSamePlanetConditional(Dictionary parameters)
- : base(parameters) { }
-
public override bool IsMet(Game game)
{
- List instanceIDs = (List)Parameters["UnitInstanceIDs"];
- List sceneNodes = game.GetSceneNodesByInstanceIDs(instanceIDs);
+ List sceneNodes = game.GetSceneNodesByInstanceIDs(UnitInstanceIDs);
Planet comparator = null;
// Check if all units are on the same planet.
@@ -115,20 +115,18 @@ public override bool IsMet(Game game)
///
///
///
+[PersistableObject(Name = "AreOnOpposingFactions")]
public class AreOnOpposingFactionsConditional : GameConditional
{
+ List UnitInstanceIDs { get; set; } = new List();
+
public AreOnOpposingFactionsConditional()
: base() { }
- public AreOnOpposingFactionsConditional(Dictionary parameters)
- : base(parameters) { }
-
public override bool IsMet(Game game)
{
- List instanceIDs = (List)Parameters["UnitInstanceIDs"];
-
// Get the scene nodes for the units.
- List sceneNodes = game.GetSceneNodesByInstanceIDs(instanceIDs);
+ List sceneNodes = game.GetSceneNodesByInstanceIDs(UnitInstanceIDs);
// Check if the units are on opposing factions.
return sceneNodes.Count == 2
@@ -139,19 +137,18 @@ public override bool IsMet(Game game)
///
///
///
+///
+[PersistableObject(Name = "IsOnMission")]
public class IsOnMissionConditional : GameConditional
{
public IsOnMissionConditional()
: base() { }
- public IsOnMissionConditional(Dictionary parameters)
- : base(parameters) { }
-
public override bool IsMet(Game game)
{
- string instanceID = (string)Value;
- ISceneNode sceneNode = game.GetSceneNodeByInstanceID(instanceID);
-
+ string instanceId = this.GetConditionalValue();
+ GameLogger.Log("VALUE " + GetConditionalValue());
+ ISceneNode sceneNode = game.GetSceneNodeByInstanceID(instanceId);
// Check if the unit is on a mission.
return sceneNode != null && sceneNode.GetParent() is Mission;
}
@@ -160,18 +157,16 @@ public override bool IsMet(Game game)
///
///
///
+[PersistableObject(Name = "IsMovable")]
public class IsMovableConditional : GameConditional
{
public IsMovableConditional()
: base() { }
- public IsMovableConditional(Dictionary parameters)
- : base(parameters) { }
-
public override bool IsMet(Game game)
{
- string instanceID = (string)Value;
- ISceneNode sceneNode = game.GetSceneNodeByInstanceID(instanceID);
+ string instanceId = this.GetConditionalValue();
+ ISceneNode sceneNode = game.GetSceneNodeByInstanceID(instanceId);
// Check if the ISceneNode implements IMovable and is movable.
if (sceneNode is IMovable movable)
@@ -186,19 +181,19 @@ public override bool IsMet(Game game)
///
///
///
+///
+[PersistableObject(Name = "AreOnPlanet")]
public class AreOnPlanetConditional : GameConditional
{
+ public List UnitInstanceIDs { get; set; }
+
public AreOnPlanetConditional()
: base() { }
- public AreOnPlanetConditional(Dictionary parameters)
- : base(parameters) { }
-
public override bool IsMet(Game game)
{
// Get the instance IDs of the units to check.
- List instanceIDs = (List)Parameters["UnitInstanceIDs"];
- List sceneNodes = game.GetSceneNodesByInstanceIDs(instanceIDs);
+ List sceneNodes = game.GetSceneNodesByInstanceIDs(UnitInstanceIDs);
// Check if all units are on a planet.
return sceneNodes.All(node => node.GetParentOfType() != null);
@@ -208,6 +203,7 @@ public override bool IsMet(Game game)
///
///
///
+[PersistableObject(Name = "TickCount")]
public class TickCountConditional : GameConditional
{
private enum ComparisonType
@@ -220,25 +216,25 @@ private enum ComparisonType
public TickCountConditional()
: base() { }
- public TickCountConditional(Dictionary parameters)
- : base(parameters) { }
-
public override bool IsMet(Game game)
{
- string comparisonValue = (string)Parameters["Comparison"];
- ComparisonType comparison = Enum.TryParse(comparisonValue, out ComparisonType result)
+ ComparisonType comparison = Enum.TryParse(
+ this.GetConditionalType(),
+ out ComparisonType result
+ )
? result
: ComparisonType.EqualTo;
- int targetTickCount = Convert.ToInt32(Parameters["Value"]);
- // Check if the current tick count meets the comparison.
return comparison switch
{
- ComparisonType.EqualTo => game.CurrentTick == targetTickCount,
- ComparisonType.GreaterThan => game.CurrentTick > targetTickCount,
- ComparisonType.LessThan => game.CurrentTick < targetTickCount,
+ ComparisonType.EqualTo => game.CurrentTick
+ == Convert.ToInt32(this.GetConditionalValue()),
+ ComparisonType.GreaterThan => game.CurrentTick
+ > Convert.ToInt32(this.GetConditionalValue()),
+ ComparisonType.LessThan => game.CurrentTick
+ < Convert.ToInt32(this.GetConditionalValue()),
_ => throw new InvalidSceneOperationException(
- "Invalid comparison type for TickCountConditional."
+ $"Invalid comparison type \"{comparison}\" for TickCountConditional."
),
};
}
@@ -247,17 +243,15 @@ public override bool IsMet(Game game)
///
///
///
+[PersistableObject(Name = "IsEventComplete")]
public class IsEventCompleteConditional : GameConditional
{
public IsEventCompleteConditional()
: base() { }
- public IsEventCompleteConditional(Dictionary parameters)
- : base(parameters) { }
-
public override bool IsMet(Game game)
{
- string eventInstanceId = (string)Value;
+ string eventInstanceId = this.GetConditionalValue();
// Check if the event is complete.
return game.IsEventComplete(eventInstanceId);
diff --git a/Assets/Scripts/Events/GameEvent.cs b/Assets/Scripts/Events/GameEvent.cs
index 4337af8..dc4887b 100644
--- a/Assets/Scripts/Events/GameEvent.cs
+++ b/Assets/Scripts/Events/GameEvent.cs
@@ -19,11 +19,11 @@ public class GameEvent : BaseGameEntity
{
// Event Properties
public bool IsRepeatable { get; set; }
- public List Conditionals { get; set; }
- public List Actions { get; set; }
+ public List Conditionals { get; set; } = new List();
+ public List Actions { get; set; } = new List();
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
public GameEvent() { }
diff --git a/Assets/Scripts/Events/ScheduledEvent.cs b/Assets/Scripts/Events/ScheduledEvent.cs
deleted file mode 100644
index 75569c7..0000000
--- a/Assets/Scripts/Events/ScheduledEvent.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-///
-/// Represents a scheduled event within the game.
-/// A ScheduledEvent is a wrapper around a GameEvent, responsible for tracking
-/// when (at what game tick) the event should be executed.
-///
-///
-/// The purpose of the ScheduledEvent class is to separate the timing aspect from the logic of the GameEvent itself.
-///
-/// This allows the GameEvent class to be immutable and stateless after creation, while the ScheduledEvent class
-/// can be modified to change the execution time of the event, in addition to anything else that might be needed.
-///
-public class ScheduledEvent
-{
- public GameEvent Event { get; }
- public int ScheduledTick { get; set; }
-
- ///
- /// Default constructor used for serialization.
- ///
- public ScheduledEvent() { }
-
- ///
- /// Initializes a new instance of the ScheduledEvent class with a given event and tick.
- ///
- /// The GameEvent to be executed.
- /// The tick at which the event should execute.
- public ScheduledEvent(GameEvent gameEvent, int scheduledTick)
- {
- Event = gameEvent;
- ScheduledTick = scheduledTick;
- }
-
- ///
- /// Gets the GameEvent associated with this ScheduledEvent.
- ///
- ///
- public GameEvent GetEvent()
- {
- return Event;
- }
-}
diff --git a/Assets/Scripts/Game/Building.cs b/Assets/Scripts/Game/Building.cs
index 989112d..0e2eb39 100644
--- a/Assets/Scripts/Game/Building.cs
+++ b/Assets/Scripts/Game/Building.cs
@@ -45,6 +45,7 @@ public class Building : LeafNode, IManufacturable, IMovable
public int Bombardment { get; set; }
public int WeaponStrength { get; set; }
public int ShieldStrength { get; set; }
+ public int WeaponPower { get; set; }
// Manufacturing Info
public string ProducerOwnerID { get; set; }
diff --git a/Assets/Scripts/Game/CapitalShip.cs b/Assets/Scripts/Game/CapitalShip.cs
index a145d8d..08365a2 100644
--- a/Assets/Scripts/Game/CapitalShip.cs
+++ b/Assets/Scripts/Game/CapitalShip.cs
@@ -76,7 +76,7 @@ public class CapitalShip : ContainerNode, IManufacturable, IMovable
public string InitialParentInstanceID { get; set; }
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
public CapitalShip() { }
@@ -98,6 +98,24 @@ public int GetCurrentStarfighterCount()
return Starfighters.Count;
}
+ ///
+ ///
+ ///
+ ///
+ public int GetRegimentCapacity()
+ {
+ return RegimentCapacity;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public int GetCurrentRegimentCount()
+ {
+ return Regiments.Count;
+ }
+
///
/// Adds a starfighter to the capital ship.
///
diff --git a/Assets/Scripts/Game/Faction.cs b/Assets/Scripts/Game/Faction.cs
index c6ae805..2c1c8ba 100644
--- a/Assets/Scripts/Game/Faction.cs
+++ b/Assets/Scripts/Game/Faction.cs
@@ -8,17 +8,7 @@
///
public class Faction : BaseGameEntity
{
- public Dictionary> Messages = new Dictionary<
- MessageType,
- List
- >()
- {
- { MessageType.Conflict, new List() },
- { MessageType.Mission, new List() },
- { MessageType.PopularSupport, new List() },
- { MessageType.Resource, new List() },
- };
-
+ public List UnrecruitedOfficers { get; set; } = new List();
public Dictionary<
ManufacturingType,
SortedDictionary>
@@ -49,8 +39,19 @@ public Dictionary<
{ typeof(Starfighter), new List() },
};
+ public Dictionary> Messages = new Dictionary<
+ MessageType,
+ List
+ >()
+ {
+ { MessageType.Conflict, new List() },
+ { MessageType.Mission, new List() },
+ { MessageType.PopularSupport, new List() },
+ { MessageType.Resource, new List() },
+ };
+
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
public Faction() { }
@@ -172,6 +173,15 @@ public List GetResearchedTechnologies(ManufacturingType manufacturin
.ToList();
}
+ ///
+ ///
+ ///
+ ///
+ public Dictionary GetResearchLevels()
+ {
+ return ManufacturingResearchLevels;
+ }
+
///
/// Returns the research level for a specific manufacturing type.
///
diff --git a/Assets/Scripts/Game/Fleet.cs b/Assets/Scripts/Game/Fleet.cs
index 891c717..cced508 100644
--- a/Assets/Scripts/Game/Fleet.cs
+++ b/Assets/Scripts/Game/Fleet.cs
@@ -28,6 +28,10 @@ public int GetStarfighterCapacity()
return CapitalShips.Sum(capitalShip => capitalShip.GetStarfighterCapacity());
}
+ ///
+ ///
+ ///
+ ///
public int GetCurrentStarfighterCount()
{
return CapitalShips.Sum(capitalShip => capitalShip.GetCurrentStarfighterCount());
@@ -42,6 +46,33 @@ public int GetExcessStarfighterCapacity()
return GetStarfighterCapacity() - GetCurrentStarfighterCount();
}
+ ///
+ ///
+ ///
+ ///
+ public int GetRegimentCapacity()
+ {
+ return CapitalShips.Sum(capitalShip => capitalShip.GetRegimentCapacity());
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public int GetCurrentRegimentCount()
+ {
+ return CapitalShips.Sum(capitalShip => capitalShip.GetCurrentRegimentCount());
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public int GetExcessRegimentCapacity()
+ {
+ return GetRegimentCapacity() - GetCurrentRegimentCount();
+ }
+
///
/// Constructor that initializes the fleet with an owner.
///
diff --git a/Assets/Scripts/Game/Game.cs b/Assets/Scripts/Game/Game.cs
index 24297c4..2b51671 100644
--- a/Assets/Scripts/Game/Game.cs
+++ b/Assets/Scripts/Game/Game.cs
@@ -15,8 +15,6 @@ public class Game
public int CurrentTick = 0;
// Game Events
- public Dictionary> ScheduledEvents =
- new Dictionary>();
public List EventPool = new List();
public HashSet CompletedEventIDs = new HashSet();
@@ -101,9 +99,8 @@ public GalaxyMap GetGalaxyMap()
///
/// The node to attach.
/// The parent node to attach the node to.
- /// Whether to attach the node and its children recursively.
/// Thrown when the node is not allowed to be attached.
- public void AttachNode(ISceneNode node, ISceneNode parent, bool recursive = true)
+ public void AttachNode(ISceneNode node, ISceneNode parent)
{
// If the node already has a parent, throw an exception.
if (node.GetParent() != null)
@@ -119,11 +116,8 @@ public void AttachNode(ISceneNode node, ISceneNode parent, bool recursive = true
// Register the node to the faction's list of owned units.
RegisterOwnedUnit(node);
- if (recursive)
- {
- // Register the node and its children.
- node.Traverse(AddSceneNodeByInstanceID);
- }
+ // Register the node and its children.
+ node.Traverse(AddSceneNodeByInstanceID);
}
///
@@ -164,7 +158,7 @@ public void AddSceneNodeByInstanceID(ISceneNode node)
catch (ArgumentException)
{
throw new GameException(
- $"Node with Instance ID \"{node.GetInstanceID()}\" and Display Name \"{node.GetDisplayName()}\" already exists in the game."
+ $"Cannot add duplicate node \"{node.GetInstanceID()}\" and Display Name \"{node.GetDisplayName()}\" to scene."
);
}
}
@@ -239,7 +233,7 @@ public List GetSceneNodesByOwnerInstanceID(string ownerInstanceId)
/// A list of nodes of type T.
public List GetSceneNodesByType()
{
- var result = new List();
+ List result = new List();
// Recursive function to traverse nodes.
void Traverse(ISceneNode node)
@@ -289,62 +283,31 @@ public void DeregsiterOwnedUnit(ISceneNode node)
}
///
- /// Retrieves all nodes of a specified type T, stopping further traversal if the type is found.
+ ///
///
- /// The ID of the event to retrieve.
- /// The game event with the specified ID.
- public GameEvent GetPoolEventByID(string eventID)
+ ///
+ public List GetEventPool()
{
- return EventPool.FirstOrDefault(gameEvent => gameEvent.InstanceID == eventID);
- }
-
- ///
- /// Retrieves a list of scheduled events for the specified tick.
- ///
- /// A list of scheduled events for the specified tick.
- public List GetScheduledEvents(int tick)
- {
- if (ScheduledEvents.TryGetValue(tick, out List scheduledEvents))
- {
- return scheduledEvents;
- }
- else
- {
- return new List();
- }
+ return EventPool;
}
///
- /// Adds a game event to the game event dictionary.
+ ///
///
- /// The game event to add.
- /// The tick at which the game event occurs.
- public void ScheduleGameEvent(GameEvent gameEvent, int tick)
+ ///
+ public void RemoveEvent(GameEvent gameEvent)
{
- if (ScheduledEvents.ContainsKey(tick))
- {
- ScheduledEvents[tick].Add(new ScheduledEvent(gameEvent, tick));
- }
- else
- {
- ScheduledEvents[tick] = new List
- {
- new ScheduledEvent(gameEvent, tick),
- };
- }
+ EventPool.Remove(gameEvent);
}
///
- /// Removes a scheduled event from the game event dictionary.
+ ///
///
- /// The scheduled event to remove.
- /// The tick at which the scheduled event occurs.
- public void RemoveScheduledEvent(ScheduledEvent scheduledEvent, int tick)
+ ///
+ ///
+ public GameEvent GetEventByInstanceID(string instanceId)
{
- if (ScheduledEvents.ContainsKey(tick))
- {
- ScheduledEvents[tick].Remove(scheduledEvent);
- }
+ return EventPool.Find(gameEvent => gameEvent.InstanceID == instanceId);
}
///
diff --git a/Assets/Scripts/Game/GameSummary.cs b/Assets/Scripts/Game/GameSummary.cs
index e370185..993f67f 100644
--- a/Assets/Scripts/Game/GameSummary.cs
+++ b/Assets/Scripts/Game/GameSummary.cs
@@ -45,7 +45,7 @@ public sealed class GameSummary
public string PlayerFactionID;
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
public GameSummary() { }
}
diff --git a/Assets/Scripts/Missions/IMissionParticipant.cs b/Assets/Scripts/Game/IMissionParticipant.cs
similarity index 100%
rename from Assets/Scripts/Missions/IMissionParticipant.cs
rename to Assets/Scripts/Game/IMissionParticipant.cs
diff --git a/Assets/Scripts/Game/Message.cs b/Assets/Scripts/Game/Message.cs
index 54ce249..76ccf8e 100644
--- a/Assets/Scripts/Game/Message.cs
+++ b/Assets/Scripts/Game/Message.cs
@@ -16,7 +16,7 @@ public class Message : BaseGameEntity
public bool Read = false;
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
public Message() { }
diff --git a/Assets/Scripts/Game/Officer.cs b/Assets/Scripts/Game/Officer.cs
index 5173926..6a62b29 100644
--- a/Assets/Scripts/Game/Officer.cs
+++ b/Assets/Scripts/Game/Officer.cs
@@ -25,6 +25,7 @@ public class Officer : LeafNode, IMissionParticipant, IMovable
// Officer Info
public bool IsMain { get; set; }
+ public bool IsRecruitable { get; set; }
public bool IsCaptured { get; set; }
public bool CanBetray { get; set; }
public bool IsTraitor { get; set; }
@@ -88,7 +89,7 @@ public class Officer : LeafNode, IMissionParticipant, IMovable
public bool CanImproveMissionSkill => true;
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
public Officer() { }
diff --git a/Assets/Scripts/Game/Planet.cs b/Assets/Scripts/Game/Planet.cs
index 3a0e46f..62f07f8 100644
--- a/Assets/Scripts/Game/Planet.cs
+++ b/Assets/Scripts/Game/Planet.cs
@@ -52,7 +52,7 @@ public class Planet : ContainerNode
new Dictionary>();
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
public Planet() { }
@@ -113,30 +113,32 @@ public void SetPopularSupport(string factionInstanceId, int support)
else
{
int overage = totalSupport + supportDifference - MAX_POPULAR_SUPPORT;
+ ShiftFactionSupport(factionInstanceId, overage);
+ PopularSupport[factionInstanceId] = support;
+ }
+ }
- foreach (
- KeyValuePair kvp in PopularSupport
- .Where(kvp => kvp.Key != factionInstanceId)
- .ToList()
- )
- {
- int reduction = Math.Min(overage, kvp.Value);
- PopularSupport[kvp.Key] -= reduction;
- overage -= reduction;
-
- if (overage == 0)
- break;
- }
-
- if (overage > 0)
- {
- throw new GameStateException(
- $"Cannot set popular support for faction {factionInstanceId} to {support}. "
- + $"Total support would exceed {MAX_POPULAR_SUPPORT} even after reducing other factions' support."
- );
- }
+ ///
+ /// Shifts the support of other factions to accommodate the increase in support for the given faction.
+ ///
+ /// The faction ID to exclude from reduction.
+ /// The amount of support to reduce from other factions.
+ private void ShiftFactionSupport(string excludedFactionId, int overage)
+ {
+ // Sort the factions by support in descending order.
+ foreach (
+ KeyValuePair kvp in PopularSupport
+ .Where(kvp => kvp.Key != excludedFactionId)
+ .ToList()
+ )
+ {
+ // Reduce the support for the faction by the minimum of the overage and the faction's current support.
+ int reduction = Math.Min(overage, kvp.Value);
+ PopularSupport[kvp.Key] -= reduction;
+ overage -= reduction;
- PopularSupport[factionInstanceId] = support;
+ if (overage == 0)
+ break;
}
}
@@ -173,18 +175,7 @@ public int GetBuildTime(IManufacturable manufacturable, int quantity)
{
int totalMaterialCost = manufacturable.GetConstructionCost() * quantity;
ManufacturingType requiredManufacturingType = manufacturable.GetManufacturingType();
- double combinedRate = 0;
-
- foreach (List buildingList in Buildings.Values)
- {
- foreach (Building building in buildingList)
- {
- if (building.GetProductionType() == requiredManufacturingType)
- {
- combinedRate += 1.0 / building.GetProcessRate();
- }
- }
- }
+ double combinedRate = GetCombinedProductionRate(requiredManufacturingType);
if (combinedRate == 0)
return 0;
@@ -193,6 +184,19 @@ public int GetBuildTime(IManufacturable manufacturable, int quantity)
return (int)Math.Ceiling(combinedTime);
}
+ ///
+ /// Calculates the combined production rate for a specific manufacturing type.
+ ///
+ /// The manufacturing type to calculate the rate for.
+ /// The combined production rate.
+ private double GetCombinedProductionRate(ManufacturingType manufacturingType)
+ {
+ return Buildings
+ .Values.SelectMany(buildingList => buildingList)
+ .Where(building => building.GetProductionType() == manufacturingType)
+ .Sum(building => 1.0 / building.GetProcessRate());
+ }
+
///
/// Calculates the total production progress per tick for a given manufacturing type on a planet.
///
@@ -200,7 +204,7 @@ public int GetBuildTime(IManufacturable manufacturable, int quantity)
/// The calculated progress.
public int GetProductionRate(ManufacturingType type)
{
- double combinedRate = GetBuildings(type).Sum(building => 1.0 / building.GetProcessRate());
+ double combinedRate = GetCombinedProductionRate(type);
return (int)Math.Ceiling(combinedRate);
}
@@ -209,10 +213,29 @@ public int GetProductionRate(ManufacturingType type)
///
/// The unit to be added to the manufacturing queue.
public void AddToManufacturingQueue(IManufacturable manufacturable)
+ {
+ ValidateManufacturable(manufacturable);
+ ManufacturingType type = manufacturable.GetManufacturingType();
+
+ if (!ManufacturingQueue.ContainsKey(type))
+ {
+ ManufacturingQueue.Add(type, new List());
+ }
+
+ manufacturable.SetPosition(GetPosition());
+ ManufacturingQueue[type].Add(manufacturable);
+ }
+
+ ///
+ /// Validates if a manufacturable can be added to the manufacturing queue.
+ ///
+ /// The manufacturable to validate.
+ ///
+ private void ValidateManufacturable(IManufacturable manufacturable)
{
if (manufacturable is ISceneNode sceneNode && sceneNode.GetParent() == null)
{
- throw new GameStateException(
+ throw new InvalidSceneOperationException(
$"Unit {sceneNode.GetDisplayName()} must have a parent to be added to the manufacturing queue."
);
}
@@ -221,16 +244,6 @@ public void AddToManufacturingQueue(IManufacturable manufacturable)
{
throw new SceneAccessException(manufacturable, this);
}
-
- ManufacturingType type = manufacturable.GetManufacturingType();
-
- if (!ManufacturingQueue.ContainsKey(type))
- {
- ManufacturingQueue.Add(type, new List());
- }
-
- manufacturable.SetPosition(GetPosition());
- ManufacturingQueue[type].Add(manufacturable);
}
///
@@ -321,6 +334,17 @@ public List GetBuildings(ManufacturingType productionType)
/// The building to add.
/// Thrown when the planet is not colonized or at capacity.
private void AddBuilding(Building building)
+ {
+ ValidateBuilding(building);
+ BuildingSlot slot = building.GetBuildingSlot();
+ Buildings[slot].Add(building);
+ }
+
+ ///
+ /// Validates if a building can be added to the planet.
+ ///
+ /// The building to validate.
+ private void ValidateBuilding(Building building)
{
if (!IsColonized)
{
@@ -335,18 +359,15 @@ private void AddBuilding(Building building)
}
BuildingSlot slot = building.GetBuildingSlot();
-
if (
- slot == BuildingSlot.Ground && Buildings[slot].Count == GroundSlots
- || slot == BuildingSlot.Orbit && Buildings[slot].Count == OrbitSlots
+ (slot == BuildingSlot.Ground && Buildings[slot].Count == GroundSlots)
+ || (slot == BuildingSlot.Orbit && Buildings[slot].Count == OrbitSlots)
)
{
throw new GameStateException(
$"Cannot add {building.GetDisplayName()} to {this.GetDisplayName()}. Planet is at capacity."
);
}
-
- Buildings[slot].Add(building);
}
///
@@ -487,6 +508,11 @@ public override void RemoveChild(ISceneNode child)
case Regiment regiment:
RemoveRegiment(regiment);
break;
+ default:
+ throw new InvalidSceneOperationException(
+ $"Cannot remove {child.GetDisplayName()} from {this.GetDisplayName()}. "
+ + $"Only fleets, officers, buildings, missions, and regiments are allowed."
+ );
}
}
diff --git a/Assets/Scripts/Game/PlanetSystem.cs b/Assets/Scripts/Game/PlanetSystem.cs
index 8003cb2..842f7bb 100644
--- a/Assets/Scripts/Game/PlanetSystem.cs
+++ b/Assets/Scripts/Game/PlanetSystem.cs
@@ -41,7 +41,7 @@ public class PlanetSystem : ContainerNode
public List Planets { get; set; } = new List();
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
public PlanetSystem() { }
diff --git a/Assets/Scripts/Game/Regiment.cs b/Assets/Scripts/Game/Regiment.cs
index f1c4ff7..42c318f 100644
--- a/Assets/Scripts/Game/Regiment.cs
+++ b/Assets/Scripts/Game/Regiment.cs
@@ -31,7 +31,7 @@ public class Regiment : LeafNode, IManufacturable, IMovable
public int PositionY { get; set; }
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
public Regiment() { }
diff --git a/Assets/Scripts/Game/SpecialForces.cs b/Assets/Scripts/Game/SpecialForces.cs
index fa41204..604fbe9 100644
--- a/Assets/Scripts/Game/SpecialForces.cs
+++ b/Assets/Scripts/Game/SpecialForces.cs
@@ -36,7 +36,7 @@ public class SpecialForces : LeafNode, IMissionParticipant, IManufacturable, IMo
public bool CanImproveMissionSkill => true;
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
public SpecialForces() { }
diff --git a/Assets/Scripts/Game/Starfighter.cs b/Assets/Scripts/Game/Starfighter.cs
index d9ce677..60a56a0 100644
--- a/Assets/Scripts/Game/Starfighter.cs
+++ b/Assets/Scripts/Game/Starfighter.cs
@@ -46,7 +46,7 @@ public class Starfighter : LeafNode, IManufacturable, IMovable
public int PositionY { get; set; }
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
public Starfighter() { }
diff --git a/Assets/Scripts/Game/Technology.cs b/Assets/Scripts/Game/Technology.cs
index d6d058e..36924cf 100644
--- a/Assets/Scripts/Game/Technology.cs
+++ b/Assets/Scripts/Game/Technology.cs
@@ -11,7 +11,7 @@ public class Technology
public IManufacturable Manufacturable { get; set; }
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
public Technology() { }
@@ -28,6 +28,7 @@ public Technology(IManufacturable manufacturable)
/// Returns the referenced manufacturable.
///
///
+ ///
public IManufacturable GetReference()
{
return Manufacturable;
@@ -37,6 +38,7 @@ public IManufacturable GetReference()
/// Returns a deep copy of the referenced manufacturable.
///
/// The deep copy of the referenced manufacturable.
+ ///
public IManufacturable GetReferenceCopy()
{
IManufacturable clonedManufacturable = Manufacturable.GetDeepCopy();
@@ -48,18 +50,19 @@ public IManufacturable GetReferenceCopy()
}
///
- ///
+ /// Returns the manufacturing type of the referenced manufacturable.
///
- ///
+ /// The manufacturing type of the referenced manufacturable.
+ ///
public ManufacturingType GetManufacturingType()
{
return Manufacturable.GetManufacturingType();
}
///
- ///
+ /// Returns the required research level of the referenced manufacturable.
///
- ///
+ /// The required research level of the referenced manufacturable.
public int GetRequiredResearchLevel()
{
return Manufacturable.GetRequiredResearchLevel();
diff --git a/Assets/Scripts/Generation/CapitalShipGenerator.cs b/Assets/Scripts/Generation/CapitalShipGenerator.cs
index a2bf809..25f6a2f 100644
--- a/Assets/Scripts/Generation/CapitalShipGenerator.cs
+++ b/Assets/Scripts/Generation/CapitalShipGenerator.cs
@@ -21,30 +21,28 @@ public CapitalShipGenerator(GameSummary summary, IResourceManager resourceManage
///
/// Retrieves the configuration for all capital ships based on the current galaxy size.
///
- /// An array of IConfig objects representing the capital ships for the current galaxy size.
+ /// An array of objects representing the capital ships for the current galaxy size.
private IConfig[] GetCapitalShipConfigs()
{
GameSize galaxySize = GetGameSummary().GalaxySize;
IConfig config = GetConfig();
// Generate a range of galaxy sizes and retrieve configurations for each
- return Enumerable
- .Range((int)GameSize.Small, (int)galaxySize)
- .SelectMany(size =>
- config.GetValue(
- $"CapitalShips.InitialCapitalShips.GalaxySize.{(GameSize)size}"
- )
+ return config
+ .GetValue(
+ $"CapitalShips.InitialCapitalShips.GalaxySize.{(galaxySize).ToString()}"
)
.ToArray();
}
///
- /// Maps the provided capital ships to their corresponding configurations.
+ /// Maps the provided capital ships to the config files, capital ship selection and placement
+ /// are set. The result is a list of capital ships which should then be deployed to the scene graph.
///
/// The list of available capital ships.
/// The configurations to map to the ships.
/// An array of mapped CapitalShip objects with updated configurations.
- private CapitalShip[] MapCapitalShipsToConfigs(
+ private CapitalShip[] GetCapitalShipsToDeploy(
CapitalShip[] capitalShips,
IConfig[] capitalShipConfigs
)
@@ -52,7 +50,7 @@ IConfig[] capitalShipConfigs
return capitalShipConfigs
.Select(config =>
{
- // Find matching ship and update its properties
+ // Find matching ship and update its properties.
CapitalShip ship = capitalShips.First(s =>
s.TypeID == config.GetValue("TypeID")
);
@@ -184,7 +182,7 @@ public override CapitalShip[] DecorateUnits(CapitalShip[] capitalShips)
public override CapitalShip[] DeployUnits(CapitalShip[] units, PlanetSystem[] destinations)
{
IConfig[] capitalShipConfigs = GetCapitalShipConfigs();
- CapitalShip[] mappedCapitalShips = MapCapitalShipsToConfigs(units, capitalShipConfigs);
- return AssignUnits(mappedCapitalShips, destinations);
+ CapitalShip[] shipsToDeploy = GetCapitalShipsToDeploy(units, capitalShipConfigs);
+ return AssignUnits(shipsToDeploy, destinations);
}
}
diff --git a/Assets/Scripts/Generation/FactionGenerator.cs b/Assets/Scripts/Generation/FactionGenerator.cs
index 6aca01a..e4e705b 100644
--- a/Assets/Scripts/Generation/FactionGenerator.cs
+++ b/Assets/Scripts/Generation/FactionGenerator.cs
@@ -25,6 +25,13 @@ private void SetFactionHQs(Faction[] factions, PlanetSystem[] planetSystems)
Dictionary hqs = factions.ToDictionary(f => f.HQInstanceID);
HashSet filledHQs = new HashSet();
+ if (planetSystems.Length == 0)
+ {
+ throw new GameException(
+ "Cannot assign faction headquarters. No planet systems available."
+ );
+ }
+
foreach (PlanetSystem planetSystem in planetSystems)
{
foreach (Planet planet in planetSystem.Planets)
@@ -57,7 +64,7 @@ private void SetStartingPlanets(Faction[] factions, PlanetSystem[] planetSystems
string galaxySize = GetGameSummary().GalaxySize.ToString();
int numStartingPlanets = startConfig.GetValue(galaxySize);
- // Select and shuffle starting planets
+ // Select and shuffle starting planets.
Queue startingPlanets = new Queue(
planetSystems
.SelectMany(ps => ps.Planets)
@@ -66,7 +73,7 @@ private void SetStartingPlanets(Faction[] factions, PlanetSystem[] planetSystems
.Take(numStartingPlanets * factions.Length)
);
- // Assign planets to factions
+ // Assign planets to factions.
foreach (Faction faction in factions)
{
for (int i = 0; i < numStartingPlanets; i++)
diff --git a/Assets/Scripts/Generation/GameBuilder.cs b/Assets/Scripts/Generation/GameBuilder.cs
index 0ecf608..6f04b3e 100644
--- a/Assets/Scripts/Generation/GameBuilder.cs
+++ b/Assets/Scripts/Generation/GameBuilder.cs
@@ -1,41 +1,181 @@
using System;
-using System.Collections;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
-using ICollectionExtensions;
-using IEnumerableExtensions;
///
-/// Represents a class responsible for building the game by generating the galaxy map and decorating it with units.
+/// Builds the game by generating the galaxy map and populating it with units and factions.
///
public sealed class GameBuilder
{
- private PlanetSystemGenerator psGenerator;
- private FactionGenerator factionGenerator;
- private OfficerGenerator officerGenerator;
- private BuildingGenerator buildingGenerator;
- private CapitalShipGenerator csGenerator;
- private StarfighterGenerator starfighterGenerator;
- private GameSummary summary;
+ private readonly GameSummary summary;
+ private readonly PlanetSystemGenerator planetSystemGenerator;
+ private readonly FactionGenerator factionGenerator;
+ private readonly OfficerGenerator officerGenerator;
+ private readonly BuildingGenerator buildingGenerator;
+ private readonly CapitalShipGenerator capitalShipGenerator;
+ private readonly StarfighterGenerator starfighterGenerator;
+ private readonly RegimentGenerator regimentGenerator;
+ private readonly GameEventGenerator gameEventGenerator;
///
/// Initializes a new instance of the GameBuilder class.
///
- /// The summary of the game.
+ /// The summary of the game to be built.
public GameBuilder(GameSummary summary)
{
+ this.summary = summary;
IResourceManager resourceManager = ResourceManager.Instance;
- // Initialize our unit generators.
- psGenerator = new PlanetSystemGenerator(summary, resourceManager);
+ planetSystemGenerator = new PlanetSystemGenerator(summary, resourceManager);
factionGenerator = new FactionGenerator(summary, resourceManager);
officerGenerator = new OfficerGenerator(summary, resourceManager);
buildingGenerator = new BuildingGenerator(summary, resourceManager);
- csGenerator = new CapitalShipGenerator(summary, resourceManager);
+ capitalShipGenerator = new CapitalShipGenerator(summary, resourceManager);
starfighterGenerator = new StarfighterGenerator(summary, resourceManager);
+ regimentGenerator = new RegimentGenerator(summary, resourceManager);
+ gameEventGenerator = new GameEventGenerator(summary, resourceManager);
+ }
- this.summary = summary;
+ ///
+ /// Builds the game by generating all necessary components.
+ ///
+ /// The fully constructed game.
+ public Game BuildGame()
+ {
+ PlanetSystem[] galaxyMap = GenerateGalaxyMap();
+ Faction[] factions = GenerateFactions(galaxyMap);
+
+ Building[] buildings = GenerateBuildings(galaxyMap);
+ CapitalShip[] capitalShips = GenerateCapitalShips(galaxyMap);
+ Starfighter[] starfighters = GenerateStarfighters(galaxyMap);
+ Regiment[] regiments = GenerateRegiments(galaxyMap);
+
+ IUnitGenerationResults officerResults = GenerateOfficers(galaxyMap);
+ Officer[] unrecruitedOfficers = GetUnrecruitedOfficers(officerResults);
+
+ GameEvent[] gameEvents = GenerateGameEvents(galaxyMap);
+
+ SetupFactionTechnologies(factions, buildings, capitalShips, starfighters, regiments);
+
+ return CreateGame(galaxyMap, factions, gameEvents, unrecruitedOfficers);
+ }
+
+ ///
+ /// Generates the galaxy map.
+ ///
+ /// An array of PlanetSystem objects representing the galaxy map.
+ private PlanetSystem[] GenerateGalaxyMap()
+ {
+ return planetSystemGenerator.GenerateUnits().SelectedUnits;
+ }
+
+ ///
+ /// Generates factions for the game.
+ ///
+ /// The galaxy map to use for generation.
+ /// An array of Faction objects.
+ private Faction[] GenerateFactions(PlanetSystem[] galaxyMap)
+ {
+ return factionGenerator.GenerateUnits(galaxyMap).UnitPool;
+ }
+
+ ///
+ /// Generates buildings for the game.
+ ///
+ /// The galaxy map to use for generation.
+ /// An array of Building objects.
+ private Building[] GenerateBuildings(PlanetSystem[] galaxyMap)
+ {
+ return buildingGenerator.GenerateUnits(galaxyMap).UnitPool;
+ }
+
+ ///
+ /// Generates capital ships for the game.
+ ///
+ /// The galaxy map to use for generation.
+ /// An array of CapitalShip objects.
+ private CapitalShip[] GenerateCapitalShips(PlanetSystem[] galaxyMap)
+ {
+ return capitalShipGenerator.GenerateUnits(galaxyMap).UnitPool;
+ }
+
+ ///
+ /// Generates starfighters for the game.
+ ///
+ /// The galaxy map to use for generation.
+ /// An array of Starfighter objects.
+ private Starfighter[] GenerateStarfighters(PlanetSystem[] galaxyMap)
+ {
+ return starfighterGenerator.GenerateUnits(galaxyMap).UnitPool;
+ }
+
+ ///
+ /// Generates regiments for the game.
+ ///
+ /// The galaxy map to use for generation.
+ /// An array of Regiment objects.
+ private Regiment[] GenerateRegiments(PlanetSystem[] galaxyMap)
+ {
+ return regimentGenerator.GenerateUnits(galaxyMap).UnitPool;
+ }
+
+ ///
+ /// Generates officers for the game.
+ ///
+ /// The galaxy map to use for generation.
+ /// The results of officer generation, including both selected and unselected officers.
+ private IUnitGenerationResults GenerateOfficers(PlanetSystem[] galaxyMap)
+ {
+ return officerGenerator.GenerateUnits(galaxyMap);
+ }
+
+ ///
+ /// Retrieves the list of unrecruited officers.
+ ///
+ /// The results of officer generation.
+ /// An array of Officer objects representing unrecruited officers.
+ private Officer[] GetUnrecruitedOfficers(IUnitGenerationResults officerResults)
+ {
+ return officerResults.UnitPool.Except(officerResults.SelectedUnits).ToArray();
+ }
+
+ ///
+ /// Generates game events for the game.
+ ///
+ /// The galaxy map to use for generation.
+ /// An array of GameEvent objects.
+ private GameEvent[] GenerateGameEvents(PlanetSystem[] galaxyMap)
+ {
+ return gameEventGenerator.GenerateUnits(galaxyMap).UnitPool;
+ }
+
+ ///
+ /// Sets up the technology trees for all factions.
+ ///
+ /// The array of factions.
+ /// The array of buildings.
+ /// The array of capital ships.
+ /// The array of starfighters.
+ /// The array of regiments.
+ private void SetupFactionTechnologies(
+ Faction[] factions,
+ Building[] buildings,
+ CapitalShip[] capitalShips,
+ Starfighter[] starfighters,
+ Regiment[] regiments
+ )
+ {
+ Dictionary factionMap = factions.ToDictionary(faction =>
+ faction.InstanceID
+ );
+ IManufacturable[] combinedTechnologies = buildings
+ .Cast()
+ .Concat(capitalShips)
+ .Concat(starfighters)
+ .Concat(regiments)
+ .ToArray();
+
+ SetTechnologyLevels(factionMap, combinedTechnologies);
}
///
@@ -68,52 +208,29 @@ IManufacturable[] combinedTechnologies
}
///
- /// Builds the game by generating the galaxy map and decorating it with units.
+ /// Creates and returns the final Game object.
///
- /// The built game.
- public Game BuildGame()
+ /// The generated galaxy map.
+ /// The generated factions.
+ /// The generated game events.
+ /// The list of unrecruited officers.
+ /// A fully initialized Game object.
+ private Game CreateGame(
+ PlanetSystem[] galaxyMap,
+ Faction[] factions,
+ GameEvent[] gameEvents,
+ Officer[] unrecruitedOfficers
+ )
{
- // Generate our galaxy map with stat decorated planets.
- IUnitGenerationResults psResults = psGenerator.GenerateUnits();
- PlanetSystem[] galaxyMap = psResults.SelectedUnits;
-
- // Decorate the galaxy map with units.
- Faction[] factions = factionGenerator.GenerateUnits(galaxyMap).UnitPool;
- Building[] buildings = buildingGenerator.GenerateUnits(galaxyMap).UnitPool;
- CapitalShip[] capitalShips = csGenerator.GenerateUnits(galaxyMap).UnitPool;
- Starfighter[] starfighters = starfighterGenerator.GenerateUnits(galaxyMap).UnitPool;
- IUnitGenerationResults officerResults = officerGenerator.GenerateUnits(galaxyMap);
-
- // Retrieve list of unrecruited officers.
- Officer[] unrecruitedOfficers = officerResults
- .UnitPool.Except(officerResults.SelectedUnits)
- .ToArray();
-
- // Initialize each faction's technology tree.
- Dictionary factionMap = factions.ToDictionary(faction =>
- faction.InstanceID
- );
- IManufacturable[] combinedTechnologies = Array
- .Empty()
- .Concat(buildings)
- .Concat(capitalShips)
- .Concat(starfighters)
- .ToArray();
-
- // Set the technology tree for each faction.
- SetTechnologyLevels(factionMap, combinedTechnologies);
-
- // Set the galaxy map.
- GalaxyMap galaxy = new GalaxyMap { PlanetSystems = galaxyMap.ToList() };
+ GalaxyMap galaxy = new GalaxyMap { PlanetSystems = galaxyMap.ToList() };
- // Initialize our new game.
- Game game = new Game
+ return new Game
{
+ EventPool = gameEvents.ToList(),
Summary = this.summary,
- Factions = factions.ToList(),
+ Factions = factions.ToList(),
Galaxy = galaxy,
UnrecruitedOfficers = unrecruitedOfficers.ToList(),
};
- return game;
}
}
diff --git a/Assets/Scripts/Generation/GameEventGenerator.cs b/Assets/Scripts/Generation/GameEventGenerator.cs
new file mode 100644
index 0000000..9f00360
--- /dev/null
+++ b/Assets/Scripts/Generation/GameEventGenerator.cs
@@ -0,0 +1,44 @@
+///
+///
+///
+public class GameEventGenerator : UnitGenerator
+{
+ ///
+ /// Constructs an EventGenerator object.
+ ///
+ /// The GameSummary options selected by the player.
+ /// The resource manager from which to load game data.
+ public GameEventGenerator(GameSummary summary, IResourceManager resourceManager)
+ : base(summary, resourceManager) { }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override GameEvent[] DecorateUnits(GameEvent[] events)
+ {
+ return events;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override GameEvent[] DeployUnits(GameEvent[] events, PlanetSystem[] destinations)
+ {
+ return events;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override GameEvent[] SelectUnits(GameEvent[] events)
+ {
+ return events;
+ }
+}
diff --git a/Assets/Scripts/Generation/OfficerGenerator.cs b/Assets/Scripts/Generation/OfficerGenerator.cs
index 983e622..3a7925b 100644
--- a/Assets/Scripts/Generation/OfficerGenerator.cs
+++ b/Assets/Scripts/Generation/OfficerGenerator.cs
@@ -33,6 +33,7 @@ private Officer[] SelectInitialOfficers(Dictionary> office
foreach (var (ownerInstanceId, officers) in officersByFaction)
{
IEnumerable reducedOfficers = officers
+ .Where(officer => officer.IsMain || officer.IsRecruitable)
.TakeWhile((officer, index) => officer.IsMain || index < numAllowedOfficers)
.Select(officer =>
{
@@ -117,11 +118,11 @@ public override Officer[] SelectUnits(Officer[] units)
if (officer.OwnerInstanceID != null)
{
- officersByFaction[factionId].Insert(0, officer); // Add to front
+ officersByFaction[factionId].Insert(0, officer);
}
else
{
- officersByFaction[factionId].Add(officer); // Add to end
+ officersByFaction[factionId].Add(officer);
}
}
}
diff --git a/Assets/Scripts/Generation/PlanetSystemGenerator.cs b/Assets/Scripts/Generation/PlanetSystemGenerator.cs
index 8b7567b..6687a00 100644
--- a/Assets/Scripts/Generation/PlanetSystemGenerator.cs
+++ b/Assets/Scripts/Generation/PlanetSystemGenerator.cs
@@ -72,14 +72,16 @@ private void SetColonizationStatus(PlanetSystem parentsystem, Planet planet)
///
public override PlanetSystem[] SelectUnits(PlanetSystem[] units)
{
- GameSize galaxySize = GetGameSummary().GalaxySize;
+ int galaxySize = (int)GetGameSummary().GalaxySize;
List galaxyMap = new List();
- IEnumerable sizeRange = Enumerable.Range((int)GameSize.Small, (int)galaxySize);
-
+ // Select planet systems with visibility greater than or equal to the galaxy size.
foreach (PlanetSystem planetSystem in units)
{
- if (sizeRange.Contains((int)planetSystem.Visibility))
+ int visibilityValue = (int)planetSystem.Visibility;
+
+ // Add the planet system to the galaxy map if it is visible.
+ if (visibilityValue <= galaxySize)
{
galaxyMap.Add(planetSystem);
}
diff --git a/Assets/Scripts/Generation/RegimentGenerator.cs b/Assets/Scripts/Generation/RegimentGenerator.cs
new file mode 100644
index 0000000..6155b5b
--- /dev/null
+++ b/Assets/Scripts/Generation/RegimentGenerator.cs
@@ -0,0 +1,47 @@
+using System.Linq;
+
+public class RegimentGenerator : UnitGenerator
+{
+ ///
+ /// Constructs an EventGenerator object.
+ ///
+ /// The GameSummary options selected by the player.
+ /// The resource manager from which to load game data.
+ public RegimentGenerator(GameSummary summary, IResourceManager resourceManager)
+ : base(summary, resourceManager) { }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override Regiment[] SelectUnits(Regiment[] regiments)
+ {
+ return regiments
+ .Where(regiment =>
+ regiment.RequiredResearchLevel <= GetGameSummary().StartingResearchLevel
+ )
+ .ToArray();
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override Regiment[] DecorateUnits(Regiment[] regiments)
+ {
+ return regiments;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override Regiment[] DeployUnits(Regiment[] regiments, PlanetSystem[] destinations)
+ {
+ return regiments;
+ }
+}
diff --git a/Assets/Scripts/Generation/StarfighterGenerator.cs b/Assets/Scripts/Generation/StarfighterGenerator.cs
index 02eacc4..e54b6c4 100644
--- a/Assets/Scripts/Generation/StarfighterGenerator.cs
+++ b/Assets/Scripts/Generation/StarfighterGenerator.cs
@@ -26,8 +26,11 @@ public StarfighterGenerator(GameSummary summary, IResourceManager resourceManage
/// An array of all starfighters.
public override Starfighter[] SelectUnits(Starfighter[] starfighters)
{
- // No op.
- return starfighters;
+ return starfighters
+ .Where(starfighter =>
+ starfighter.RequiredResearchLevel <= GetGameSummary().StartingResearchLevel
+ )
+ .ToArray();
}
///
diff --git a/Assets/Scripts/Generation/UnitGenerationResults.cs b/Assets/Scripts/Generation/UnitGenerationResults.cs
new file mode 100644
index 0000000..d6fcf32
--- /dev/null
+++ b/Assets/Scripts/Generation/UnitGenerationResults.cs
@@ -0,0 +1,18 @@
+///
+///
+///
+///
+class UnitGenerationResults : IUnitGenerationResults
+ where TUnit : BaseGameEntity
+{
+ public TUnit[] UnitPool { get; set; }
+ public TUnit[] SelectedUnits { get; set; }
+ public TUnit[] DeployedUnits { get; set; }
+
+ public UnitGenerationResults(TUnit[] unitPool, TUnit[] selectedUnits, TUnit[] deployedUnits)
+ {
+ UnitPool = unitPool;
+ SelectedUnits = selectedUnits;
+ DeployedUnits = deployedUnits;
+ }
+}
diff --git a/Assets/Scripts/Generation/UnitGenerator.cs b/Assets/Scripts/Generation/UnitGenerator.cs
index 825a184..1ed4ca5 100644
--- a/Assets/Scripts/Generation/UnitGenerator.cs
+++ b/Assets/Scripts/Generation/UnitGenerator.cs
@@ -1,24 +1,5 @@
using System;
-///
-///
-///
-///
-class UnitGenerationResults : IUnitGenerationResults
- where TUnit : BaseGameEntity
-{
- public TUnit[] UnitPool { get; set; }
- public TUnit[] SelectedUnits { get; set; }
- public TUnit[] DeployedUnits { get; set; }
-
- public UnitGenerationResults(TUnit[] unitPool, TUnit[] selectedUnits, TUnit[] deployedUnits)
- {
- UnitPool = unitPool;
- SelectedUnits = selectedUnits;
- DeployedUnits = deployedUnits;
- }
-}
-
///
///
///
diff --git a/Assets/Scripts/Managers/AIManager.cs b/Assets/Scripts/Managers/AIManager.cs
index 0aa6da1..f170da0 100644
--- a/Assets/Scripts/Managers/AIManager.cs
+++ b/Assets/Scripts/Managers/AIManager.cs
@@ -2,15 +2,15 @@
using System.Linq;
///
-/// @NOTE: This is a dummy class meant for testing. It does NOT represent the
-/// actual AI that will be implemented in the game.
+/// Manages AI behavior for factions in the game.
+/// Note: This is a simplified implementation for testing purposes.
///
public class AIManager
{
- private Game game;
- private MissionManager missionManager;
- private UnitManager unitManager;
- private PlanetManager planetManager;
+ private readonly Game game;
+ private readonly MissionManager missionManager;
+ private readonly UnitManager unitManager;
+ private readonly PlanetManager planetManager;
public AIManager(
Game game,
@@ -26,170 +26,255 @@ PlanetManager planetManager
}
///
- /// Updates the AI for all factions.
+ /// Updates the AI for all AI-controlled factions.
///
public void Update()
{
- // Update the AI for each faction.
- foreach (Faction faction in game.Factions)
+ foreach (Faction faction in game.Factions.Where(f => f.IsAIControlled()))
{
- if (faction.IsAIControlled())
- {
- UpdateFaction(faction);
- }
+ UpdateFaction(faction);
}
}
///
- /// Updates the AI for the specified faction.
+ /// Updates various aspects of the AI for a specific faction.
///
- ///
+ /// The faction to update.
private void UpdateFaction(Faction faction)
{
UpdateOfficers(faction);
- UpdateManufacturing(faction);
+ UpdateBuildings(faction);
UpdateShipyards(faction);
+ UpdateTrainingFacilities(faction);
}
///
- ///
- ///
- ///
- ///
- private List GetAvailableOfficers(string ownerInstanceId)
- {
- return game.GetSceneNodesByOwnerInstanceID(ownerInstanceId)
- .FindAll(o => o.IsMovable());
- }
-
- ///
- ///
+ /// Manages officer assignments for missions.
///
+ /// The faction to update officer assignments for.
private void UpdateOfficers(Faction faction)
{
- List officers = faction.GetAvailableOfficers(faction);
+ List availableOfficers = faction.GetAvailableOfficers(faction);
- foreach (Officer officer in officers)
+ foreach (Officer officer in availableOfficers)
{
- if (officer.IsMovable())
+ if (!officer.IsMovable())
+ continue;
+
+ if (officer.IsMain && game.GetUnrecruitedOfficers(faction.InstanceID).Any())
+ {
+ InitiateRecruitmentMission(officer, faction);
+ }
+ else if (
+ officer.IsMain
+ || officer.GetSkillValue(MissionParticipantSkill.Diplomacy) > 60
+ )
{
- if (officer.IsMain && game.GetUnrecruitedOfficers(faction.InstanceID).Count > 0)
- {
- Planet nearestFriendlyPlanet = faction.GetNearestPlanetTo(officer);
-
- GameLogger.Log(
- $"Sending {officer.GetDisplayName()} on a recruitment mission to {nearestFriendlyPlanet.GetDisplayName()}."
- );
-
- missionManager.InitiateMission(
- MissionType.Recruitment,
- officer,
- nearestFriendlyPlanet
- );
- }
- else if (
- officer.IsMain
- || officer.GetSkillValue(MissionParticipantSkill.Diplomacy) > 60
- )
- {
- Planet nearestUnownedPlanet =
- officer
- .GetParentOfType()
- .GetChildrenByOwnerInstanceID(null)
- .FirstOrDefault() as Planet;
-
- GameLogger.Log(
- $"Sending {officer.GetDisplayName()} on a diplomacy mission to {nearestUnownedPlanet.GetDisplayName()}."
- );
-
- missionManager.InitiateMission(
- MissionType.Diplomacy,
- officer,
- nearestUnownedPlanet
- );
- }
+ InitiateDiplomacyMission(officer);
}
}
}
///
- ///
+ /// Initiates a recruitment mission for the given officer.
///
- private void UpdateManufacturing(Faction faction)
+ /// The officer to send on the recruitment mission.
+ /// The faction the officer belongs to.
+ private void InitiateRecruitmentMission(Officer officer, Faction faction)
{
- List idleConstructionFacilities = faction.GetIdleFacilities(
- ManufacturingType.Building
+ Planet nearestFriendlyPlanet = faction.GetNearestPlanetTo(officer);
+ GameLogger.Log(
+ $"Sending {officer.GetDisplayName()} on a recruitment mission to {nearestFriendlyPlanet.GetDisplayName()}."
);
+ missionManager.InitiateMission(MissionType.Recruitment, officer, nearestFriendlyPlanet);
+ }
+
+ ///
+ /// Initiates a diplomacy mission for the given officer.
+ ///
+ /// The officer to send on the diplomacy mission.
+ private void InitiateDiplomacyMission(Officer officer)
+ {
+ Planet nearestUnownedPlanet =
+ officer
+ .GetParentOfType()
+ .GetChildrenByOwnerInstanceID(null)
+ .FirstOrDefault() as Planet;
- if (idleConstructionFacilities.Count == 0)
+ if (nearestUnownedPlanet != null)
{
- return;
+ GameLogger.Log(
+ $"Sending {officer.GetDisplayName()} on a diplomacy mission to {nearestUnownedPlanet.GetDisplayName()}."
+ );
+ missionManager.InitiateMission(MissionType.Diplomacy, officer, nearestUnownedPlanet);
}
+ }
- List buildingOptions = faction.GetResearchedTechnologies(
+ ///
+ /// Manages construction of buildings.
+ ///
+ /// The faction to update building construction for.
+ private void UpdateBuildings(Faction faction)
+ {
+ List idleConstructionFacilities = faction.GetIdleFacilities(
ManufacturingType.Building
);
+ if (!idleConstructionFacilities.Any())
+ return;
- Technology constructionYardTech = buildingOptions
- .FindAll(technology =>
- (technology.GetReference() as Building).GetBuildingType()
- == BuildingType.ConstructionFacility
- )
- .LastOrDefault();
+ Technology constructionYardTech = GetHighestTierTechnology(
+ faction,
+ ManufacturingType.Building,
+ BuildingType.ConstructionFacility
+ );
foreach (Planet planet in idleConstructionFacilities)
{
- int facilityCount = planet.GetBuildingTypeCount(BuildingType.ConstructionFacility);
- if (
- constructionYardTech.GetReference() is Building building
- && planet.GetAvailableSlots(building.GetBuildingSlot()) > 0
- && facilityCount < 5
- )
+ if (ShouldBuildConstructionFacility(planet, constructionYardTech))
{
planetManager.AddToManufacturingQueue(planet, planet, constructionYardTech, 1);
}
}
}
+ ///
+ /// Determines if a construction facility should be built on the given planet.
+ ///
+ /// The planet to check.
+ /// The construction yard technology to use.
+ /// True if a construction facility should be built, false otherwise.
+ private bool ShouldBuildConstructionFacility(Planet planet, Technology constructionYardTech)
+ {
+ if (constructionYardTech?.GetReference() is not Building building)
+ return false;
+
+ return planet.GetAvailableSlots(building.GetBuildingSlot()) > 0
+ && planet.GetBuildingTypeCount(BuildingType.ConstructionFacility) < 5;
+ }
+
+ ///
+ /// Manages production of ships.
+ ///
+ /// The faction to update ship production for.
private void UpdateShipyards(Faction faction)
{
List idleShipyards = faction.GetIdleFacilities(ManufacturingType.Ship);
+ if (!idleShipyards.Any())
+ return;
+
+ Technology starfighterTech = GetHighestTierTechnology(
+ faction,
+ ManufacturingType.Ship,
+ typeof(Starfighter)
+ );
+
+ foreach (Planet planet in idleShipyards)
+ {
+ AssignStarfightersToFleets(faction, planet, starfighterTech);
+ }
+ }
- if (idleShipyards.Count == 0)
+ ///
+ /// Assigns starfighters to fleets with available capacity.
+ ///
+ /// The faction to assign starfighters for.
+ /// The planet with the idle shipyard.
+ /// The starfighter technology to use.
+ private void AssignStarfightersToFleets(
+ Faction faction,
+ Planet planet,
+ Technology starfighterTech
+ )
+ {
+ List fleets = faction
+ .GetOwnedUnitsByType()
+ .OrderBy(fleet => fleet.GetExcessStarfighterCapacity())
+ .Where(fleet => fleet.GetExcessStarfighterCapacity() > 0)
+ .ToList();
+
+ foreach (Fleet fleet in fleets)
{
- return;
+ planetManager.AddToManufacturingQueue(planet, fleet, starfighterTech, 1);
}
+ }
- List shipyardOptions = faction.GetResearchedTechnologies(
- ManufacturingType.Ship
- );
+ ///
+ /// Manages training of troops.
+ ///
+ /// The faction to update troop training for.
+ private void UpdateTrainingFacilities(Faction faction)
+ {
+ List idleTrainingFacilities = faction.GetIdleFacilities(ManufacturingType.Troop);
+ if (!idleTrainingFacilities.Any())
+ return;
- Technology starfighterTech = shipyardOptions
- .FindAll(technology => (technology.GetReference().GetType() == typeof(Starfighter)))
- .LastOrDefault();
+ Technology regimentTech = GetHighestTierTechnology(
+ faction,
+ ManufacturingType.Troop,
+ typeof(Regiment)
+ );
- foreach (Planet planet in idleShipyards)
+ foreach (Planet planet in idleTrainingFacilities)
{
- int shipyardCount = planet.GetBuildingTypeCount(BuildingType.Shipyard);
+ AssignRegimentsToFleets(faction, planet, regimentTech);
+ }
+ }
- List fleets = faction.GetOwnedUnitsByType();
- List sortedFleets = fleets
- .OrderBy(fleet => fleet.GetExcessStarfighterCapacity())
- .ToList();
+ ///
+ /// Assigns regiments to fleets with available capacity.
+ ///
+ /// The faction to assign regiments for.
+ /// The planet with the idle training facility.
+ /// The regiment technology to use.
+ private void AssignRegimentsToFleets(Faction faction, Planet planet, Technology regimentTech)
+ {
+ List fleets = faction
+ .GetOwnedUnitsByType()
+ .OrderBy(fleet => fleet.GetExcessRegimentCapacity())
+ .Where(fleet => fleet.GetExcessRegimentCapacity() > 0)
+ .ToList();
- GameLogger.Log($"Found {fleets.Count} fleets for {faction.GetDisplayName()}.");
- foreach (Fleet fleet in sortedFleets)
- {
- GameLogger.Log(
- $"Adding {fleet.GetExcessStarfighterCapacity()} {starfighterTech.GetReference().GetDisplayName()} to the manufacturing queue of {planet.GetDisplayName()}."
- );
- planetManager.AddToManufacturingQueue(
- planet,
- fleet,
- starfighterTech,
- fleet.GetExcessStarfighterCapacity()
- );
- }
+ foreach (Fleet fleet in fleets)
+ {
+ planetManager.AddToManufacturingQueue(planet, fleet, regimentTech, 1);
}
}
+
+ ///
+ /// Retrieves the highest-tier technology for the given manufacturing type and reference type.
+ ///
+ /// The faction to get the technology for.
+ /// The manufacturing type to filter by.
+ /// The type of the technology reference to filter by.
+ /// The highest-tier technology matching the criteria, or null if not found.
+ private Technology GetHighestTierTechnology(
+ Faction faction,
+ ManufacturingType manufacturingType,
+ System.Type referenceType
+ )
+ {
+ return faction
+ .GetResearchedTechnologies(manufacturingType)
+ .Where(tech => tech.GetReference().GetType() == referenceType)
+ .LastOrDefault();
+ }
+
+ ///
+ /// Retrieves the highest-tier technology for the given manufacturing type and building type.
+ ///
+ /// The faction to get the technology for.
+ /// The manufacturing type to filter by.
+ /// The building type to filter by.
+ /// The highest-tier technology matching the criteria, or null if not found.
+ private Technology GetHighestTierTechnology(
+ Faction faction,
+ ManufacturingType manufacturingType,
+ BuildingType buildingType
+ )
+ {
+ return faction
+ .GetResearchedTechnologies(manufacturingType)
+ .Where(tech => (tech.GetReference() as Building)?.GetBuildingType() == buildingType)
+ .LastOrDefault();
+ }
}
diff --git a/Assets/Scripts/Managers/GameEventManager.cs b/Assets/Scripts/Managers/GameEventManager.cs
index d7a1784..6f44510 100644
--- a/Assets/Scripts/Managers/GameEventManager.cs
+++ b/Assets/Scripts/Managers/GameEventManager.cs
@@ -14,65 +14,41 @@ public GameEventManager(Game game)
this.game = game;
}
- ///
- /// Schedules the specified game event to occur at the specified tick.
- ///
- ///
- ///
- public void ScheduleEvent(GameEvent gameEvent, int tick)
+ private void ProcessEvent(GameEvent gameEvent)
{
- game.ScheduleGameEvent(gameEvent, tick);
- }
-
- ///
- ///
- ///
- ///
- ///
- public void ScheduleEvent(List actions, int tick)
- {
- List conditionals = new List();
- GameEvent gameEvent = new GameEvent(conditionals, actions);
- ScheduleEvent(gameEvent, tick);
+ if (gameEvent.AreConditionsMet(game))
+ {
+ GameLogger.Log($"Executing game event: {gameEvent.GetDisplayName()}");
+ gameEvent.Execute(game);
+ game.AddCompletedEvent(gameEvent);
+ }
}
///
///
///
- ///
- ///
- ///
- public void ScheduleEvent(
- List conditionals,
- List actions,
- int tick
- )
+ ///
+ public void ProcessEvents(List gameEvents)
{
- GameEvent gameEvent = new GameEvent(conditionals, actions);
- ScheduleEvent(gameEvent, tick);
- }
+ List eventsToRemove = new List();
- ///
- /// Processes the game events for the specified tick.
- ///
- /// The current tick.
- public void ProcessEvents(int currentTick)
- {
- List scheduledEvents = game.GetScheduledEvents(currentTick);
-
- // Check if there are any events scheduled for this tick.
- if (scheduledEvents.Any())
+ foreach (GameEvent gameEvent in gameEvents)
{
- // Execute each event.
- foreach (ScheduledEvent scheduledEvent in scheduledEvents)
- {
- GameEvent gameEvent = scheduledEvent.GetEvent();
+ ProcessEvent(gameEvent);
- gameEvent.Execute(game);
-
- // Add the event to the list of completed events.
- game.AddCompletedEvent(gameEvent);
+ if (!gameEvent.IsRepeatable)
+ {
+ if (!gameEvent.IsRepeatable)
+ {
+ eventsToRemove.Add(gameEvent);
+ }
}
}
+
+ // Remove events that are no longer needed.
+ foreach (GameEvent eventToRemove in eventsToRemove)
+ {
+ game.RemoveEvent(eventToRemove);
+ }
}
}
diff --git a/Assets/Scripts/Managers/GameManager.cs b/Assets/Scripts/Managers/GameManager.cs
index 299aaae..9581555 100644
--- a/Assets/Scripts/Managers/GameManager.cs
+++ b/Assets/Scripts/Managers/GameManager.cs
@@ -118,7 +118,7 @@ public void Update()
///
///
///
- private void ProcessNode(ISceneNode node)
+ private void UpdateNode(ISceneNode node)
{
// Update the movement of movable units.
if (node is IMovable moveable)
@@ -149,12 +149,13 @@ private void ProcessTick()
GameLogger.Log("Tick: " + game.CurrentTick);
- game.Galaxy.Traverse(ProcessNode);
-
- // Update game states.
- aiManager.Update();
+ // Update the state of each scene node in the game.
+ game.GetGalaxyMap().Traverse(UpdateNode);
// Process any events scheduled for this tick.
- eventManager.ProcessEvents(game.CurrentTick);
+ eventManager.ProcessEvents(game.GetEventPool());
+
+ // Update the NPC AI factions in the game.
+ aiManager.Update();
}
}
diff --git a/Assets/Scripts/Managers/MissionManager.cs b/Assets/Scripts/Managers/MissionManager.cs
index 77d541a..0b87709 100644
--- a/Assets/Scripts/Managers/MissionManager.cs
+++ b/Assets/Scripts/Managers/MissionManager.cs
@@ -3,172 +3,96 @@
using System.Linq;
///
-///
-///
-public enum MissionType
-{
- Diplomacy,
- Recruitment,
-}
-
-///
-/// Manager for handling missions in the game.
-/// This includes scheduling missions and rescheduling mission events.
+/// Manager for handling the lifecycle of missions in the game.
+/// Responsible for updating missions and managing their progression.
+/// Creation and initiation of missions are delegated to the .
///
public class MissionManager
{
- private Game game;
- private readonly Random random = new Random();
+ private readonly Game game;
+ private readonly MissionFactory missionFactory;
///
- ///
+ /// Initializes a new instance of the class.
///
- ///
+ /// The game instance being managed.
public MissionManager(Game game)
{
this.game = game;
+ // Initialize the MissionFactory for mission creation and initiation.
+ this.missionFactory = new MissionFactory(game);
}
///
- ///
+ /// Initiates a mission with a single participant and target.
///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- private Mission CreateMission(
- MissionType missionType,
- string ownerInstanceId,
- List mainParticipants,
- List decoyParticipants,
- ISceneNode target
- )
- {
- return missionType switch
- {
- MissionType.Diplomacy => new DiplomacyMission(
- ownerInstanceId,
- target.InstanceID,
- mainParticipants,
- decoyParticipants
- ),
- MissionType.Recruitment => new RecruitmentMission(
- ownerInstanceId,
- target.InstanceID,
- mainParticipants,
- decoyParticipants
- ),
- _ => throw new ArgumentException($"Unhandled mission type: {missionType}"),
- };
- }
-
- ///
- ///
- ///
- ///
- ///
- ///
+ /// The type of mission to initiate.
+ /// The main participant of the mission.
+ /// The target of the mission.
public void InitiateMission(
- MissionType type,
+ MissionType missionType,
IMissionParticipant participant,
ISceneNode target
)
{
+ // Wrap the participant in a list to use the overload for multiple participants.
List mainParticipants = new List { participant };
List decoyParticipants = new List();
string ownerInstanceId = participant.OwnerInstanceID;
- InitiateMission(type, ownerInstanceId, mainParticipants, decoyParticipants, target);
- }
-
- ///
- /// Initiates a mission with the specified parameters.
- /// The mission is scheduled to occur at the next possible tick.
- ///
- /// The type of mission to initiate.
- /// The Instance ID of the owner of the mission.
- /// The main participants of the mission.
- /// The decoy participants of the mission.
- /// The target of the mission. This can be a planet or a unit.
- public void InitiateMission(
- MissionType missionType,
- string ownerInstanceId,
- List mainParticipants,
- List decoyParticipants,
- ISceneNode target
- )
- {
- if (mainParticipants.Count == 0)
- {
- throw new ArgumentException("Main participants list cannot be empty.");
- }
-
- // Get the nearest planet related to the target and the participants' current planet.
- Planet closestPlanet = target is Planet ? (Planet)target : target.GetParentOfType();
- IMissionParticipant firstParticipant = mainParticipants.FirstOrDefault();
- Planet currentPlanet = firstParticipant.GetParentOfType();
-
- // Instantiate the mission based on the mission type.
- Mission mission = CreateMission(
+ missionFactory.CreateAndInitiateMission(
missionType,
ownerInstanceId,
mainParticipants,
decoyParticipants,
target
);
-
- // Attach the mission to scene graph.
- game.AttachNode(mission, closestPlanet, false);
-
- // Initiate the mission with the given arguments.
- // This will set the movement status of all participants to InTransit.
- mission.Initiate();
}
///
- ///
+ /// Updates the state of an ongoing mission.
+ /// This involves incrementing mission progress, evaluating success,
+ /// and handling mission completion or continuation.
///
- ///
+ /// The mission to update.
public void UpdateMission(Mission mission)
{
- List missions = game.GetSceneNodesByType();
- GalaxyMap galaxyMap = game.GetGalaxyMap();
-
- // Increment the mission progress.
+ // Increment the mission's progress.
mission.IncrementProgress();
+ // Check if the mission is complete.
if (mission.IsComplete())
{
- // Evaluate the mission success.
+ // Evaluate the mission's success or failure.
mission.Execute(game);
- // Check if the mission can continue.
- // If so, reset the mission progress and re-initiate the mission.
+ // Check if the mission can continue (e.g., repeatable missions).
if (mission.CanContinue(game))
{
+ // Reset progress and re-initiate the mission.
mission.Initiate();
}
- // Otherwise, move the participants to the closest planet and remove the mission.
else
{
- // Move the units to the closest planet.
+ // Handle mission completion and return participants to the nearest planet.
+
+ // Get all participants (both main and decoy) that can be moved.
List combinedParticipants = mission
.GetAllParticipants()
.Cast()
.ToList();
+ // Find the nearest planet to the mission's location for participants to return to.
Faction faction = game.GetFactionByOwnerInstanceID(mission.OwnerInstanceID);
- Planet planet = faction.GetNearestPlanetTo(mission);
+ Planet nearestPlanet = faction.GetNearestPlanetTo(mission);
+ // Move each participant to the nearest planet.
foreach (IMovable movable in combinedParticipants)
{
- movable.MoveTo(planet);
+ movable.MoveTo(nearestPlanet);
}
- // Permanently remove the mission from the game.
+ // Remove the mission from the game permanently.
game.DetachNode(mission);
}
}
diff --git a/Assets/Scripts/Managers/PlanetManager.cs b/Assets/Scripts/Managers/PlanetManager.cs
index 33d6238..37a1d11 100644
--- a/Assets/Scripts/Managers/PlanetManager.cs
+++ b/Assets/Scripts/Managers/PlanetManager.cs
@@ -48,7 +48,7 @@ int quantity
IManufacturable clonedNode = technology.GetReferenceCopy();
clonedNode.SetOwnerInstanceID(planet.GetOwnerInstanceID());
GameLogger.Log(
- $"Adding {clonedNode.GetDisplayName()} to manufacturing queue on {planet.GetDisplayName()}"
+ $"{planet.GetDisplayName()} adding {quantity} {clonedNode.GetDisplayName()}(s) to manufacturing queue on {planet.GetDisplayName()}"
);
game.AttachNode(clonedNode, target);
planet.AddToManufacturingQueue(clonedNode);
@@ -135,7 +135,7 @@ ref int index
$"Manufacturable completed: {manufacturable.GetDisplayName()} {manufacturable.GetParent().GetDisplayName()}"
);
productionQueue.RemoveAt(index);
- index--; // Decrement index to account for removed item.
+ index--; // Decrement index to account for the removed item.
manufacturable.SetManufacturingStatus(ManufacturingStatus.Complete);
diff --git a/Assets/Scripts/Missions/AbductionMission.cs b/Assets/Scripts/Missions/AbductionMission.cs
index 6ed4b6e..5649c31 100644
--- a/Assets/Scripts/Missions/AbductionMission.cs
+++ b/Assets/Scripts/Missions/AbductionMission.cs
@@ -6,7 +6,7 @@ public class AbductionMission : Mission
public Officer TargetOfficer { get; set; }
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
public AbductionMission()
: base()
diff --git a/Assets/Scripts/Missions/DiplomacyMission.cs b/Assets/Scripts/Missions/DiplomacyMission.cs
index 62fcd4f..99c2a36 100644
--- a/Assets/Scripts/Missions/DiplomacyMission.cs
+++ b/Assets/Scripts/Missions/DiplomacyMission.cs
@@ -4,7 +4,7 @@
public class DiplomacyMission : Mission
{
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
public DiplomacyMission()
: base()
diff --git a/Assets/Scripts/Missions/Mission.cs b/Assets/Scripts/Missions/Mission.cs
index 2d4ca7c..8b813cb 100644
--- a/Assets/Scripts/Missions/Mission.cs
+++ b/Assets/Scripts/Missions/Mission.cs
@@ -17,6 +17,7 @@ public abstract class Mission : ContainerNode
[PersistableIgnore]
public List DecoyParticipants { get; set; }
public MissionParticipantSkill ParticipantSkill { get; set; }
+ public bool HasInitiated = false;
// Success Probability Variables
[PersistableIgnore]
@@ -59,7 +60,7 @@ public abstract class Mission : ContainerNode
protected static readonly Random random = new Random();
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
protected Mission() { }
@@ -131,6 +132,8 @@ public void Initiate()
// Then, set the max progress to a random value between the min and max ticks.
CurrentProgress = 0;
MaxProgress = random.Next(MinTicks, MaxTicks);
+
+ HasInitiated = true;
}
///
@@ -419,7 +422,13 @@ public void Execute(Game game)
///
public override IEnumerable GetChildren()
{
- return MainParticipants.Cast().Concat(DecoyParticipants.Cast());
+ if (HasInitiated)
+ {
+ return MainParticipants.Cast().Concat(DecoyParticipants.Cast());
+ }
+
+ // Return an empty list.
+ return new List();
}
///
diff --git a/Assets/Scripts/Missions/MissionFactory.cs b/Assets/Scripts/Missions/MissionFactory.cs
new file mode 100644
index 0000000..043cd3c
--- /dev/null
+++ b/Assets/Scripts/Missions/MissionFactory.cs
@@ -0,0 +1,138 @@
+using System;
+using System.Collections.Generic;
+
+///
+///
+///
+public enum MissionType
+{
+ Diplomacy,
+ Recruitment,
+}
+
+///
+/// Factory class responsible for creating and initializing missions.
+///
+public class MissionFactory
+{
+ private readonly Game game;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The game instance for which the factory creates missions.
+ public MissionFactory(Game game)
+ {
+ this.game = game;
+ }
+
+ ///
+ /// Creates a mission based on the specified mission type and parameters.
+ ///
+ /// The type of mission to create.
+ /// The Instance ID of the owner of the mission.
+ /// The main participants of the mission.
+ /// The decoy participants of the mission.
+ /// The target of the mission.
+ /// A new instance of the requested mission type.
+ public Mission CreateMission(
+ MissionType missionType,
+ string ownerInstanceId,
+ List mainParticipants,
+ List decoyParticipants,
+ ISceneNode target
+ )
+ {
+ return missionType switch
+ {
+ MissionType.Diplomacy => new DiplomacyMission(
+ ownerInstanceId,
+ target.InstanceID,
+ mainParticipants,
+ decoyParticipants
+ ),
+ MissionType.Recruitment => new RecruitmentMission(
+ ownerInstanceId,
+ target.InstanceID,
+ mainParticipants,
+ decoyParticipants
+ ),
+ _ => throw new ArgumentException($"Unhandled mission type: {missionType}"),
+ };
+ }
+
+ ///
+ /// Creates and initiates a mission, attaching it to the game scene graph.
+ ///
+ /// The type of mission to create.
+ /// The Instance ID of the mission owner.
+ /// The main participants of the mission.
+ /// The decoy participants of the mission.
+ /// The target of the mission.
+ public Mission CreateAndInitiateMission(
+ MissionType missionType,
+ string ownerInstanceId,
+ List mainParticipants,
+ List decoyParticipants,
+ ISceneNode target
+ )
+ {
+ if (mainParticipants.Count == 0)
+ {
+ throw new ArgumentException("Main participants list cannot be empty.");
+ }
+
+ // Create the mission.
+ Mission mission = CreateMission(
+ missionType,
+ ownerInstanceId,
+ mainParticipants,
+ decoyParticipants,
+ target
+ );
+
+ // Determine the closest planet to the target for attaching the mission.
+ Planet closestPlanet = target is Planet ? (Planet)target : target.GetParentOfType();
+
+ // Attach the mission to the scene graph.
+ game.AttachNode(mission, closestPlanet);
+
+ // Initiate the mission (e.g., set participants to InTransit).
+ mission.Initiate();
+
+ return mission;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public Mission CreateAndInitiateMission(
+ string missionTypeString,
+ string ownerInstanceId,
+ List mainParticipants,
+ List decoyParticipants,
+ ISceneNode target
+ )
+ {
+ if (Enum.TryParse(missionTypeString, true, out MissionType missionType))
+ {
+ return CreateAndInitiateMission(
+ missionType,
+ ownerInstanceId,
+ mainParticipants,
+ decoyParticipants,
+ target
+ );
+ }
+ else
+ {
+ throw new ArgumentException($"Invalid mission type: {missionTypeString} .");
+ }
+ }
+}
diff --git a/Assets/Scripts/Missions/RecruitmentMission.cs b/Assets/Scripts/Missions/RecruitmentMission.cs
index 1affbe4..e7e338b 100644
--- a/Assets/Scripts/Missions/RecruitmentMission.cs
+++ b/Assets/Scripts/Missions/RecruitmentMission.cs
@@ -5,7 +5,7 @@
public class RecruitmentMission : Mission
{
///
- /// Default constructor used for serialization.
+ /// Default constructor used for deserialization.
///
public RecruitmentMission()
: base()
@@ -56,25 +56,37 @@ protected override void OnSuccess(Game game)
{
Planet planet = GetParent() as Planet;
- List unrecruitedOfficers = game.GetUnrecruitedOfficers(OwnerInstanceID);
- Officer recruitedOfficer = unrecruitedOfficers.RandomElement();
- recruitedOfficer.OwnerInstanceID = OwnerInstanceID;
+ if (game.GetUnrecruitedOfficers(OwnerInstanceID).Count > 0)
+ {
+ List unrecruitedOfficers = game.GetUnrecruitedOfficers(OwnerInstanceID);
+ Officer recruitedOfficer = unrecruitedOfficers.RandomElement();
+ recruitedOfficer.OwnerInstanceID = OwnerInstanceID;
- game.RemoveUnrecruitedOfficer(recruitedOfficer);
+ game.RemoveUnrecruitedOfficer(recruitedOfficer);
- // Attach the recruited officer to the planet.
- game.AttachNode(recruitedOfficer, planet);
+ // Attach the recruited officer to the planet.
+ game.AttachNode(recruitedOfficer, planet);
- GameLogger.Log(
- "Recruited officer "
- + recruitedOfficer.GetDisplayName()
- + " to "
- + planet.GetDisplayName()
- + " by "
- + MainParticipants[0].GetDisplayName()
- );
+ GameLogger.Log(
+ "Recruited officer "
+ + recruitedOfficer.GetDisplayName()
+ + " to "
+ + planet.GetDisplayName()
+ + " by "
+ + MainParticipants[0].GetDisplayName()
+ );
+ }
+ else
+ {
+ // @TODO: No one left to recruit so abort the mission.
+ }
}
+ ///
+ ///
+ ///
+ ///
+ ///
public override bool CanContinue(Game game)
{
return game.GetUnrecruitedOfficers(OwnerInstanceID).Count > 0;
diff --git a/Assets/Scripts/Util/Serialization/PersistableAttributeAttribute.cs b/Assets/Scripts/Util/Attributes/PersistableAttributeAttribute.cs
similarity index 100%
rename from Assets/Scripts/Util/Serialization/PersistableAttributeAttribute.cs
rename to Assets/Scripts/Util/Attributes/PersistableAttributeAttribute.cs
diff --git a/Assets/Scripts/Util/Serialization/PersistableIgnoreAttribute.cs b/Assets/Scripts/Util/Attributes/PersistableIgnoreAttribute.cs
similarity index 100%
rename from Assets/Scripts/Util/Serialization/PersistableIgnoreAttribute.cs
rename to Assets/Scripts/Util/Attributes/PersistableIgnoreAttribute.cs
diff --git a/Assets/Scripts/Util/Serialization/PersistableIncludeAttribute.cs b/Assets/Scripts/Util/Attributes/PersistableIncludeAttribute.cs
similarity index 87%
rename from Assets/Scripts/Util/Serialization/PersistableIncludeAttribute.cs
rename to Assets/Scripts/Util/Attributes/PersistableIncludeAttribute.cs
index 577fbd0..ad08113 100644
--- a/Assets/Scripts/Util/Serialization/PersistableIncludeAttribute.cs
+++ b/Assets/Scripts/Util/Attributes/PersistableIncludeAttribute.cs
@@ -20,6 +20,11 @@ public class PersistableIncludeAttribute : Attribute
///
public Type PersistableType { get; }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public PersistableIncludeAttribute() { }
+
///
/// Initializes a new instance of the class.
///
diff --git a/Assets/Scripts/Util/Serialization/PersistableMemberAttribute.cs b/Assets/Scripts/Util/Attributes/PersistableMemberAttribute.cs
similarity index 100%
rename from Assets/Scripts/Util/Serialization/PersistableMemberAttribute.cs
rename to Assets/Scripts/Util/Attributes/PersistableMemberAttribute.cs
diff --git a/Assets/Scripts/Util/Serialization/PersistableObjectAttribute.cs b/Assets/Scripts/Util/Attributes/PersistableObjectAttribute.cs
similarity index 100%
rename from Assets/Scripts/Util/Serialization/PersistableObjectAttribute.cs
rename to Assets/Scripts/Util/Attributes/PersistableObjectAttribute.cs
diff --git a/Assets/Scripts/Util/Common/GameLogger.cs b/Assets/Scripts/Util/Common/GameLogger.cs
index 1ec5b79..560aecd 100644
--- a/Assets/Scripts/Util/Common/GameLogger.cs
+++ b/Assets/Scripts/Util/Common/GameLogger.cs
@@ -138,7 +138,8 @@ private static void InitializeLogFile()
{
if (!File.Exists(logFilePath))
{
- File.Create(logFilePath).Dispose(); // Create and immediately close the file.
+ // Create and immediately close the file.
+ File.Create(logFilePath).Dispose();
}
WriteToFile($"Log initialized at {DateTime.Now}");
}
diff --git a/Assets/Scripts/Util/Common/TypeHelper.cs b/Assets/Scripts/Util/Common/TypeHelper.cs
index a29d299..0d49402 100644
--- a/Assets/Scripts/Util/Common/TypeHelper.cs
+++ b/Assets/Scripts/Util/Common/TypeHelper.cs
@@ -140,6 +140,8 @@ public static bool HasAttribute(Type type, Type attributeType)
/// The converted primitive value.
public static object ConvertToPrimitive(string content, Type targetType)
{
+ if (targetType.IsEnum)
+ return Enum.Parse(targetType, content);
if (targetType == typeof(string))
return content;
if (targetType == typeof(int))
diff --git a/Assets/Scripts/Util/Serialization/GameSerializer.cs b/Assets/Scripts/Util/Serialization/GameSerializer.cs
index 64094b2..a5dcbc6 100644
--- a/Assets/Scripts/Util/Serialization/GameSerializer.cs
+++ b/Assets/Scripts/Util/Serialization/GameSerializer.cs
@@ -255,7 +255,6 @@ private static void WritePersistable(object obj, XmlWriter writer, string key =
objType,
ReflectionHelper.OperationType.Write
);
-
writer.WriteStartElement(key ?? objType.Name);
foreach (MemberInfo attribute in attributes)
@@ -541,22 +540,23 @@ private static object ReadPersistable(
IDictionary attributes = ReflectionHelper.GetPersistableAttributeMap(
actualType,
- ReflectionHelper.OperationType.Write
+ ReflectionHelper.OperationType.Read
);
+
IEnumerable members = ReflectionHelper.GetPersistableMembers(
actualType,
ReflectionHelper.OperationType.Read
);
- if (reader.IsEmptyElement)
+ if (reader.HasAttributes)
{
- reader.Read();
- return obj;
+ ReadAttributes(reader, attributes, obj);
}
- if (reader.HasAttributes)
+ if (reader.IsEmptyElement)
{
- ReadAttributes(reader, attributes, obj);
+ reader.Read();
+ return obj;
}
reader.ReadStartElement();
@@ -565,7 +565,9 @@ private static object ReadPersistable(
{
if (reader.NodeType == XmlNodeType.Element)
{
+ string elementName = reader.Name;
MemberInfo member = FindMemberForElement(members, reader.Name);
+
if (member != null)
{
object value = ReadMember(member, reader);
@@ -573,7 +575,9 @@ private static object ReadPersistable(
}
else
{
- reader.Skip();
+ throw new InvalidOperationException(
+ $"Unknown element '{elementName}' encountered while deserializing {objType.Name}."
+ );
}
}
else
@@ -638,10 +642,12 @@ object obj
reader.MoveToAttribute(i);
if (attributes.TryGetValue(reader.Name, out MemberInfo attribute))
{
- object value = ReadValue(ReflectionHelper.GetMemberType(attribute), reader);
+ Type attributeType = ReflectionHelper.GetMemberType(attribute);
+ object value = TypeHelper.ConvertToPrimitive(reader.Value, attributeType);
ReflectionHelper.SetMemberValue(attribute, obj, value);
}
}
+ reader.MoveToElement();
}
///
@@ -659,6 +665,7 @@ string elementName
{
PersistableIncludeAttribute[] typeAttributes = (PersistableIncludeAttribute[])
Attribute.GetCustomAttributes(member, typeof(PersistableIncludeAttribute));
+
foreach (PersistableIncludeAttribute typeAttr in typeAttributes)
{
if (typeAttr.PersistableType.Name == elementName)
@@ -748,7 +755,6 @@ public static IEnumerable GetPersistableMembers(
OperationType operationType
)
{
- // Determine if a member is persistable based on the operation type
bool IsPersistable(MemberInfo member)
{
bool isPublicField = member is FieldInfo field && field.IsPublic;
@@ -775,20 +781,17 @@ bool IsPersistable(MemberInfo member)
&& !hasAlternativePersistableAttribute;
}
- // Retrieve persistable fields.
IEnumerable fields = classType
.GetFields(CommonBindingFlags)
.Where(IsPersistable)
.Cast();
- // Retrieve persistable properties.
IEnumerable properties = classType
.GetProperties(CommonBindingFlags)
.Where(property => property.CanRead && property.CanWrite)
.Where(IsPersistable)
.Cast();
- // Combine and return the result
return fields.Concat(properties);
}
@@ -803,7 +806,6 @@ public static IEnumerable GetPersistableAttributes(
OperationType operationType
)
{
- // Determine if a member is a persistable attribute based on the operation type
bool IsPersistableAttribute(MemberInfo member)
{
bool hasPersistableAttribute = member
@@ -821,20 +823,17 @@ bool IsPersistableAttribute(MemberInfo member)
return hasPersistableAttribute;
}
- // Retrieve persistable fields.
IEnumerable fields = classType
.GetFields(CommonBindingFlags)
.Where(IsPersistableAttribute)
.Cast();
- // Retrieve persistable properties.
IEnumerable properties = classType
.GetProperties(CommonBindingFlags)
.Where(property => property.CanRead && property.CanWrite)
.Where(IsPersistableAttribute)
.Cast();
- // Combine and return the result
return fields.Concat(properties);
}
@@ -871,18 +870,27 @@ public static IDictionary GetPersistableAttributeMap(
OperationType operationType
)
{
- return GetPersistableAttributes(classType, operationType)
- .ToDictionary(
- member =>
- (
- (PersistableAttributeAttribute)
- Attribute.GetCustomAttribute(
- member,
- typeof(PersistableAttributeAttribute)
- )
- )?.Name ?? member.Name,
- member => member
- );
+ Dictionary attributeMap = new Dictionary();
+ Type currentType = classType;
+
+ while (currentType != null)
+ {
+ foreach (MemberInfo member in GetPersistableAttributes(currentType, operationType))
+ {
+ PersistableAttributeAttribute attr = (PersistableAttributeAttribute)
+ Attribute.GetCustomAttribute(member, typeof(PersistableAttributeAttribute));
+ string key = attr?.Name ?? member.Name;
+
+ if (!attributeMap.ContainsKey(key))
+ {
+ attributeMap[key] = member;
+ }
+ }
+
+ currentType = currentType.BaseType;
+ }
+
+ return attributeMap;
}
///
@@ -933,7 +941,9 @@ public static object GetMemberValue(MemberInfo member, object obj)
public static void SetMemberValue(MemberInfo member, object obj, object value)
{
if (value == null)
+ {
return;
+ }
switch (member)
{
diff --git a/Assets/Tests/EditMode/Game/GameTests.cs b/Assets/Tests/EditMode/Game/GameTests.cs
index 10914a3..8c5e3ee 100644
--- a/Assets/Tests/EditMode/Game/GameTests.cs
+++ b/Assets/Tests/EditMode/Game/GameTests.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
@@ -5,206 +6,434 @@
[TestFixture]
public class GameTests
{
- private List factions = new List
- {
- new Faction { InstanceID = "FNALL1" },
- new Faction { InstanceID = "FNEMP1" },
- };
+ private Game game;
+ private GameSummary summary;
+ private Faction faction1;
+ private Faction faction2;
+ private GalaxyMap galaxyMap;
+ private PlanetSystem planetSystem;
+ private Planet planet;
+ private Fleet fleet;
- [Test]
- public void InitializeGame_WithSummary_SetsPropertiesCorrectly()
+ [SetUp]
+ public void SetUp()
{
- GameSummary summary = new GameSummary
+ // Initialize game summary.
+ summary = new GameSummary
{
- GalaxySize = GameSize.Large,
- Difficulty = GameDifficulty.Easy,
- VictoryCondition = GameVictoryCondition.Headquarters,
- ResourceAvailability = GameResourceAvailability.Abundant,
- PlayerFactionID = "FNALL1",
+ GalaxySize = GameSize.Medium,
+ Difficulty = GameDifficulty.Medium,
+ VictoryCondition = GameVictoryCondition.Conquest,
+ ResourceAvailability = GameResourceAvailability.Normal,
+ PlayerFactionID = "FACTION1",
};
- Game game = new Game(summary)
- {
- Summary = summary,
- Factions = factions,
- Galaxy = new GalaxyMap(),
- };
+ // Create factions.
+ faction1 = new Faction { InstanceID = "FACTION1", DisplayName = "Alliance" };
+ faction2 = new Faction { InstanceID = "FACTION2", DisplayName = "Empire" };
- Assert.IsNotNull(game.Summary, "Game summary should not be null.");
- Assert.AreEqual(GameSize.Large, game.Summary.GalaxySize, "GalaxySize should match.");
- Assert.AreEqual(GameDifficulty.Easy, game.Summary.Difficulty, "Difficulty should match.");
- Assert.AreEqual(
- GameVictoryCondition.Headquarters,
- game.Summary.VictoryCondition,
- "VictoryCondition should match."
- );
- Assert.AreEqual(
- GameResourceAvailability.Abundant,
- game.Summary.ResourceAvailability,
- "ResourceAvailability should match."
+ // Create game objects.
+ galaxyMap = new GalaxyMap();
+ planetSystem = new PlanetSystem { InstanceID = "SYSTEM1" };
+ planet = new Planet { InstanceID = "PLANET1", OwnerInstanceID = "FACTION1" };
+ fleet = new Fleet { InstanceID = "FLEET1", OwnerInstanceID = "FACTION1" };
+
+ // Initialize the game.
+ game = new Game(summary);
+ game.Factions.Add(faction1);
+ game.Factions.Add(faction2);
+ }
+
+ [Test]
+ public void Constructor_WithSummary_InitializesCorrectly()
+ {
+ // Verify game initialization.
+ Assert.AreEqual(summary, game.Summary, "Game summary should match the provided summary");
+ Assert.IsNotNull(game.Galaxy, "Galaxy should be initialized");
+ Assert.AreEqual(0, game.CurrentTick, "Current tick should be initialized to 0");
+ Assert.IsEmpty(game.EventPool, "Event pool should be empty initially");
+ Assert.IsEmpty(game.CompletedEventIDs, "Completed event IDs should be empty initially");
+ }
+
+ [Test]
+ public void GetFactions_ReturnsCorrectFactions()
+ {
+ // Get factions and verify the count and contents.
+ List factions = game.GetFactions();
+ Assert.AreEqual(2, factions.Count, "Should return two factions");
+ Assert.Contains(faction1, factions, "Should contain faction1");
+ Assert.Contains(faction2, factions, "Should contain faction2");
+ }
+
+ [Test]
+ public void GetFactionByOwnerInstanceID_ReturnsCorrectFaction()
+ {
+ // Get faction by ID and verify.
+ Faction retrievedFaction = game.GetFactionByOwnerInstanceID("FACTION1");
+ Assert.AreEqual(faction1, retrievedFaction, "Should return the correct faction");
+ }
+
+ [Test]
+ public void GetFactionByOwnerInstanceID_ThrowsException_WhenFactionNotFound()
+ {
+ // Attempt to get non-existent faction.
+ Assert.Throws(
+ () => game.GetFactionByOwnerInstanceID("NONEXISTENT"),
+ "Should throw exception for non-existent faction"
);
- Assert.AreEqual("FNALL1", game.Summary.PlayerFactionID, "PlayerFactionID should match.");
- Assert.IsNotNull(game.Galaxy, "Galaxy should not be null.");
}
[Test]
- public void AttachNode_ToParent_AddsNodeCorrectly()
+ public void GetGalaxyMap_ReturnsCorrectGalaxyMap()
{
- PlanetSystem planetSystem = new PlanetSystem();
- GalaxyMap galaxy = new GalaxyMap
- {
- PlanetSystems = new List { planetSystem },
- };
- Game game = new Game { Factions = factions, Galaxy = galaxy };
- Planet planet = new Planet { OwnerInstanceID = "FNALL1" };
+ // Get galaxy map and verify.
+ GalaxyMap retrievedGalaxyMap = game.GetGalaxyMap();
+ Assert.AreEqual(game.Galaxy, retrievedGalaxyMap, "Should return the correct galaxy map");
+ }
+ [Test]
+ public void AttachNode_AddsNodeCorrectly()
+ {
+ // Attach node and verify.
game.AttachNode(planet, planetSystem);
+ Assert.AreEqual(
+ planetSystem,
+ planet.GetParent(),
+ "Planet should have planetSystem as parent"
+ );
Assert.Contains(
planet,
planetSystem.GetChildren().ToList(),
- "Planet should be attached to the PlanetSystem."
+ "PlanetSystem should contain planet as child"
+ );
+ Assert.IsTrue(
+ game.NodesByInstanceID.ContainsKey(planet.InstanceID),
+ "Game should contain planet in NodesByInstanceID"
+ );
+ Assert.IsTrue(
+ game.GetFactionByOwnerInstanceID(planet.OwnerInstanceID)
+ .GetAllOwnedUnits()
+ .Contains(planet),
+ "Faction should contain planet in owned units"
);
}
[Test]
- public void DetachNode_FromParent_RemovesNodeCorrectly()
+ public void AttachNode_ThrowsException_WhenNodeAlreadyHasParent()
{
- PlanetSystem planetSystem = new PlanetSystem();
- GalaxyMap galaxy = new GalaxyMap
- {
- PlanetSystems = new List { planetSystem },
- };
- Game game = new Game { Factions = factions, Galaxy = galaxy };
- Planet planet = new Planet { OwnerInstanceID = "FNALL1" };
+ // Attach node to a parent.
+ game.AttachNode(planet, planetSystem);
+
+ // Attempt to attach the same node to another parent.
+ Assert.Throws(
+ () => game.AttachNode(planet, new PlanetSystem()),
+ "Should throw exception when attaching a node that already has a parent"
+ );
+ }
+ [Test]
+ public void DetachNode_RemovesNodeCorrectly()
+ {
+ // Attach and then detach node.
game.AttachNode(planet, planetSystem);
game.DetachNode(planet);
+ // Verify detachment is successful and node is removed from all relevant structures.
+ Assert.IsNull(planet.GetParent(), "Planet should have no parent after detachment");
Assert.IsFalse(
planetSystem.GetChildren().Contains(planet),
- "Planet should be detached from the PlanetSystem."
+ "PlanetSystem should not contain planet as child"
+ );
+ Assert.IsFalse(
+ game.NodesByInstanceID.ContainsKey(planet.InstanceID),
+ "Game should not contain planet in NodesByInstanceID"
+ );
+ Assert.IsFalse(
+ game.GetFactionByOwnerInstanceID(planet.OwnerInstanceID)
+ .GetAllOwnedUnits()
+ .Contains(planet),
+ "Faction should not contain planet in owned units"
+ );
+ }
+
+ [Test]
+ public void DetachNode_ThrowsException_WhenNodeHasNoParent()
+ {
+ // Attempt to detach a node with no parent.
+ Assert.Throws(
+ () => game.DetachNode(planet),
+ "Should throw exception when detaching a node with no parent"
);
- Assert.IsNull(planet.GetParent(), "Planet should not have a parent after being detached.");
}
[Test]
- public void AttachNode_WithExistingParent_ThrowsException()
+ public void AddSceneNodeByInstanceID_AddsNodeCorrectly()
{
- Game game = new Game { Factions = factions };
- Planet planet1 = new Planet { OwnerInstanceID = "FNALL1" };
- Planet planet2 = new Planet { OwnerInstanceID = "FNALL1" };
- Fleet fleet = new Fleet { OwnerInstanceID = "FNALL1" };
+ // Add node and verify that it is added to the game.
+ game.AddSceneNodeByInstanceID(planet);
+ Assert.IsTrue(
+ game.NodesByInstanceID.ContainsKey(planet.InstanceID),
+ "Game should contain planet in NodesByInstanceID"
+ );
+ }
- game.AttachNode(fleet, planet1);
+ [Test]
+ public void AddSceneNodeByInstanceID_ThrowsException_WhenDuplicateNodeAdded()
+ {
+ // Add node to the game.
+ game.AddSceneNodeByInstanceID(planet);
- Assert.Throws(
- () => game.AttachNode(fleet, planet2),
- "Exception should be thrown when attaching a node with a parent."
+ // Attempt to add the same node again.
+ Assert.Throws(
+ () => game.AddSceneNodeByInstanceID(planet),
+ "Should throw exception when adding a duplicate node"
);
}
[Test]
- public void DetachNode_WithoutParent_ThrowsException()
+ public void RemoveSceneNodeByInstanceID_RemovesNodeCorrectly()
{
- Game game = new Game();
- Fleet fleet = new Fleet { OwnerInstanceID = "FNALL1" };
+ // Add and then remove node from the game.
+ game.AddSceneNodeByInstanceID(planet);
+ game.RemoveSceneNodeByInstanceID(planet);
- Assert.Throws(
- () => game.DetachNode(fleet),
- "Exception should be thrown when detaching a node without a parent."
+ // Verify removal from all relevant structures.
+ Assert.IsFalse(
+ game.NodesByInstanceID.ContainsKey(planet.InstanceID),
+ "Game should not contain planet in NodesByInstanceID after removal"
);
}
[Test]
- public void DetachNodeByInstanceID_RegistersAndDeregistersCorrectly()
+ public void GetSceneNodeByInstanceID_ReturnsCorrectNode()
{
- Planet planet = new Planet { OwnerInstanceID = "FNALL1" };
- PlanetSystem planetSystem = new PlanetSystem { Planets = new List { planet } };
- GalaxyMap galaxy = new GalaxyMap();
+ // Add node and retrieve it.
+ game.AddSceneNodeByInstanceID(planet);
+ Planet retrievedNode = game.GetSceneNodeByInstanceID(planet.InstanceID);
- Game game = new Game
- {
- Factions = new List { new Faction { InstanceID = "FNALL1" } },
- Galaxy = galaxy,
- };
+ // Verify retrieval of the correct node.
+ Assert.AreEqual(planet, retrievedNode, "Should return the correct node");
+ }
+
+ [Test]
+ public void GetSceneNodeByInstanceID_ReturnsNull_WhenNodeNotFound()
+ {
+ // Attempt to retrieve non-existent node.
+ Planet retrievedNode = game.GetSceneNodeByInstanceID("NONEXISTENT");
+ Assert.IsNull(retrievedNode, "Should return null for non-existent node");
+ }
- game.AttachNode(planetSystem, game.Galaxy);
+ [Test]
+ public void GetSceneNodesByInstanceIDs_ReturnsCorrectNodes()
+ {
+ // Add nodes to the game.
+ game.AddSceneNodeByInstanceID(planet);
+ game.AddSceneNodeByInstanceID(fleet);
- Assert.AreEqual(
- game.GetSceneNodeByInstanceID(planetSystem.InstanceID),
- planetSystem
+ // Retrieve nodes by IDs.
+ List retrievedNodes = game.GetSceneNodesByInstanceIDs(
+ new List { planet.InstanceID, fleet.InstanceID }
);
- Assert.AreEqual(game.GetSceneNodeByInstanceID(planet.InstanceID), planet);
- game.DetachNode(planetSystem);
+ // Verify retrieval of planets and fleets.
+ Assert.AreEqual(2, retrievedNodes.Count, "Should return two nodes");
+ Assert.Contains(planet, retrievedNodes, "Should contain planet");
+ Assert.Contains(fleet, retrievedNodes, "Should contain fleet");
+ }
- Assert.IsNull(game.GetSceneNodeByInstanceID(planetSystem.InstanceID));
- Assert.IsNull(game.GetSceneNodeByInstanceID(planet.InstanceID));
+ [Test]
+ public void GetSceneNodesByOwnerInstanceID_ReturnsCorrectNodes()
+ {
+ // Add nodes to the game.
+ game.AddSceneNodeByInstanceID(planet);
+ game.AddSceneNodeByInstanceID(fleet);
+
+ // Retrieve nodes by owner ID.
+ List retrievedNodes = game.GetSceneNodesByOwnerInstanceID(
+ "FACTION1"
+ );
+
+ // Verify retrieval of planets and fleets.
+ Assert.AreEqual(2, retrievedNodes.Count, "Should return two nodes");
+ Assert.Contains(planet, retrievedNodes, "Should contain planet");
+ Assert.Contains(fleet, retrievedNodes, "Should contain fleet");
}
[Test]
- public void SerializeAndDeserialize_GameObject_ReturnsEquivalentObject()
+ public void GetSceneNodesByType_ReturnsCorrectNodes()
{
- // Arrange: Create a game object with some data
- Game game = new Game
- {
- Summary = new GameSummary
- {
- GalaxySize = GameSize.Medium,
- Difficulty = GameDifficulty.Medium,
- VictoryCondition = GameVictoryCondition.Headquarters,
- ResourceAvailability = GameResourceAvailability.Limited,
- PlayerFactionID = "FNALL1",
- },
- Factions = new List
- {
- new Faction { InstanceID = "FNALL1", DisplayName = "Alliance" },
- new Faction { InstanceID = "FNEMP1", DisplayName = "Empire" },
- },
- UnrecruitedOfficers = new List
- {
- new Officer { InstanceID = "OFC001", DisplayName = "Luke Skywalker" },
- new Officer { InstanceID = "OFC002", DisplayName = "Darth Vader" },
- },
- };
+ // Set up galaxy structure.
+ game.Galaxy = galaxyMap;
+ game.AttachNode(planetSystem, galaxyMap);
+ game.AttachNode(planet, planetSystem);
+ game.AttachNode(fleet, planet);
- // Act: Serialize and deserialize the object
- string serializedGame = SerializationHelper.Serialize(game);
- Game deserializedGame = SerializationHelper.Deserialize(serializedGame);
+ // Retrieve planets and verify the count and contents.
+ List retrievedPlanets = game.GetSceneNodesByType();
+ Assert.AreEqual(1, retrievedPlanets.Count, "Should return one planet");
+ Assert.Contains(planet, retrievedPlanets, "Should contain the specific planet");
+
+ // Retrieve fleets and verify the count and contents.
+ List retrievedFleets = game.GetSceneNodesByType();
+ Assert.AreEqual(1, retrievedFleets.Count, "Should return one fleet");
+ Assert.Contains(fleet, retrievedFleets, "Should contain the specific fleet");
+ }
+
+ [Test]
+ public void RegisterOwnedUnit_AddsUnitToFaction()
+ {
+ // Register unit.
+ game.RegisterOwnedUnit(planet);
+
+ // Verify registration is successful.
+ Assert.IsTrue(
+ game.GetFactionByOwnerInstanceID(planet.OwnerInstanceID)
+ .GetAllOwnedUnits()
+ .Contains(planet),
+ "Faction should contain planet in owned units after registration"
+ );
+ }
+
+ [Test]
+ public void DeregsiterOwnedUnit_RemovesUnitFromFaction()
+ {
+ // Register and then deregister unit.
+ game.RegisterOwnedUnit(planet);
+ game.DeregsiterOwnedUnit(planet);
+
+ // Verify deregistration was successful.
+ Assert.IsFalse(
+ game.GetFactionByOwnerInstanceID(planet.OwnerInstanceID)
+ .GetAllOwnedUnits()
+ .Contains(planet),
+ "Faction should not contain planet in owned units after deregistration"
+ );
+ }
+
+ [Test]
+ public void GetEventPool_ReturnsCorrectEventPool()
+ {
+ // Add events to pool.
+ GameEvent event1 = new GameEvent { InstanceID = "EVENT1" };
+ GameEvent event2 = new GameEvent { InstanceID = "EVENT2" };
+ game.EventPool.Add(event1);
+ game.EventPool.Add(event2);
+
+ // Retrieve event pool and verify the count and contents.
+ List eventPool = game.GetEventPool();
+ Assert.AreEqual(2, eventPool.Count, "Should return two events");
+ Assert.Contains(event1, eventPool, "Should contain event1");
+ Assert.Contains(event2, eventPool, "Should contain event2");
+ }
+
+ [Test]
+ public void RemoveEvent_RemovesEventCorrectly()
+ {
+ // Add event and then remove it.
+ GameEvent event1 = new GameEvent { InstanceID = "EVENT1" };
+ game.EventPool.Add(event1);
+ game.RemoveEvent(event1);
+
+ // Verify removal from the event pool.
+ Assert.IsFalse(
+ game.EventPool.Contains(event1),
+ "Event pool should not contain the removed event"
+ );
+ }
+
+ [Test]
+ public void GetEventByInstanceID_ReturnsCorrectEvent()
+ {
+ // Add event and retrieve it.
+ GameEvent event1 = new GameEvent { InstanceID = "EVENT1" };
+ game.EventPool.Add(event1);
+
+ // Retrieve event and verify.
+ GameEvent retrievedEvent = game.GetEventByInstanceID("EVENT1");
+ Assert.AreEqual(event1, retrievedEvent, "Should return the correct event");
+ }
+
+ [Test]
+ public void AddCompletedEvent_AddsEventToCompletedList()
+ {
+ // Add completed event to the completed list.
+ GameEvent event1 = new GameEvent { InstanceID = "EVENT1" };
+ game.AddCompletedEvent(event1);
+
+ // Verify addition to the completed list.
+ Assert.IsTrue(
+ game.CompletedEventIDs.Contains(event1.InstanceID),
+ "Completed event IDs should contain the added event's ID"
+ );
+ }
+
+ [Test]
+ public void IsEventComplete_ReturnsCorrectStatus()
+ {
+ // Add completed event to the completed list.
+ GameEvent event1 = new GameEvent { InstanceID = "EVENT1" };
+ game.AddCompletedEvent(event1);
+
+ // Check completion status.
+ Assert.IsTrue(game.IsEventComplete("EVENT1"), "EVENT1 should be marked as complete");
+ Assert.IsFalse(game.IsEventComplete("EVENT2"), "EVENT2 should not be marked as complete");
+ }
+
+ [Test]
+ public void Galaxy_Setter_InitializesGalaxyCorrectly()
+ {
+ // Set up galaxy structure.
+ game.Galaxy = galaxyMap;
+ game.AttachNode(planetSystem, galaxyMap);
+ game.AttachNode(planet, planetSystem);
+ game.AttachNode(fleet, planetSystem);
+
+ // Verify galaxy initialization.
+ Assert.IsTrue(
+ game.NodesByInstanceID.ContainsKey(galaxyMap.InstanceID),
+ "Game should contain galaxyMap in NodesByInstanceID"
+ );
+ Assert.IsTrue(
+ game.NodesByInstanceID.ContainsKey(planetSystem.InstanceID),
+ "Game should contain planetSystem in NodesByInstanceID"
+ );
+ Assert.IsTrue(
+ game.NodesByInstanceID.ContainsKey(planet.InstanceID),
+ "Game should contain planet in NodesByInstanceID"
+ );
+ Assert.IsTrue(
+ game.NodesByInstanceID.ContainsKey(fleet.InstanceID),
+ "Game should contain fleet in NodesByInstanceID"
+ );
- // Assert: Verify the deserialized object matches the original
- Assert.IsNotNull(deserializedGame);
- Assert.AreEqual(game.Summary.GalaxySize, deserializedGame.Summary.GalaxySize);
- Assert.AreEqual(game.Summary.Difficulty, deserializedGame.Summary.Difficulty);
- Assert.AreEqual(game.Summary.VictoryCondition, deserializedGame.Summary.VictoryCondition);
Assert.AreEqual(
- game.Summary.ResourceAvailability,
- deserializedGame.Summary.ResourceAvailability
+ galaxyMap,
+ planetSystem.GetParent(),
+ "PlanetSystem should have galaxyMap as parent"
+ );
+ Assert.AreEqual(
+ planetSystem,
+ planet.GetParent(),
+ "Planet should have planetSystem as parent"
+ );
+ Assert.AreEqual(
+ planetSystem,
+ fleet.GetParent(),
+ "Fleet should have planetSystem as parent"
);
- Assert.AreEqual(game.Summary.PlayerFactionID, deserializedGame.Summary.PlayerFactionID);
- Assert.AreEqual(game.Factions.Count, deserializedGame.Factions.Count);
- for (int i = 0; i < game.Factions.Count; i++)
- {
- Assert.AreEqual(game.Factions[i].InstanceID, deserializedGame.Factions[i].InstanceID);
- Assert.AreEqual(
- game.Factions[i].GetDisplayName(),
- deserializedGame.Factions[i].GetDisplayName()
- );
- }
-
- Assert.AreEqual(game.UnrecruitedOfficers.Count, deserializedGame.UnrecruitedOfficers.Count);
- for (int i = 0; i < game.UnrecruitedOfficers.Count; i++)
- {
- Assert.AreEqual(
- game.UnrecruitedOfficers[i].InstanceID,
- deserializedGame.UnrecruitedOfficers[i].InstanceID
- );
- Assert.AreEqual(
- game.UnrecruitedOfficers[i].GetDisplayName(),
- deserializedGame.UnrecruitedOfficers[i].GetDisplayName()
- );
- }
+ Assert.IsTrue(
+ game.GetFactionByOwnerInstanceID(planet.OwnerInstanceID)
+ .GetAllOwnedUnits()
+ .Contains(planet),
+ "Faction should contain planet in owned units"
+ );
+ Assert.IsTrue(
+ game.GetFactionByOwnerInstanceID(fleet.OwnerInstanceID)
+ .GetAllOwnedUnits()
+ .Contains(fleet),
+ "Faction should contain fleet in owned units"
+ );
}
}
diff --git a/Assets/Tests/EditMode/Game/PlanetTests.cs b/Assets/Tests/EditMode/Game/PlanetTests.cs
index 14df637..97ce93a 100644
--- a/Assets/Tests/EditMode/Game/PlanetTests.cs
+++ b/Assets/Tests/EditMode/Game/PlanetTests.cs
@@ -215,7 +215,7 @@ public void AddToManufacturingQueue_UnitWithoutParent_ThrowsException()
{
IManufacturable unit = new Starfighter();
- Assert.Throws(
+ Assert.Throws(
() => planet.AddToManufacturingQueue(unit),
"Adding a manufacturable unit without a parent should throw a GameStateException."
);
diff --git a/Assets/Tests/EditMode/Generation/GameBuilderTests.cs b/Assets/Tests/EditMode/Generation/GameBuilderTests.cs
index b5c6f2b..1e7d060 100644
--- a/Assets/Tests/EditMode/Generation/GameBuilderTests.cs
+++ b/Assets/Tests/EditMode/Generation/GameBuilderTests.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -7,57 +8,172 @@
[TestFixture]
public class GameBuilderTests
{
- private Game game;
+ private static readonly Lazy LazyGameTestCases = new Lazy(
+ () =>
+ new[]
+ {
+ CreateGame(GameSize.Small, GameDifficulty.Medium, GameVictoryCondition.Conquest),
+ CreateGame(GameSize.Medium, GameDifficulty.Medium, GameVictoryCondition.Conquest),
+ CreateGame(GameSize.Large, GameDifficulty.Medium, GameVictoryCondition.Conquest),
+ }
+ );
- [OneTimeSetUp]
- public void OneTimeSetUp()
+ private static Game[] GameTestCases => LazyGameTestCases.Value;
+
+ private static Game CreateGame(
+ GameSize size,
+ GameDifficulty difficulty,
+ GameVictoryCondition victoryCondition
+ )
{
- // Create a new GameSummary object with specific configurations
+ // Create a new GameSummary object with specific configurations.
GameSummary summary = new GameSummary
{
- GalaxySize = GameSize.Large,
- Difficulty = GameDifficulty.Easy,
- VictoryCondition = GameVictoryCondition.Headquarters,
- ResourceAvailability = GameResourceAvailability.Abundant,
+ GalaxySize = size,
+ Difficulty = difficulty,
+ VictoryCondition = victoryCondition,
+ ResourceAvailability = GameResourceAvailability.Normal,
PlayerFactionID = "FNALL1",
};
- // Create a new GameBuilder instance with the summary
+ // Create a new GameBuilder instance with the summary.
GameBuilder builder = new GameBuilder(summary);
- // Build the game using the GameBuilder
- game = builder.BuildGame();
+ // Build the game using the GameBuilder.
+ return builder.BuildGame();
+ }
+
+ [Test, TestCaseSource(nameof(GameTestCases))]
+ public void BuildGame_SetsConsistentOwners(Game game)
+ {
+ // Traverse the galaxy map to find planets.
+ game.Galaxy.Traverse(node =>
+ {
+ // Skip nodes without an owner.
+ if (node.GetOwnerInstanceID() == null)
+ {
+ return;
+ }
+
+ List children = node.GetChildren().ToList();
+
+ // Ensure each child has the same owner as its parent.
+ foreach (ISceneNode child in children)
+ {
+ Assert.AreEqual(
+ node.GetOwnerInstanceID(),
+ child.GetOwnerInstanceID(),
+ $"Child \"{child.GetDisplayName()}\" should have the same owner as its parent, \"{node.GetDisplayName()}\"."
+ );
+ }
+ });
+ }
+
+ [Test, TestCaseSource(nameof(GameTestCases))]
+ public void BuildGame_SetsChildParentRelationships(Game game)
+ {
+ game.Galaxy.Traverse(node =>
+ {
+ List children = node.GetChildren().ToList();
+
+ foreach (ISceneNode child in children)
+ {
+ // Ensure the child has the parent as its parent.
+ Assert.AreEqual(
+ node,
+ child.GetParent(),
+ "Child should have the parent as its parent."
+ );
+ }
+ });
}
- [Test]
- public void BuildGame_CreatesGameSummary()
+ [Test, TestCaseSource(nameof(GameTestCases))]
+ public void BuildGame_SetsGameSummary(Game game)
{
Assert.IsNotNull(game, "Game should not be null.");
+ Assert.IsNotNull(game.Summary, "Game summary should not be null.");
- // Assert that the game's summary properties match the provided configurations
- Assert.AreEqual(GameSize.Large, game.Summary.GalaxySize, "GalaxySize should match.");
- Assert.AreEqual(GameDifficulty.Easy, game.Summary.Difficulty, "Difficulty should match.");
- Assert.AreEqual(
- GameVictoryCondition.Headquarters,
- game.Summary.VictoryCondition,
- "VictoryCondition should match."
+ // Check that the game's summary properties are within expected ranges.
+ Assert.IsTrue(
+ Enum.IsDefined(typeof(GameSize), game.Summary.GalaxySize),
+ "GalaxySize should be a valid enum value."
+ );
+ Assert.IsTrue(
+ Enum.IsDefined(typeof(GameDifficulty), game.Summary.Difficulty),
+ "Difficulty should be a valid enum value."
+ );
+ Assert.IsTrue(
+ Enum.IsDefined(typeof(GameVictoryCondition), game.Summary.VictoryCondition),
+ "VictoryCondition should be a valid enum value."
);
- Assert.AreEqual(
- GameResourceAvailability.Abundant,
- game.Summary.ResourceAvailability,
- "ResourceAvailability should match."
+ Assert.IsTrue(
+ Enum.IsDefined(typeof(GameResourceAvailability), game.Summary.ResourceAvailability),
+ "ResourceAvailability should be a valid enum value."
);
- Assert.AreEqual("FNALL1", game.Summary.PlayerFactionID, "PlayerFactionID should match.");
+
+ // Check that PlayerFactionID is not null or empty.
+ Assert.IsFalse(
+ string.IsNullOrEmpty(game.Summary.PlayerFactionID),
+ "PlayerFactionID should not be null or empty."
+ );
+ }
+
+ [Test, TestCaseSource(nameof(GameTestCases))]
+ public void BuildGame_SetsFactions(Game game)
+ {
+ Assert.IsNotNull(game.Factions, "Factions should not be null.");
+
+ // Ensure the game has at least two factions.
+ Assert.GreaterOrEqual(game.Factions.Count, 2, "Game should have at least two factions.");
}
- [Test]
- public void BuildGame_SetsHQs()
+ [Test, TestCaseSource(nameof(GameTestCases))]
+ public void BuildGame_SetsFactionTechnologies(Game game)
+ {
+ foreach (Faction faction in game.Factions)
+ {
+ // Ensure the faction has technology levels.
+ Assert.IsNotEmpty(faction.TechnologyLevels, "Faction should have technology levels.");
+
+ // Ensure the faction has at least one technology level for each manufacturing type.
+ foreach (
+ KeyValuePair<
+ ManufacturingType,
+ SortedDictionary>
+ > manufacturingTypeTechLevels in faction.TechnologyLevels
+ )
+ {
+ ManufacturingType manufacturingType = manufacturingTypeTechLevels.Key;
+ SortedDictionary> techLevels =
+ manufacturingTypeTechLevels.Value;
+
+ Assert.IsNotEmpty(
+ techLevels,
+ $"Faction should have technology levels for {manufacturingType}."
+ );
+
+ foreach (KeyValuePair> levelTechnologies in techLevels)
+ {
+ int level = levelTechnologies.Key;
+ List technologies = levelTechnologies.Value;
+
+ Assert.IsNotEmpty(
+ technologies,
+ $"Faction should have at least one technology for {manufacturingType} at level {level}."
+ );
+ }
+ }
+ }
+ }
+
+ [Test, TestCaseSource(nameof(GameTestCases))]
+ public void BuildGame_SetsHQs(Game game)
{
// Assert that the game's factions and galaxy map are not null.
Assert.IsNotNull(game.Factions, "Factions should not be null.");
Assert.IsNotNull(game.Galaxy, "GalaxyMap should not be null.");
- // Iterate through each faction in the game.
foreach (Faction faction in game.Factions)
{
// Check if the faction has a headquarters on any planet in the galaxy map.
@@ -68,12 +184,12 @@ public void BuildGame_SetsHQs()
);
// Assert that the faction has a headquarters
- Assert.IsTrue(hasHQ, $"Faction {faction.InstanceID} should have a headquarters.");
+ Assert.IsTrue(hasHQ, $"Faction {faction.GetDisplayName()} should have a headquarters.");
}
}
- [Test]
- public void BuildGame_AssignsFactionsPlanets()
+ [Test, TestCaseSource(nameof(GameTestCases))]
+ public void BuildGame_AssignsFactionsPlanets(Game game)
{
Dictionary> factionPlanets = new Dictionary>();
@@ -102,59 +218,44 @@ public void BuildGame_AssignsFactionsPlanets()
}
}
- [Test]
- public void BuildGame_SetsCorrectOwners()
+ [Test, TestCaseSource(nameof(GameTestCases))]
+ public void BuildGame_DeploysOfficers(Game game)
{
- // Traverse the galaxy map to find planets.
+ List officers = new List();
+
+ // Traverse the galaxy map to find officers.
game.Galaxy.Traverse(node =>
{
- // Skip nodes without an owner.
- if (node.GetOwnerInstanceID() == null)
- {
- return;
- }
-
- List children = node.GetChildren().ToList();
-
- // Ensure each child has the same owner as its parent.
- foreach (ISceneNode child in children)
+ if (node is Officer officer)
{
- Assert.AreEqual(
- node.GetOwnerInstanceID(),
- child.GetOwnerInstanceID(),
- "Child should have the same owner as its parent."
- );
+ officers.Add(officer);
}
});
+
+ // Ensure the game has at least two officers.
+ Assert.Greater(officers.Count, 2, "Game should have at least two officers.");
}
- [Test]
- public void BuildGame_DeploysOfficers()
+ [Test, TestCaseSource(nameof(GameTestCases))]
+ public void BuildGame_InitializesOfficers(Game game)
{
- List officers = new List();
-
// Traverse the galaxy map to find officers.
game.Galaxy.Traverse(node =>
{
if (node is Officer officer)
{
- officers.Add(officer);
-
// Ensure at least one skill is non-zero.
bool hasNonZeroSkill = officer.Skills.Values.Any(skillValue => skillValue > 0);
Assert.IsTrue(
hasNonZeroSkill,
- $"Officer {officer.InstanceID} should have at least one non-zero skill."
+ $"Officer {officer.GetDisplayName()} should have at least one non-zero skill."
);
}
});
-
- // Ensure the game has at least two officers.
- Assert.Greater(officers.Count, 2, "Game should have at least two officers.");
}
- [Test]
- public void BuildGame_DeploysFleets()
+ [Test, TestCaseSource(nameof(GameTestCases))]
+ public void BuildGame_DeploysFleets(Game game)
{
Dictionary fleetsPerFaction = new Dictionary();
@@ -175,18 +276,18 @@ public void BuildGame_DeploysFleets()
}
});
- foreach (var factionID in game.Factions)
+ foreach (var faction in game.Factions)
{
// Ensure the faction has at least one fleet.
Assert.IsTrue(
- fleetsPerFaction.ContainsKey(factionID.InstanceID),
- $"Faction {factionID.InstanceID} should have at least one fleet."
+ fleetsPerFaction.ContainsKey(faction.GetInstanceID()),
+ $"Faction {faction.GetDisplayName()} should have at least one fleet."
);
}
}
- [Test]
- public void BuildGame_DeploysMaxOneFleet()
+ [Test, TestCaseSource(nameof(GameTestCases))]
+ public void BuildGame_DeploysMaxOneFleet(Game game)
{
// Traverse the galaxy map to find planets.
game.Galaxy.Traverse(node =>
@@ -197,14 +298,14 @@ public void BuildGame_DeploysMaxOneFleet()
Assert.LessOrEqual(
planet.GetFleets().Count(),
1,
- $"Planet {planet.InstanceID} should have at most one fleet."
+ $"Planet {planet.GetDisplayName()} should have at most one fleet."
);
}
});
}
- [Test]
- public void BuildGame_DeploysCapitalShips()
+ [Test, TestCaseSource(nameof(GameTestCases))]
+ public void BuildGame_DeploysCapitalShips(Game game)
{
// Traverse the galaxy map to find fleets.
game.Galaxy.Traverse(node =>
@@ -212,9 +313,7 @@ public void BuildGame_DeploysCapitalShips()
if (node is Fleet fleet)
{
bool hasCapitalShips = fleet.GetChildren().Count() > 0;
- GameLogger.Log(
- $"Fleet {fleet.InstanceID} has {fleet.GetChildren().Count()} capital ships. Parent {fleet.GetParent().GetDisplayName()}"
- );
+
// Ensure the fleet has at least one capital ship.
Assert.IsTrue(
hasCapitalShips,
@@ -223,4 +322,15 @@ public void BuildGame_DeploysCapitalShips()
}
});
}
+
+ [Test, TestCaseSource(nameof(GameTestCases))]
+ public void BuildGame_SetsGameEvents(Game game)
+ {
+ // Ensure the game has at least one event in the event pool.
+ Assert.GreaterOrEqual(
+ game.GetEventPool().Count(),
+ 1,
+ "Game should have at most one event in the event pool."
+ );
+ }
}
diff --git a/Assets/Tests/EditMode/Managers/SaveGameManagerTests.cs b/Assets/Tests/EditMode/Managers/SaveGameManagerTests.cs
index 1c74089..cbd065b 100644
--- a/Assets/Tests/EditMode/Managers/SaveGameManagerTests.cs
+++ b/Assets/Tests/EditMode/Managers/SaveGameManagerTests.cs
@@ -130,7 +130,6 @@ public void TestBasicSceneGraphLoaded()
// Create planets.
Planet planet = new Planet { DisplayName = "Planet", OwnerInstanceID = "FNALL1" };
- // planetSystem.Planets.Add(planet);
game.AttachNode(planet, planetSystem);
// Create fleets.
diff --git a/Assets/Tests/EditMode/Util/Serialization/GameSerializerTests.cs b/Assets/Tests/EditMode/Util/Serialization/GameSerializerTests.cs
index 3329eb9..dacad8b 100644
--- a/Assets/Tests/EditMode/Util/Serialization/GameSerializerTests.cs
+++ b/Assets/Tests/EditMode/Util/Serialization/GameSerializerTests.cs
@@ -159,6 +159,9 @@ public class ItemWithCustomName
[PersistableMember(Name = "CustomNamedProperty")]
public string Property { get; set; }
+ [PersistableAttribute(Name = "CustomNamedAttribute")]
+ public string Attribute { get; set; }
+
public ItemWithCustomName() { }
}
@@ -1281,12 +1284,16 @@ public void Deserialize_EnumArray_RetainsValues()
public void Serialize_ItemWithCustomName_UsesCustomName()
{
GameSerializer serializer = new GameSerializer(typeof(ItemWithCustomName));
- ItemWithCustomName item = new ItemWithCustomName { Property = "TestValue" };
+ ItemWithCustomName item = new ItemWithCustomName
+ {
+ Property = "TestValue",
+ Attribute = "TestAttr",
+ };
string serializedXml = SerializeToString(serializer, item);
string expectedXml =
@"
-
+
TestValue
";
@@ -1303,7 +1310,7 @@ public void Deserialize_ItemWithCustomName_UsesCustomName()
GameSerializer serializer = new GameSerializer(typeof(ItemWithCustomName));
string xmlInput =
@"
-
+
TestValue
";
@@ -1317,6 +1324,11 @@ public void Deserialize_ItemWithCustomName_UsesCustomName()
deserialized.Property,
"Deserialized value should match the input for the custom-named property."
);
+ Assert.AreEqual(
+ "TestAttr",
+ deserialized.Attribute,
+ "Deserialized value should match the input for the custom-named attribute."
+ );
}
[Test]