Skip to content

How to write an event

Christopher Carney edited this page Jul 20, 2016 · 31 revisions

How to write an event

Events are separated broadly into two categories KNOWN and UNKNOWN. Known events are events that the user will be able to see and anticipate on their calendar. Unknown events are invisible to the user but are still spawned when the calendar is first initialized (at the start of the game / year) not "on the fly" as each day passes.

Additionally, all events follow this function signature

public static Outcome Event(DataManager, Requirements)

  • DataManager holds all information about the league, player, and current game session.

  • Requirements holds the different objects the user will need to send in, for instance a child to participate in an event or a sum of money.

  • Outcome holds a status integer and outcome strings which will be displayed to the user upon completion of the Event

KNOWN EVENTS:

In this example we'll be writing an event in which a random grandchild is going on a field trip with a parent chaperone. Additionally, you'll need to provide funds to your grandchild so they can bring home a souvenir.

XML WRITING

First step in writing a known event is to layout the basic design in the xml file: Resources\events.xml The top level tag in this document is <events></events>, to add an event use the tag directly beneath like so (remember to wrap ALL your attributes (name, id, type, etc) in double quotes ""):

<event type="1" id="5" name="Grandkids go on a field trip" description="{0} is going on a field trip! Additionally the school has asked for more chaperones so please provide one, {1}!" priority="2" req_children="11" req_parent="01" req_grandpa="00" req_money="01" req_accept="00" month="3" day="0" qualification="IS_RESPONSIBLE" age="6-15"/>

