0
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ Hey! I cleared up the title to make it more specific to your situation, I hope I got it right. (I also removed the 'tags' from the title, which are considered redundants as tags are always close-by). \$\endgroup\$ – Alexandre Vaillancourt Mar 19 at 18:44
  • \$\begingroup\$ Are you looking for a polymorphic way to pass event arguments? That's a problem without a solution anyone really likes. You can always cast up using "is" and "as" operators in c#. For example an empty base class EventArgs and then for say, disaster event have DisasterEventArgs. Alternatively, you can give the event access to the entire game state, and have the event itself modify the system. This however involves exposing tons of public fields, or using some design patterns. \$\endgroup\$ – gjh33 Mar 20 at 13:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.