/*
* 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 : OAI_MAIN.CPP
//Description: AI - main functions
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//--------- Begin of function Nation::Nation --------//
Nation::Nation() : action_array( sizeof(ActionNode), 30 )
{
ai_town_array = NULL;
}
//---------- End of function Nation::Nation --------//
//--------- Begin of function Nation::~Nation --------//
Nation::~Nation()
{
err_when( nation_recno ); // deinit() must be called first before this destructor is called
}
//---------- End of function Nation::~Nation --------//
//--------- Begin of function Nation::init --------//
void Nation::init(int nationType, int raceId, int colorSchemeId, uint32_t playerId)
{
NationBase::init(nationType, raceId, colorSchemeId, playerId);
//----- init other AI vars -----//
last_action_id = 0;
ai_capture_enemy_town_recno = 0;
ai_capture_enemy_town_start_attack_date = 0;
ai_last_defend_action_date = 0;
memset( firm_should_close_array, 0, sizeof(firm_should_close_array) );
ai_base_town_count = 0;
attack_camp_count = 0;
//------ init AI info arrays -----//
init_all_ai_info();
//----- init AI personality ----//
init_personalty();
}
//---------- End of function Nation::init --------//
//--------- Begin of function Nation::deinit --------//
void Nation::deinit()
{
NationBase::deinit();
deinit_all_ai_info();
}
//---------- End of function Nation::deinit --------//
//--------- Begin of function Nation::init_all_ai_info --------//
void Nation::init_all_ai_info()
{
err_when( ai_town_array );
init_ai_info(&ai_town_array, ai_town_count, ai_town_size, AI_TOWN_INIT_SIZE);
init_ai_info(&ai_base_array, ai_base_count, ai_base_size, AI_BASE_INIT_SIZE);
init_ai_info(&ai_mine_array, ai_mine_count, ai_mine_size, AI_MINE_INIT_SIZE);
init_ai_info(&ai_factory_array, ai_factory_count, ai_factory_size, AI_FACTORY_INIT_SIZE);
init_ai_info(&ai_market_array, ai_market_count, ai_market_size, AI_MARKET_INIT_SIZE);
init_ai_info(&ai_inn_array, ai_inn_count, ai_inn_size, AI_INN_INIT_SIZE);
init_ai_info(&ai_camp_array, ai_camp_count, ai_camp_size, AI_CAMP_INIT_SIZE);
init_ai_info(&ai_research_array, ai_research_count, ai_research_size, AI_RESEARCH_INIT_SIZE);
init_ai_info(&ai_war_array, ai_war_count, ai_war_size, AI_WAR_INIT_SIZE);
init_ai_info(&ai_harbor_array, ai_harbor_count, ai_harbor_size, AI_HARBOR_INIT_SIZE);
init_ai_info(&ai_caravan_array, ai_caravan_count, ai_caravan_size, AI_CARAVAN_INIT_SIZE);
init_ai_info(&ai_ship_array, ai_ship_count, ai_ship_size, AI_SHIP_INIT_SIZE);
init_ai_info(&ai_general_array, ai_general_count, ai_general_size, AI_GENERAL_INIT_SIZE);
}
//---------- End of function Nation::init_all_ai_info --------//
//--------- Begin of function Nation::init_ai_info --------//
//
// aiInfoArrayPtr - poniter to the AI info array.
// aiInfoCount - the count of the AI info array.
// aiInfoSize - the size of the AI info array.
// arrayInitSize - the init size of the array.
//
void Nation::init_ai_info(short** aiInfoArrayPtr, short& aiInfoCount, short& aiInfoSize, int arrayInitSize )
{
*aiInfoArrayPtr = (short*) mem_add( sizeof(short) * arrayInitSize );
memset( *aiInfoArrayPtr, 0, sizeof(short) * arrayInitSize );
aiInfoCount = 0;
aiInfoSize = arrayInitSize;
}
//---------- End of function Nation::init_ai_info --------//
//--------- Begin of function Nation::deinit_all_ai_info --------//
void Nation::deinit_all_ai_info()
{
err_when( !ai_town_array );
//------- debug checking -------//
#ifdef DEBUG
if( !sys.signal_exit_flag )
{
err_when( ai_town_count > 0 );
err_when( ai_base_town_count > 0 );
err_when( ai_base_count > 0 );
err_when( ai_mine_count > 0 );
err_when( ai_factory_count > 0 );
err_when( ai_market_count > 0 );
err_when( ai_inn_count > 0 );
err_when( ai_camp_count > 0 );
err_when( ai_research_count > 0 );
err_when( ai_war_count > 0 );
err_when( ai_harbor_count > 0 );
err_when( ai_caravan_count > 0 );
err_when( ai_ship_count > 0 );
err_when( ai_general_count > 0 );
}
#endif
//------- release array from memory -------//
mem_del(ai_town_array);
mem_del(ai_base_array);
mem_del(ai_mine_array);
mem_del(ai_factory_array);
mem_del(ai_market_array);
mem_del(ai_inn_array);
mem_del(ai_camp_array);
mem_del(ai_research_array);
mem_del(ai_war_array);
mem_del(ai_harbor_array);
mem_del(ai_caravan_array);
mem_del(ai_ship_array);
mem_del(ai_general_array);
}
//---------- End of function Nation::deinit_all_ai_info --------//
//--------- Begin of function Nation::init_personalty --------//
void Nation::init_personalty()
{
pref_force_projection = misc.random(101);
pref_military_development = misc.random(101);
pref_economic_development = 100-pref_military_development;
pref_inc_pop_by_capture = misc.random(101);
pref_inc_pop_by_growth = 100-pref_inc_pop_by_capture;
pref_peacefulness = misc.random(101);
pref_military_courage = misc.random(101);
pref_territorial_cohesiveness = misc.random(101);
pref_trading_tendency = misc.random(101);
pref_allying_tendency = misc.random(101);
pref_honesty = misc.random(101);
pref_town_harmony = misc.random(101);
pref_loyalty_concern = misc.random(101);
pref_forgiveness = misc.random(101);
pref_collect_tax = misc.random(101);
pref_hire_unit = misc.random(101);
pref_use_weapon = misc.random(101);
pref_keep_general = misc.random(101);
pref_keep_skilled_unit = misc.random(101);
pref_diplomacy_retry = misc.random(101);
pref_attack_monster = misc.random(101);
pref_spy = misc.random(101);
pref_counter_spy = misc.random(101);
pref_cash_reserve = misc.random(101);
pref_food_reserve = misc.random(101);
pref_use_marine = misc.random(101);
pref_unit_chase_distance = 15+misc.random(15);
pref_repair_concern = misc.random(101);
pref_scout = misc.random(101);
}
//---------- End of function Nation::init_personalty --------//
//--------- Begin of function Nation::process_ai --------//
void Nation::process_ai()
{
//-*********** simulate aat ************-//
#ifdef DEBUG
if(debug_sim_game_type)
return;
#endif
//-*********** simulate aat ************-//
if( config.disable_ai_flag || game.game_mode == GAME_TEST )
return;
//---- if the king has just been killed ----//
int nationRecno = nation_recno;
if( !king_unit_recno )
{
if( think_succeed_king() )
return;
if( think_surrender() )
return;
defeated();
return;
}
//-------- process main AI actions ---------//
process_ai_main();
if( nation_array.is_deleted(nationRecno) ) // the nation can have surrendered
return;
//------ process queued diplomatic messges first --------//
// ##### begin Gilbert 4/10 ######//
if( (info.game_date-nation_recno)%3 == 0 )
{
LOG_MSG("begin process_action(0,ACTION_AI_PROCESS_TALK_MSG)");
process_action(0, ACTION_AI_PROCESS_TALK_MSG);
LOG_MSG("end process_action(0,ACTION_AI_PROCESS_TALK_MSG)");
LOG_MSG(misc.get_random_seed());
if( nation_array.is_deleted(nationRecno) ) // the nation can have surrendered
return;
}
// ##### end Gilbert 4/10 ######//
//--------- process queued actions ----------//
// ##### begin Gilbert 4/10 ######//
if( (info.game_date-nation_recno)%3 == 0 )
{
LOG_MSG("begin process_action()");
process_action();
LOG_MSG("end process_action()");
LOG_MSG(misc.get_random_seed());
if( nation_array.is_deleted(nationRecno) ) // the nation can have surrendered
return;
}
// ##### end Gilbert 4/10 ######//
//--- process action that are on-going and need continous checking ---//
process_on_going_action();
//--------- cheat ---------//
//
// In tutorial mode only so that your opponent won't surrender
// and you won't go to the end game screen.
//
//-------------------------//
if( game.game_mode == GAME_TUTORIAL )
{
if( cash < 100 )
add_cheat( (float)200+misc.random(500) );
if( food < 100 )
food += 1000;
}
//----- think about updating relationship with other nations -----//
if( info.game_date%360 == nation_recno%360 )
ai_improve_relation();
//------ think about surrendering -------//
if( info.game_date%60 == nation_recno%60 )
{
if( think_surrender() )
return;
if( think_unite_against_big_enemy() )
return;
}
}
//---------- End of function Nation::process_ai --------//
//--------- Begin of function Nation::process_on_going_action --------//
//
// Process action that are on-going and need continous checking.
//
void Nation::process_on_going_action()
{
//--- if the nation is in the process of trying to capture an enemy town ---//
if( ai_capture_enemy_town_recno )
{
if( info.game_date%5 == nation_recno%5 )
think_capturing_enemy_town();
}
//----- if the nation is in the process of attacking a target ----//
if( attack_camp_count > 0 )
ai_attack_target_execute(1);
}
//---------- End of function Nation::process_on_going_action --------//
//------- Begin of function Nation::process_ai_main --------//
//
void Nation::process_ai_main()
{
#if defined(DEBUG) && defined(ENABLE_LOG)
String debugStr;
debugStr = "Nation ";
debugStr += nation_recno;
#endif
static short intervalDaysArray[] = { 90, 30, 15, 15 };
int intervalDays = intervalDaysArray[config.ai_aggressiveness-OPTION_LOW];
if( game.game_mode == GAME_TUTORIAL )
intervalDays = 120;
switch( (info.game_date-nation_recno*4) % intervalDays )
{
case 0:
#if defined(DEBUG) && defined(ENABLE_LOG)
debugStr += " think_build_firm";
#endif
think_build_firm();
break;
case 1:
#if defined(DEBUG) && defined(ENABLE_LOG)
debugStr += " think_trading";
#endif
think_trading();
break;
case 2:
#if defined(DEBUG) && defined(ENABLE_LOG)
debugStr += " think_capture";
#endif
think_capture();
break;
case 3:
#if defined(DEBUG) && defined(ENABLE_LOG)
debugStr += " think_explore";
#endif
think_explore();
break;
case 4: // think about expanding its military force
#if defined(DEBUG) && defined(ENABLE_LOG)
debugStr += " think_military";
#endif
think_military();
break;
case 5:
#if defined(DEBUG) && defined(ENABLE_LOG)
debugStr += " think_secret_attack";
#endif
think_secret_attack();
break;
case 6:
#if defined(DEBUG) && defined(ENABLE_LOG)
debugStr += " think_attack_monster";
#endif
think_attack_monster();
break;
case 7:
#if defined(DEBUG) && defined(ENABLE_LOG)
debugStr += " think_diplomacy";
#endif
think_diplomacy();
break;
case 8:
#if defined(DEBUG) && defined(ENABLE_LOG)
debugStr += " think_marine";
#endif
think_marine();
break;
case 9:
#if defined(DEBUG) && defined(ENABLE_LOG)
debugStr += " think_grand_plan";
#endif
think_grand_plan();
break;
case 10:
#if defined(DEBUG) && defined(ENABLE_LOG)
debugStr += " think_reduce_expense";
#endif
think_reduce_expense();
break;
case 11:
#if defined(DEBUG) && defined(ENABLE_LOG)
debugStr += " think_town";
#endif
think_town();
break;
}
LOG_MSG(debugStr);
LOG_MSG(misc.get_random_seed());
}
//---------- End of function Nation::process_ai_main --------//
//--------- Begin of function Nation::think_explore --------//
void Nation::think_explore()
{
}
//---------- End of function Nation::think_explore --------//
//-------- Begin of function Nation::think_succeed_king --------//
//
// return: 1 - a unit succeed the king
// 0 - no unit available for succeeding the king,
// the nation is defeated.
//
int Nation::think_succeed_king()
{
int i, curRating, bestRating=0;
Unit *unitPtr, *bestUnitPtr=NULL;
Firm *firmPtr, *bestFirmPtr=NULL;
int bestWorkerId=0;
//---- try to find the best successor from mobile units ----//
for( i=unit_array.size() ; i>0 ; i-- )
{
if( unit_array.is_deleted(i) )
continue;
unitPtr = unit_array[i];
if( unitPtr->nation_recno != nation_recno || !unitPtr->race_id )
continue;
if( !unitPtr->is_visible() && unitPtr->unit_mode != UNIT_MODE_OVERSEE )
continue;
err_when( unitPtr->skill.combat_level<= 0 );
curRating = 0;
if( unitPtr->race_id == race_id )
curRating += 50;
if( unitPtr->rank_id == RANK_GENERAL )
curRating += 50;
if( unitPtr->skill.skill_id == SKILL_LEADING )
curRating += unitPtr->skill.skill_level;
if( curRating > bestRating )
{
bestRating = curRating;
bestUnitPtr = unitPtr;
}
}
//---- try to find the best successor from military camps ----//
for( i=firm_array.size() ; i>0 ; i-- )
{
if( firm_array.is_deleted(i) )
continue;
firmPtr = firm_array[i];
if( firmPtr->nation_recno != nation_recno )
continue;
//------ only military camps -------//
if( firmPtr->firm_id == FIRM_CAMP )
{
Worker* workerPtr = firmPtr->worker_array;
for(int j=1 ; j<=firmPtr->worker_count ; j++, workerPtr++ )
{
if( !workerPtr->race_id )
continue;
curRating = 0;
if( workerPtr->race_id == race_id )
curRating += 50;
if( workerPtr->rank_id == RANK_GENERAL )
curRating += 50;
if( workerPtr->skill_id == SKILL_LEADING )
curRating += workerPtr->skill_level;
if( curRating > bestRating )
{
bestRating = curRating;
bestUnitPtr = NULL;
bestFirmPtr = firmPtr;
bestWorkerId = j;
}
}
}
}
//------- if the best successor is a mobile unit -------//
if( bestUnitPtr )
{
//-- if the unit is in a command base or seat of power, mobilize it --//
if( !bestUnitPtr->is_visible() )
{
err_when( bestUnitPtr->unit_mode != UNIT_MODE_OVERSEE );
firm_array[bestUnitPtr->unit_mode_para]->mobilize_overseer();
err_when( bestUnitPtr->skill.combat_level<= 0 );
}
//---------- succeed the king -------------//
if( bestUnitPtr->is_visible() ) // it may still be not visible if there is no space for the unit to be mobilized
{
if( bestUnitPtr->spy_recno && bestUnitPtr->true_nation_recno() == nation_recno ) // if this is a spy and he's our spy
spy_array[bestUnitPtr->spy_recno]->drop_spy_identity(); // revert the spy to a normal unit
succeed_king( bestUnitPtr->sprite_recno );
return 1;
}
}
//------- if the best successor is a soldier in a camp -------//
if( bestFirmPtr )
{
int unitRecno = bestFirmPtr->mobilize_worker(bestWorkerId, COMMAND_AI);
if( unitRecno )
{
succeed_king( unitRecno );
return 1;
}
}
//--- if stil not found here, then try to locate the sucessor from villages ---//
Town* townPtr;
for( i=town_array.size() ; i>0 ; i-- )
{
if( town_array.is_deleted(i) )
continue;
townPtr = town_array[i];
if( townPtr->nation_recno != nation_recno )
continue;
if( townPtr->recruitable_race_pop(race_id, 0) > 0 ) // if this town has people with the same race as the original king
{
int unitRecno = townPtr->mobilize_town_people(race_id, 1, 0); // 1-dec pop, 0-don't mobilize spies
if( unitRecno )
{
succeed_king( unitRecno );
return 1;
}
}
}
return 0;
}
//---------- End of function Nation::think_succeed_king ---------//
//--------- Begin of function Nation::ai_improve_relation --------//
//
// This function is called once every year.
//
void Nation::ai_improve_relation()
{
NationRelation* nationRelation;
for( int i=nation_array.size() ; i>0 ; i-- )
{
if( nation_array.is_deleted(i) )
continue;
nationRelation = get_relation(i);
if( nationRelation->status == NATION_HOSTILE )
continue;
//--- It improves the AI relation with nations that have trade with us. ---//
change_ai_relation_level( i, trade_rating(i) / 10 );
//--- decrease the started_war_on_us_count once per year, gradually forgiving other nations' wrong doing ---//
if( nationRelation->started_war_on_us_count > 0
&& misc.random(5-pref_forgiveness/20) > 0 )
{
nationRelation->started_war_on_us_count--;
}
}
}
//---------- End of function Nation::ai_improve_relation --------//