Let's break this event down by each "attribute" that is required:

  • type: 0 for unknown, 1 for known events, 2 for reserved system events

  • id: the unique numerical id for which to represent your event. It is very important that this is unique. It is recommended you work in your own space this way two people working on events and committing them won't have merge conflicts. To check how we currently distribute the ID numbers check the bottom of this page.

  • name: The name of the event that will be displayed to the user

  • description: A brief description of the event and what the user will need to provide. Note {0} denotes a empty space that can be filled in later. Each different name requires a new {#}. As shown, the user's name would go in {1}

  • priority: A number which describes how important the current event is. 0 is lowest and 2 is the highest. An event with a priority of 0 will be passed over silently with output appearing only in the mail panel. An event with priority of 1 will "pause" the current simulation to prompt the user for input before continuing. An event with priority 2 will stop the simulation completely. Presumably this is only for major events

  • req_children, req_parent, req_grandpa, req_money, req_accept: This is coded binary for the required inputs in our even. The "first" bit (bit on the right) represents if it is required for the event. The "second" (left) bit says if it is random or not. Please note 1 denotes true and 0 denotes false. For our event the user needs to provide a parent from their team and also an amount of money hence we have 01. Now the child going on this field trip will be randomly selected by the computer. Hence we have 11 coded for them.

  • month and day: additionally programmers can specify the specific month and day the event occur (please remember we only have 28 day month currently). Moreover, the DAY field is OPTIONAL. Leaving this out or setting it to 0 will result being "seasonal" and a random day during the specified month will be chosen for the event.

  • qualification (optional): we now have a basic qualification system in place. These are simple attributes (denoted by a unique string, int pair) granted to characters to add more flavor. For example we may have an event where a child in your family needs the ON_FOOTBALL_TEAM qualification to complete. The Event system is robust enough that it can silently execute this event and only display output if someone in your family qualifies (and the player is none the wiser). On this example having a qualification doesn't quite make sense but we include it for completeness. More on a qualified event at the end of this document.

  • age (optional): this is a range of ages, in our case 6 to 15, that a child needs to be in order to be eligible for this event. In this particular context it means that the system will only choose a child in this age range (inclusive of the endpoints). Please note this age range only applies to the child chosen for the event (not parents or grandparents).

PROGRAMMING THE FUNCTION

Now we can begin programming the function which will interact with data. Open EventManager.cs and scroll to the bottom. You will see functions of the form "Event#". The # corresponds to the event id you gave in your xml, so find the numerical correct place to put your event and use the function signature: public static Outcome Event5(DataManager manager, Requirement requirements) { }

By the time this function is called the Requirement object will already be initialized with our needed Parent and money so we don't need to worry about them and can assume they have already been initialized. You can access them using properties requirements.Parent and requirements.Money. Additionally, because this event chose a random child requirements.Child will also be initialized. It is up to the developer to know what they need to get from the Requirements object.

DataManager is a complex datastructure with literally every piece of data in the game. Check out DataManager.cs for the various properties you can work with.

Now lets write the body of our function. If our parents intelligence and popularity have a sum of greater than 110 and our child's popularity is > 50 we can say the event is a success. Additionally if your child has > $50 he can buy a kickass souvenir.

int successes = 0;
List<string> outcome = new List<string>();
Outcome returnObj = new Outcome();
if(requirements.Parent.Popularity + requirements.Parent.Intelligence > 110)
{
    requirements.Parent.Popularity += 5; 
    string outcome += outcome.Add(String.Format("{0} gained popularity ", requirements.Parent.Name));
    successes++;
}
else
{
    //some parent failure outcomes
}

if(requirements.Child.Popularity > 50 && requirements.Money > 50)
{
    requirements.Child.Popularity += 10
    successes+=2;
    //some strings for child gains
}
else if (requirements.Child.Popularity > 50)
{
    requirements.Child.Popularity += 5
    successes++;
    //some strings for child gains / loses, etc
}
//event can be as complex as you want...continuing onwards

manager.PlayerFamily.Grandpa.Money -= requirements.Money

if (successes >= 2)
{
    string outComeString = string.Join(", ", outcome);
    returnObj.Status = (int)Enums.EventOutcome.SUCCESS;
    returnObj.OutcomeDescription = String.Format("{0} had a great time at the field trip, {1}", requirements.Child.Name, outComeString)
}
else
{
    returnObj.Status = (int)Enums.EventOutcome.FAILURE;
    returnObj.OutcomeDescription = "etc, etc";
    returnObj.Mail = "Dear Mr {0} ...";
}

return returnObj;

Above is the function body, pay special attention to strings. Events don't have to be concrete pass or fail, depending on different control flows it can go many different ways and have many different varied outcomes. If you want a random integer check out Constants.RANDOM.Next(int minValue, int maxValue)

NOTE: Objects in requirements are passed by reference this means that when you change an object in the requirements (e.g. a Parent or Child stat) is is immediately reflected in the DataManager. So you don't have to worry about finding the matching Child or Parent object in the DataManager, you can just directly change it from requirements. Also know this means any changes you make will be permanent so only edit the stats if you really need to.

NOTE: The Outcome.Status field is not as important as the Outcome.OutcomeDescription field. While we may use the Outcome Status int later for more dynamic events currently only the Outcome.OutcomeDescription will be displayed to the user! So it is important to alert the user to what happened in detail. Outcome.Mail is the "brief" or "story element" kind of message that will automatically be shown / remain in the player's mailbox so they can recall what has happened.

UNKNOWN (random) EVENTS

Now we'll look at a random event. They are extremely similar to known events except for how they're declared in the xml file:

<event type="0" id="1" name="Grandpa wins the lottery" description="You won the lottery! con fucking gratys" priority="1" chance="0.2" req_children="false" req_parent="false" req_grandpa="false" req_money="false" req_accept="false"/>

Key differences:

  • Event type is 0 for "hidden/unknown"

  • chance is a new field not on a known event. The chance is a decimal of how likely this event is to happen on any given day. Here our chance is 0.2, or 20%. Please remember there are 336 days in a year. If the chance was 1% it would still occur on average about 3 times a year

  • month: Note that day is absent (that would inherently make the event known) but month is not. You can program an event to happen only on a specific month. I am working on making this a range.

Other than that it is the same as writing a known event, as long as the Event function matches the event ID and function signature you will be good to go!

RESERVED (system) EVENTS

The last type of event is a system event. It has type "2". An example of a system event is the weekly_stat_upgrade. Additionally, these events use the reserved ids 0-100

QUALIFICATION EVENTS (WORK IN PROGRESS IGNORE FOR NOW)

First step in writing a qualification event is to write down a qualification. Open up Resources\qualifications.xml and take a look. You'll want to add a new <qualification /> tag like so:

<qualification name="ON_FOOTBALL_TEAM" id="3" />

As you can see, our qualification has both a unique id and name. Make sure what you're looking for isn't already in the file.

Next in events.xml we need to add the qualification string to the event we are looking to add under a qualification attribute, e.g. qualification="ON_FOOTBALL_TEAM"

Next lets program an event that can only happen if a child can has this qualification. Children can be given qualifications by using DataManager.PlayerFamily.Children[0].AddQualification(Qualification.GetQualificationByString("ON_FOOTBALL_TEAM");

Additionally, Qualification has another method Qualification.GetQualificationString(int qual) that can be used to go the other way.

Next in our event method like above we'll begin programming

bool hasQual = false;
Child qualChar;
foreach(Child ch in dataManager.PlayerFamily.Children)
{
    foreach(Qualification qual in ch.Qualifications)
        if (qual == requirements.Qualification)
        {
            qualChar = ch;
            hasQual = true;
        }
}

if (!hasQual)
   return new Outcome((int)Enums.EventOutcome.PASS, "", "");

//do stuff with qualChar because they have the qualification
return new Outcome((int)Enums.EventOutcome.SUCCESS, "yada", "yada");

First we find someone with the qualification. If the system cannot find anyone in the current family (in this case we are looking for a child to score a touchdown) the outcome sends the Enums.EventOutcome.PASS int back to the Simulation loop. When the system sees this it will pass over the event without any output to the user, they won't even know the event could have triggered. If the loop sees any other outcome like SUCCESS or FAIL it will output the description and mail and results, etc.

CURRENT EVENT ID NAMESPACES:

These are the current event id namespaces, please use the corresponding ids as specified below.

  • 0-100: reserved (do not use)
  • 101-1000: Kevin
  • 1001-2000: Patrick
  • 2001-3000: Christoher
Clone this wiki locally