
Overview of how the AI and craft classes work.
The GeoData class is the root node. Every frame it's Update() function is
called, with the amount of time the game's state is to be advanced by.
The GeoData delegates the the Update() to the items it contains.
Currently these are a list of HumanBases, and the Alien "Overmind".
(And these subsequently delegate to their contained items, and so on.)
Conceptually, the Alien AI is made up of 3 layers.
- Overmind
- InvasionTask
- Craft
Overmind
This layer will be responsible for the big picture planning. That is
deciding on the sorts of missions to undertake. E.g. deciding to try
and harvest a country, Undertake offensive missions against X-Corp
because the humans are getting annoying, Creating Bases, etc.
The Overmind owns a number of InvasionTasks
InvasionTask
Is responsible for a mission, or set of missions, that may involve one or more UFOs.
It's job is synchronization of behaviour between multiple UFOs.
E.g.
In X-COM, a "retaliation task", is made up of a number of UFO missions,
a number of scouting missions to find the base. Followed by a series of
attacks on the base which attempt to destroy it. (If the scouting
missions fail, the attack missions won't be launched.)
Likewise, a
"research task" in X-COM is made up of a number of separate UFO
research missions, with days or weeks separating each of the missions.
The InvasionTask will have a number of derived classes, one for each different type of mission.
The InvasionTask owns the UFOs that are currently "live" on the Geoscape
Craft
Each
craft has its own "autonomous" AI. The AI is implemented as a 2 level
Hierachial Finite State Machine (HFSM). The HFSM is built by the
classes Mission MissionState, and their derived classes.
How this works is a bit hard to explain. It's probably easier to give an example.
Consider the case of an intercept mission. In the simplest form, the mission can be thought of as:- Aircraft waits in a hanger (being refuelled, repaired, reloaded etc.)
- Aircraft is launched, and tries to intercept a UFO.
- When the aircraft reaches the UFO, the UFO is shot down
- The aircraft flies back to its base.
- Aircraft waits in a hanger (being refuelled, repaired, reloaded etc.)
From this, you can see that the aircraft has 3 "states" during the mission:- InBase
- Intercepting
- ReturningToBase
In
each of these states, the craft's behaviour is different, and the craft
transitions from state to state when an "event" occurs. Also note,
other events can occur and cause other state transitions. For example,
if the aircraft runs short on fuel while in the "Intercepting" state,
it must return to base. However, if the craft is in the
"ReturningToBase" state when fuel is low, then the event can be ignored.
Thus,
the update() method of the "MissionState" classes implements the
behaviour of each state. The MissionState also implements the
ICraftEvents interface, which provides a virtual function that is
called for each "event" that may generate a state transition. Thus, if
a state needs to implement behaviour in response to an event, the
derived class just needs to override the appropriate virtual function.
Thus,
we could build the missions by deriving a class from MissionState for
each state of each mission type. (Which might result in a lot of very
similar mission states and replicated code if some mission states had
very similar behaviour.)
To reduce this, the "Mission" class exists:
- It allows us to reuse a mission state in multiple missions (e.g.
MoveToPostionState can be used in a Research mission, where a UFO needs
to move between random locations on the globe, and in intercept
missions, when the craft is returning to base.)
- It allows us to
put code that may be common to all states in one place. E.g. If
OnCraftDestroyed() does the same thing for every state in a mission,
then we can put it's behaviour in OnCraftDestroyed() in the Mission,
rather than each MissionState.