/*
* 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 : OBATTLE.CPP
//Description : Battle Object
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//---------- define static functions -------------//
static int is_space(int xLoc1, int yLoc1, int xLoc2, int yLoc2, char mobileType);
//-------- Begin of function Battle::init --------//
//
void Battle::init()
{
}
//-------- End of function Battle::init --------//
//-------- Begin of function Battle::deinit --------//
//
void Battle::deinit()
{
}
//-------- End of function Battle::deinit --------//
//-------- Begin of function Battle::run --------//
//
// mpGame - whether this is a multiplayer game or not.
//
void Battle::run(NewNationPara *mpGame, int mpPlayerCount)
{
int oldCursor = mouse_cursor.get_icon();
mouse_cursor.set_icon(CURSOR_WAITING);
#ifdef DEBUG
debug_sim_game_type = (m.is_file_exist("sim.sys")) ? 2 : 0;
if(debug_sim_game_type)
{
run_sim();
return;
}
#endif
// ####### begin Gilbert 24/10 #######//
//-- random seed is initalized at connecting multiplayer --//
//if( !mpGame )
// info.init_random_seed(0);
// ####### end Gilbert 24/10 #######//
//----------- save the current seed for generating map -----------//
#ifdef DEBUG2
File seedFile;
char *chPtr = m.format(m.get_random_seed());
seedFile.file_create("mapseed.rs");
seedFile.file_write(chPtr, strlen(chPtr));
seedFile.file_close();
#endif
world.generate_map();
//------- create player nation --------//
if( !mpGame )
{
// if config.race_id == 0, select a random race, but don't call m.random
int nationRecno = nation_array.new_nation( NATION_OWN,
config.race_id ? config.race_id : 1+m.get_time() % MAX_RACE,
config.player_nation_color );
nation_array.set_human_name( nationRecno, config.player_name );
}
else
{
for( int i = 0; i < mpPlayerCount; ++i )
{
int nationRecno = nation_array.new_nation(mpGame[i]);
if( nationRecno != mpGame[i].nation_recno )
err.run( "Unexpected nation recno created" );
nation_array.set_human_name( nationRecno, mpGame[i].player_name );
}
}
//--------- create ai nations --------//
if( mpGame )
{
int aiToCreate = config.ai_nation_count;
if( aiToCreate + mpPlayerCount > MAX_NATION )
aiToCreate = MAX_NATION - mpPlayerCount;
err_when( aiToCreate < 0 );
create_ai_nation(aiToCreate);
}
else
{
create_ai_nation(config.ai_nation_count);
}
//------ create pregame objects ------//
create_pregame_object();
//------- update nation statistic -------//
nation_array.update_statistic();
//--- highlight the player's town in the beginning of the game ---//
Town* townPtr;
for( int i=1 ; i<=town_array.size() ; i++ )
{
townPtr = town_array[i];
if( townPtr->nation_recno == nation_array.player_recno )
{
world.go_loc( townPtr->loc_x1, townPtr->loc_y1 );
break;
}
}
//---- reset config parameter ----//
sys.set_speed(12); // set the speed to normal.
//---- reset cheats ----//
config.fast_build = 0;
config.king_undie_flag = sys.testing_session && !mpGame;
config.blacken_map = 1;
config.disable_ai_flag = 0;
if( sys.testing_session )
config.show_unit_path = 3;
// ######## begin Gilbert 11/11 #######//
// enable tech and god, useful for multi-player
#if(0)
for( i = 1; i < nation_array.size(); ++i )
{
if( !nation_array.is_deleted(i) && !nation_array[i]->is_ai() )
{
tech_res.inc_all_tech_level(i);
god_res.enable_know_all(i);
}
}
#endif
// ######## end Gilbert 11/11 #######//
//------- enable/disable sound effects -------//
#ifdef AMPLUS
int songId = (~nation_array)->race_id <= 7 ? (~nation_array)->race_id+1 : music.random_bgm_track();
music.play(songId, sys.cdrom_drive ? MUSIC_CD_THEN_WAV : 0 );
#else
music.play((~nation_array)->race_id +1, sys.cdrom_drive ? MUSIC_CD_THEN_WAV : 0 );
#endif
mouse_cursor.restore_icon(oldCursor);
//--- give the control to the system main loop, start the game now ---//
sys.run();
}
//--------- End of function Battle::run ---------//
//-------- Begin of function Battle::run_sim --------//
// run simulation
//
#ifdef DEBUG
void Battle::run_sim()
{
err_when(!debug_sim_game_type);
info.disp_panel();
//info.init_random_seed(0);
//info.init_random_seed(869451513); // for testing marine
info.init_random_seed(869639665); // for testing marine
//info.init_random_seed(866608391);
//info.init_random_seed(866621716);
//info.init_random_seed(867299236);
world.generate_map();
//--------- refresh world ---------//
world.refresh();
vga.blt_buf(0, 0, VGA_WIDTH-1, VGA_HEIGHT-1);
world.paint();
//------- create player nation --------//
// if config.race_id == 0, select a random race, but don't call m.random
nation_array.new_nation( NATION_OWN,
config.race_id ? config.race_id : 1+m.get_time() % MAX_RACE,
config.player_nation_color );
//--------- create ai nations --------//
create_ai_nation(config.ai_nation_count);
//------ create pregame objects ------//
create_pregame_object();
//--- highlight the player's town in the beginning of the game ---//
Town* townPtr;
for( int i=1 ; i<=town_array.size() ; i++ )
{
townPtr = town_array[i];
if( townPtr->nation_recno == nation_array.player_recno )
{
world.go_loc( townPtr->loc_x1, townPtr->loc_y1 );
break;
}
}
//-*************** create units, objects *****************-//
int maxNationCount = 2;
int unitId = UNIT_DRAGON;
int nationCount;
SpriteInfo *spriteInfo;
char teraMask;
int unitRecno, x, y, xLoc, yLoc;
//--------- create dragon ---------//
spriteInfo = sprite_res[unit_res[unitId]->sprite_id];
teraMask = UnitRes::mobile_type_to_mask(unit_res[unitId]->mobile_type);
for(nationCount=1; nationCount<=maxNationCount; nationCount++)
{
xLoc = 0;
yLoc = min(20*nationCount, 180);
for(int createCount=0; createCount<10; createCount++, xLoc+=4) // createCount<50
{
for(y=0; y<4; y+=2)
{
for(x=0; x<4; x+=2)
{
unitRecno = unit_array.add_unit(unitId, nationCount, RANK_SOLDIER, 100, xLoc+x, yLoc+y);
unit_array[unitRecno]->set_combat_level(100);
((UnitGod*)unit_array[unitRecno])->god_id = 1;
}
}
}
}
//--------- create marine units ---------//
unitId = UNIT_CARAVEL;
spriteInfo = sprite_res[unit_res[unitId]->sprite_id];
teraMask = UnitRes::mobile_type_to_mask(unit_res[unitId]->mobile_type);
for(nationCount=1; nationCount<=maxNationCount; nationCount++)
{
for(int t=0; t<30; t++)
{
xLoc=0;
yLoc=0;
if(world.locate_space_random(xLoc, yLoc, MAX_WORLD_X_LOC-1,
MAX_WORLD_Y_LOC-1, spriteInfo->loc_width*4, spriteInfo->loc_height*4,
MAX_WORLD_X_LOC*MAX_WORLD_Y_LOC, 0, 0, teraMask))
{
if(xLoc%2)
xLoc++;
if(yLoc%2)
yLoc++;
for(y=0; y<2; y++)
{
for(x=0; x<2; x++)
{
//world.get_loc(startXLoc, startYLoc)->can_move(unit_res[unit_id]->mobile_type)
unitRecno = unit_array.add_unit(unitId, nationCount, RANK_SOLDIER, 100, xLoc+x*2, yLoc+y*2);
unit_array[unitRecno]->set_combat_level(100);
}
}
}
}
}
//--------- create land units ---------//
for(nationCount=1; nationCount<=maxNationCount; nationCount++)
{
for(unitId=UNIT_NORMAN; unitId<=UNIT_JAPANESE; unitId++)
{
spriteInfo = sprite_res[unit_res[unitId]->sprite_id];
teraMask = UnitRes::mobile_type_to_mask(unit_res[unitId]->mobile_type);
for(int t=0; t<5; t++)
{
xLoc=0;
yLoc=0;
if(world.locate_space_random(xLoc, yLoc, MAX_WORLD_X_LOC-1,
MAX_WORLD_Y_LOC-1, spriteInfo->loc_width*2, spriteInfo->loc_height*2,
MAX_WORLD_X_LOC*MAX_WORLD_Y_LOC, 0, 0, teraMask))
{
for(y=0; y<2; y++)
{
for(x=0; x<2; x++)
{
//world.get_loc(startXLoc, startYLoc)->can_move(unit_res[unit_id]->mobile_type)
unitRecno = unit_array.add_unit(unitId, nationCount, RANK_SOLDIER, 100, xLoc+x, yLoc+y);
unit_array[unitRecno]->set_combat_level(100);
}
}
}
}
}
}
//-- enable power after the game objets has been initialized --//
power.enable(); // enable power, which handle mouse inputs
//--- give the control to the system main loop, start the game now ---//
sys.run();
}
#endif
//--------- End of function Battle::run_sim ---------//
//-------- Begin of function Battle::create_ai_nation --------//
//
// Create AI nations.
//
void Battle::create_ai_nation(int aiNationCount)
{
int raceId;
for( int i=0 ; i MAX_RACE );
int nationRecno;
nationRecno = nation_array.new_nation( NATION_AI, raceId, nation_array.random_unused_color() ); // 2nd parameter = the race id., 3rd parameters = color scheme id.
}
}
//--------- End of function Battle::create_ai_nation ---------//
//-------- Begin of function Battle::create_pregame_object --------//
//
// Initialize pre-game objects - towns, sites, independent towns.
//
void Battle::create_pregame_object()
{
#define CREATE_UNIT_AREA_WIDTH 16
#define CREATE_UNIT_AREA_HEIGHT 16
// ###### begin Gilbert 24/10 ######//
const int dispProgress = 1;
const int maxGenMapSteps = 100;
const int newWorldSection = 1;
vga_front.unlock_buf();
int curGenMapSteps = 0;
if( dispProgress )
{
vga_front.lock_buf();
game.disp_gen_map_status( curGenMapSteps, maxGenMapSteps, newWorldSection);
vga_front.unlock_buf();
}
// ###### end Gilbert 24/10 ######//
//------- create nation and units --------//
int nationRecno, unitId, rankId, xLoc, yLoc, townRecno;
int kingUnitRecno;
int noSpaceFlag=0;
Nation* nationPtr;
for( nationRecno=1 ; nationRecno<=nation_array.size() ; nationRecno++ )
{
if( nation_array.is_deleted(nationRecno) )
continue;
nationPtr = nation_array[nationRecno];
//--------- create town -----------//
townRecno = create_town( nationRecno, nationPtr->race_id, xLoc, yLoc );
if( !townRecno )
{
noSpaceFlag = 1;
break;
}
//------- create military camp -------//
Town* townPtr = town_array[townRecno];
int firmRecno = firm_array.build_firm(townPtr->loc_x1+6, townPtr->loc_y1,
nationRecno, FIRM_CAMP, race_res[nationPtr->race_id]->code);
if( !firmRecno )
{
noSpaceFlag = 1;
break;
}
firm_array[firmRecno]->complete_construction();
//--------- create units ----------//
unitId = race_res[nationPtr->race_id]->basic_unit_id;
kingUnitRecno = create_unit(townRecno, unitId, RANK_KING);
if( kingUnitRecno )
{
nation_array[nationRecno]->set_king(kingUnitRecno, 1); // 1-this is the first king of the nation
firm_array[firmRecno]->assign_overseer(kingUnitRecno); // assign the king as the overseer of the command base
}
else
{
noSpaceFlag = 1;
break;
}
//----- create skilled units if config.random_start_up is 1 -----//
if( config.random_start_up )
{
int createCount = (50-townPtr->population)/3; // the less population the villager has the more mobile units will be created
for( int i=0 ; irace_id]->basic_unit_id;
else
unitId = race_res[ m.random(MAX_RACE)+1 ]->basic_unit_id;
if( m.random(3)==0 )
rankId = RANK_GENERAL;
else
rankId = RANK_SOLDIER;
if( !create_unit(townRecno, unitId, rankId) )
break;
}
}
//------ create mines near towns in the beginning -----//
if( config.start_up_has_mine_nearby && !nationPtr->is_ai() )
site_array.create_raw_site(0, townRecno);
}
//--- if there is no space for creating new town/firm or unit, delete the unprocessed nations ---//
if( noSpaceFlag )
{
for( ; nationRecno<=nation_array.size() ; nationRecno++ )
nation_array.del_nation(nationRecno); // no space for creating a town for the nation, so we have to delete the nation
}
// ###### begin Gilbert 24/10 ########//
curGenMapSteps += 10; // 10
if( dispProgress )
{
vga_front.lock_buf();
game.disp_gen_map_status( curGenMapSteps, maxGenMapSteps, newWorldSection);
vga_front.unlock_buf();
}
// ###### end Gilbert 24/10 ######//
//---- init the type of active monsters in this game ----//
monster_res.init_active_monster();
// ###### begin Gilbert 24/10 ########//
curGenMapSteps += 5; // 15
if( dispProgress )
{
vga_front.lock_buf();
game.disp_gen_map_status( curGenMapSteps, maxGenMapSteps, newWorldSection);
vga_front.unlock_buf();
}
// ###### end Gilbert 24/10 ######//
//------ create independent towns -------//
//### begin alex 27/8 ###//
int startUpIndependentTown = config.start_up_independent_town;
//int startUpRawSite = config.start_up_raw_site;
int startUpMonsterFirm = 10;
int i, j, raceId;
site_array.generate_raw_site(config.start_up_raw_site);
// ###### begin Gilbert 24/10 ########//
curGenMapSteps += 10; // 25
if( dispProgress )
{
vga_front.lock_buf();
game.disp_gen_map_status( curGenMapSteps, maxGenMapSteps, newWorldSection);
vga_front.unlock_buf();
}
int targetStep = maxGenMapSteps;
// ###### end Gilbert 24/10 ######//
int maxLoopCount = startUpIndependentTown + startUpMonsterFirm;
for(j=0, i=1; jking_unit_recno) )
// if there is king in the nation take his race
kingRace = unit_array[(~nation_array)->king_unit_recno]->race_id;
else
// if king is killed, get the original nation
kingRace = (~nation_array)->race_id;
}
#ifdef AMPLUS
int songId = kingRace <= 7 ? kingRace+1 : music.random_bgm_track();
music.play(songId, sys.cdrom_drive ? MUSIC_CD_THEN_WAV : 0 );
#else
music.play( kingRace+1, sys.cdrom_drive ? MUSIC_CD_THEN_WAV : 0 );
#endif
//--- give the control to the system main loop, start the game now ---//
sys.run(1);
}
//--------- End of function Battle::run_loaded ---------//
//-------- Begin of function Battle::run_test --------//
//
void Battle::run_test()
{
info.disp_panel();
info.init_random_seed(153542);
world.generate_map();
//--------- refresh world ---------//
world.refresh();
vga.blt_buf(0, 0, VGA_WIDTH-1, VGA_HEIGHT-1);
world.paint();
//-------- create nation and units --------//
nation_array.new_nation( NATION_OWN, 1, 1 ); // 2nd parameter = the race id. 3rd parameters = color scheme id.
nation_array.new_nation( NATION_AI , 2, 2 ); // 2nd parameter = the race id. 3rd parameters = color scheme id.
tech_res.inc_all_tech_level(1); // set all tech of nation 1 to level 1
tech_res.inc_all_tech_level(2); // set all tech of nation 2 to level 1
create_test_unit(1);
create_test_unit(2);
//-- enable power after the game objets has been initialized --//
power.enable(); // enable power, which handle mouse inputs
//--- give the control to the system main loop, start the game now ---//
sys.run();
}
//--------- End of function Battle::run_test ---------//
//-------- Begin of function Battle::create_test_unit --------//
//
void Battle::create_test_unit(int nationRecno)
{
int x, y, unitId, xLoc, yLoc;
SpriteInfo* spriteInfo;
for( unitId=1 ; unitId<=MAX_UNIT_TYPE ; unitId++ )
{
if( unitId == UNIT_CARAVAN ) // caravan cannot be created independently without market places
continue;
xLoc=0;
yLoc=0;
spriteInfo = sprite_res[ unit_res[unitId]->sprite_id ];
char teraMask = UnitRes::mobile_type_to_mask(unit_res[unitId]->mobile_type);
if( world.locate_space_random(xLoc, yLoc, MAX_WORLD_X_LOC-1,
MAX_WORLD_Y_LOC-1, spriteInfo->loc_width*4, spriteInfo->loc_height*4,
MAX_WORLD_X_LOC*MAX_WORLD_Y_LOC, 0, 0, teraMask) )
{
for( y=spriteInfo->loc_height ; yloc_height*4 ; y+=spriteInfo->loc_height*2 )
{
for( x=spriteInfo->loc_width ; xloc_width*4 ; x+=spriteInfo->loc_width*2 )
{
// force the location to be even number
int unitRecno = unit_array.add_unit( unitId, nationRecno, RANK_SOLDIER, 100, (xLoc+x)& ~1, (yLoc+y) & ~1 );
unit_array[unitRecno]->set_combat_level(100);
}
}
}
}
}
//--------- End of function Battle::create_test_unit ---------//
//-------- Begin of function Battle::create_town --------//
//
// nationRecno = the nation recno of the town
// raceId = the race id. of the town
//
// xLoc = for the starting location of the town
// yLoc = for the starting location of the town
//
// return: townRecno - >0 the recno of the town created
// ==0 no town created
//
int Battle::create_town(int nationRecno, int raceId, int& xLoc, int& yLoc)
{
//------- locate for a space to build the town ------//
if( !town_array.think_town_loc(MAX_WORLD_X_LOC*MAX_WORLD_Y_LOC, xLoc, yLoc) )
return 0;
//--------------- create town ---------------//
int townRecno = town_array.add_town(nationRecno, raceId, xLoc, yLoc);
Town* townPtr = town_array[townRecno];
//--------- no. of mixed races ---------//
if( nationRecno )
{
int initPop;
if( config.random_start_up )
initPop = 25 + m.random(26); // 25 to 50
else
initPop = 40;
Town* townPtr = town_array[townRecno];
townPtr->init_pop( raceId, initPop, 100, 0, 1 ); // 100-startup loyalty, last 1-first initialization at the beginning of the game
}
else
{
int mixedRaceCount;
if( nationRecno )
mixedRaceCount = 1;
else
mixedRaceCount= m.random(3)+1; // 1 to 3 mixed races
int curPop, totalPop=0, townResistance;
for( int i=0 ; i=MAX_TOWN_POPULATION)
break;
townResistance = town_array.independent_town_resistance();
if( i==0 )
{
curPop = 15/mixedRaceCount + m.random(15/mixedRaceCount);
if(curPop>=MAX_TOWN_POPULATION)
curPop = MAX_TOWN_POPULATION;
err_when(curPop==0);
townPtr->init_pop( raceId, curPop, townResistance, 0, 1 ); // last 1-first initialization at the beginning of the game
totalPop += curPop;
}
else
{
curPop = 10/mixedRaceCount + m.random(10/mixedRaceCount);
if(curPop>=MAX_TOWN_POPULATION-totalPop)
curPop = MAX_TOWN_POPULATION-totalPop;
err_when(curPop==0);
townPtr->init_pop( m.random(MAX_RACE)+1, curPop, townResistance, 0, 1 );
totalPop += curPop;
}
}
}
//---------- set town layout -----------//
townPtr->auto_set_layout();
return townRecno;
}
//--------- End of function Battle::create_town ---------//
//-------- Begin of function Battle::create_unit --------//
//
// Add a specific no. of units within a given area
//
// townRecno - create units around this town.
// unitId - id. of the units to be added
// rankId - rank id. of the unit
//
// return: recno of the unit created.
//
int Battle::create_unit(int townRecno, int unitId, int rankId)
{
SpriteInfo* spriteInfo = sprite_res[ unit_res[unitId]->sprite_id ];
Town* townPtr = town_array[townRecno];
//------ locate space for the unit ------//
int xLoc = townPtr->loc_x1;
int yLoc = townPtr->loc_y1;
if( !world.locate_space( xLoc, yLoc, xLoc+STD_TOWN_LOC_WIDTH-1, yLoc+STD_TOWN_LOC_HEIGHT-1,
spriteInfo->loc_width, spriteInfo->loc_height ) )
{
return 0;
}
//---------- create the unit --------//
int unitLoyalty = 80 + m.random(20);
int unitRecno = unit_array.add_unit( unitId, townPtr->nation_recno, rankId, unitLoyalty, xLoc, yLoc );
if( !unitRecno )
return 0;
Unit* unitPtr = unit_array[unitRecno];
//----------- set skill -------------//
switch( rankId )
{
case RANK_KING:
unitPtr->skill.set_skill(SKILL_LEADING);
unitPtr->skill.skill_level = 100;
unitPtr->set_combat_level(100);
break;
case RANK_GENERAL:
unitPtr->skill.set_skill(SKILL_LEADING);
unitPtr->skill.skill_level = 40 + m.random(50); // 40 to 90
unitPtr->set_combat_level(30 + m.random(70)); // 30 to 100
break;
case RANK_SOLDIER:
{
int skillId = m.random(MAX_TRAINABLE_SKILL)+1;
int spyFlag = 0;
if( skillId == SKILL_SPYING )
{
spyFlag = 1;
unitPtr->set_combat_level(10+m.random(10));
}
else
{
unitPtr->skill.skill_id = skillId;
unitPtr->skill.skill_level = 30+m.random(70);
if( skillId == SKILL_LEADING )
unitPtr->set_combat_level(30+m.random(70));
else
unitPtr->set_combat_level(10+m.random(10));
if( m.random(5)==0 )
spyFlag = 1;
}
if( spyFlag )
{
int spySkill = 20 + m.random(80); // 20 to 100
unitPtr->spy_recno = spy_array.add_spy(unitRecno, spySkill);
}
break;
}
default:
err_here();
}
return unitRecno;
}
//--------- End of function Battle::create_unit ---------//
//-------- Begin of static function is_space --------//
//
// Check whether all locations in the given area are space
//
// return : 1 - all are space
// 0 - not all are space
//
static int is_space(int xLoc1, int yLoc1, int xLoc2, int yLoc2, char mobileType)
{
int xLoc, yLoc;
Location* locPtr;
for( yLoc=yLoc1 ; yLoc<=yLoc2 ; yLoc++ )
{
locPtr = world.get_loc(xLoc1, yLoc);
for( xLoc=xLoc1 ; xLoc<=xLoc2 ; xLoc++, locPtr++ )
{
if( !locPtr->can_move(mobileType) )
return 0;
}
}
return 1;
}
//--------- End of static function is_space ---------//