mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
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:
@@ -338,25 +338,49 @@
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\src\AbstractPlanFactory.cpp"
|
||||
>
|
||||
<\File>
|
||||
<File
|
||||
RelativePath=".\src\CrowPlan.cpp"
|
||||
>
|
||||
<\File>
|
||||
<File
|
||||
RelativePath=".\src\LegacyAIPlan.cpp"
|
||||
>
|
||||
</File>
|
||||
<\File>
|
||||
<File
|
||||
RelativePath=".\src\LegacyAIPlanFactory.cpp"
|
||||
>
|
||||
</File>
|
||||
<\File>
|
||||
<File
|
||||
RelativePath=".\src\LegacyCreaturePlan.cpp"
|
||||
>
|
||||
<\File>
|
||||
<File
|
||||
RelativePath=".\src\LegacyZombiePlan.cpp"
|
||||
>
|
||||
<\File>
|
||||
<File
|
||||
RelativePath=".\src\NullPlan.cpp"
|
||||
>
|
||||
</File>
|
||||
<\File>
|
||||
<File
|
||||
RelativePath=".\src\NullPlanFactory.cpp"
|
||||
>
|
||||
</File>
|
||||
<\File>
|
||||
<File
|
||||
RelativePath=".\src\Plan.cpp"
|
||||
>
|
||||
<\File>
|
||||
<File
|
||||
RelativePath=".\src\PlanFactoryLibrary.cpp"
|
||||
>
|
||||
<\File>
|
||||
<File
|
||||
RelativePath=".\src\PlanList.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
@@ -369,7 +393,7 @@
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\include\LegacyAIPlan.h"
|
||||
RelativePath=".\include\CrowPlan.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
@@ -377,7 +401,15 @@
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\include\NullPlan.h"
|
||||
RelativePath=".\include\LegacyAIPlan.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\include\LegacyCreaturePlan.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\include\LegacyZombiePlan.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
@@ -385,13 +417,21 @@
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\include\Plan.h"
|
||||
RelativePath=".\include\NullPlan.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\include\PlanFactoryLibrary.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\include\Plan.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\include\PlanList.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="9,00"
|
||||
Version="9.00"
|
||||
Name="ModularizedTacticalAI"
|
||||
ProjectGUID="{FF0A809E-A370-4640-A301-17B76D7A5B4E}"
|
||||
RootNamespace="TacticalAI"
|
||||
@@ -340,6 +340,14 @@
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\src\AbstractPlanFactory.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\CrowPlan.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\LegacyAIPlan.cpp"
|
||||
>
|
||||
@@ -348,6 +356,14 @@
|
||||
RelativePath=".\src\LegacyAIPlanFactory.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\LegacyCreaturePlan.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\LegacyZombiePlan.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\NullPlan.cpp"
|
||||
>
|
||||
@@ -356,10 +372,18 @@
|
||||
RelativePath=".\src\NullPlanFactory.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\Plan.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\PlanFactoryLibrary.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\src\PlanList.cpp"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
@@ -370,6 +394,10 @@
|
||||
RelativePath=".\include\AbstractPlanFactory.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\include\CrowPlan.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\include\LegacyAIPlan.h"
|
||||
>
|
||||
@@ -378,6 +406,14 @@
|
||||
RelativePath=".\include\LegacyAIPlanFactory.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\include\LegacyCreaturePlan.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\include\LegacyZombiePlan.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\include\NullPlan.h"
|
||||
>
|
||||
@@ -394,6 +430,10 @@
|
||||
RelativePath=".\include\PlanFactoryLibrary.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\include\PlanList.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
|
||||
@@ -128,20 +128,30 @@
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\AbstractPlanFactory.cpp" />
|
||||
<ClCompile Include="src\CrowPlan.cpp" />
|
||||
<ClCompile Include="src\LegacyAIPlan.cpp" />
|
||||
<ClCompile Include="src\LegacyAIPlanFactory.cpp" />
|
||||
<ClCompile Include="src\LegacyCreaturePlan.cpp" />
|
||||
<ClCompile Include="src\LegacyZombiePlan.cpp" />
|
||||
<ClCompile Include="src\NullPlan.cpp" />
|
||||
<ClCompile Include="src\NullPlanFactory.cpp" />
|
||||
<ClCompile Include="src\Plan.cpp" />
|
||||
<ClCompile Include="src\PlanFactoryLibrary.cpp" />
|
||||
<ClCompile Include="src\PlanList.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="include\AbstractPlanFactory.h" />
|
||||
<ClInclude Include="include\LegacyAIPlan.h" />
|
||||
<ClInclude Include="include\CrowPlan.h" />
|
||||
<ClInclude Include="include\LegacyAIPlanFactory.h" />
|
||||
<ClInclude Include="include\NullPlan.h" />
|
||||
<ClInclude Include="include\LegacyAIPlan.h" />
|
||||
<ClInclude Include="include\LegacyCreaturePlan.h" />
|
||||
<ClInclude Include="include\LegacyZombiePlan.h" />
|
||||
<ClInclude Include="include\NullPlanFactory.h" />
|
||||
<ClInclude Include="include\Plan.h" />
|
||||
<ClInclude Include="include\NullPlan.h" />
|
||||
<ClInclude Include="include\PlanFactoryLibrary.h" />
|
||||
<ClInclude Include="include\Plan.h" />
|
||||
<ClInclude Include="include\PlanList.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,24 +5,102 @@
|
||||
|
||||
#include "../include/LegacyAIPlan.h"
|
||||
|
||||
#include "../../TacticalAI/ai.h" // for EndAIGuysTurn
|
||||
#include "../../TacticalAI/ai.h"
|
||||
#include "../../TacticalAI/AIInternals.h" // ACTING_ON_SCHEDULE
|
||||
#include "../../TacticalAI/NPC.h" // NPCReachedDestination
|
||||
#include "../../Tactical/Animation Control.h" // defines ANIM_...
|
||||
#include "../../Tactical/Soldier Macros.h" // CREATURE_OR_BLOODCAT
|
||||
#include "../../Tactical/opplist.h" // EndMuzzleFlash
|
||||
#include "../../Tactical/Dialogue Control.h" // DialogueQueueIsEmpty
|
||||
#include "../../TileEngine/Isometric Utils.h" // defines NOWHERE
|
||||
#include "../../Utils/Debug Control.h" // LiveMessage
|
||||
#include "../../Utils/Font Control.h" // ScreenMsg about deadlock
|
||||
#include "../../Utils/Text.h" // Sniper warning
|
||||
#include "../../Utils/message.h" // ditto
|
||||
|
||||
|
||||
// Forward declarations for the two legacy functions called here
|
||||
void RTHandleAI( SOLDIERTYPE * pSoldier ); // defined in TacticalAI/Realtime.cpp
|
||||
void TurnBasedHandleNPCAI(SOLDIERTYPE *pSoldier); // defined in TacticalAI/AIMain.cpp
|
||||
|
||||
namespace AI
|
||||
{
|
||||
namespace tactical
|
||||
{
|
||||
void LegacyAIPlan::execute(bool turn_based, PlanInputData& manipulated_object)
|
||||
LegacyAIPlan::LegacyAIPlan(SOLDIERTYPE* npc)
|
||||
: Plan(npc)
|
||||
{
|
||||
if(turn_based)
|
||||
TurnBasedHandleNPCAI(manipulated_object.controlled_npc_);
|
||||
else
|
||||
RTHandleAI(manipulated_object.controlled_npc_);
|
||||
}
|
||||
|
||||
|
||||
void LegacyAIPlan::execute(PlanInputData& environment)
|
||||
{
|
||||
if(!environment.turn_based())
|
||||
{
|
||||
if ( (get_npc()->ubProfile != NO_PROFILE) && (gMercProfiles[ get_npc()->ubProfile ].ubMiscFlags3 & PROFILE_MISC_FLAG3_HANDLE_DONE_TRAVERSAL ) )
|
||||
{
|
||||
TriggerNPCWithGivenApproach( get_npc()->ubProfile, APPROACH_DONE_TRAVERSAL, FALSE );
|
||||
gMercProfiles[ get_npc()->ubProfile ].ubMiscFlags3 &= (~PROFILE_MISC_FLAG3_HANDLE_DONE_TRAVERSAL);
|
||||
get_npc()->ubQuoteActionID = 0;
|
||||
// wait a tiny bit
|
||||
get_npc()->aiData.usActionData = 100;
|
||||
get_npc()->aiData.bAction = AI_ACTION_WAIT;
|
||||
return;
|
||||
}
|
||||
if (get_npc()->bTeam == gbPlayerNum)
|
||||
{
|
||||
if (environment.get_tactical_status().fAutoBandageMode)
|
||||
{
|
||||
get_npc()->aiData.bAction = DecideAutoBandage( get_npc() );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( get_npc()->bTeam != MILITIA_TEAM )
|
||||
{
|
||||
if ( !sniperwarning && get_npc()->aiData.bOrders == SNIPER )
|
||||
{
|
||||
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_WATHCHOUTFORSNIPERS] );
|
||||
sniperwarning = TRUE;
|
||||
}
|
||||
|
||||
if (!biggunwarning && FindRocketLauncherOrCannon(get_npc()) != NO_SLOT )
|
||||
{
|
||||
biggunwarning = TRUE;
|
||||
//TODO: don't say this again after reloading a savegame
|
||||
SayQuoteFromAnyBodyInSector( QUOTE_WEARY_SLASH_SUSPUCIOUS );
|
||||
}
|
||||
}
|
||||
get_npc()->aiData.fAIFlags &= (~AI_CAUTIOUS); // turn off cautious flag
|
||||
// if status override is set, bypass RED/YELLOW and go directly to GREEN!
|
||||
if ((get_npc()->aiData.bBypassToGreen) && (get_npc()->aiData.bAlertStatus < STATUS_BLACK))
|
||||
{
|
||||
get_npc()->aiData.bAction = DecideActionGreen(get_npc());
|
||||
if ( !gfTurnBasedAI )
|
||||
{
|
||||
// reset bypass now
|
||||
get_npc()->aiData.bBypassToGreen = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (get_npc()->aiData.bAlertStatus)
|
||||
{
|
||||
case STATUS_GREEN:
|
||||
get_npc()->aiData.bAction = DecideActionGreen(get_npc());
|
||||
break;
|
||||
case STATUS_YELLOW:
|
||||
get_npc()->aiData.bAction = DecideActionYellow(get_npc());
|
||||
break;
|
||||
case STATUS_RED:
|
||||
get_npc()->aiData.bAction = DecideActionRed(get_npc(),TRUE);
|
||||
break;
|
||||
case STATUS_BLACK:
|
||||
get_npc()->aiData.bAction = DecideActionBlack(get_npc());
|
||||
break;
|
||||
}
|
||||
}
|
||||
DEBUGAIMSG("Deciding for guynum "<<(int)get_npc()->ubID<<" at gridno "<<get_npc()->sGridNo<<", APs "<<get_npc()->bActionPoints<<
|
||||
", decided action: "<<(int)get_npc()->aiData.bAction<<", data "<<(int)get_npc()->aiData.usActionData);
|
||||
}
|
||||
|
||||
} // namespace tactical
|
||||
} // namespace AI
|
||||
|
||||
|
||||
@@ -4,21 +4,49 @@
|
||||
*/
|
||||
#include "../include/LegacyAIPlanFactory.h"
|
||||
#include "../include/LegacyAIPlan.h"
|
||||
#include "../include/LegacyCreaturePlan.h"
|
||||
#include "../include/LegacyZombiePlan.h"
|
||||
#include "../include/CrowPlan.h"
|
||||
#include "../include/PlanList.h"
|
||||
|
||||
#include "../../TacticalAI/AIInternals.h" // DEBUGAIMSG
|
||||
#include "../../Tactical/Soldier Control.h" // For SOLDIERTYPE definition
|
||||
#include "../../Tactical/Animation Data.h" // For the definition of, wait for it... BLOODCAT!
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
namespace AI
|
||||
{
|
||||
namespace tactical
|
||||
{
|
||||
Plan* LegacyAIPlanFactory::create_plan(const AIInputData& input)
|
||||
Plan* LegacyAIPlanFactory::create_plan(SOLDIERTYPE* npc, const AIInputData& input)
|
||||
{
|
||||
return new LegacyAIPlan();
|
||||
DEBUGAIMSG("Planning for "<<(int)npc->ubID);
|
||||
if((npc->flags.uiStatusFlags & SOLDIER_MONSTER) || npc->ubBodyType == BLOODCAT )
|
||||
return new LegacyCreaturePlan(npc);
|
||||
if(npc->ubBodyType == CROW)
|
||||
{
|
||||
PlanList* find_supper = new PlanList(npc);
|
||||
CrowSeekCorpsePlan* seek_corpse = new CrowSeekCorpsePlan(npc);
|
||||
CrowPeckPlan* peck = new CrowPeckPlan(npc, seek_corpse->get_corpse_grid());
|
||||
find_supper->add_subplan(seek_corpse);
|
||||
find_supper->add_subplan(peck);
|
||||
return find_supper;
|
||||
}
|
||||
#ifdef ENABLE_ZOMBIES
|
||||
if(npc->IsZombie())
|
||||
return new LegacyZombiePlan(npc);
|
||||
#endif
|
||||
return new LegacyAIPlan(npc); // no special plan for other cases yet, return default legacy AI wrapper
|
||||
}
|
||||
|
||||
void LegacyAIPlanFactory::update_plan(const AIInputData& input, Plan* plan_to_change)
|
||||
|
||||
void LegacyAIPlanFactory::update_plan(SOLDIERTYPE* npc, const AIInputData& input)
|
||||
{
|
||||
// TODO: currently, everything is handled in the LegacyAIPlan. The goal is to modularize the AI, so that in
|
||||
// the end, the LegacyAIPlan is split into so many "new" AI Plan subclasses, that every case is covered.
|
||||
// Then the behavior of the old AI would be implemented here.
|
||||
DEBUGAIMSG("Update called for "<<(int)npc->ubID<<" event: "<<input);
|
||||
if(!npc->ai_masterplan_)
|
||||
npc->ai_masterplan_ = create_plan(npc, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,10 +11,15 @@ namespace AI
|
||||
{
|
||||
namespace tactical
|
||||
{
|
||||
/// Simply end the turn for the npc passed in the manipulated_object wrapper, then return.
|
||||
void NullPlan::execute(bool turn_based, PlanInputData& manipulated_object)
|
||||
NullPlan::NullPlan(SOLDIERTYPE* npc)
|
||||
: Plan(npc)
|
||||
{
|
||||
EndAIGuysTurn(manipulated_object.controlled_npc_);
|
||||
}
|
||||
|
||||
/// Simply set the action to be performed to AI_ACTION_NONE
|
||||
void NullPlan::execute(PlanInputData& environment)
|
||||
{
|
||||
get_npc()->aiData.bAction = AI_ACTION_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,12 +11,12 @@ namespace AI
|
||||
{
|
||||
namespace tactical
|
||||
{
|
||||
Plan* NullPlanFactory::create_plan(const AIInputData& input)
|
||||
Plan* NullPlanFactory::create_plan(SOLDIERTYPE* npc, const AIInputData& input)
|
||||
{
|
||||
return new NullPlan();
|
||||
return new NullPlan(npc);
|
||||
}
|
||||
|
||||
void NullPlanFactory::update_plan(const AIInputData& input, Plan* plan_to_change)
|
||||
void NullPlanFactory::update_plan(SOLDIERTYPE* npc, const AIInputData& input)
|
||||
{
|
||||
// the idea is to do nothing, so let's do it...
|
||||
}
|
||||
|
||||
@@ -3,21 +3,20 @@
|
||||
* @author feynman (bears-pit.com)
|
||||
*/
|
||||
|
||||
// XXX Add includes for new factories here XXX
|
||||
#include "../include/PlanFactoryLibrary.h"
|
||||
#include "../include/NullPlanFactory.h"
|
||||
#include "../include/LegacyAIPlanFactory.h"
|
||||
// XXX Add includes for new factories here XXX
|
||||
|
||||
|
||||
#include "../../Utils/INIReader.h"
|
||||
|
||||
#undef max // Who the fuck writes MACROS not using CAPITAL LETTERS??? You, sir, have lost your coding license. Please hand over your compiler and leave. Now.
|
||||
|
||||
#include <stdexcept>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#undef max
|
||||
|
||||
namespace AI
|
||||
{
|
||||
namespace tactical
|
||||
@@ -28,6 +27,7 @@ namespace AI
|
||||
*
|
||||
* This is the place where new factories need to be "registred" in order for them to be available using the
|
||||
* settings in AI.ini
|
||||
* The constructor is private due to the singleton pattern
|
||||
*/
|
||||
PlanFactoryLibrary::PlanFactoryLibrary()
|
||||
{
|
||||
@@ -66,18 +66,35 @@ namespace AI
|
||||
|
||||
/**@brief Create a plan for the given input using the factory at the given index
|
||||
* @param index Index of the concrete plan factory to use, set in the constructor via ini settings
|
||||
* @param npc The NPC the created plan is for
|
||||
* @param input The environmental data required for a factory to plan
|
||||
* @throws std::out_of_range for invalid index
|
||||
* @throws std::logic_error for valid index, but undefined factory (most likely due to typo in AI.ini)
|
||||
* @return Pointer to the only instance of the PlanFactoryLibrary
|
||||
* @return A new plan
|
||||
*/
|
||||
Plan* PlanFactoryLibrary::create_plan(size_t index, AIInputData& input) const
|
||||
Plan* PlanFactoryLibrary::create_plan(size_t index, SOLDIERTYPE* npc, const AIInputData& input) const
|
||||
{
|
||||
if(index >= ai_index_to_factory_mapping_.size())
|
||||
throw std::out_of_range("PlanFactoryLibrary detected invalid factory index");
|
||||
if(!ai_index_to_factory_mapping_[index])
|
||||
throw std::logic_error("PlanFactoryLibrary encountered a nullptr for a valid index (typo in AI.ini?)");
|
||||
return ai_index_to_factory_mapping_[index]->create_plan(input);
|
||||
return ai_index_to_factory_mapping_[index]->create_plan(npc, input);
|
||||
}
|
||||
|
||||
/**@brief Update a plan for the given input using the factory at the given index
|
||||
* @param index Index of the concrete plan factory to use, set in the constructor via ini settings
|
||||
* @param npc The NPC the updated plan is for
|
||||
* @param input The environmental data required for a factory to plan
|
||||
* @throws std::out_of_range for invalid index
|
||||
* @throws std::logic_error for valid index, but undefined factory (most likely due to typo in AI.ini)
|
||||
*/
|
||||
void PlanFactoryLibrary::update_plan(size_t index, SOLDIERTYPE* npc, const AIInputData& input) const
|
||||
{
|
||||
if(index >= ai_index_to_factory_mapping_.size())
|
||||
throw std::out_of_range("PlanFactoryLibrary detected invalid factory index");
|
||||
if(!ai_index_to_factory_mapping_[index])
|
||||
throw std::logic_error("PlanFactoryLibrary encountered a nullptr for a valid index (typo in AI.ini?)");
|
||||
return ai_index_to_factory_mapping_[index]->update_plan(npc, input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -57,7 +57,7 @@ enum
|
||||
#define NUM_PANIC_TRIGGERS 3
|
||||
#define ENEMY_OFFERED_SURRENDER 0x01
|
||||
|
||||
typedef struct
|
||||
struct TacticalStatusType
|
||||
{
|
||||
UINT32 uiFlags;
|
||||
TacticalTeamType Team[ MAXTEAMS ];
|
||||
@@ -163,7 +163,7 @@ typedef struct
|
||||
|
||||
// PADDING GONE!!!!!
|
||||
|
||||
} TacticalStatusType;
|
||||
};
|
||||
|
||||
|
||||
extern UINT8 gbPlayerNum;
|
||||
|
||||
+12
-59
@@ -57,6 +57,9 @@
|
||||
#endif
|
||||
|
||||
#include "connect.h"
|
||||
#include "../ModularizedTacticalAI/include/Plan.h"
|
||||
#include "../ModularizedTacticalAI/include/PlanFactoryLibrary.h"
|
||||
#include "../ModularizedTacticalAI/include/AbstractPlanFactory.h"
|
||||
|
||||
//rain
|
||||
//#define VIS_DIST_DECREASE_PER_RAIN_INTENSITY 20
|
||||
@@ -2154,67 +2157,21 @@ void ManSeesMan(SOLDIERTYPE *pSoldier, SOLDIERTYPE *pOpponent, INT32 sOppGridNo,
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"ManSeesMan");
|
||||
|
||||
if (pSoldier->ubID >= TOTAL_SOLDIERS)
|
||||
{
|
||||
/*
|
||||
#ifdef BETAVERSION
|
||||
NumMessage("ManSeesMan: ERROR - ptr->guynum = ",ptr->guynum);
|
||||
#endif
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
if (pOpponent->ubID >= TOTAL_SOLDIERS)
|
||||
{
|
||||
/*
|
||||
#ifdef BETAVERSION
|
||||
NumMessage("ManSeesMan: ERROR - oppPtr->guynum = ",oppPtr->guynum);
|
||||
#endif
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
// if we're somehow looking while inactive, at base, dying or already dead
|
||||
if (!pSoldier->bActive || !pSoldier->bInSector || (pSoldier->stats.bLife < OKLIFE))
|
||||
{
|
||||
/*
|
||||
#ifdef BETAVERSION
|
||||
sprintf(tempstr,"ManSeesMan: ERROR - %s is SEEING ManSeesMan while inactive/at base/dead/dying",ExtMen[ptr->guynum].name);
|
||||
PopMessage(tempstr);
|
||||
#endif
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
// if we're somehow seeing a guy who is inactive, at base, or already dead
|
||||
if (!pOpponent->bActive || !pOpponent->bInSector || pOpponent->stats.bLife <= 0)
|
||||
{
|
||||
/*
|
||||
#ifdef BETAVERSION
|
||||
sprintf(tempstr,"ManSeesMan: ERROR - %s sees %s, ManSeesMan, who is inactive/at base/dead",ExtMen[ptr->guynum].name,ExtMen[oppPtr->guynum].name);
|
||||
PopMessage(tempstr);
|
||||
#endif
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// if we're somehow seeing a guy who is on the same team
|
||||
if (pSoldier->bTeam == pOpponent->bTeam)
|
||||
{
|
||||
/*
|
||||
#ifdef BETAVERSION
|
||||
sprintf(tempstr,"ManSeesMan: ERROR - on SAME TEAM. ptr->guynum = %d, oppPtr->guynum = %d",
|
||||
ptr->guynum,oppPtr->guynum);
|
||||
PopMessage(tempstr);
|
||||
#endif
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
// Flugente: if the other guy is in med or deep water and wearing scua gear, then we cannot see him as he is submerged
|
||||
if ( pOpponent->UsesScubaGear() )
|
||||
return;
|
||||
|
||||
// Flugente: update our sight concerning this guy, otherwise we could get way with open attacks because this does not get updated
|
||||
pSoldier->RecognizeAsCombatant(pOpponent->ubID);
|
||||
|
||||
@@ -2355,15 +2312,6 @@ void ManSeesMan(SOLDIERTYPE *pSoldier, SOLDIERTYPE *pOpponent, INT32 sOppGridNo,
|
||||
{
|
||||
switch( pSoldier->ubProfile )
|
||||
{
|
||||
/*
|
||||
case MIKE:
|
||||
if ( gfPlayerTeamSawMike && !( gMercProfiles[ pSoldier->ubProfile ].ubMiscFlags2 & PROFILE_MISC_FLAG2_SAID_FIRSTSEEN_QUOTE ) )
|
||||
{
|
||||
InitiateConversation( pSoldier, pOpponent, NPC_INITIAL_QUOTE, 0 );
|
||||
gMercProfiles[ pSoldier->ubProfile ].ubMiscFlags2 |= PROFILE_MISC_FLAG2_SAID_FIRSTSEEN_QUOTE;
|
||||
}
|
||||
break;
|
||||
*/
|
||||
case IGGY:
|
||||
if ( ! ( gMercProfiles[ pSoldier->ubProfile ].ubMiscFlags2 & PROFILE_MISC_FLAG2_SAID_FIRSTSEEN_QUOTE ) )
|
||||
{
|
||||
@@ -2813,7 +2761,10 @@ if(SEE_MENT)
|
||||
// ATE: Check stance, change to threatending
|
||||
ReevaluateEnemyStance( pSoldier, pSoldier->usAnimState );
|
||||
}
|
||||
|
||||
AI::tactical::AIInputData ai_input(AI::tactical::AIInputData::Visual(), pOpponent, sOppGridNo, bOppLevel, ubCaller, ubCaller2);
|
||||
AI::tactical::PlanInputData plan_input((gTacticalStatus.uiFlags & TURNBASED)!=0, gTacticalStatus);
|
||||
AI::tactical::PlanFactoryLibrary* plan_lib(AI::tactical::PlanFactoryLibrary::instance());
|
||||
plan_lib->update_plan(pSoldier->bAIIndex, pSoldier, ai_input);
|
||||
}
|
||||
|
||||
|
||||
@@ -6333,14 +6284,12 @@ void HearNoise(SOLDIERTYPE *pSoldier, UINT8 ubNoiseMaker, INT32 sGridNo, INT8 bL
|
||||
INT8 bDirection;
|
||||
BOOLEAN fMuzzleFlash = FALSE;
|
||||
|
||||
// DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "%d hears noise from %d (%d/%d) volume %d", pSoldier->ubID, ubNoiseMaker, sGridNo, bLevel, ubVolume ) );
|
||||
|
||||
|
||||
if ( pSoldier->ubBodyType == CROW )
|
||||
{
|
||||
CrowsFlyAway( pSoldier->bTeam );
|
||||
return;
|
||||
}
|
||||
// DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "%d hears noise from %d (%d/%d) volume %d", pSoldier->ubID, ubNoiseMaker, sGridNo, bLevel, ubVolume ) );
|
||||
|
||||
// "Turn head" towards the source of the noise and try to see what's there
|
||||
|
||||
@@ -6649,6 +6598,10 @@ void HearNoise(SOLDIERTYPE *pSoldier, UINT8 ubNoiseMaker, INT32 sGridNo, INT8 bL
|
||||
}
|
||||
}
|
||||
}
|
||||
AI::tactical::AIInputData ai_input(AI::tactical::AIInputData::Auditive(), ubNoiseMaker, sGridNo, bLevel, ubVolume, ubNoiseType);
|
||||
AI::tactical::PlanInputData plan_input((gTacticalStatus.uiFlags & TURNBASED)!=0, gTacticalStatus);
|
||||
AI::tactical::PlanFactoryLibrary* plan_lib(AI::tactical::PlanFactoryLibrary::instance());
|
||||
plan_lib->update_plan(pSoldier->bAIIndex, pSoldier, ai_input);
|
||||
}
|
||||
|
||||
void TellPlayerAboutNoise( SOLDIERTYPE *pSoldier, UINT8 ubNoiseMaker, INT32 sGridNo, INT8 bLevel, UINT8 ubVolume, UINT8 ubNoiseType, UINT8 ubNoiseDir )
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
#include "random.h"
|
||||
#include "Points.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
|
||||
|
||||
extern BOOLEAN gfTurnBasedAI;
|
||||
|
||||
@@ -104,6 +107,14 @@ enum
|
||||
#undef max
|
||||
#define max(a,b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
// Added by feynman - remove all the ugly #ifdefs printf combinations for required for DebugAI
|
||||
//#define DEBUGAI
|
||||
#ifdef DEBUGAI
|
||||
#define DEBUGAIMSG(X) std::cerr<<"AIDEBUGMSG "<<std::setw(40)<<__FILE__<<":"<<std::setw(4)<<__LINE__<<": "<< X<<std::endl
|
||||
#else
|
||||
#define DEBUGAIMSG(X)
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SOLDIERTYPE * pOpponent;
|
||||
|
||||
+12
-33
@@ -63,8 +63,6 @@
|
||||
#include "ModularizedTacticalAI/include/Plan.h"
|
||||
#include "ModularizedTacticalAI/include/PlanFactoryLibrary.h"
|
||||
#include "ModularizedTacticalAI/include/AbstractPlanFactory.h"
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
|
||||
#ifdef JA2UB
|
||||
#include "Ja25_Tactical.h"
|
||||
@@ -500,19 +498,10 @@ void HandleSoldierAI( SOLDIERTYPE *pSoldier ) // FIXME - this function is named
|
||||
|
||||
gubAICounter++;
|
||||
|
||||
if(!pSoldier->ai_masterplan_) // if the Soldier has no plan, create one
|
||||
{
|
||||
if(pSoldier->bAIIndex == 0) // not yet initialized, use bTeam+1 as default
|
||||
pSoldier->bAIIndex = pSoldier->bTeam + 1;
|
||||
AI::tactical::AIInputData ai_input;
|
||||
ai_input.npc_to_plan_for_ = pSoldier;
|
||||
AI::tactical::PlanFactoryLibrary* plan_lib(AI::tactical::PlanFactoryLibrary::instance());
|
||||
pSoldier->ai_masterplan_ = plan_lib->create_plan(pSoldier->bAIIndex, ai_input);
|
||||
}
|
||||
|
||||
AI::tactical::PlanInputData plan_input;
|
||||
plan_input.controlled_npc_ = pSoldier;
|
||||
pSoldier->ai_masterplan_->execute(gfTurnBasedAI, plan_input);
|
||||
if(gfTurnBasedAI)
|
||||
TurnBasedHandleNPCAI(pSoldier);
|
||||
else
|
||||
RTHandleAI( pSoldier );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1445,26 +1434,16 @@ void TurnBasedHandleNPCAI(SOLDIERTYPE *pSoldier)
|
||||
{
|
||||
if (!(gTacticalStatus.uiFlags & ENGAGED_IN_CONV))
|
||||
{
|
||||
#ifdef ENABLE_ZOMBIES
|
||||
if ( pSoldier->IsZombie() )
|
||||
if(!pSoldier->ai_masterplan_) // if the Soldier has no plan, create one
|
||||
{
|
||||
pSoldier->aiData.bAction = ZombieDecideAction(pSoldier);
|
||||
}
|
||||
else if (CREATURE_OR_BLOODCAT( pSoldier ))
|
||||
#else
|
||||
if (CREATURE_OR_BLOODCAT( pSoldier ))
|
||||
#endif
|
||||
{
|
||||
pSoldier->aiData.bAction = CreatureDecideAction( pSoldier );
|
||||
}
|
||||
else if (pSoldier->ubBodyType == CROW)
|
||||
{
|
||||
pSoldier->aiData.bAction = CrowDecideAction( pSoldier );
|
||||
}
|
||||
else
|
||||
{
|
||||
pSoldier->aiData.bAction = DecideAction(pSoldier);
|
||||
if(pSoldier->bAIIndex == 0) // not yet initialized, use bTeam+1 as default
|
||||
pSoldier->bAIIndex = pSoldier->bTeam + 1;
|
||||
AI::tactical::AIInputData ai_input;
|
||||
AI::tactical::PlanFactoryLibrary* plan_lib(AI::tactical::PlanFactoryLibrary::instance());
|
||||
pSoldier->ai_masterplan_ = plan_lib->create_plan(pSoldier->bAIIndex, pSoldier, ai_input);
|
||||
}
|
||||
AI::tactical::PlanInputData plan_input(true, gTacticalStatus);
|
||||
pSoldier->ai_masterplan_->execute(plan_input);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1670,91 +1670,3 @@ void CreatureDecideAlertStatus( SOLDIERTYPE *pSoldier )
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
INT8 CrowDecideActionRed( SOLDIERTYPE * pSoldier )
|
||||
{
|
||||
// OK, Fly away!
|
||||
//HandleCrowFlyAway( pSoldier );
|
||||
if (!gfTurnBasedAI)
|
||||
{
|
||||
pSoldier->aiData.usActionData = 30000;
|
||||
return( AI_ACTION_WAIT );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( AI_ACTION_NONE );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
INT8 CrowDecideActionGreen( SOLDIERTYPE * pSoldier )
|
||||
{
|
||||
INT32 sCorpseGridNo;
|
||||
UINT8 ubDirection;
|
||||
INT16 sFacingDir;
|
||||
|
||||
// Look for a corse!
|
||||
sCorpseGridNo = FindNearestRottingCorpse( pSoldier );
|
||||
|
||||
if (!TileIsOutOfBounds(sCorpseGridNo))
|
||||
{
|
||||
// Are we close, if so , peck!
|
||||
if ( SpacesAway( pSoldier->sGridNo, sCorpseGridNo ) < 2 )
|
||||
{
|
||||
// Change facing
|
||||
sFacingDir = GetDirectionFromGridNo( sCorpseGridNo, pSoldier );
|
||||
|
||||
if ( sFacingDir != pSoldier->ubDirection )
|
||||
{
|
||||
pSoldier->aiData.usActionData = sFacingDir;
|
||||
return(AI_ACTION_CHANGE_FACING);
|
||||
}
|
||||
else if (!gfTurnBasedAI)
|
||||
{
|
||||
pSoldier->aiData.usActionData = 30000;
|
||||
return( AI_ACTION_WAIT );
|
||||
}
|
||||
else
|
||||
{
|
||||
return( AI_ACTION_NONE );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Walk to nearest one!
|
||||
pSoldier->aiData.usActionData = FindGridNoFromSweetSpot( pSoldier, sCorpseGridNo, 4, &ubDirection );
|
||||
if (!TileIsOutOfBounds(pSoldier->aiData.usActionData))
|
||||
{
|
||||
return( AI_ACTION_GET_CLOSER );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return( AI_ACTION_NONE );
|
||||
}
|
||||
|
||||
INT8 CrowDecideAction( SOLDIERTYPE * pSoldier )
|
||||
{
|
||||
if ( pSoldier->usAnimState == CROW_FLY )
|
||||
{
|
||||
return( AI_ACTION_NONE );
|
||||
}
|
||||
|
||||
switch( pSoldier->aiData.bAlertStatus )
|
||||
{
|
||||
case STATUS_GREEN:
|
||||
case STATUS_YELLOW:
|
||||
return( CrowDecideActionGreen( pSoldier ) );
|
||||
|
||||
case STATUS_RED:
|
||||
case STATUS_BLACK:
|
||||
return( CrowDecideActionRed( pSoldier ) );
|
||||
|
||||
default:
|
||||
Assert( FALSE );
|
||||
return( AI_ACTION_NONE );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5766,163 +5766,6 @@ INT16 ubMinAPCost;
|
||||
|
||||
}
|
||||
|
||||
INT8 DecideAction(SOLDIERTYPE *pSoldier)
|
||||
{
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"DecideAction");
|
||||
INT8 bAction=AI_ACTION_NONE;
|
||||
#ifdef DEBUGDECISIONS
|
||||
STR16 tempstr;
|
||||
#endif
|
||||
|
||||
#ifdef AI_TIMING_TESTS
|
||||
UINT32 uiStartTime, uiEndTime;
|
||||
#endif
|
||||
|
||||
if ( pSoldier->bTeam != MILITIA_TEAM )
|
||||
{
|
||||
if ( !sniperwarning && pSoldier->aiData.bOrders == SNIPER )
|
||||
{
|
||||
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, New113Message[MSG113_WATHCHOUTFORSNIPERS] );
|
||||
sniperwarning = TRUE;
|
||||
}
|
||||
|
||||
if (!biggunwarning && FindRocketLauncherOrCannon(pSoldier) != NO_SLOT )
|
||||
{
|
||||
biggunwarning = TRUE;
|
||||
//TODO: don't say this again after reloading a savegame
|
||||
SayQuoteFromAnyBodyInSector( QUOTE_WEARY_SLASH_SUSPUCIOUS );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// turn off cautious flag
|
||||
pSoldier->aiData.fAIFlags &= (~AI_CAUTIOUS);
|
||||
//reset flank count
|
||||
|
||||
//NumMessage("DecideAction - Guy#",pSoldier->ubID);
|
||||
|
||||
// if the NPC is being "ESCORTED" (seen civilians only for now)
|
||||
if (pSoldier->aiData.bUnderEscort)
|
||||
{
|
||||
#ifdef DEBUGDECISIONS
|
||||
AIPopMessage("AlertStatus = ESCORT");
|
||||
#endif
|
||||
|
||||
//bAction = DecideActionEscort(pSoldier);
|
||||
}
|
||||
else // "normal" NPC AI
|
||||
{
|
||||
// if status over-ride is set, bypass RED/YELLOW and go directly to GREEN!
|
||||
if ((pSoldier->aiData.bBypassToGreen) && (pSoldier->aiData.bAlertStatus < STATUS_BLACK))
|
||||
{
|
||||
bAction = DecideActionGreen(pSoldier);
|
||||
if ( !gfTurnBasedAI )
|
||||
{
|
||||
// reset bypass now
|
||||
pSoldier->aiData.bBypassToGreen = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (pSoldier->aiData.bAlertStatus)
|
||||
{
|
||||
case STATUS_GREEN:
|
||||
#ifdef DEBUGDECISIONS
|
||||
AIPopMessage("AlertStatus = GREEN");
|
||||
#endif
|
||||
#ifdef AI_TIMING_TESTS
|
||||
uiStartTime = GetJA2Clock();
|
||||
#endif
|
||||
bAction = DecideActionGreen(pSoldier);
|
||||
#ifdef AI_TIMING_TESTS
|
||||
uiEndTime = GetJA2Clock();
|
||||
guiGreenTimeTotal += (uiEndTime - uiStartTime);
|
||||
guiGreenCounter++;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case STATUS_YELLOW:
|
||||
#ifdef DEBUGDECISIONS
|
||||
AIPopMessage("AlertStatus = YELLOW");
|
||||
#endif
|
||||
#ifdef AI_TIMING_TESTS
|
||||
uiStartTime = GetJA2Clock();
|
||||
#endif
|
||||
bAction = DecideActionYellow(pSoldier);
|
||||
#ifdef AI_TIMING_TESTS
|
||||
uiEndTime = GetJA2Clock();
|
||||
guiYellowTimeTotal += (uiEndTime - uiStartTime);
|
||||
guiYellowCounter++;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case STATUS_RED:
|
||||
#ifdef DEBUGDECISIONS
|
||||
AIPopMessage("AlertStatus = RED");
|
||||
#endif
|
||||
#ifdef AI_TIMING_TESTS
|
||||
uiStartTime = GetJA2Clock();
|
||||
#endif
|
||||
bAction = DecideActionRed(pSoldier,TRUE);
|
||||
#ifdef AI_TIMING_TESTS
|
||||
uiEndTime = GetJA2Clock();
|
||||
guiRedTimeTotal += (uiEndTime - uiStartTime);
|
||||
guiRedCounter++;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case STATUS_BLACK:
|
||||
#ifdef DEBUGDECISIONS
|
||||
AIPopMessage("AlertStatus = BLACK");
|
||||
#endif
|
||||
#ifdef AI_TIMING_TESTS
|
||||
uiStartTime = GetJA2Clock();
|
||||
#endif
|
||||
bAction = DecideActionBlack(pSoldier);
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,String("DecideActionBlack=%d",bAction));
|
||||
#ifdef AI_TIMING_TESTS
|
||||
uiEndTime = GetJA2Clock();
|
||||
guiBlackTimeTotal += (uiEndTime - uiStartTime);
|
||||
guiBlackCounter++;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef DEBUGDECISIONS
|
||||
sprintf( tempstr,"DecideAction: selected action %d, actionData %d\n\n",bAction,pSoldier->aiData.usActionData);
|
||||
DebugAI( tempstr );
|
||||
#endif
|
||||
|
||||
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"DecideAction done");
|
||||
|
||||
return(bAction);
|
||||
}
|
||||
|
||||
INT8 DecideActionEscort(SOLDIERTYPE *pSoldier)
|
||||
{
|
||||
#ifdef DEBUGDECISIONS
|
||||
STR16 tempstr;
|
||||
#endif
|
||||
|
||||
// if he has a place to go, and isn't already there... go!
|
||||
if (!TileIsOutOfBounds(pSoldier->aiData.usActionData) && (pSoldier->sGridNo != pSoldier->aiData.usActionData))
|
||||
{
|
||||
#ifdef DEBUGDECISIONS
|
||||
sprintf(tempstr,"%s - ESCORTED NPC GOING to grid %d",pSoldier->name,pSoldier->aiData.usActionData);
|
||||
AIPopMessage(tempstr);
|
||||
#endif
|
||||
|
||||
return(AI_ACTION_ESCORTED_MOVE);
|
||||
}
|
||||
else
|
||||
return(AI_ACTION_NONE);
|
||||
}
|
||||
|
||||
|
||||
void DecideAlertStatus( SOLDIERTYPE *pSoldier )
|
||||
{
|
||||
#ifdef DEBUGDECISIONS
|
||||
|
||||
+14
-65
@@ -19,71 +19,11 @@
|
||||
#include "Quests.h"
|
||||
#include "GameSettings.h"
|
||||
#endif
|
||||
// needed to use the modularized tactical AI:
|
||||
#include "ModularizedTacticalAI/include/Plan.h"
|
||||
#include "ModularizedTacticalAI/include/PlanFactoryLibrary.h"
|
||||
#include "ModularizedTacticalAI/include/AbstractPlanFactory.h"
|
||||
|
||||
INT8 RTPlayerDecideAction( SOLDIERTYPE * pSoldier )
|
||||
{
|
||||
INT8 bAction=AI_ACTION_NONE;
|
||||
|
||||
if (gTacticalStatus.fAutoBandageMode)
|
||||
{
|
||||
bAction = DecideAutoBandage( pSoldier );
|
||||
}
|
||||
#ifdef ENABLE_ZOMBIES
|
||||
else if ( pSoldier->IsZombie() )
|
||||
{
|
||||
bAction = ZombieDecideAction( pSoldier );
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
bAction = DecideAction( pSoldier );
|
||||
}
|
||||
|
||||
#ifdef DEBUGDECISIONS
|
||||
STR tempstr;
|
||||
sprintf(tempstr,"DecideAction: selected action %d, actionData %d\n\n",bAction,pSoldier->aiData.usActionData);
|
||||
DebugAI( tempstr );
|
||||
#endif
|
||||
|
||||
return(bAction);
|
||||
}
|
||||
|
||||
INT8 RTDecideAction(SOLDIERTYPE *pSoldier)
|
||||
{
|
||||
if (CREATURE_OR_BLOODCAT( pSoldier ) )
|
||||
{
|
||||
return( CreatureDecideAction( pSoldier ) );
|
||||
}
|
||||
#ifdef ENABLE_ZOMBIES
|
||||
else if ( pSoldier->IsZombie() )
|
||||
{
|
||||
return( ZombieDecideAction( pSoldier ) );
|
||||
}
|
||||
#endif
|
||||
else if (pSoldier->ubBodyType == CROW)
|
||||
{
|
||||
return( CrowDecideAction( pSoldier ) );
|
||||
}
|
||||
else if (pSoldier->bTeam == gbPlayerNum)
|
||||
{
|
||||
return( RTPlayerDecideAction( pSoldier ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
// handle traversal
|
||||
if ( (pSoldier->ubProfile != NO_PROFILE) && (gMercProfiles[ pSoldier->ubProfile ].ubMiscFlags3 & PROFILE_MISC_FLAG3_HANDLE_DONE_TRAVERSAL ) )
|
||||
{
|
||||
TriggerNPCWithGivenApproach( pSoldier->ubProfile, APPROACH_DONE_TRAVERSAL, FALSE );
|
||||
gMercProfiles[ pSoldier->ubProfile ].ubMiscFlags3 &= (~PROFILE_MISC_FLAG3_HANDLE_DONE_TRAVERSAL);
|
||||
pSoldier->ubQuoteActionID = 0;
|
||||
// wait a tiny bit
|
||||
pSoldier->aiData.usActionData = 100;
|
||||
return( AI_ACTION_WAIT );
|
||||
}
|
||||
|
||||
return( DecideAction( pSoldier ) );
|
||||
}
|
||||
}
|
||||
|
||||
UINT16 RealtimeDelay( SOLDIERTYPE * pSoldier )
|
||||
{
|
||||
@@ -236,7 +176,16 @@ void RTHandleAI( SOLDIERTYPE * pSoldier )
|
||||
{
|
||||
if (!(gTacticalStatus.uiFlags & ENGAGED_IN_CONV))
|
||||
{
|
||||
pSoldier->aiData.bAction = RTDecideAction( pSoldier );
|
||||
if(!pSoldier->ai_masterplan_) // if the Soldier has no plan, create one
|
||||
{
|
||||
if(pSoldier->bAIIndex == 0) // not yet initialized, use bTeam+1 as default
|
||||
pSoldier->bAIIndex = pSoldier->bTeam + 1;
|
||||
AI::tactical::AIInputData ai_input;
|
||||
AI::tactical::PlanFactoryLibrary* plan_lib(AI::tactical::PlanFactoryLibrary::instance());
|
||||
pSoldier->ai_masterplan_ = plan_lib->create_plan(pSoldier->bAIIndex, pSoldier, ai_input);
|
||||
}
|
||||
AI::tactical::PlanInputData plan_input(false, gTacticalStatus);
|
||||
pSoldier->ai_masterplan_->execute(plan_input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user