Further refinement of new ModularizedTacticalAI architecture; added plans for Crows

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@6017 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
feynman
2013-04-18 20:02:54 +00:00
parent 52cb4ff424
commit 26383d83a8
22 changed files with 451 additions and 490 deletions
@@ -6,6 +6,8 @@
#ifndef ABSTRACT_PLAN_FACTORY_H_
#define ABSTRACT_PLAN_FACTORY_H_
#include <iosfwd>
class SOLDIERTYPE;
namespace AI
@@ -13,16 +15,48 @@ namespace AI
namespace tactical
{
/**@class AIInputData
* @brief Wrapper class around the environmental data required to build an AI plan
* @brief Wrapper class around the environmental data required to build or update an AI plan
*
* At the moment, simply a wrapper around SOLDIERTYPE. Future versions might require more data; without the
* wrapper, *every* Concrete Factory ever created would need to be changed in this event. With the wrapper, no
* change is required at all.
* The data contained in objects of this class are currently collected in HanldeNoise() and ManSeesMan(), both
* defined in Tactical/opplist.cpp
* Future versions might require more or different data; without the wrapper, *every* Concrete Factory ever
* created would need to be changed in this event. With the wrapper, no change is required at all.
*/
struct AIInputData
class AIInputData
{
SOLDIERTYPE* npc_to_plan_for_;
private:
enum event_type {no_event, auditive_event, visual_event};
event_type event_type_;
/// Only visual: the opponent seen by the updated NPC
SOLDIERTYPE* opponent_;
/// Only auditive: ID of the noise maker; (at least for NPC-generated sounds) ID of NPC
int noise_maker_;
/// Location of event
int grid_no_;
int level_;
/// Only auditive: volume of heard sound (at the hearing NPC, not the source)
int volume_;
int type_;
int caller1_;
int caller2_;
public:
/// Empty types are used for overload resolution in order to generate different types of events with
/// simple constructor calls.
struct Auditive{ };
struct Visual{ };
/// Constructor for noise events
AIInputData(Auditive, int noise_maker, int grid_no, int level, int volume, int type);
/// Constructor for sight events
AIInputData(Visual, SOLDIERTYPE* opponent, int grid_no, int level, int caller1, int caller2);
/// Default constructor, called when no event occurred
AIInputData();
bool is_auditive_event() const {return event_type_ == auditive_event;}
bool is_visual_event() const {return event_type_ == visual_event;}
/// Allow the output function used for debugging to access the members directly
friend std::ostream& operator<<(std::ostream& os, const AIInputData& i);
};
std::ostream& operator<<(std::ostream& os, const AIInputData& i);
class Plan;
/**@class AbstractPlanFactory
* @brief Abstract Factory. Base class for all plan factories.
@@ -37,6 +71,7 @@ namespace AI
class AbstractPlanFactory
{
private:
/// Flag set iff delayed initialization was already performed
bool initialize_called_;
public:
AbstractPlanFactory() : initialize_called_(false) { }
@@ -54,21 +89,25 @@ namespace AI
* The subclasses (i.e., Concrete Factories) implpmenting this routine contain
* the decision-making AI core.
*
* @param input A pointer to a structure containing all required input data in order for the AI
* algorithms to perform their task. At the moment, simply a wrapper around SOLDIERTYPE. Future versions
* @param npc The NPC the plan is for
* @param input A reference to a structure containing all required input data in order for the AI
* algorithms to perform their task. At the moment, empty. Future versions
* might require other datatypes; without the wrapper, *every* Concrete Factory ever created would need
* to be changed in this event.
* @return A Plan object tree representing the produced strategy
*/
virtual Plan* create_plan(const AIInputData& input) = 0;
/**@brief Abstract Plan object hierarchy update function, called in order to update an already created
* plan
virtual Plan* create_plan(SOLDIERTYPE* npc, const AIInputData& input) = 0;
/**@brief Abstract Plan object hierarchy update function, called in order to update an already created plan
*
* @param input A pointer to a structure containing all required input data in order for the AI
* algorithms to perform their task
* @param plan_to_change The Plan object hierarchy that is to be updated
* If no plan exists, it is created; this should not be the general case, but might happen for
* 'interrupted' NPCs that haven't had the chance to plan yet.
*
* @param npc A pointer to the npc owning the ai_masterplan_ to be changed.
* @param input A reference to a structure containing all required input data in order for the AI
* @post npc->ai_masterplan_ is not null
*/
virtual void update_plan(const AIInputData& input, Plan* plan_to_change) = 0;
virtual void update_plan(SOLDIERTYPE* npc, const AIInputData& input) = 0;
};
}
}
+7 -5
View File
@@ -13,17 +13,19 @@ namespace AI
namespace tactical
{
/**@class LegacyAIPlan
* @brief Component/Concrete Product. Generates a Plan that executes the AI as it was before modularization.
* @brief Component/Concrete Product. Wrapper/Re-Write of DecideAction()
*
* A simple encapsulation of the functions TurnBasedHandleNPCAI() and RTHandleAI(), defined in
* TacticalAI/AIMain.cpp and TacticalAI/Realtime.cpp, respectively. The AI handles decisions for all kinds of
* NPCs (enemies, zombies, civilians, ...)
* This plan began as a simple forwarding object for DecideAction(), and will, in the course of the AI redesign,
* be split into elementary NPC actions. It is as such only a intermediate product used to bring structure where
* the is currently none.
*/
class LegacyAIPlan: public Plan
{
private:
public:
virtual void execute(bool turn_based, PlanInputData& manipulated_object);
LegacyAIPlan(SOLDIERTYPE* npc);
virtual void execute(PlanInputData& environment);
virtual bool done() const {return false;}
};
}
}
@@ -14,16 +14,21 @@ namespace AI
namespace tactical
{
/**@class LegacyAIPlanFactory
* @brief Concrete Factory. Generates the LegacyAIPlan, making NPCs do exactly what it did before this AI
* modularization/re-write.
* @brief Concrete Factory. Generates the appropriate LegacyAIPlan, making NPCs do exactly what it did before
* this AI modularization/re-write.
*
* Currently, the factory handles the "outermost" layer of selection statements by instantiating plans that will
* call CreatureDecideAction(), ZombieDecideAction() or DecideAction(), respectively. This is not the intended
* use of factories, but for the time of the AI redesign a necessity nonetheless.
*/
class LegacyAIPlanFactory : public AbstractPlanFactory
{
private:
public:
/// The name is required for registration in the PlanFactoryLibrary
static std::string get_name() {return "LegacyAIPlanFactory";}
virtual Plan* create_plan(const AIInputData& input);
virtual void update_plan(const AIInputData& input, Plan* plan_to_change);
virtual Plan* create_plan(SOLDIERTYPE* npc, const AIInputData& input);
virtual void update_plan(SOLDIERTYPE* npc, const AIInputData& input);
};
}
}
+3 -1
View File
@@ -32,7 +32,9 @@ namespace AI
{
private:
public:
virtual void execute(bool turn_based, PlanInputData& manipulated_object);
NullPlan(SOLDIERTYPE* npc);
virtual void execute(PlanInputData& environment);
bool done() const {return false;}
};
}
}
@@ -36,8 +36,8 @@ namespace AI
private:
public:
static std::string get_name() {return "NullPlanFactory";}
virtual Plan* create_plan(const AIInputData& input);
virtual void update_plan(const AIInputData& input, Plan* plan_to_change);
virtual Plan* create_plan(SOLDIERTYPE* npc, const AIInputData& input);
virtual void update_plan(SOLDIERTYPE* npc, const AIInputData& input);
};
}
}
+53 -13
View File
@@ -6,44 +6,84 @@
#ifndef PLAN_H_
#define PLAN_H_
#include <iostream>
class SOLDIERTYPE;
struct TacticalStatusType;
namespace AI
{
namespace tactical
{
/**@class PlanInputData
* @brief Wrapper class around the entity manipulated through plan execution
* @brief Wrapper class for environmental data required for plan execution
*
* At the moment, simply a wrapper around SOLDIERTYPE. Future versions might require other datatypes; without
* the wrapper, *every* Plan subclass ever created would need to be changed in this event. With the wrapper, no
* change is required at all.
* Future versions might require other data types; without the wrapper, *every* Plan subclass ever created would
* need to be changed in this event. With the wrapper, no change is required at all (see also AIInputData).
*/
struct PlanInputData
class PlanInputData
{
SOLDIERTYPE* controlled_npc_;
private:
/// True iff we are in turn based mode
bool turn_based_;
/// Reference to the global variable gTacticalStatus (we want to keep this place tidy and don't use global variables here)
const TacticalStatusType& tactical_status_;
/// left undefined to prevent copying
PlanInputData(const TacticalStatusType&);
/// left undefined to prevent assignment
PlanInputData& operator=(const TacticalStatusType&);
public:
/// Trivial member initialization
PlanInputData(bool turn_based, const TacticalStatusType& tactical_status);
/** @name Accessor functions */ /*@{*/
/// Return a reference to a global "tactical status" struct, formerly known as gTacticalStatus
const TacticalStatusType& get_tactical_status() const { return tactical_status_; }
bool turn_based() const { return turn_based_; }
/*@}*/
};
/**@class Plan
* @brief Composite/Abstract Product. Base class for all plan compositions and components.
*
* The Plan class provides a common interface for both plan compositions and plan components.
* The Plan class provides a common interface for both plan compositions and plan components. The composite
* design pattern chosen for plan representation allows objects inheriting from this class to form trees,
* allowing a clean and easy representation of sub- and super-plans
*/
class Plan
{
private:
/// A pointer to the NPC who owns this plan.
SOLDIERTYPE* npc_;
protected:
/// Return a pointer to the controlled NPC
SOLDIERTYPE* get_npc() {return npc_;}
/// Return a pointer to the controlled NPC
const SOLDIERTYPE* get_npc() const {return npc_;}
public:
/** @brief Plan execution is the encapsulation of an action sequence, making up higher-level building blocks
Plan(SOLDIERTYPE* npc);
/// A virtual destructor is essential here, destruction will be handled through this interface.
virtual ~Plan() { };
/** @brief Plan execution is the encapsulation of an "action sequence", which can be used to make up
* higher-level building blocks
*
* Each plan objects represents a high-level action, implemented by means of lower-level actions. These
* lower level actions can be other Plans subtypes; at some point, a Plan object has no further
* sub-plans, i.e., the objet is no composition, but a component, forming a leaf in the Plan hierarchy.
* The components' lower level actions are formed by the fundamental npc actions made available outside
* the AI framework.
* The components' lower level actions are formed by the fundamental NPC actions which must be made
* available outside the AI framework.
*
* @param turn_based true if turn-based mode is active, false for real-time mode
* @param manipulated_object Encapsulation of around object(s) a plan may manipulate.
* @param environment Encapsulation of environmental data required for plan execution
*/
virtual void execute(bool turn_based, PlanInputData& manipulated_object) = 0;
virtual void execute(PlanInputData& environment) = 0;
/** Determine if the plan is finished.
*
* The semantics are not "this plan *can* be aborted" - every plan must anticipate that - but instead
* "this plan cannot be continued"; for example, a movement plan is finished once the destination is
* reached, as no further movement can be carried out by the plan.
*@return true iff the plan can no longer be executed (or execution will always lead to AI_ACTION_NONE)
*/
virtual bool done() const = 0;
};
}
}
@@ -10,6 +10,8 @@
#include <map>
#include <deque>
class SOLDIERTYPE;
namespace AI
{
namespace tactical
@@ -17,23 +19,29 @@ namespace AI
// forward declarations
class AbstractPlanFactory;
class Plan;
struct AIInputData;
class AIInputData;
/**@class PlanFactoryLibrary
* @brief Singleton. A "library" containing all available PlanFactories, accessible via the strings returned by
* their get_name() function.
*
* Used as one "entry point" for the new AI system within the legacy code (the other being the 'execute()' methods of Plan)
* Handles the mapping of a SOLDIERTYPE's bAIIndex to a concrete plan factory instantiation.
*/
class PlanFactoryLibrary
{
private:
/// Only instance, required for singleton pattern implementation
static PlanFactoryLibrary* instance_;
/// A mapping of factory names to pointers of said factories
std::map<std::string, AbstractPlanFactory*> registred_factories_;
/// A mapping of bAIIndex to factory pointers
std::deque<AbstractPlanFactory*> ai_index_to_factory_mapping_;
PlanFactoryLibrary();
public:
static PlanFactoryLibrary* instance();
Plan* create_plan(size_t index, AIInputData& input) const;
Plan* create_plan(size_t index, SOLDIERTYPE* npc, const AIInputData& input) const;
void update_plan(size_t intdex, SOLDIERTYPE* npc, const AIInputData& input) const;
};
}
}