/*
* 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_SEEK.CPP
//Description: AI - action progressing functions
#include
#include
#include
#include
#include
#include
#include
#include
//--------- Begin of function Nation::seek_mine --------//
//
// xLoc, yLoc - reference vars for returning the building
// location.
// refXLoc, refYLoc - reference vars for returning the exact
// location of the raw material.
//
// return: >0 the raw id. of the site.
// ==0 no suitable site found.
//
int Nation::seek_mine(short& xLoc, short& yLoc, short& refXLoc, short& refYLoc)
{
err_when( site_array.untapped_raw_count < 0 );
if( site_array.untapped_raw_count==0 )
return 0;
int raw_kind_mined[MAX_RAW], i;
Firm *firmPtr;
FirmMine *minePtr;
//-----------------------------------------------------------------//
// count each kind of raw material that is being mined
//-----------------------------------------------------------------//
memset(raw_kind_mined, 0, sizeof(int)*MAX_RAW);
for(i=0; icast_to_FirmMine();
if( minePtr->raw_id>=1 && minePtr->raw_id<=MAX_RAW )
raw_kind_mined[minePtr->raw_id-1]++;
}
//-------------------- define parameters ----------------------//
FirmInfo *firmInfo = firm_res[FIRM_MINE];
Location *locPtr, *siteLocPtr;
Site *sitePtr;
Town *townPtr;
int nearSite[MAX_RAW], minDist[MAX_RAW], townWithMine[MAX_RAW];
short buildXLoc[MAX_RAW], buildYLoc[MAX_RAW];
short dist;
int canBuild, connected, allHave;
int j, k, siteId;
memset(townWithMine, 0, sizeof(int)*MAX_RAW);
memset(nearSite, 0, sizeof(int)*MAX_RAW);
memset(buildXLoc, 0, sizeof(short)*MAX_RAW);
memset(buildYLoc, 0, sizeof(short)*MAX_RAW);
for(i=0; i0; i--)
{
if(site_array.is_deleted(i))
continue;
sitePtr = site_array[i];
if( sitePtr->site_type != SITE_RAW )
continue;
siteLocPtr = world.get_loc(sitePtr->map_x_loc, sitePtr->map_y_loc);
if(!siteLocPtr->can_build_firm())
continue;
siteId = sitePtr->object_id - 1;
err_when(siteId<0 || siteId>MAX_RAW);
if(townWithMine[siteId])
continue; // a site connected to town is found before
//--------------------------------------------//
// continue if action to this site already exist
//--------------------------------------------//
if(is_action_exist(-1, -1, sitePtr->map_x_loc, sitePtr->map_y_loc, ACTION_AI_BUILD_FIRM, FIRM_MINE))
continue;
for(j=0; jloc_x1, townPtr->loc_y1);
//-********* codes to move to other territory ***********-//
if(siteLocPtr->region_id!=locPtr->region_id)
continue; // not on the same territory
dist = m.points_distance(sitePtr->map_x_loc, sitePtr->map_y_loc, townPtr->center_x, townPtr->center_y);
//-------------------------------------------------------------------------//
// check whether a mine is already connected to this town, if so, use it
//-------------------------------------------------------------------------//
for(connected=0, k=townPtr->linked_firm_count-1; k>=0; k--)
{
err_when(!townPtr->linked_firm_array[k] || firm_array.is_deleted(townPtr->linked_firm_array[k]));
firmPtr = firm_array[townPtr->linked_firm_array[k]];
if(firmPtr->nation_recno==nation_recno && firmPtr->firm_id==FIRM_MINE)
{
connected++;
break;
}
}
//-------------------------------------------------------------------------//
// calculate the minimum distance from own towns
//-------------------------------------------------------------------------//
if(distmap_x_loc-firmInfo->loc_width+1; ix<=sitePtr->map_x_loc && !canBuild; ix++)
{
if(ix<0 || ix>=MAX_WORLD_X_LOC)
continue;
for(int iy=sitePtr->map_y_loc-firmInfo->loc_height+1; iy<=sitePtr->map_y_loc && !canBuild; iy++)
{
if(iy<0 || iy>=MAX_WORLD_Y_LOC)
continue;
if(world.can_build_firm(ix, iy, FIRM_MINE))
{
canBuild++;
buildXLoc[siteId] = ix;
buildYLoc[siteId] = iy;
break;
}
}
}
if(canBuild)
{
nearSite[siteId] = i;
minDist[siteId] = dist;
if(connected && dist<=EFFECTIVE_FIRM_TOWN_DISTANCE)
townWithMine[siteId]++;
}
}
}
for(allHave=1, j=0; jraw_kind_mined[j]) // scan for the kind of material with least num of this site
{
weight = raw_kind_mined[j];
pos = j;
}
}
if(!siteRecno && pos>=0)
siteRecno = nearSite[pos];
if(siteRecno)
{
sitePtr = site_array[siteRecno];
xLoc = buildXLoc[pos];
yLoc = buildYLoc[pos];
refXLoc = sitePtr->map_x_loc;
refYLoc = sitePtr->map_y_loc;
err_when((xLoc-refXLoc)>=firm_res[FIRM_MINE]->loc_width || (yLoc-refYLoc)>=firm_res[FIRM_MINE]->loc_height);
//--------------------------------------------------------------//
// do some adjustment such that the firm will be built far away
// from other firms by at least one step.
//--------------------------------------------------------------//
seek_best_build_mine_location(xLoc, yLoc, sitePtr->map_x_loc, sitePtr->map_y_loc);
err_when((xLoc-refXLoc)>=firm_res[FIRM_MINE]->loc_width || (yLoc-refYLoc)>=firm_res[FIRM_MINE]->loc_height);
return sitePtr->object_id; // the raw id.
}
return 0;
}
//---------- End of function Nation::seek_mine --------//
//----- Begin of function Nation::seek_best_build_mine_location -----//
//
// xLoc - a possible x location to build the mine, the final location is also returned by this reference
// yLoc - a possible y location to build the mine.
//
// mapXLoc - the x location of the raw material
// mapYLoc - the y location of the raw material
//
void Nation::seek_best_build_mine_location(short& xLoc, short& yLoc, short mapXLoc, short mapYLoc)
{
//--------------- define parameters -----------------//
FirmInfo *firmInfo = firm_res[FIRM_MINE];
int weight = 0, maxWeight = 0;
short xLeftLimit = mapXLoc-firmInfo->loc_width+1;
short yLeftLimit = mapYLoc-firmInfo->loc_height+1;
short resultXLoc = xLoc;
short resultYLoc = yLoc;
short ix, iy;
for(ix=xLeftLimit; ix<=mapXLoc; ix++)
{
if(ix<0 || ix>=MAX_WORLD_X_LOC)
continue;
for(iy=yLeftLimit; iy<=mapYLoc; iy++)
{
if(iy<0 || iy>=MAX_WORLD_Y_LOC)
continue;
//---------------------------------------------------------------//
// remove previous checked and useless locaton
// Since all the possible location is checked from the top left
// to the bottom right, the previous checked location should all
// be impossible to build the mine.
//---------------------------------------------------------------//
if(ixloc_width, firmInfo->loc_height, weight);
if(weight>maxWeight)
{
resultXLoc = ix;
resultYLoc = iy;
if(weight == MAX_SCORE) // very good locaton, stop checking
break;
maxWeight = weight;
}
}
}
}
xLoc = resultXLoc;
yLoc = resultYLoc;
}
//---------- End of function Nation::seek_best_build_mine_location --------//
//--------- Begin of function Nation::cal_location_score --------//
// Called by seek_best_build_mine_location() to calculate a specifed
// location score to build the mine.
//
// x1 - the upper left corner x location of the firm
// y1 - the upper left corner y location of the firm
// width - the width of the firm
// height - the height of the firm
// score - return the location score
//
void Nation::cal_location_score(short x1, short y1, short width, short height, int& score)
{
//-----------------------------------------------------------------//
// the score is calculated as follows, for instance, the firm is
// 2x2 in dimension
//
// LU U U RU L--left, R--right, U--upper, O--lower
// L x x R
// L x x R
// LO O O RO
//
// if any L can build, score += 1, if all set of L can build the total
// score of this edge is 100. For each corner, the score is 50 if
// can build. Thus, the max. score == 600
//
//-----------------------------------------------------------------//
short x, y, i, count;
score = 0;
//---------- left edge ---------//
if((x=x1-1)>=0)
{
for(i=0, count=0; ican_build_firm())
count++;
}
score += (count==height) ? 100 : count;
}
//---------- upper edge ---------//
if((y=y1-1)>=0)
{
for(i=0, count=0; ican_build_firm())
count++;
}
score += (count==width) ? 100 : count;
}
//---------- right edge ---------//
if((x=x1+width)can_build_firm())
count++;
}
score += (count==height) ? 100 : count;
}
//----------- lower edge -----------//
if((y=y1+height)can_build_firm())
count++;
}
score += (count==width) ? 100 : count;
}
//------------------------------------------//
// extra score
//------------------------------------------//
//------- upper left corner -------//
if(x1>0 && y1>0 && world.get_loc(x1-1, y1-1)->can_build_firm())
score += 50;
//------- upper right corner ---------//
if(x10 && world.get_loc(x1+1, y1-1)->can_build_firm())
score += 50;
//----------- lower left corner ----------//
if(x1>0 && y1can_build_firm())
score += 50;
//------- lower right corner ---------//
if(x1can_build_firm())
score += 50;
}
//---------- End of function Nation::cal_location_score --------//
//----------- Begin of function Nation::find_best_firm_loc ----------//
//
// Determine the location of a new firm. It's best to have the
// new firm within the refective range of: towns, factories and
// mines.
//
// buildFirmId - id. of the firm to be built
// refXLoc, refYLoc - either the location of a town or a firm,
// the market must be built next to it.
// resultXLoc, resultYLoc - result location of the firm.
//
// return: 1 - succeed, 0 - fail
//
int Nation::find_best_firm_loc(short buildFirmId, short refXLoc, short refYLoc, short& resultXLoc, short& resultYLoc)
{
Location *locPtr = world.get_loc(refXLoc, refYLoc);
short centerX, centerY, refX1, refY1, refX2, refY2;
//-------- get the refective area ---------//
int originFirmRecno=0, originTownRecno=0;
Firm* firmPtr;
Town* townPtr;
BYTE buildRegionId = locPtr->region_id;
int buildIsPlateau = locPtr->is_plateau();
if( locPtr->is_firm() )
{
originFirmRecno = locPtr->firm_recno();
firmPtr = firm_array[originFirmRecno];
centerX = firmPtr->center_x;
centerY = firmPtr->center_y;
refX1 = centerX - EFFECTIVE_FIRM_FIRM_DISTANCE;
refY1 = centerY - EFFECTIVE_FIRM_FIRM_DISTANCE;
refX2 = centerX + EFFECTIVE_FIRM_FIRM_DISTANCE;
refY2 = centerY + EFFECTIVE_FIRM_FIRM_DISTANCE;
if( firmPtr->firm_id == FIRM_HARBOR )
{
buildRegionId = ((FirmHarbor*)firmPtr)->land_region_id;
buildIsPlateau = 0;
}
}
else if( locPtr->is_town() )
{
originTownRecno = locPtr->town_recno();
townPtr = town_array[originTownRecno];
centerX = townPtr->center_x;
centerY = townPtr->center_y;
refX1 = centerX - EFFECTIVE_FIRM_TOWN_DISTANCE;
refY1 = centerY - EFFECTIVE_FIRM_TOWN_DISTANCE;
refX2 = centerX + EFFECTIVE_FIRM_TOWN_DISTANCE;
refY2 = centerY + EFFECTIVE_FIRM_TOWN_DISTANCE;
}
else
err_here();
//------------------------------------------------------//
FirmInfo* firmInfo = firm_res[buildFirmId];
int firmLocWidth = firmInfo->loc_width;
int firmLocHeight = firmInfo->loc_height;
refX1 -= firmLocWidth/2; // since we use loc_x1 as the building reference, we need to shift it so it will match the use of center_x in effective distance
refY1 -= firmLocHeight/2;
refX1 = max(0, refX1);
refY1 = max(0, refY1);
if( refX2 - firmLocWidth/2 >= MAX_WORLD_X_LOC )
refX2 = MAX_WORLD_X_LOC-1;
else
refX2 -= firmLocWidth/2;
if( refY2 - firmLocHeight/2 >= MAX_WORLD_Y_LOC )
refY2 = MAX_WORLD_Y_LOC-1;
else
refY2 -= firmLocHeight/2;
err_when( refX2 >= MAX_WORLD_X_LOC );
err_when( refY2 >= MAX_WORLD_Y_LOC );
//-------- build a matrix on the refective area ---------//
int refWidth=refX2-refX1+1, refHeight=refY2-refY1+1;
short* refMatrix = (short*) mem_add( sizeof(short) * refWidth * refHeight );
short* refMatrixPtr;
//------ initialize the weights of the matrix ------//
int xLoc, yLoc; // inner locations in the matrix receives more weights than outer locations do
int t1, t2;
for( yLoc=refY1 ; yLoc<=refY2 ; yLoc++ )
{
refMatrixPtr = refMatrix + (yLoc-refY1)*refWidth;
locPtr = world.get_loc( refX1, yLoc );
for( xLoc=refX1 ; xLoc<=refX2 ; xLoc++, refMatrixPtr++, locPtr++ )
{
t1 = abs(xLoc-centerX);
t2 = abs(yLoc-centerY);
if( locPtr->region_id != buildRegionId ||
locPtr->is_plateau() != buildIsPlateau ||
locPtr->is_power_off() )
{
*refMatrixPtr = -1000;
}
else
{
*refMatrixPtr = 10-max(t1, t2); // it's negative value, and the value is lower for the outer ones
}
}
}
//----- calculate weights of the locations in the matrix ----//
int xLocB, yLocB, weightAdd, weightReduce;
short refBX1, refBY1, refBX2, refBY2;
short refCX1, refCY1, refCX2, refCY2;
for( yLoc=refY1 ; yLoc<=refY2 ; yLoc++ )
{
locPtr = world.get_loc(refX1, yLoc);
for( xLoc=refX1 ; xLoc<=refX2 ; xLoc++, locPtr++ )
{
if( locPtr->region_id != buildRegionId ||
locPtr->is_plateau() != buildIsPlateau ||
locPtr->is_power_off() )
{
continue;
}
//------- if there is a firm on the location ------//
weightAdd = 0;
weightReduce = 0;
if( locPtr->is_firm() )
{
firmPtr = firm_array[locPtr->firm_recno()];
if( buildFirmId==FIRM_MARKET || buildFirmId==FIRM_FACTORY ) // only factories & market places need building close to other firms
{
int rc = 1;
if( firmPtr->nation_recno != nation_recno )
rc = 0;
//----- check if the firm is of the right type ----//
if( buildFirmId==FIRM_MARKET ) // build a market place close to mines and factories
{
if( firmPtr->firm_id!=FIRM_MINE && firmPtr->firm_id!=FIRM_FACTORY ) // market places should be built close to factories and mines and they are the only two firms that influence the location of the market place
rc = 0;
}
else if( buildFirmId==FIRM_FACTORY ) // build a factory close to mines and market places
{
if( firmPtr->firm_id!=FIRM_MINE && firmPtr->firm_id!=FIRM_MARKET ) // market places should be built close to factories and mines and they are the only two firms that influence the location of the market place
rc = 0;
}
//------------------------------------------/
if( rc )
{
refBX1 = firmPtr->center_x - EFFECTIVE_FIRM_FIRM_DISTANCE;
refBY1 = firmPtr->center_y - EFFECTIVE_FIRM_FIRM_DISTANCE;
refBX2 = firmPtr->center_x + EFFECTIVE_FIRM_FIRM_DISTANCE;
refBY2 = firmPtr->center_y + EFFECTIVE_FIRM_FIRM_DISTANCE;
weightAdd = 30;
}
}
refCX1 = firmPtr->loc_x1-1; // add negative weights on space around this firm
refCY1 = firmPtr->loc_y1-1; // so to prevent firms from building right next to the firm
refCX2 = firmPtr->loc_x2+1; // and leave some space for walking path.
refCY2 = firmPtr->loc_y2+1;
weightReduce = 20;
}
//------- if there is a town on the location ------//
else if( locPtr->is_town() )
{
townPtr = town_array[locPtr->town_recno()];
refBX1 = townPtr->center_x - EFFECTIVE_FIRM_TOWN_DISTANCE;
refBY1 = townPtr->center_y - EFFECTIVE_FIRM_TOWN_DISTANCE;
refBX2 = townPtr->center_x + EFFECTIVE_FIRM_TOWN_DISTANCE;
refBY2 = townPtr->center_y + EFFECTIVE_FIRM_TOWN_DISTANCE;
weightAdd = townPtr->population*2;
//----- if the town is not our own -----//
if(townPtr->nation_recno != nation_recno)
{
if( townPtr->nation_recno==0 ) // it's an independent town
weightAdd = weightAdd * (100-townPtr->average_resistance(nation_recno)) / 100;
else // more friendly nations get higher weights
{
int relationStatus = get_relation_status(townPtr->nation_recno);
if( relationStatus >= NATION_NEUTRAL )
weightAdd = weightAdd * (relationStatus-NATION_NEUTRAL+1) / 4;
}
}
refCX1 = townPtr->loc_x1-1; // add negative weights on space around this firm
refCY1 = townPtr->loc_y1-1; // so to prevent firms from building right next to the firm
refCX2 = townPtr->loc_x2+1; // and leave some space for walking path.
refCY2 = townPtr->loc_y2+1;
weightReduce = 100;
}
else
continue;
//------ add weights to the matrix ------//
if( weightAdd )
{
for( yLocB=max(refY1,refBY1) ; yLocB<=min(refY2,refBY2) ; yLocB++ )
{
xLocB = max(refX1,refBX1);
refMatrixPtr = refMatrix + (yLocB-refY1)*refWidth + (xLocB-refX1);
for( ; xLocB<=min(refX2,refBX2) ; xLocB++ )
{
*refMatrixPtr++ += weightAdd;
}
}
}
//------ reduce weights from the matrix ------//
if( weightReduce )
{
for( yLocB=max(refY1,refCY1) ; yLocB<=min(refY2,refCY2) ; yLocB++ )
{
xLocB = max(refX1,refCX1);
refMatrixPtr = refMatrix + (yLocB-refY1)*refWidth + (xLocB-refX1);
for( ; xLocB<=min(refX2,refCX2) ; xLocB++ )
{
*refMatrixPtr++ -= weightReduce;
}
}
}
}
}
//------ select the best building site in the matrix -------//
resultXLoc = -1;
resultYLoc = -1;
short thisWeight, bestWeight=0;
refX2 -= firmLocWidth-1; // do not scan beyond the border
refY2 -= firmLocHeight-1;
for( yLoc=refY1 ; yLoc<=refY2 ; yLoc++ )
{
for( xLoc=refX1 ; xLoc<=refX2 ; xLoc++ )
{
if( world.get_region_id(xLoc, yLoc) != buildRegionId ||
!world.can_build_firm(xLoc, yLoc, buildFirmId) )
{
continue;
}
//---- calculate the average weight of a firm area ----//
int totalWeight=0;
refMatrixPtr = refMatrix + (yLoc-refY1)*refWidth + (xLoc-refX1);
for( int yCount=0 ; yCount bestWeight )
{
bestWeight = thisWeight;
resultXLoc = xLoc;
resultYLoc = yLoc;
}
}
}
//------ release the refective matrix -----//
mem_del( refMatrix );
return resultXLoc >= 0;
}
//-------- End of function Nation::find_best_firm_loc --------//