/*
* 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 : OF_MARK2.CPP
//Description : Firm Market Place - AI functions
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//------- Begin of function FirmMarket::process_ai -----------//
//
void FirmMarket::process_ai()
{
//---- think about deleting this firm ----//
if( info.game_date%30==firm_recno%30 )
{
if( think_del() )
return;
}
//----- think about demand trade treaty -----//
if( info.game_date%30==firm_recno%30 )
think_demand_trade_treaty();
//----- think about building a factory next to this market ----//
if( info.game_date%60==firm_recno%60 )
think_market_build_factory();
//-------- think about new trading routes --------//
if( info.game_date < last_import_new_goods_date+60 ) // don't new imports until it's 60 days after the last one was imported
return;
if( can_hire_caravan() )
{
Nation* ownNation = nation_array[nation_recno];
int thinkInterval = 10 + (100-ownNation->pref_trading_tendency)/5; // think once every 10 to 30 days
if( is_market_linked_to_town() )
{
if( info.game_date%thinkInterval==firm_recno%thinkInterval )
think_import_new_product();
if( info.game_date%60 == (firm_recno+20)%60 )
{
//------------------------------------------------------//
// Don't think about increaseing existing product supply
// if we have just import a new goods, it takes time
// to transport and pile up goods.
//------------------------------------------------------//
if( !last_import_new_goods_date ||
info.game_date > last_import_new_goods_date+180 ) // only think increase existing supply 180 days after importing a new one
{
think_increase_existing_product_supply();
}
}
}
if( info.game_date%thinkInterval == firm_recno%thinkInterval )
think_export_product();
}
}
//--------- End of function FirmMarket::process_ai -----------//
//------- Begin of function FirmMarket::think_market_build_factory ------//
//
void FirmMarket::think_market_build_factory()
{
if( no_neighbor_space ) // if there is no space in the neighbor area for building a new firm.
return;
//---- think about building factories to manufacture goods using raw materials in the market place ---//
MarketGoods* marketGoods = market_goods_array;
Firm* firmPtr;
ai_should_build_factory_count = 2; // always set it to 2, so think_build_factory() will start to build a market as soon as there is a need
for( int i=0 ; iraw_id )
continue;
if( marketGoods->stock_qty < 250 ) // only when the stock is >= 250
continue;
//----- check if the raw materials are from a local mine, if so don't build a factory, we only build a factory to manufacture goods using raw materials from a remote town.
int j;
for( j=0 ; jfirm_id == FIRM_MINE &&
firmPtr->nation_recno == nation_recno &&
((FirmMine*)firmPtr)->raw_id == marketGoods->raw_id )
{
break;
}
}
if( jraw_id) )
return;
}
}
//-------- End of function FirmMarket::think_market_build_factory ------//
//------- Begin of function FirmMarket::is_market_linked_to_town --------//
//
// Return whether this market is linked (enabled link) to any town.
//
// [int] ownBaseTownOnly - whether only count own base town or not.
// (default:0)
//
int FirmMarket::is_market_linked_to_town(int ownBaseTownOnly)
{
Town* townPtr;
for(int i=0 ; ination_recno == nation_recno &&
townPtr->is_base_town )
{
return 1;
}
}
else
{
return 1;
}
}
return 0;
}
//--------- End of function FirmMarket::is_market_linked_to_town --------//
//------ Begin of function FirmMarket::ai_update_link_status ------//
//
void FirmMarket::ai_update_link_status()
{
//---- consider enabling/disabling links to firms ----//
Nation* nationPtr = nation_array[nation_recno];
Firm* firmPtr;
int rc;
int i;
for(i=0; ifirm_id == FIRM_FACTORY;
else
rc = firmPtr->firm_id == FIRM_MINE || firmPtr->firm_id == FIRM_FACTORY; // for output raw materials to the factory to manufacture
toggle_firm_link( i+1, rc, COMMAND_AI ); // enable the link
}
//----- always enable links to towns as there is no downsides for selling goods to the villagers ----//
for(i=0; i 0 )
{
no_linked_town_since_date = 0; // reset it
return 0;
}
else
{
no_linked_town_since_date = info.game_date;
}
//---- don't delete it if there are still signiciant stockhere ---//
if( stock_value_index() >= 10 )
{
Nation* ownNation = nation_array[nation_recno];
//--- if the market has been sitting idle for too long, delete it ---//
if( info.game_date < no_linked_town_since_date
+ 180 + 180 * ownNation->pref_trading_tendency / 100 )
{
return 0;
}
}
//------------------------------------------------//
ai_del_firm();
return 1;
}
//--------- End of function FirmMarket::think_del -----------//
//------- Begin of function FirmMarket::think_import_new_product ------//
//
// Think about importing goods to sell in this market place.
//
int FirmMarket::think_import_new_product()
{
//---- check if the market place has free space for new supply ----//
int i, j, emptySlot=0;
MarketGoods *marketGoods = market_goods_array;
for( i=0 ; iproduct_raw_id && !marketGoods->raw_id )
emptySlot++;
}
if( emptySlot==0 )
return 0;
//--- update what products are needed for this market place ---//
Town* townPtr;
short needProductSupplyPop[MAX_PRODUCT]; // the total population in the towns linked to the market that needs the supply of the product
Nation* nationPtr = nation_array[nation_recno];
memset( needProductSupplyPop, 0, sizeof(needProductSupplyPop) );
for( i=0; iregion_id != region_id )
continue;
if( !townPtr->is_base_town ) // don't import if it isn't a base town
continue;
//------------------------------------------------//
//
// Only if the population of the town is equal or
// larger than minTradePop, the AI will try to do trade.
// The minTradePop is between 10 to 20 depending on the
// pref_trading_tendency.
//
//------------------------------------------------//
townPtr->update_product_supply();
for( j=0 ; jhas_product_supply[j] )
needProductSupplyPop[j] += townPtr->population;
}
}
//---- think about importing the products that need supply ----//
int minTradePop = 10;
if( is_retail_market )
{
for( int productId=1 ; productId<=MAX_PRODUCT ; productId++ )
{
if( needProductSupplyPop[productId-1] >= minTradePop || emptySlot==MAX_MARKET_GOODS ) // if market is empty, try to import some goods
{
if( think_import_specific_product(productId) )
{
last_import_new_goods_date = info.game_date;
return 1;
}
}
}
}
//----------------------------------------------------------//
// Think about importing the raw materials of the needed
// products and build factories to manufacture them ourselves
//----------------------------------------------------------//
//--- first check if we can build a new factory to manufacture the products ---//
if( !is_retail_market && is_market_linked_to_town(1) ) // 1-only count towns that are our own and are base towns
{
if( !no_neighbor_space &&
nationPtr->total_jobless_population >= MAX_WORKER*2 &&
can_hire_caravan() >= 2 ) // if there is a shortage of caravan supplies, use it for transporting finished products instead of raw materials
{
if( nationPtr->can_ai_build(FIRM_FACTORY) )
{
for( int productId=1 ; productId<=MAX_PRODUCT ; productId++ )
{
if( needProductSupplyPop[productId-1] >= minTradePop )
{
if( think_mft_specific_product(productId) )
{
last_import_new_goods_date = info.game_date;
return 1;
}
}
}
}
}
}
return 0;
}
//--------- End of function FirmMarket::think_import_new_product -------//
//--- Begin of function FirmMarket::think_increase_existing_product_supply ---//
//
// Think about increasing the supply of existing products.
//
int FirmMarket::think_increase_existing_product_supply()
{
MarketGoods *marketGoods = market_goods_array;
for( int i=0 ; iproduct_raw_id )
{
if( marketGoods->stock_qty < MAX_MARKET_STOCK/10 &&
marketGoods->month_demand * 0.8 > marketGoods->supply_30days() ) // the supply falls behind the demand by at least 20%
{
if( think_import_specific_product(marketGoods->product_raw_id) )
return 1;
}
}
}
return 0;
}
//---- End of function FirmMarket::think_increase_existing_product_supply ---//
//--- Begin of function FirmMarket::think_import_specific_product ---//
//
// Think about importing a specific product.
//
int FirmMarket::think_import_specific_product(int productId)
{
int i, firmRecno;
Firm* firmPtr, *bestFirmPtr=NULL;
Nation* nationPtr = nation_array[nation_recno];
int stockLevel, curRating, bestRating=0;
int canHireCaravan = can_hire_caravan();
RawInfo* rawInfo = raw_res[productId];
for( i=rawInfo->product_supply_firm_array.size() ; i>0 ; i-- )
{
firmRecno = rawInfo->get_product_supply_firm(i);
if( firm_array.is_deleted(firmRecno) || firmRecno==firm_recno )
continue;
//-- if there is already a caravan travelling between two points --//
firmPtr = firm_array[firmRecno];
if( firmPtr->region_id != region_id )
continue;
//-----------------------------------------//
// The rating of a supply is determined by:
// - distance
// - supply
// - nation relationship
//-----------------------------------------//
//------ determine the stock level of this supply ------//
stockLevel = 0;
//---- think about inputing goods from this factory ----//
if( firmPtr->firm_id == FIRM_FACTORY )
{
if( firmPtr->nation_recno != nation_recno ) // can import goods from own factories only
continue;
FirmFactory* firmFactory = (FirmFactory*) firmPtr;
if( firmFactory->product_raw_id == productId )
stockLevel = 100 * (int) firmFactory->stock_qty / (int) firmFactory->max_stock_qty;
}
//---- think about inputing goods from this market ----//
else if( firmPtr->firm_id == FIRM_MARKET )
{
//--- if this is a foreign sale market, don't import from it (e.g. Nation A's market built near Nation B's village -----//
if( firmPtr->nation_recno != nation_recno ) // if this is not our market
{
int j;
for( j=firmPtr->linked_town_count-1 ; j>=0 ; j-- )
{
Town* townPtr = town_array[ firmPtr->linked_town_array[j] ];
if( townPtr->nation_recno == firmPtr->nation_recno )
break;
}
if( j<0 ) // if this market is not linked to its own town (then it must be a foreign market)
continue;
}
//-- only either from own market place or from nations that trade with you --//
if( nation_array[firmPtr->nation_recno]->get_relation(nation_recno)->trade_treaty == 0 )
continue;
MarketGoods* marketGoods = ((FirmMarket*)firmPtr)->market_product_array[productId-1];
//--- if this market has the supply of this goods ----//
if( marketGoods && marketGoods->supply_30days() > 0 )
{
stockLevel = 100 * (int) marketGoods->stock_qty / MAX_MARKET_STOCK;
}
}
//----------------------------------------------//
if( firmPtr->nation_recno == nation_recno )
{
if( stockLevel < 10 ) // for our own market, the stock level requirement is lower
continue;
curRating = 50;
}
else
{
if( stockLevel < 20 ) // for other player's market, only import when the stock level is high enough
continue;
curRating = nationPtr->get_relation_status(firmPtr->nation_recno) * 5;
}
//---- calculate the current overall rating ----//
curRating += stockLevel/2 + world.distance_rating(center_x, center_y, firmPtr->center_x, firmPtr->center_y);
//----------- compare ratings -------------//
if( curRating > bestRating )
{
bestRating = curRating;
bestFirmPtr = firmPtr;
}
}
//---- if a suitable supplier is found -----//
if( bestFirmPtr )
return ai_create_new_trade(bestFirmPtr, 0, PICK_UP_PRODUCT_FIRST+productId-1);
return 0;
}
//---- End of function FirmMarket::think_import_specific_product ----//
//------- Begin of function FirmMarket::think_export_product -----------//
//
// Think about exporting products from this market to another market.
//
int FirmMarket::think_export_product()
{
//--- first check if there is any excessive supply for export ---//
int exportProductId = 0;
MarketGoods *marketGoods = market_goods_array;
for( int i=0 ; iproduct_raw_id )
{
if( marketGoods->stock_qty > MAX_MARKET_STOCK * 3 / 4 &&
marketGoods->month_demand < marketGoods->supply_30days() / 2 ) // the supply is at least double of the demand
{
exportProductId = marketGoods->product_raw_id;
break;
}
}
}
if( !exportProductId )
return 0;
//----- locate for towns that do not have the supply of the product ----//
Town* townPtr;
Nation* nationPtr = nation_array[nation_recno];
for( int townRecno=town_array.size() ; townRecno>0 ; townRecno-- )
{
if( town_array.is_deleted(townRecno) )
continue;
townPtr = town_array[townRecno];
if( townPtr->population < 20 - (10*nationPtr->pref_trading_tendency/100) ) // 10 to 20 as the minimum population for considering trade
continue;
if( townPtr->has_product_supply[exportProductId-1] ) // if the town already has the supply of product, return now
continue;
if( townPtr->region_id != region_id )
continue;
if( townPtr->no_neighbor_space ) // if there is no space in the neighbor area for building a new firm.
continue;
if( m.points_distance( center_x, center_y, townPtr->center_x, center_y ) > MAX_WORLD_X_LOC/4 ) // don't consider if it is too far away
continue;
if( townPtr->town_recno &&
nationPtr->get_relation_status(townPtr->town_recno) < NATION_FRIENDLY ) // only build markets to friendly nation's town
{
continue;
}
//-----------------------------------------//
if( townPtr->nation_recno )
{
//--- if it's a nation town, only export if we have trade treaty with it ---//
if( !nationPtr->get_relation(townPtr->nation_recno)->trade_treaty )
continue;
}
else
{
//--- if it's an independent town, only export if the resistance is low ---//
if( townPtr->average_resistance(nation_recno) > INDEPENDENT_LINK_RESISTANCE )
continue;
}
//----- think about building a new market to the town for exporting our goods -----//
if( think_build_export_market(townRecno) )
return 1;
}
return 0;
}
//--------- End of function FirmMarket::think_export_product -----------//
//------- Begin of function FirmMarket::think_build_export_market -----------//
//
// Think about export goods of this market to other markets.
//
int FirmMarket::think_build_export_market(int townRecno)
{
Town* townPtr = town_array[townRecno];
Firm* firmPtr;
Nation* nationPtr = nation_array[nation_recno];
//---- see if we already have a market linked to this town ----//
for( int i=0 ; ilinked_firm_count ; i++ )
{
firmPtr = firm_array[ townPtr->linked_firm_array[i] ];
if( firmPtr->firm_id != FIRM_MARKET || firmPtr->firm_recno==firm_recno )
continue;
//--- if we already have a market there, no need to build a new market ----//
if( firmPtr->nation_recno == nation_recno )
return 0;
}
//--- if there is no market place linked to this town, we can set up one ourselves ---//
short buildXLoc, buildYLoc;
if( !nationPtr->find_best_firm_loc(FIRM_MARKET, townPtr->loc_x1, townPtr->loc_y1, buildXLoc, buildYLoc) )
{
townPtr->no_neighbor_space = 1;
return 0;
}
nationPtr->add_action(buildXLoc, buildYLoc, townPtr->loc_x1, townPtr->loc_y1, ACTION_AI_BUILD_FIRM, FIRM_MARKET);
return 1;
}
//--------- End of function FirmMarket::think_build_export_market -----------//
//--- Begin of function FirmMarket::think_mft_specific_product ---//
//
// Think about importing a specific type of raw material and build a
// factory to manufacture ourselves.
//
// rawId - id. of the raw material.
//
int FirmMarket::think_mft_specific_product(int rawId)
{
int i, j, firmRecno;
Firm* firmPtr, *bestFirmPtr=NULL;
Nation* nationPtr = nation_array[nation_recno];
int stockLevel, curRating, bestRating=0;
RawInfo* rawInfo = raw_res[rawId];
for( i=rawInfo->raw_supply_firm_array.size() ; i>0 ; i-- )
{
firmRecno = rawInfo->get_raw_supply_firm(i);
if( firm_array.is_deleted(firmRecno) || firmRecno == firm_recno )
continue;
//-- if there is already a caravan travelling between two points --//
firmPtr = firm_array[firmRecno];
if( firmPtr->region_id != region_id )
continue;
//-- if this is our own supply, don't import the raw material, but import the finished goods instead. --//
if( firmPtr->nation_recno == nation_recno )
continue;
//-----------------------------------------//
// The rating of a supply is determined by:
// - distance
// - supply
// - nation relationship
//-----------------------------------------//
//------ determine the stock level of this supply ------//
stockLevel = 0;
if( firmPtr->firm_id == FIRM_MARKET )
{
//-- only either from own market place or from nations that trade with you --//
if( nation_array[firmPtr->nation_recno]->get_relation(nation_recno)->trade_treaty == 0 )
continue;
//----- check if this market is linked to any mines directly ----//
for( j=firmPtr->linked_firm_count-1 ; j>=0 ; j-- )
{
Firm* linkedFirm = firm_array[ firmPtr->linked_firm_array[j] ];
if( linkedFirm->firm_id == FIRM_MINE && linkedFirm->nation_recno == firmPtr->nation_recno )
{
if( ((FirmMine*)linkedFirm)->raw_id == rawId )
break;
}
}
if( j<0 ) // this market does not have any direct supplies, so don't pick up goods from it
continue;
//---------------------------------------------------------------//
MarketGoods* marketGoods = ((FirmMarket*)firmPtr)->market_goods_array;
for( j=0 ; jstock_qty > MAX_MARKET_STOCK / 5 )
{
if( marketGoods->raw_id == rawId )
stockLevel = 100 * (int) marketGoods->stock_qty / MAX_MARKET_STOCK;
}
}
}
if( stockLevel < 50 ) // if the stock is too low, don't consider it
continue;
//---- calculate the current overall rating ----//
NationRelation* nationRelation = nationPtr->get_relation(firmPtr->nation_recno);
curRating = stockLevel
- 100 * m.points_distance( center_x, center_y,
firmPtr->center_x, firmPtr->center_y ) / MAX_WORLD_X_LOC;
if( firmPtr->nation_recno == nation_recno )
curRating += 100;
else
curRating += nationRelation->status * 20;
//----------- compare ratings -------------//
if( curRating > bestRating )
{
bestRating = curRating;
bestFirmPtr = firmPtr;
}
}
if( !bestFirmPtr )
return 0;
if( !ai_create_new_trade(bestFirmPtr, 0, PICK_UP_RAW_FIRST+rawId-1) )
return 0;
return 1;
}
//---- End of function FirmMarket::think_mft_specific_product ----//
//------- Begin of function FirmMarket::think_demand_trade_treaty ------//
//
void FirmMarket::think_demand_trade_treaty()
{
Nation* nationPtr = nation_array[nation_recno];
int nationRecno;
//----- demand towns to open up market ----//
for( int i=0 ; ination_recno;
if( nationRecno )
nationPtr->get_relation(nationRecno)->ai_demand_trade_treaty++;
}
}
}
//-------- End of function FirmMarket::think_demand_trade_treaty -------//
//------- Begin of function FirmMarket::ai_create_new_trade ------//
//
int FirmMarket::ai_create_new_trade(Firm* firmPtr, int stop1PickUpType, int stop2PickUpType)
{
//---- see if there is already a caravan moving along the route -----//
Nation* ownNation = nation_array[nation_recno];
UnitCaravan* unitCaravan;
int rc, stop1Id, stop2Id;
int caravanInRouteCount=0;
for( int i=ownNation->ai_caravan_count-1 ; i>=0 ; i-- )
{
unitCaravan = (UnitCaravan*) unit_array[ ownNation->ai_caravan_array[i] ];
err_when( unitCaravan->nation_recno != nation_recno );
err_when( unitCaravan->unit_id != UNIT_CARAVAN );
if( unitCaravan->stop_defined_num < 2 )
continue;
if( unitCaravan->stop_array[0].firm_recno == firm_recno &&
unitCaravan->stop_array[1].firm_recno == firmPtr->firm_recno )
{
stop1Id = 1;
stop2Id = 2;
}
else if( unitCaravan->stop_array[1].firm_recno == firm_recno &&
unitCaravan->stop_array[0].firm_recno == firmPtr->firm_recno )
{
stop1Id = 2;
stop2Id = 1;
}
else
{
continue;
}
//------- add the goods to the pick up list ----//
rc = 0;
if( stop1PickUpType && !unitCaravan->has_pick_up_type(stop1Id, stop1PickUpType) )
{
if( unitCaravan->is_visible() ) // can't set stop when the caravan is in a firm
unitCaravan->set_stop_pick_up(stop1Id, stop1PickUpType, COMMAND_AI);
rc = 1;
}
if( stop2PickUpType && !unitCaravan->has_pick_up_type(stop2Id, stop2PickUpType) )
{
if( unitCaravan->is_visible() ) // can't set stop when the caravan is in a firm
unitCaravan->set_stop_pick_up(stop2Id, stop2PickUpType, COMMAND_AI);
rc = 1;
}
if( rc ) // don't add one if we can utilize an existing one.
return 1;
caravanInRouteCount++;
}
if( caravanInRouteCount >= 2 ) // don't have more than 2 caravans on a single route
return 0;
//----------- hire a new caravan -----------//
int unitRecno = hire_caravan(COMMAND_AI);
if( !unitRecno )
return 0;
//----------- set up the trade route ----------//
unitCaravan = (UnitCaravan*) unit_array[unitRecno];
unitCaravan->set_stop(2, firmPtr->loc_x1, firmPtr->loc_y1, COMMAND_AI);
err_when( unitCaravan->stop_array[0].firm_recno == firmPtr->firm_recno ); // cannot set both stops to the same firm
unitCaravan->set_stop_pick_up(1, NO_PICK_UP, COMMAND_AI);
unitCaravan->set_stop_pick_up(2, NO_PICK_UP, COMMAND_AI);
if( stop1PickUpType )
unitCaravan->set_stop_pick_up(1, stop1PickUpType, COMMAND_AI);
if( stop2PickUpType )
unitCaravan->set_stop_pick_up(2, stop2PickUpType, COMMAND_AI);
return 1;
}
//-------- End of function FirmMarket::ai_create_new_trade -------//