Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members  

TRuleAtom Class Reference

The base class for all rule atoms. More...

#include <ruleatom.h>

Inheritance diagram for TRuleAtom:

Inheritance graph
[legend]
Collaboration diagram for TRuleAtom:

Collaboration graph
[legend]
List of all members.

Public Methods

virtual ~TRuleAtom ()
void addInfo (const string &identifier, TLinkable *obj)
void addInfo (const string &identifier, double data)
void addInfo (const string &identifier, int2 data)
virtual bool checkDependencies (list< TRuleSystem::tError > *errors) const=0
virtual bool execute ()=0
double getInfoDouble (const string &identifier) const
int2 getInfoInt2 (const string &identifier) const
TLinkablegetInfoObject (const string &identifier) const
const TDataPackageListgetResults () const
virtual bool validateInfo ()=0

Protected Methods

 TRuleAtom ()

Protected Attributes

TDataPackageListfInfo
map< string, TLinkable *> fInfoObj
TDataPackageListfResults

Detailed Description

The base class for all rule atoms.

Author:
Marco Krohn <marco.krohn@gmx.de>
Version:
Id:
ruleatom.h,v 1.7 2002/06/07 00:36:42 mkrohn5 Exp
See also:
TRuleSystem
FIXME: TRuleAtoms are now created with the prototype pattern. this means that you can't directly create prototypes, but instead have

FIXME: some other stuff is outdated as well.

This class is the base class for all rule atoms. The general idea is, that all game rules are seperated into small pieces and implemented as so called "rule atoms". The good thing is that you can write very different implementations for the same rule name, e.g. you are allowed to provide a "stars!", "basic" and a "StellarLegacy" implementation for the rule "fleet movement". The "basic" rule could be implemented in a way that moving fleets are not stopped by any obstacles (such as minefields), while the "StellarLegacy" rule automatically reduces the fleets speed in a gas cloud etc.

Under certain restrictions it is also possible to mix rulesets! So during the game setup you might want to use rules for economy and population growth which take a lot of things into account, and use very simple rules from the "basic" ruleset for moving and building fleets.

Using Rule Atoms

For example we need to move a fleet around, and need the rule which is responsible for that. Well, since code tells more than thousand words ;-)

 TRuleAtom* ra = TheGame->getRuleSystem()->fetchRule("fleet_movement");
 ra->addInfo("fleet", a_fleet);
 ra->execute( ra );
 delete ra;
 

The first thing you have to do is fetching the right rule from the rule system (since as noted above there might exist different implementations for a rule). After that you almost always have to add information before the rule can be executed. Which information is needed for executing the rule should be written down in the base class description of the rule, e.g. in this case: TRAFleetMovement. Eventually the rule can be executed: the rule will calculate everything and also modify all participating objects if neccessary (for example applying damage to a fleet, reducing the number of mines in a mine field, setting new fleet pos, ...). The last thing one has to do is deleting the rule atom, and that's it. Also note that a rule can return information about the result by using the TDataPackageList mechanism.

Writing Rule Atoms

First we should make clear again that we are talking of one rule, but that it is possible to write different implementations of that rules for different rule sets (e.g. basic, stars!, SL). For example the rule "fleet_movement" exists in at least three "flavours": TFleetMovementBasic, TFleetMovementStars and TFleetMovementSL. These three implementations of the rule are all derived from the base class TFleetMovement (which is almost only there for documentation purposes and the method provideMissingInfo).

O.k. now we assume that you want to write an implementation of a rule atom. A real world example looks like:

  1  bool TRAFleetEngineFailureStars :: execute()
  2  {
  3    if ( TRAFleetEngineFailure::provideMissingInfo() == false )  return false;
  4
  5    fResults->set( "engine_failure", false );
  6    TFleet* fleet = dynamic_cast<TFleet*>( getInfo("fleet") );
  7    if ( fleet->getSpeed() >= 50.0 ) {
  8      TRace* creator = fleet->getCreator();
  9      if ( TRandom::getDouble() < creator->getFleetEngineFailure() ) {
 10        fResults->set( "engine_failure", true );
 11      }
 12    }
 13
 14    return true;
 15  }
 

The first thing an execute method should always do is calling (line 3) the provideMissingInfo() (see more below). Lets not talk about l. 5 here, which is explained in more detail in the section about "return" values. The interesting thing starts in l.6: as we find out in the documentation there exists a piece of information called "fleet" and this fleet is indeed a TFleet object. This fleet is converted to the correct time and used to find out the current speed of this fleet and also some other things.

Note that we refer to the base class of TFleet and not to a special implementation like TFleetStars. While the latter is allowed we have to introduce dependencies from this special module which is explained below. In this case we don't have any dependencies on special modules.

Actually this is all you need to know to write an implementation of a rule. Always check which information (as in l. 6) you can use by reading the documentation of the base class (in this case TFleetEngineFailure). And vice versa document the information which is needed to execute the rule in the base class.

Providing Additional Information

This method is implemented in the base class and tries to find out additional information if needed. It also checks the existence of all neccessary information and therefore if called by execute prevents execution of the rule atom if not all information is gathered. (That is why execute always should call this method).

