I been working on a game in Unity that reads its story (Actions, scene descriptions, NPC's, ect...) from an XML and have been wondering there was a better way to store actions based on player data such as item in an inventory or attribute.
An example would be the player having a light source greater then 5 on hand while entering a dark room.
<scene>
<description>You enter a dark room </description>
<sceneReq>
<text>and do not have enough light to see it well.</text>
<action trigger="player.lightLevel > 4" sceneIndex="5"/>
</sceneReq>
</scene>
How would I be able to act on the above logic in C# without hard coding the actions and load dynamically from the XML? Also how should I handle logic in XML if it requires multiple actions or parameters?
For example having a crowbar and the strength greater then 15 to open a door.
<scene>
<description>You see a stuck door in front of you.</description>
<sceneReq>
<text>Your effort to remove the door has failed.</text>
<and>
<action trigger="player.item == 1" sceneIndex="10"/>
<action trigger="player.strLevel > 15" sceneIndex="10"/>
</and>
</sceneReq>
</scene>
Because I am rather new to parsing XML I can't think of a way to break the logic out of the XML and use it in code dynamically. I looked over https://stackoverflow.com/questions/372915/game-logic-in-xml-files and really did not seem to answer my question on how to solve it in C#.
Edit: Spent more time on this and with Blue's answer I was able to come up with a viable solution.
Sample XML
<scene>
<description>You enter a dark room </description>
<condition type="itemEquip" itemType="Flashlight">
<message>and do you do not have a light source.</message>
</condition>
<button text="Use flashlight" sceneIndex="2">
<condition type="itemHave" itemType="Flashlight">
<message> and you forgot your flashlight at home.</message>
</condition>
</button>
<button text="Go back" sceneIndex="0"/>
</scene>
Made it so that you can have a condition for a button and for the scene as separate checks, that way buttons can be also dynamic based on the condition class.
The condition class:
public enum ConditionType
{
itemLevelReq,
itemEquip,
itemHas,
playerLevel,
playerAttribute,
baseDefault
}
abstract class Condition
{
public ConditionType type;
public string failMessage = "Condition not met";
public abstract bool IsThisConditionMet();
}
class BaseCondition : Condition
{
ConditionType type = ConditionType.baseDefault;
string failMessage = "Condition not met";
override public bool IsThisConditionMet()
{
return false;
}
}
//example concrete condition
class ItemHasCondition : Condition
{
ConditionType type = ConditionType.itemHas;
string itemType;
int levelReq;
public ItemHasCondition(String item, string fail)
{
this.itemType = item;
this.failMessage = fail;
}
override public bool IsThisConditionMet()
{
if(Program.testPlayer.items.Contains(this.itemType)){
return true;
} else {
return false;
}
}
}
Then here is the parser for the XML(Simplified)
private static void readThisXML()
{
XDocument doc = XDocument.Load("Scenes.xml");
foreach (XElement root in doc.Root.Elements("scene"))
{
SceneStruct scene = new SceneStruct();
List<ButtonStruct> buttons = new List<ButtonStruct>();
List<Condition> conditions = new List<Condition>();
foreach (XElement element in root.Elements("button"))
{
ButtonStruct btn = new ButtonStruct();
List<int> btnIndex = new List<int>();
List<Condition> btnConditions = new List<Condition>();
btn.text = (string)element.Attribute("text");
//Maybe different indexes based on conditions?
btnIndex.Add((int)element.Attribute("sceneIndex"));
btn.btnIndex = btnIndex;
foreach (XElement elementC in element.Elements("condition"))
{
if (elementC.Attribute("type").Value.Equals("itemEquip"))
{
Condition con = new ItemHasCondition((string)elementC.Attribute("itemType"), (string)elementC.Element("message").Value);
btnConditions.Add(con);
}
}
btn.conditions = btnConditions;
buttons.Add(btn);
}
dwStruct.buttons = buttons;
foreach (XElement element in root.Elements("description"))
{
scene.description = (string)element.Value;
}
foreach (XElement element in root.Elements("condition"))
{
if (element.Attribute("type").Value.Equals("itemEquip"))
{
Condition con = new ItemHasCondition((string)element.Attribute("itemType"), (string)element.Element("message").Value);
conditions.Add(con);
}
}
scene.conditions = conditions;
sceneList.Add(scene);
}
}
Hopefully this helps anyone who happens to stumble over this problem like I did. You might not need the enum but it could be handy for later.