/*
* 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 : OTOWNA.CPP
//Description : Object Town Array
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef DEBUG
#include
//### begin alex 20/9 ###//
static unsigned long last_town_ai_profile_time = 0L;
static unsigned long town_ai_profile_time = 0L;
static unsigned long last_town_profile_time = 0L;
static unsigned long town_profile_time = 0L;
//#### end alex 20/9 ####//
#endif
//--------- Begin of function TownArray::TownArray ----------//
TownArray::TownArray() : DynArrayB(sizeof(Town*), 10, DEFAULT_REUSE_INTERVAL_DAYS)
{
}
//--------- End of function TownArray::TownArary ----------//
//------- Begin of function TownArray::~TownArray ----------//
//
TownArray::~TownArray()
{
deinit();
}
//--------- End of function TownArray::~TownArray ----------//
//--------- Begin of function TownArray::init ----------//
//
void TownArray::init()
{
memset( race_wander_pop_array, 0, sizeof(race_wander_pop_array) );
}
//---------- End of function TownArray::init ----------//
//--------- Begin of function TownArray::deinit ----------//
//
void TownArray::deinit()
{
//----- delete Town objects ------//
if( size() > 0 )
{
Town* townPtr;
for( int i=1 ; i<=size() ; i++ )
{
townPtr = (Town*) get_ptr(i);
if( townPtr )
delete townPtr;
}
zap();
}
}
//---------- End of function TownArray::deinit ----------//
//----- Begin of function TownArray::create_town -------//
//
// Create a blank town object and link it into town_array.
//
Town* TownArray::create_town()
{
Town* townPtr;
townPtr = new Town;
linkin(&townPtr);
return townPtr;
}
//------- End of function TownArray::create_town -------//
//----- Begin of function TownArray::add_town -------//
//
// nationRecno - the nation recno
// raceId - the race of the majority of the town poulation
// xLoc, yLoc - location of the town
//
int TownArray::add_town(int nationRecno, int raceId, int xLoc, int yLoc)
{
Town* townPtr;
townPtr = new Town;
linkin(&townPtr);
townPtr->town_recno = recno();
townPtr->init(nationRecno, raceId, xLoc, yLoc);
nation_array.update_statistic(); // update largest_town_recno
return recno();
}
//------- End of function TownArray::add_town -------//
//----- Begin of function TownArray::del_town -------//
//
void TownArray::del_town(int townRecno)
{
Town* townPtr = operator[](townRecno);
townPtr->deinit();
delete townPtr;
linkout(townRecno);
nation_array.update_statistic();
}
//------- End of function TownArray::del_town -------//
//--------- Begin of function TownArray::process ---------//
//
// Process all town in town_array for action and movement for next frame
//
void TownArray::process()
{
//----- call Town::next_day --------//
int i;
Town *townPtr;
for( i=size() ; i>0 ; i-- )
{
townPtr = (Town*) get_ptr(i);
if( !townPtr )
continue;
//------- if all the population are gone --------//
if( townPtr->population==0 )
{
del_town(i);
continue;
}
//-------------------------------//
err_when(town_array.is_deleted(i));
err_when( townPtr->town_recno==0 );
if( i%FRAMES_PER_DAY == int(sys.frame_count%FRAMES_PER_DAY) ) // only process each firm once per day
{
err_when( townPtr->town_recno==0 );
if( townPtr->nation_recno==0 )
{
townPtr->think_independent_town();
}
else
{
#ifdef DEBUG
if(!config.disable_ai_flag && townPtr->ai_town)
#else
if( townPtr->ai_town )
#endif
{
#ifdef DEBUG
unsigned long profileStartTime = m.get_time();
#endif
townPtr->process_ai();
#ifdef DEBUG
town_profile_time += m.get_time() - profileStartTime;
#endif
}
}
if( town_array.is_deleted(i) )
continue;
err_when( townPtr->town_recno==0 );
//### begin alex 20/9 ###//
#ifdef DEBUG
unsigned long profileStartTime = m.get_time();
#endif
townPtr->next_day();
#ifdef DEBUG
town_profile_time += m.get_time() - profileStartTime;
#endif
//#### end alex 20/9 ####//
}
}
//------ distribute demand -------//
if( sys.day_frame_count==0 && info.game_date%15==0 ) // distribute demand every 15 days
distribute_demand();
//------ create new independent town -----//
if( info.game_date%30==0 && sys.frame_count%FRAMES_PER_DAY==0 )
think_new_independent_town();
}
//----------- End of function TownArray::process ---------//
//----- Begin of function TownArray::think_new_independent_town -----//
//
// Think about creating new independent towns.
//
void TownArray::think_new_independent_town()
{
if( m.random(3) != 0 ) // 1/3 chance
return;
//---- count the number of independent towns ----//
Town* townPtr;
int independentTownCount=0, allTotalPop=0;
int i;
for( i=town_array.size() ; i>0 ; i-- )
{
if( town_array.is_deleted(i) )
continue;
townPtr = town_array[i];
allTotalPop += townPtr->population;
if( townPtr->nation_recno == 0 )
independentTownCount++;
}
if( independentTownCount >= 10 ) // only when the no. of independent town is less than 10
return;
//--- if the total population of all nations combined > 1000, then no new independent town will emerge ---//
if( allTotalPop > 1000 )
return;
//--- add 1 to 2 wanderer per month per race ---//
for( i=0 ; i MAX_RACE )
raceId = 1;
if( race_wander_pop_array[raceId-1] >= 10 ) // one of the race must have at least 10 people
break;
}
if( i==MAX_RACE )
return;
//------- locate for a space to build the town ------//
int xLoc, yLoc;
if( !think_town_loc(MAX_WORLD_X_LOC*MAX_WORLD_Y_LOC/4, xLoc, yLoc) )
return;
//--------------- create town ---------------//
int townRecno = town_array.add_town(0, raceId, xLoc, yLoc);
int maxTownPop = 20 + m.random(10);
int addPop, townResistance;
int loopCount=0;
townPtr = town_array[townRecno];
while(1)
{
err_when( loopCount++ > 100 );
addPop = race_wander_pop_array[raceId-1];
addPop = min(maxTownPop-townPtr->population, addPop);
townResistance = independent_town_resistance();
townPtr->init_pop( raceId, addPop, townResistance, 0, 1 ); // 0-the add pop do not have jobs, 1-first init
race_wander_pop_array[raceId-1] -= addPop;
err_when( race_wander_pop_array[raceId-1] < 0 );
if( townPtr->population >= maxTownPop )
break;
//---- next race to be added to the independent town ----//
raceId = m.random(MAX_RACE)+1;
for( i=0 ; i MAX_RACE )
raceId = 1;
if( race_wander_pop_array[raceId-1] >= 5 )
break;
}
if( i==MAX_RACE ) // no suitable race
break;
}
//---------- set town layout -----------//
townPtr->auto_set_layout();
// if( sys.debug_session )
// box.msg( "A new independent town has emerged." );
}
//------ End of function TownArray::think_new_independent_town -----//
//----- Begin of function TownArray::independent_town_resistance ------//
//
// Return a random resistance value for new independent town.
//
int TownArray::independent_town_resistance()
{
switch(config.independent_town_resistance)
{
case OPTION_LOW:
return 40 + m.random(20);
break;
case OPTION_MODERATE:
return 50 + m.random(30);
break;
case OPTION_HIGH:
return 60 + m.random(40);
break;
default:
err_here();
return 60 + m.random(40);
}
}
//----- End of function TownArray::independent_town_resistance ------//
//-------- Begin of function TownArray::think_town_loc --------//
//
// Locate for an area of free space
//
// maxTries = maximum no. of tries
// xLoc = for returning result
// yLoc = for returning result
//
// return : 1 - free space found
// 0 - free space found
//
int TownArray::think_town_loc(int maxTries, int& xLoc, int& yLoc)
{
#define MIN_INTER_TOWN_DISTANCE 16
#define BUILD_TOWN_LOC_WIDTH 16
#define BUILD_TOWN_LOC_HEIGHT 16
int i, x, y, canBuildFlag, townRecno, firmRecno;
Location* locPtr;
Town* townPtr;
Firm* firmPtr;
for( i=0 ; ican_build_town() )
{
canBuildFlag=0;
break;
}
}
}
if( !canBuildFlag )
continue;
//-------- check if it's too close to other towns --------//
for( townRecno=town_array.size() ; townRecno>0 ; townRecno-- )
{
if( town_array.is_deleted(townRecno) )
continue;
townPtr = town_array[townRecno];
if( m.points_distance(xLoc+1, yLoc+1, townPtr->center_x, // xLoc+1 and yLoc+1 to take the center location of the town
townPtr->center_y) < MIN_INTER_TOWN_DISTANCE )
{
break;
}
}
if( townRecno > 0 ) // if it's too close to other towns
continue;
//-------- check if it's too close to monster firms --------//
for( firmRecno=firm_array.size() ; firmRecno>0 ; firmRecno-- )
{
if( firm_array.is_deleted(firmRecno) )
continue;
firmPtr = firm_array[firmRecno];
if( m.points_distance(xLoc+1, yLoc+1, firmPtr->center_x,
firmPtr->center_y) < MONSTER_ATTACK_NEIGHBOR_RANGE )
{
break;
}
}
if( firmRecno > 0 ) // if it's too close to monster firms
continue;
//----------------------------------------//
return 1;
}
return 0;
}
//--------- End of function TownArray::think_town_loc ---------//
//--------- Begin of function TownArray::draw ---------//
//
// Draw town towns on the zoom map.
//
void TownArray::draw()
{
int i;
Town *townPtr;
for( i=size() ; i>0 ; i-- )
{
townPtr = (Town*) get_ptr(i);
if( townPtr )
townPtr->draw();
}
}
//----------- End of function TownArray::draw ---------//
//--------- Begin of function TownArray::draw_dot ---------//
//
// Draw tiny dots on map window representing the location of the town town.
//
void TownArray::draw_dot()
{
char* vgaBufPtr = vga_back.buf_ptr();
char* writePtr;
int i, x, y;
Town* townPtr;
TownLayout* townLayout;
char nationColor;
char* nationColorArray = nation_array.nation_color_array;
int vgaBufPitch = vga_back.buf_pitch();
// ##### begin Gilbert 16/8 #######//
const unsigned int excitedColorCount = 4;
char excitedColorArray[MAX_NATION+1][excitedColorCount];
for( i = 0; i <= MAX_NATION; ++i )
{
if( i == 0 || !nation_array.is_deleted(i) )
{
char *remapTable = game.get_color_remap_table(i, 0);
excitedColorArray[i][0] = remapTable[0xe0];
excitedColorArray[i][1] = remapTable[0xe1];
excitedColorArray[i][2] = remapTable[0xe2];
excitedColorArray[i][3] = remapTable[0xe3];
}
else
{
excitedColorArray[i][0] =
excitedColorArray[i][1] =
excitedColorArray[i][2] =
excitedColorArray[i][3] = (char) V_WHITE;
}
}
// ##### end Gilbert 16/8 #######//
for(i=1; i <=size() ; i++)
{
townPtr = (Town*) get_ptr(i);
if( !townPtr)
continue;
nationColor = info.game_date - townPtr->last_being_attacked_date > 2 ?
nationColorArray[townPtr->nation_recno] :
excitedColorArray[townPtr->nation_recno][sys.frame_count % excitedColorCount];
townLayout = town_res.get_layout(townPtr->layout_id);
writePtr = vgaBufPtr + (MAP_Y1+townPtr->loc_y1)*vgaBufPitch + (MAP_X1+townPtr->loc_x1);
char shadowColor = (char) VGA_GRAY;
for( y=STD_TOWN_LOC_HEIGHT ; y>0 ; y--, writePtr+=vgaBufPitch-STD_TOWN_LOC_WIDTH )
{
for( x=STD_TOWN_LOC_WIDTH ; x>0 ; x--, writePtr++ )
{
if( *writePtr != UNEXPLORED_COLOR )
*writePtr = nationColor;
}
if( *(writePtr+vgaBufPitch) != UNEXPLORED_COLOR)
*(writePtr+vgaBufPitch) = shadowColor;
}
for( x = STD_TOWN_LOC_WIDTH ; x>0; x--)
{
if( *(++writePtr) != UNEXPLORED_COLOR )
*writePtr = shadowColor;
}
}
}
//----------- End of function TownArray::draw_dot -----------//
//--------- Begin of function TownArray::draw_profile ---------//
void TownArray::draw_profile()
{
#ifdef DEBUG
static unsigned long lastDrawTime = m.get_time();
//### begin alex 20/9 ###//
if(m.get_time() >= lastDrawTime + 1000)
{
last_town_ai_profile_time = town_ai_profile_time;
town_ai_profile_time = 0L;
last_town_profile_time = town_profile_time;
town_profile_time = 0L;
lastDrawTime = m.get_time();
}
String str;
str = "Town : ";
font_news.disp( ZOOM_X1+10, ZOOM_Y1+90, str, MAP_X2);
str = "";
str += last_town_ai_profile_time;
font_news.disp( ZOOM_X1+60, ZOOM_Y1+90, str, MAP_X2);
str = "";
str += last_town_profile_time;
font_news.disp( ZOOM_X1+100, ZOOM_Y1+90, str, MAP_X2);
//#### end alex 20/9 ####//
#endif
}
//----------- End of function TownArray::draw_profile -----------//
//--------- Begin of function TownArray::find_nearest_town --------//
//
// Find the town that is nearest to the firm.
//
// xLoc, yLoc = the x and y location of the town
// [int] nationRecno = nation recno of the town town
// (default: 0, any nation)
//
// return : town town recno of where the firm is nearest to
//
int TownArray::find_nearest_town(int xLoc, int yLoc, int nationRecno)
{
int i, curDistance, bestTownRecno=0, minDistance=0x7FFFFFFF;
Town* townPtr;
for( i=size() ; i>0 ; i-- )
{
if( town_array.is_deleted(i) )
continue;
townPtr = town_array[i];
curDistance = m.points_distance( xLoc, yLoc, townPtr->center_x, townPtr->center_y );
if( nationRecno && townPtr->nation_recno != nationRecno )
continue;
//--- when the firm is outside the town, find the town nearest to the firm ---//
if( curDistance < minDistance )
{
minDistance = curDistance;
bestTownRecno = i;
}
}
return bestTownRecno;
}
//----------- End of function TownArray::find_nearest_town ---------//
//--------- Begin of function TownArray::settle --------//
//
// A unit settles on a given location and form a town town.
//
// return: 1 - settled and a town town is formed successfully.
// 0 - settling failed. too close to a town of another nation.
//
int TownArray::settle(int unitRecno, int xLoc, int yLoc)
{
if(!world.can_build_town(xLoc, yLoc, unitRecno))
return 0;
//--------- get the nearest town town ------------//
Unit* unitPtr = unit_array[unitRecno];
char nationRecno = unitPtr->nation_recno;
//----- it's far enough to form another town --------//
int townRecno = town_array.add_town( nationRecno, unitPtr->race_id, xLoc, yLoc );
//---------- init town population ----------//
Town* townPtr = town_array[townRecno];
//----------------------------------------------------//
// if the settle unit is standing in the town area
// cargo_recno of that location is unchange in town_array.add_town
// so update the location now
//----------------------------------------------------//
short uXLoc = unitPtr->next_x_loc();
short uYLoc = unitPtr->next_y_loc();
Location *locPtr = world.get_loc(uXLoc, uYLoc);
townPtr->assign_unit(unitRecno);
if( uXLoc>=townPtr->loc_x1 && uXLoc<=townPtr->loc_x2 && uYLoc>=townPtr->loc_y1 && uYLoc<=townPtr->loc_y2 )
locPtr->set_town(townPtr->town_recno);
#ifdef DEBUG
// make sure cargo recno is set to town's
for( uYLoc = townPtr->loc_y1; uYLoc <= townPtr->loc_y2; ++uYLoc)
{
for( uXLoc = townPtr->loc_x1; uXLoc <= townPtr->loc_x2; ++uXLoc)
{
err_when( world.get_loc(uXLoc, uYLoc)->town_recno() != townPtr->town_recno );
}
}
#endif
townPtr->update_target_loyalty();
//--------- hide the unit from the map ----------//
return 1;
}
//----------- End of function TownArray::settle ---------//
//--------- Begin of function TownArray::distribute_demand --------//
//
void TownArray::distribute_demand()
{
//--- reset market place demand ----//
int i, j;
FirmMarket* firmMarket;
for( i=firm_array.size() ; i>0 ; i-- )
{
if( firm_array.is_deleted(i) )
continue;
firmMarket = (FirmMarket*) firm_array[i];
if( firmMarket->firm_id == FIRM_MARKET )
{
for( j=0 ; jmarket_goods_array[j].month_demand = (float) 0;
}
}
}
//-------- distribute demand -------//
for( i=size() ; i>0 ; i-- )
{
if( town_array.is_deleted(i) )
continue;
town_array[i]->distribute_demand();
}
}
//----------- End of function TownArray::distribute_demand ---------//
//------- Begin of function TownArray::stop_attack_nation -----//
void TownArray::stop_attack_nation(short nationRecno)
{
Town* townPtr;
for(int i=size(); i>0; --i)
{
townPtr = (Town*) get_ptr(i);
if(townPtr)
townPtr->reset_hostile_nation(nationRecno);
}
}
//----------- End of function TownArray::stop_attack_nation ---------//
//------- Begin of function TownArray::is_deleted -----//
int TownArray::is_deleted(int recNo)
{
Town *townPtr = (Town*) get_ptr(recNo);
if( !townPtr )
return 1;
if( townPtr->population==0 )
return 1;
return 0;
}
//--------- End of function TownArray::is_deleted ----//
#ifdef DEBUG
//------- Begin of function TownArray::operator[] -----//
Town* TownArray::operator[](int recNo)
{
Town* townPtr = (Town*) get_ptr(recNo);
if( !townPtr )
err.run( "TownArray[] is deleted" );
return townPtr;
}
//--------- End of function TownArray::operator[] ----//
#endif