mirror of
https://github.com/1dot13/source.git
synced 2026-08-05 14:00:23 +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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user