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
+91 -13
View File
@@ -1,28 +1,106 @@
/**
/**
* @file
* @author feynman (bears-pit.com)
*/
#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);
}
}
}
+8 -3
View File
@@ -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);
}
}
}