FIXME rule of thumb: anything which needs more than one simple call (like fleet->getOwner) can be provide by this method. should also check if obj!=0

Checking Dependencies

Some rule atoms might require a special functionality of a module. For example if we have a (part of a) rule like: "if a race has the LRT (lesser racial trait) X then item Y is not allowed for that race" we certainly need the functionality that the "race" module offers a method like "hasLRT". The general interface TRace does not provide this method, but the "stars!" implementation of TRace does.

Now to ensure that rule atoms and modules fit nicely together each rule atom is "asked" in the beginning if the current set of modules is o.k. or not. All you have to do is overwriting the method "checkDependencies(...)", like in the following example code:

 bool TRAItemAccessStars :: checkDependencies( list<TRuleSystem::tError>* errors ) const
 {
   if ( rules->getModule(TRuleSystem::RACE) == TRuleSystem::STARS ) {
     return true;
   }
   errors->push_back( TRuleSystem::tError("item_access", TRuleSystem::RACE) );
   return false;
 }
 

This code tests (for the stars! rule set of the rule atom "item_access") if the race module is from the stars! rule set. Of course if the SL rule set also offers the needed "hasLRT()" functionality one can extent the condition and also allow TRuleSystem::SL. If the dependency check fails the method should add an error message to the list of errors, since this list is needed by a client to give some feedback to the user what went wrong. The information you have to provide is: 1. the name of the rule atom, and 2. with which module the conflict exists. It is explicitly allowed (actually it is desired) that more than one error message is added to the list if more conflicts exist.

Note: if you only use methods which are provided by the general interface of a module (this means for example the interface of TRace, but not the interface of TRaceSL) than you don't need to care about dependencies.

Returning Information

Sometimes it is neccessary for the ruleatom to "return" some results to the caller of the ruleatom. Thereto the ruleatom has a TDataPackageList, which stores the results. The caller of a ruleatom is then able to read this list, but of course he has to do it before deleting the rule atom. Note that only some datatypes are allowed for TDataPackageList--namely in this case these are: bool, double, int2, int4, string and TPoint which should be sufficent for most cases.

Adding results and getting them is very easy--on the ruleatom side you write:

   fResults->set( "engine_failure", false );
   fResults->set( "test_string", "some useless string" );
 

to store the data (nothing else is needed) and on the other side

   ra->getResults()->getBool("engine_failure");
   ra->getResults()->getString("test_string")
 

to get the data (where ra is of course a TRuleAtom). Note that it is o.k. to change the results on the rule atom side, whenever you want, for example the following code is completly o.k.:

   fResults->set( "engine_failure", false );
   fResults->set( "engine_failure", true );               // overwrites the old result
 

You should document the possible results of the rule atom in the base class, e.g. in TRAFleetEngineFailure.

Miscellaneous


Constructor & Destructor Documentation

virtual TRuleAtom::~TRuleAtom   [virtual]
 

Deletes the result list.

TRuleAtom::TRuleAtom   [protected]
 

Initializes member data values.


Member Function Documentation

void TRuleAtom::addInfo const string &    identifier,
TLinkable   obj
 

Adds a piece of information to the rule atom. It is up to you to ensure that you do not use two times the same identifier with a different data type.

Parameters:
info  The name for the information e.g. "observer" for a race which watches a situation.
obj  The "real" information, e.g. the race from the example above.

virtual bool TRuleAtom::checkDependencies list< TRuleSystem::tError > *    errors const [pure virtual]
 

FIXME

virtual bool TRuleAtom::execute   [pure virtual]
 

After sending all neccessary information to the rule atom (see addInfo) the rule can be executed.

Returns:
true execution was successful, otherwise false (e.g. if not enought information was available).

Reimplemented in TRAItemAccess, TRAItemAccessBasic, TRAItemAccessStars, and TRAItemAccessSL.

int2 TRuleAtom::getInfoInt2 const string &    identifier const
 

Note that this command will

Parameters:
identifier  The name for the information.
Returns:
The
See also:
addInfo

TLinkable* TRuleAtom::getInfoObject const string &    identifier const
 

Parameters:
identifier  The name for the information.
Returns:
The object which belongs to the identifier or 0 if the identifier does not exist.
See also:
addInfo

const TDataPackageList* TRuleAtom::getResults   const
 

Returns:
A list with "return" values, see fResults.

virtual bool TRuleAtom::validateInfo   [pure virtual]
 

Checks if the provided information is o.k. (e.g. no null links etc.).


Member Data Documentation

TDataPackageList* TRuleAtom::fInfo [protected]
 

This list contains the information added by the creator of the rule atom.

map<string,TLinkable*> TRuleAtom::fInfoObj [protected]
 

This list contains some pointers. The reason for this it that storing pointers in a data package list is not possible and that storing links is very slow when loading back the links (since only the ID is saved and a load calls TLinkUpdate::requestUpdate which is very slow).

TDataPackageList* TRuleAtom::fResults [protected]
 

A list of data packages. This list is used to "return" values to the executer of a rule atom.


The documentation for this class was generated from the following file:
Generated on Thu Jun 20 18:13:30 2002 for Stellar Legacy by doxygen1.2.13.1 written by Dimitri van Heesch, © 1997-2001