/*
* Seven Kingdoms: Ancient Adversaries
*
* Copyright 1997,1998 Enlight Software Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
//Filename : OSPY2.CPP
//Description : Spy AI functions
#include
#include
#include
#include
#include
#define THIS ptr()
//--------- Begin of function SpyProcess::process_ai ----------//
//
void SpyProcess::process_ai()
{
if( spy_recno%30 == info.game_date%30 ) // think about changing actions once 30 days
think_reward();
switch( THIS->spy_place )
{
case SPY_TOWN:
if( spy_recno%30 == info.game_date%30 )
think_town_spy();
break;
case SPY_FIRM:
if( spy_recno%30 == info.game_date%30 )
think_firm_spy();
break;
case SPY_MOBILE:
if( spy_recno%5 == info.game_date%5 )
think_mobile_spy();
break;
}
}
//---------- End of function SpyProcess::process_ai ----------//
//--------- Begin of function SpyProcess::think_town_spy ----------//
//
void SpyProcess::think_town_spy()
{
Town* townPtr = town_array[THIS->spy_place_para];
if( townPtr->nation_recno == THIS->true_nation_recno ) // anti-spy
return;
//------ if it's an independent town ------//
if( townPtr->nation_recno == 0 )
{
THIS->set_action_mode(SPY_SOW_DISSENT);
//--- if the resistance has already drop low enough, the spy no longer needs to be in the town ---//
if( townPtr->race_loyalty_array[THIS->race_id-1] < MIN_INDEPENDENT_DEFEND_LOYALTY )
{
THIS->mobilize_town_spy();
}
}
else
{
//-------------- if it's a nation town -------------//
//
// Set to sleep mode in most time so the spying skill can increase
// gradually, when the loyalty level of the village falls to near
// rebel level, set all of your spies in the village to sow dissent
// mode and cause rebellion in the enemy village.
//
//--------------------------------------------------//
Nation* trueNation = nation_array[THIS->true_nation_recno];
if( townPtr->average_loyalty() < 50 - trueNation->pref_loyalty_concern/10 ) // pref_loyalty_concern actually does apply to here, we just use a preference var so that the decision making process will vary between nations
{
THIS->set_action_mode(SPY_SOW_DISSENT);
}
else
{
if( misc.random(5)==0 ) // 20% chance of sowing dissents.
THIS->set_action_mode(SPY_SOW_DISSENT);
else
THIS->set_action_mode(SPY_IDLE);
}
}
}
//---------- End of function SpyProcess::think_town_spy ----------//
//--------- Begin of function SpyProcess::think_firm_spy ----------//
//
void SpyProcess::think_firm_spy()
{
Firm* firmPtr = firm_array[THIS->spy_place_para];
if( firmPtr->nation_recno == THIS->true_nation_recno ) // anti-spy
return;
//-------- try to capturing the firm --------//
if( THIS->capture_firm() )
return;
//-------- think about bribing ---------//
if( think_bribe() )
return;
//-------- think about assassinating ---------//
if( think_assassinate() )
return;
//------ think about changing spy mode ----//
else if( misc.random(3)==0 ) // 1/10 chance to set it to idle to prevent from being caught
{
THIS->set_action_mode(SPY_IDLE);
}
else if( misc.random(2)==0 &&
THIS->can_sabotage() && firmPtr->is_operating() && firmPtr->productivity >= 20 )
{
THIS->set_action_mode(SPY_SABOTAGE);
}
else
{
THIS->set_action_mode(SPY_SOW_DISSENT);
}
}
//---------- End of function SpyProcess::think_firm_spy ----------//
//--------- Begin of function SpyProcess::think_bribe ----------//
//
int SpyProcess::think_bribe()
{
Firm* firmPtr = firm_array[THIS->spy_place_para];
//--- only bribe enemies in military camps ---//
if( firmPtr->firm_id != FIRM_CAMP )
return 0;
//----- only if there is an overseer in the camp -----//
if( !firmPtr->overseer_recno )
return 0;
//---- see if the overseer can be bribe (kings and your own spies can't be bribed) ----//
if( !firmPtr->can_spy_bribe(0, THIS->true_nation_recno ) ) // 0-bribe the overseer
return 0;
//------ first check our financial status ------//
Nation* ownNation = nation_array[THIS->true_nation_recno];
Unit* overseerUnit = unit_array[firmPtr->overseer_recno];
if( THIS->spy_skill < MIN(50, overseerUnit->skill.skill_level) ||
!ownNation->ai_should_spend(30) )
{
return 0;
}
//----- think about how important is this firm -----//
int firmImportance = 0;
Town* townPtr;
for( int i=firmPtr->linked_town_count-1 ; i>=0 ; i-- )
{
townPtr = town_array[ firmPtr->linked_town_array[i] ];
if( townPtr->nation_recno == firmPtr->nation_recno )
firmImportance += townPtr->population * 2;
else
firmImportance += townPtr->population;
}
//------- think about which one to bribe -------//
//-- first get the succeedChange if the bribe amount is zero --//
int succeedChange = firmPtr->spy_bribe_succeed_chance( 0, spy_recno, 0 ); // first 0 - $0 bribe amount, 3rd 0 - bribe the overseer
//-- then based on it, figure out how much we have to offer to bribe successfully --//
int bribeAmount = MAX_BRIBE_AMOUNT * (100-succeedChange) / 100;
bribeAmount = MAX(100, bribeAmount);
//--- only bribe when the nation has enough money ---//
if( !ownNation->ai_should_spend(30, (float)bribeAmount) )
return 0;
//------- try to bribe the commander ----//
int newSpyRecno = firmPtr->spy_bribe(bribeAmount, spy_recno, 0);
if( !newSpyRecno ) // bribing failed
return 1; // return 1 as the spy has been killed
Spy* newSpy = spy_array[newSpyRecno];
err_when( newSpy->true_nation_recno != THIS->true_nation_recno );
err_when( newSpy->spy_place != SPY_FIRM );
if( newSpy->capture_firm() ) // try to capture the firm now
{
err_when( firm_array[newSpy->spy_place_para]->nation_recno != THIS->true_nation_recno );
newSpy->drop_spy_identity(); // drop the spy identity of the newly bribed spy if the capture is successful, this will save the spying costs
}
return 1;
}
//---------- End of function SpyProcess::think_bribe ----------//
//--------- Begin of function SpyProcess::think_reward ----------//
//
// Think about rewarding this spy.
//
int SpyProcess::think_reward()
{
Nation* ownNation = nation_array[THIS->true_nation_recno];
//----------------------------------------------------------//
// The need to secure high loyalty on this unit is based on:
// -its skill
// -its combat level
// -soldiers commanded by this unit
//----------------------------------------------------------//
int neededLoyalty = THIS->spy_skill * (100+ownNation->pref_loyalty_concern) / 100;
neededLoyalty = MAX( UNIT_BETRAY_LOYALTY+10, neededLoyalty ); // 10 points above the betray loyalty level to prevent betrayal
neededLoyalty = MIN( 100, neededLoyalty );
//------- if the loyalty is already high enough ------//
if( THIS->spy_loyalty >= neededLoyalty )
return 0;
//---------- see how many cash & profit we have now ---------//
int rewardNeedRating = neededLoyalty - THIS->spy_loyalty;
if( THIS->spy_loyalty < UNIT_BETRAY_LOYALTY+5 )
rewardNeedRating += 50;
if( ownNation->ai_should_spend(rewardNeedRating) )
{
THIS->reward(COMMAND_AI);
return 1;
}
return 0;
}
//---------- End of function SpyProcess::think_reward ----------//
//--------- Begin of function SpyProcess::think_mobile_spy ----------//
//
int SpyProcess::think_mobile_spy()
{
Unit* unitPtr = unit_array[THIS->spy_place_para];
//--- if the spy is on the ship, nothing can be done ---//
if( !unitPtr->is_visible() )
return 0;
//---- if the spy has stopped and there is no new action ----//
if( unitPtr->is_ai_all_stop() &&
(!THIS->notify_cloaked_nation_flag || THIS->cloaked_nation_recno==0) )
{
return think_mobile_spy_new_action();
}
return 0;
}
//---------- End of function SpyProcess::think_mobile_spy ----------//
//-------- Begin of function SpyProcess::think_mobile_spy_new_action --------//
//
int SpyProcess::think_mobile_spy_new_action()
{
Nation* trueNation = nation_array[THIS->true_nation_recno];
err_when( THIS->spy_place != SPY_MOBILE );
int spyRegionId = unit_array[THIS->spy_place_para]->region_id();
//----- try to sneak into an enemy camp ------//
int firmRecno = trueNation->think_assign_spy_target_camp(THIS->race_id, spyRegionId);
if( firmRecno )
{
Firm* firmPtr = firm_array[firmRecno];
return add_assign_spy_action( firmPtr->loc_x1, firmPtr->loc_y1, firmPtr->nation_recno );
}
//--- try to sneak into an enemy town or an independent town ---//
int townRecno = trueNation->think_assign_spy_target_town(THIS->race_id, spyRegionId);
if( townRecno )
{
Town* townPtr = town_array[townRecno];
return add_assign_spy_action( townPtr->loc_x1, townPtr->loc_y1, townPtr->nation_recno );
}
//------ think if we should drop the spy identity -------//
int dropIdentity = 0;
//-------- if we already have too many spies --------//
if( trueNation->total_spy_count > trueNation->total_population * (10+trueNation->pref_spy/5) / 100 ) // 10% to 30%
{
dropIdentity = 1;
}
//--- the expense of spies should not be too large ---//
else if( trueNation->expense_365days(EXPENSE_SPY) >
trueNation->expense_365days() * (50+trueNation->pref_counter_spy) / 400 )
{
dropIdentity = 1;
}
else //------- try to assign to one of our own towns -------//
{
int townRecno = trueNation->think_assign_spy_own_town(THIS->race_id, spyRegionId);
if( townRecno )
{
Town* townPtr = town_array[townRecno];
return add_assign_spy_action( townPtr->loc_x1, townPtr->loc_y1, townPtr->nation_recno );
}
else
{
dropIdentity = 1;
}
}
//---------- drop spy identity now --------//
if( dropIdentity )
{
THIS->drop_spy_identity();
return 1;
}
return 0;
}
//---------- End of function SpyProcess::think_mobile_spy_new_action --------//
//-------- Begin of function SpyProcess::add_assign_spy_action --------//
//
int SpyProcess::add_assign_spy_action(int destXLoc, int destYLoc, int cloakedNationRecno)
{
err_when( THIS->spy_place != SPY_MOBILE );
err_when( unit_array.is_deleted(THIS->spy_place_para) );
return nation_array[THIS->true_nation_recno]->add_action( destXLoc, destYLoc,
-1, -1, ACTION_AI_ASSIGN_SPY, cloakedNationRecno, 1, THIS->spy_place_para );
}
//---------- End of function SpyProcess::add_assign_spy_action --------//
//--------- Begin of function Spy::ai_spy_being_attacked ----------//
//
// This function is called when this spy is under attack.
//
// attackerUnitRecno - recno of the attacker unit.
//
int Spy::ai_spy_being_attacked(int attackerUnitRecno)
{
err_when( spy_place != SPY_MOBILE );
Unit* attackerUnit = unit_array[attackerUnitRecno];
Unit* spyUnit = unit_array[spy_place_para];
Nation* trueNation = nation_array[true_nation_recno];
//----- if we are attacking our own units -----//
if( attackerUnit->true_nation_recno() == true_nation_recno )
{
if( spy_skill > 50-trueNation->pref_spy/10 ||
spyUnit->hit_points < spyUnit->max_hit_points * (100-trueNation->pref_military_courage/2) / 100 )
{
change_cloaked_nation( true_nation_recno );
return 1;
}
}
else
{
//---- if this unit is attacking units of other nations -----//
//
// If the nation this spy cloaked into is at war with the spy's
// true nation and the nation which the spy is currently attacking
// is not at war with the spy's true nation, then change
// the spy's cloak to the non-hostile nation.
//
//-----------------------------------------------------------//
if( trueNation->get_relation_status(attackerUnit->nation_recno) != NATION_HOSTILE &&
trueNation->get_relation_status(cloaked_nation_recno) == NATION_HOSTILE )
{
if( spy_skill > 50-trueNation->pref_spy/10 ||
spyUnit->hit_points < spyUnit->max_hit_points * (100-trueNation->pref_military_courage/2) / 100 )
{
change_cloaked_nation( true_nation_recno );
return 1;
}
}
}
return 0;
}
//---------- End of function Spy::ai_spy_being_attacked ----------//
//--------- Begin of function SpyProcess::think_assassinate ----------//
//
int SpyProcess::think_assassinate()
{
Firm* firmPtr = firm_array[THIS->spy_place_para];
//--- only bribe enemies in military camps ---//
if( firmPtr->firm_id != FIRM_CAMP )
return 0;
//----- only if there is an overseer in the camp -----//
if( !firmPtr->overseer_recno )
return 0;
//---- get the attack and defense rating ----//
int attackRating, defenseRating, otherDefenderCount;
if( !THIS->get_assassinate_rating(firmPtr->overseer_recno, attackRating, defenseRating, otherDefenderCount) ) // return 0 if assassination is not possible
return 0;
Nation* trueNation = nation_array[THIS->true_nation_recno];
if( attackRating + misc.random(20+trueNation->pref_spy/2) > defenseRating ) // the random number is to increase the chance of attempting assassination
{
THIS->assassinate(firmPtr->overseer_recno, COMMAND_AI);
return 1;
}
return 0;
}
//---------- End of function SpyProcess::think_assassinate ----------//