I'm trying to write an easy-to-use random event system that has certain conditions and effects in Unity. It's a simulation game that contains multiple Person, City, Country, Faction elements. I have Event, Condition and Effect classes and each Cond/Effect is derived from corresponding base classes. My code is here:
(I know it is a long code to read, but general idea is simple, which I explained below the code, along with actual question, so please stay with me ^^. The codes are there for "just in case")
Event:
public class RandomEvent
{
public static List<RandomEvent> gameEvents = new List<RandomEvent>();
public static List<RandomEvent> invokedEvents = new List<RandomEvent>();
public List<Condition> conditions;
public List<Effect> effects;
public List<EventOption> options;
public string name, header, description;
public double probability;
double defProbability;
int importance = 1; // 0: Don't notify player. 1: Show in up in reports. 2: Notify warning if unread. 3: Don't let player advance turn if not read.
[XmlIgnore] public bool isRead = false;
public RandomEvent()
{
}
public RandomEvent(string name, string header, string description, List<Condition> conditions, List<Effect> effects, List<EventOption> options, double probability, int importance = 1)
{
this.conditions = conditions;
this.effects = effects;
this.options = options;
this.name = name;
this.header = header;
this.description = description;
this.probability = probability;
defProbability = probability;
this.importance = importance;
gameEvents.Add(this);
}
public void Invoke()
{
float diceRoll;
if ((diceRoll = UnityEngine.Random.Range(0f, 1.0f)) > probability)
{
Debug.Log("Rolled " + diceRoll + " for " + name + " event and It will not invoke since It's higher than probability, " + probability);
probability *= 1.5f;
return;
}
Debug.Log("Rolled " + diceRoll + " for " + name + " event and It will invoke since It's lower than probability, " + probability);
foreach (Condition c in conditions)
{
if (c.IsTrue())
{
Debug.Log("The condition of '" + c.ToString() + "' is not true, " + name + " will not invoke.");
return;
}
}
foreach (Effect e in effects)
{
Debug.Log("Executing '" + e.ToString() + "' effect of " + name + ".");
e.Execute();
}
probability = defProbability;
invokedEvents.Add(this);
}
}
Condition:
[XmlInclude(typeof(HasCountryStat))]
public abstract class Condition
{
public Condition()
{
}
public virtual bool IsTrue()
{
return true;
}
}
public class HasCountryStat : Condition
{
public Country.ChangeableStats stat;
public float minValue;
public HasCountryStat()
{
}
public HasCountryStat(Country.ChangeableStats stat, float minValue)
{
this.stat = stat;
this.minValue = minValue;
}
public override bool IsTrue()
{
FieldInfo field = typeof(Country).GetField(stat.ToString());
float value = (float)field.GetValue(Country.Instance);
return minValue >= value;
}
public override string ToString()
{
FieldInfo field = typeof(Country).GetField(stat.ToString());
float value = (float)field.GetValue(Country.Instance);
return stat.ToString() + " value of the country must be equal or greater than " + minValue + ". (Currently " + value + ").";
}
}
Effect:
public abstract class Effect
{
public Effect()
{
}
public virtual void Execute()
{
}
}
public class SetCountryStat : Effect
{
Country.ChangeableStats stat;
float value;
public SetCountryStat()
{
}
public SetCountryStat(Country.ChangeableStats stat, float value)
{
this.stat = stat;
this.value = value;
}
public override void Execute()
{
FieldInfo field = typeof(Country).GetField(stat.ToString());
field.SetValue(Country.Instance, value);
}
public override string ToString()
{
return "Set " + stat.ToString() + " value to " + value + ".";
}
}
Here is an implementation of a new Event:
new RandomEvent(
"burocracy_chaos_event",
"Some news header.",
"Some description.",
new List<Condition>()
{
new HasCountryStat(Country.ChangeableStats.baseEducation,10)
},
new List<Effect>()
{
new SetCountryStat(Country.ChangeableStats.baseCulture,-1)
},
new List<EventOption>()
{
new EventOption("Oh well..."),
new EventOption("I will not allow this")
},
0.1
);
And finally, execution:
foreach (RandomEvent e in RandomEvent.gameEvents)
{
e.Invoke();
}
Generally, this code works just fine, excluding some horrible probability algorithm and minor bugs. When a new event is created, the game includes it "to be executed" list and attempts to invoke them regarding whether conditions and the probability are met. Problem starts when I want to execute events "for each" Person, City, Country etc... I'm not sure how to pass these arguments to condition and event classes.
For example, I want the game to check an event for every in individual alive but parameters of conditions & effect classes are constant for each event. So I need some sort of event scope similiar to ck2/u4 games. In the end, this approach does not work for me and I need a better approach to execute events. I don't know if I'm missing something obvious or overthinking, but I'm stuck here for few days I really appreciate a new idea for random events. Thanks for listening and have a nice day.