| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** 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 3 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 <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // AIPlayer.cpp ///////////////////////////////////////////////////////////////////////////////////
- // Computerized opponent
- // Author: Michael S. Booth, January 2002
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #include "Common/GameMemory.h"
- #include "Common/GameState.h"
- #include "Common/GlobalData.h"
- #include "Common/PerfTimer.h"
- #include "Common/Player.h"
- #include "Common/Team.h"
- #include "Common/ThingFactory.h"
- #include "Common/PlayerList.h"
- #include "Common/BuildAssistant.h"
- #include "Common/ThingTemplate.h"
- #include "Common/Upgrade.h"
- #include "Common/WellKnownKeys.h"
- #include "Common/Xfer.h"
- #include "GameClient/ControlBar.h"
- #include "GameClient/TerrainVisual.h"
- #include "GameLogic/GameLogic.h"
- #include "GameLogic/Object.h"
- #include "GameLogic/AIPlayer.h"
- #include "GameLogic/SidesList.h"
- #include "GameLogic/AI.h"
- #include "GameLogic/AIPathfind.h"
- #include "GameLogic/TerrainLogic.h"
- #include "GameLogic/Module/AIUpdate.h"
- #include "GameLogic/Module/DozerAIUpdate.h"
- #include "GameLogic/Module/UpdateModule.h"
- #include "GameLogic/ScriptEngine.h"
- #include "GameLogic/Module/ProductionUpdate.h"
- #include "GameLogic/Module/RebuildHoleBehavior.h"
- #include "GameLogic/Module/SupplyTruckAIUpdate.h"
- #include "GameLogic/Module/SupplyWarehouseDockUpdate.h"
- #include "GameLogic/PartitionManager.h"
- #if !defined(_PLAYTEST)
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- #define SUPPLY_CENTER_CLOSE_DIST (20*PATHFIND_CELL_SIZE_F)
- #define USE_DOZER 1
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- AIPlayer::AIPlayer( Player *p ) :
- m_player(p),
- m_buildDelay(0),
- m_teamDelay(0),
- m_teamTimer(2), // Important - don't start building teams until frame 1.
- m_structureTimer(2), // Important - don't start building structures until frame 1.
- m_readyToBuildTeam(false),
- m_readyToBuildStructure(false),
- m_structuresInQueue(0),
- m_repairDozer(INVALID_ID),
- m_skillsetSelector(INVALID_SKILLSET_SELECTION),
- m_dozerQueuedForRepair(false),
- m_supplySourceAttackCheckFrame(0),
- m_attackedSupplyCenter(INVALID_ID),
- m_teamSeconds(10),
- m_curWarehouseID(INVALID_ID)
- {
- m_frameLastBuildingBuilt = TheGameLogic->getFrame();
- p->setCanBuildUnits(false); // turn off ai production by default.
- Int i;
- for (i=0; i<MAX_STRUCTURES_TO_REPAIR; i++) {
- m_structuresToRepair[i] = INVALID_ID;
- }
- m_repairDozerOrigin.zero();
- m_baseCenter.zero();
- m_baseCenterSet = false;
- m_difficulty = TheScriptEngine->getGlobalDifficulty();
- m_teamSeconds = TheAI->getAiData()->m_teamSeconds;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- AIPlayer::~AIPlayer()
- {
- clearTeamsInQueue();
- }
- // ------------------------------------------------------------------------------------------------
- /** Invoked when a structure I am building is finished building. */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::onStructureProduced( Object *factory, Object *bldg )
- {
- m_teamDelay = 0; // Cause the update queues & selection to happen immediately.
- m_buildDelay = 0; // Cause
- /* Find the info building this. */
- BuildListInfo *info;
- for( info = m_player->getBuildList(); info; info = info->getNext() )
- {
- if (info->getObjectID() != bldg->getID()) continue;
- Dict d;
- d.setAsciiString(TheKey_objectName, info->getBuildingName());
- d.setAsciiString(TheKey_objectScriptAttachment, info->getScript());
- d.setInt(TheKey_objectInitialHealth, info->getHealth());
- d.setBool(TheKey_objectUnsellable, info->getUnsellable());
-
- info->setUnderConstruction(false);
- bldg->updateObjValuesFromMapProperties(&d);
- // clear the under construction status
- bldg->clearStatus( OBJECT_STATUS_UNDER_CONSTRUCTION );
- bldg->clearStatus( OBJECT_STATUS_RECONSTRUCTING );
- TheScriptEngine->addObjectToCache(bldg);
- TheScriptEngine->runObjectScript(info->getScript(), bldg);
- if (TheGlobalData->m_debugAI) {
- AsciiString bldgName = bldg->getTemplate()->getName();
- bldgName.concat(" - Building completed.");
- TheScriptEngine->AppendDebugMessage(bldgName, false);
- }
- checkForSupplyCenter(info, bldg);
- return;
- }
- // Look in build list & see if this is spawned from a hole.
- for( info = m_player->getBuildList(); info; info = info->getNext() )
- {
- const ThingTemplate *bldgPlan = TheThingFactory->findTemplate( info->getTemplateName() );
- if (!bldgPlan) {
- continue;
- }
- if (!bldgPlan->isEquivalentTo(bldg->getTemplate())) {
- continue; // not the same kind of building we're looking for.
- }
- // check for hole.
- if (info->getObjectID() != INVALID_ID) {
- // used to have a building.
- Object *obj = TheGameLogic->findObjectByID( info->getObjectID() );
- if (obj!=NULL) {
- if (obj->isKindOf(KINDOF_REBUILD_HOLE)) {
- RebuildHoleBehaviorInterface *rhbi = RebuildHoleBehavior::getRebuildHoleBehaviorInterfaceFromObject( obj );
- if( rhbi ) {
- ObjectID spawnedID = rhbi->getReconstructedBuildingID();
- if (bldg->getID() == spawnedID) {
- DEBUG_LOG(("AI got rebuilt %s\n", bldgPlan->getName().str()));
- info->setObjectID(bldg->getID());
- return;
- }
- }
- }
- }
- }
- }
- if (TheGameLogic->getFrame()>0) {
- DEBUG_LOG(("***AI PLAYER-Structure not found in production queue.\n"));
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** See if the building is a supply center, and see how many supply trucks we want. */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::checkForSupplyCenter( BuildListInfo *info, Object *bldg )
- {
- class SupplyCenterDockUpdate;
- // if it is a supply center, I must have boxes
- static const NameKeyType key_centerUpdate = NAMEKEY("SupplyCenterDockUpdate");
- SupplyCenterDockUpdate *centerModule = (SupplyCenterDockUpdate*)bldg->findUpdateModule( key_centerUpdate );
- if( centerModule )
- {
- info->setSupplyBuilding(true);
- Int desiredGatherers = 0;
- const AISideInfo *resInfo = TheAI->getAiData()->m_sideInfo;
- while (resInfo) {
- if (resInfo->m_side == m_player->getSide()) {
- GameDifficulty difficulty = m_difficulty;
- if (difficulty == DIFFICULTY_EASY) {
- desiredGatherers = resInfo->m_easy;
- }
- if (difficulty == DIFFICULTY_NORMAL) {
- desiredGatherers = resInfo->m_normal;
- }
- if (difficulty == DIFFICULTY_HARD) {
- desiredGatherers = resInfo->m_hard;
- }
- }
- resInfo = resInfo->m_next;
- }
- info->setSupplyBuilding(true);
- info->setCurrentGatherers(-1);
- info->setDesiredGatherers(desiredGatherers+1); // get a freebie with the supply depots.
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** Queue up a supply truck to be built. */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::queueSupplyTruck( void )
- {
- Bool truckInQueue = false;
- for ( DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamBuildQueue(); !iter.done(); iter.advance())
- {
- TeamInQueue *team = iter.cur();
- WorkOrder *order;
- for( order = team->m_workOrders; order; order = order->m_next )
- {
- // GLA dozers (workers) are also resource gatherers, so make sure it isn't a worker. jba.
- if (order->m_isResourceGatherer) {
- truckInQueue = true;
- }
- }
- }
-
- if (truckInQueue) {
- return; // already building a supply truck.
- }
- Int totalHarvesters = 0;
- // See how many harvesters we have servicing this supply src.
- // Scan my units.
- Player::PlayerTeamList::const_iterator it;
- for (it = m_player->getPlayerTeams()->begin(); it != m_player->getPlayerTeams()->end(); ++it) {
- for (DLINK_ITERATOR<Team> iter = (*it)->iterate_TeamInstanceList(); !iter.done(); iter.advance()) {
- Team *team = iter.cur();
- if (!team) {
- continue;
- }
- for (DLINK_ITERATOR<Object> objIter = team->iterate_TeamMemberList(); !objIter.done(); objIter.advance()) {
- Object *obj = objIter.cur();
- if (!obj) continue;
- if (!obj->isKindOf(KINDOF_HARVESTER)) continue;
- if (!obj->getAI()) continue;
- SupplyTruckAIInterface* supplyTruckAI = obj->getAI()->getSupplyTruckAIInterface();
- if( supplyTruckAI ) {
- totalHarvesters++;
- }
- }
- }
- }
- /* Find the info building this. */
- for( BuildListInfo *info = m_player->getBuildList(); info; info = info->getNext() )
- {
- if (info->isSupplyBuilding() == false) continue;
- Int desiredGatherers = info->getDesiredGatherers();
- Int curGatherers = info->getCurrentGatherers();
- if (curGatherers>=desiredGatherers) {
- // Check & see if any have died.
- Object *supplyCenter = TheGameLogic->findObjectByID(info->getObjectID());
- // Check for supplies.
- if (supplyCenter) {
- if (supplyCenter->isKindOf(KINDOF_REBUILD_HOLE)) {
- continue; // don't consider rebuild holes.
- }
- // Make sure we have a supplies near it.
- Coord3D center = *supplyCenter->getPosition();
- Real radius = SUPPLY_CENTER_CLOSE_DIST + supplyCenter->getGeometryInfo().getBoundingCircleRadius();
- PartitionFilterAcceptByKindOf f1(MAKE_KINDOF_MASK(KINDOF_SUPPLY_SOURCE), KINDOFMASK_NONE);
- PartitionFilterPlayer f2(m_player, false); // Only find other.
- PartitionFilterOnMap filterMapStatus;
- PartitionFilter *filters[] = { &f1, &f2, &filterMapStatus, 0 };
- Object *supplySource = ThePartitionManager->getClosestObject(¢er, radius, FROM_BOUNDINGSPHERE_2D, filters);
- if (!supplySource) {
- // No supplies.
- continue;
- }
- static const NameKeyType key_warehouseUpdate = NAMEKEY("SupplyWarehouseDockUpdate");
- SupplyWarehouseDockUpdate *warehouseModule = (SupplyWarehouseDockUpdate*)supplySource->findUpdateModule( key_warehouseUpdate );
- if( warehouseModule ) {
- Int availableCash = warehouseModule->getBoxesStored()*TheGlobalData->m_baseValuePerSupplyBox;
- if (availableCash<=0) continue;
- if( m_player->getRelationship(supplySource->getTeam()) == ENEMIES ) {
- continue;
- }
- }
- // Ok, it has supplies available near it.
- checkForSupplyCenter(info, supplyCenter);
- Int curGatherers = 0;
- // See how many harvesters we have servicing this supply src.
- // Scan my units.
- Player::PlayerTeamList::const_iterator it;
- for (it = m_player->getPlayerTeams()->begin(); it != m_player->getPlayerTeams()->end(); ++it) {
- for (DLINK_ITERATOR<Team> iter = (*it)->iterate_TeamInstanceList(); !iter.done(); iter.advance()) {
- Team *team = iter.cur();
- if (!team) {
- continue;
- }
- for (DLINK_ITERATOR<Object> objIter = team->iterate_TeamMemberList(); !objIter.done(); objIter.advance()) {
- Object *obj = objIter.cur();
- if (!obj) continue;
- if (!obj->isKindOf(KINDOF_HARVESTER)) continue;
- if (!obj->getAI()) continue;
- SupplyTruckAIInterface* supplyTruckAI = obj->getAI()->getSupplyTruckAIInterface();
- if( supplyTruckAI ) {
- ObjectID dock = supplyTruckAI->getPreferredDockID();
- if (dock == supplyCenter->getID()) {
- curGatherers++;
- if (!supplyTruckAI->isCurrentlyFerryingSupplies()) {
- // Note - although this is the ai, we are sending in CMD_FROM_PLAYER.
- // This causes the dock object to stick in the docking interface.
- // The supply truck ai issues dock commands, and they become confused.
- // Thus, player. jba. ;(
- obj->getAI()->aiDock(supplyCenter, CMD_FROM_PLAYER);
- }
- }
- }
- }
- }
- }
- //DEBUG_LOG(("Expected %d harvesters, found %d, need %d\n", info->getDesiredGatherers(),
- // curGatherers, info->getDesiredGatherers()-curGatherers) );
- info->setCurrentGatherers(curGatherers);
- }
- } else {
- /* See if we have any "loose" harvesters (cause my supply center got nuked.) */
- Player::PlayerTeamList::const_iterator it;
- for (it = m_player->getPlayerTeams()->begin(); it != m_player->getPlayerTeams()->end(); ++it) {
- for (DLINK_ITERATOR<Team> iter = (*it)->iterate_TeamInstanceList(); !iter.done(); iter.advance()) {
- Team *team = iter.cur();
- if (!team) continue;
- for (DLINK_ITERATOR<Object> objIter = team->iterate_TeamMemberList(); !objIter.done(); objIter.advance()) {
- Object *obj = objIter.cur();
- if (!obj) continue;
- if (!obj->isKindOf(KINDOF_HARVESTER)) continue;
- if (!obj->getAI()) continue;
- SupplyTruckAIInterface* supplyTruckAI = obj->getAI()->getSupplyTruckAIInterface();
- if( supplyTruckAI ) {
- ObjectID dock = supplyTruckAI->getPreferredDockID();
- if (TheGameLogic->findObjectByID(dock)!=NULL) continue;
- if (supplyTruckAI->isCurrentlyFerryingSupplies() || supplyTruckAI->isForcedIntoWantingState())
- {
- // This thinks he is a gatherer, but doesn't have a preferred dock id.
- Object *center = TheGameLogic->findObjectByID(info->getObjectID());
- if (center) {
- info->setCurrentGatherers(info->getCurrentGatherers()+1);
- // Note - although this is the ai, we are sending in CMD_FROM_PLAYER.
- // This causes the dock object to stick in the docking interface.
- // The supply truck ai issues dock commands, and they become confused.
- // Thus, player. jba. ;(
- obj->getAI()->aiDock(center, CMD_FROM_PLAYER);
- DEBUG_LOG(("Re-attaching supply truck to supply center.\n"));
- return;
- }
- }
- }
- }
- }
- }
- if (totalHarvesters >= desiredGatherers*3) {
- continue; // we got lotsa gatherers.
- }
- Bool canBuildUnits = m_player->getCanBuildUnits();
- // If we need a supply truck thingy, turn on unit building for a moment.
- m_player->setCanBuildUnits(true);
- const ThingTemplate *tTemplate = TheThingFactory->firstTemplate();
- while (tTemplate) {
- Bool isSupplyTruck = tTemplate->isKindOf(KINDOF_HARVESTER);;
- if (isSupplyTruck) {
- Object *factory = findFactory(tTemplate, false);
- if (factory) {
- // we can build one.
- WorkOrder *order = newInstance(WorkOrder);
- order->m_thing = tTemplate;
- order->m_factoryID = INVALID_ID;
- order->m_numRequired = 1;
- order->m_required = true;
- order->m_isResourceGatherer =true;
- // prepend to head of list
- order->m_next = NULL;
- TeamInQueue *team = newInstance(TeamInQueue);
- // Put in front of queue.
- prependTo_TeamBuildQueue(team);
- team->m_priorityBuild = true;
- team->m_workOrders = order;
- team->m_frameStarted = TheGameLogic->getFrame();
- // Stick it on the default team
- team->m_team = m_player->getDefaultTeam();
- AsciiString teamName = "Supply truck - building one at the ";
- teamName.concat(factory->getTemplate()->getName());
- TheScriptEngine->AppendDebugMessage(teamName, false);
- m_teamDelay = 0;
- if (info->getCurrentGatherers()==-1) {
- // First one is automatic. jba.
- order->m_factoryID = factory->getID();
- info->setCurrentGatherers(0);
- } else {
- startTraining( order, team->m_priorityBuild, team->m_team->getName());
- }
- break;
- }
- }
- tTemplate = tTemplate->friend_getNextTemplate();
- }
- m_player->setCanBuildUnits(canBuildUnits);
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- static void deleteQueue(TeamInQueue* o)
- {
- if (o)
- {
- o->deleteInstance();
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** Clear the current work order */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::clearTeamsInQueue( void )
- {
- removeAll_TeamBuildQueue(deleteQueue);
- removeAll_TeamReadyQueue(deleteQueue);
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Object *AIPlayer::buildStructureNow(const ThingTemplate *bldgPlan, BuildListInfo *info)
- {
- // inst-construct the building
- Object *bldg = TheBuildAssistant->buildObjectNow( NULL,
- bldgPlan,
- info->getLocation(),
- info->getAngle(),
- m_player );
- // store the object with the build order
- if (bldg)
- {
- Dict d;
- d.setAsciiString(TheKey_objectName, info->getBuildingName());
- d.setAsciiString(TheKey_objectScriptAttachment, info->getScript());
- d.setInt(TheKey_objectInitialHealth, info->getHealth());
- d.setBool(TheKey_objectUnsellable, info->getUnsellable());
-
- bldg->updateObjValuesFromMapProperties(&d);
- info->setObjectID( bldg->getID() );
- info->setObjectTimestamp( TheGameLogic->getFrame()+1 ); // has to be non-zero, so just add 1.
- // clear the under construction status
- bldg->clearStatus( OBJECT_STATUS_UNDER_CONSTRUCTION );
- bldg->clearStatus( OBJECT_STATUS_RECONSTRUCTING );
- if (TheGlobalData->m_debugAI) {
- AsciiString bldgName = bldgPlan->getName();
- bldgName.concat(" - Building completed.");
- TheScriptEngine->AppendDebugMessage(bldgName, false);
- }
- TheScriptEngine->addObjectToCache(bldg);
- TheScriptEngine->runObjectScript(info->getScript(), bldg);
- checkForSupplyCenter(info, bldg);
- ExitInterface *exitInterface = bldg->getObjectExitInterface();
- if( exitInterface )
- {
- Coord3D rallyPoint;
- Bool gotOffset = false;
- if (fabs(info->getRallyOffset()->x) > 1.0f || fabs(info->getRallyOffset()->y)>1.0f) {
- gotOffset;
- }
- if (!exitInterface->getNaturalRallyPoint(rallyPoint)) {
- rallyPoint = *info->getLocation();
- }
- if (gotOffset) {
- rallyPoint.x += info->getRallyOffset()->x;
- rallyPoint.y += info->getRallyOffset()->y;
- exitInterface->setRallyPoint(&rallyPoint);
- }
- }
- } // bldg built
- return bldg;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Object *AIPlayer::buildStructureWithDozer(const ThingTemplate *bldgPlan, BuildListInfo *info)
- {
- // Find a dozer.
- Object *dozer = findDozer(info->getLocation());
- if (dozer==NULL) {
- return NULL;
- }
- // Check available funds.
- Money *money = m_player->getMoney();
- if (money->countMoney()<bldgPlan->calcCostToBuild(m_player)) {
- return NULL;
- }
- // construct the building
- Coord3D pos = *info->getLocation();
- pos.z += TheTerrainLogic->getGroundHeight(pos.x, pos.y);
- if( !dozer->getAIUpdateInterface() )
- {
- return NULL;
- }
- Real angle = info->getAngle();
- if( TheBuildAssistant->isLocationLegalToBuild( &pos, bldgPlan, angle,
- BuildAssistant::NO_ENEMY_OBJECT_OVERLAP,
- dozer, m_player ) != LBC_OK ) {
- // If there's enemy units or structures, don't build/rebuild.
- TheTerrainVisual->removeAllBibs(); // isLocationLegalToBuild adds bib feedback, turn it off. jba.
- return NULL;
- }
- // validate the the position to build at is valid
- if( TheBuildAssistant->isLocationLegalToBuild( &pos, bldgPlan, angle,
- BuildAssistant::CLEAR_PATH |
- BuildAssistant::TERRAIN_RESTRICTIONS |
- BuildAssistant::NO_OBJECT_OVERLAP,
- dozer, m_player ) != LBC_OK ) {
- // Warn.
- AsciiString bldgName = bldgPlan->getName();
- bldgName.concat(" - Dozer unable to place. Attempting to adjust position.");
- TheScriptEngine->AppendDebugMessage(bldgName, false);
- // try to fix.
- Real posOffset;
- Bool valid = false;
- // Wiggle it a little :)
- Real limit = 10*PATHFIND_CELL_SIZE_F;
- if (isSkirmishAI()) {
- limit = 120*PATHFIND_CELL_SIZE_F;
- }
- Coord3D newPos = pos;
- for (posOffset = 0; posOffset<limit; posOffset += 2*PATHFIND_CELL_SIZE_F) {
- if (isSkirmishAI()) {
- posOffset += 2*PATHFIND_CELL_SIZE_F;
- }
- Real offset = posOffset/2;
- Real xPos, yPos;
- yPos = pos.y-offset;
- for (xPos = pos.x-offset; xPos <= pos.x+offset; xPos+=PATHFIND_CELL_SIZE_F) {
- if (isSkirmishAI()) xPos += PATHFIND_CELL_SIZE_F;
- newPos.x = xPos;
- newPos.y = yPos;
- valid = TheBuildAssistant->isLocationLegalToBuild( &newPos, bldgPlan, angle,
- BuildAssistant::CLEAR_PATH |
- BuildAssistant::TERRAIN_RESTRICTIONS |
- BuildAssistant::NO_OBJECT_OVERLAP,
- dozer, m_player ) == LBC_OK;
- if (valid) break;
- newPos.y = yPos+posOffset;
- valid = TheBuildAssistant->isLocationLegalToBuild( &newPos, bldgPlan, angle,
- BuildAssistant::CLEAR_PATH |
- BuildAssistant::TERRAIN_RESTRICTIONS |
- BuildAssistant::NO_OBJECT_OVERLAP,
- dozer, m_player ) == LBC_OK;
- }
- if (valid) break;
- xPos = pos.x-offset;
- for (yPos = pos.y-offset; yPos <= pos.y+offset; yPos+=PATHFIND_CELL_SIZE_F) {
- if (isSkirmishAI()) yPos += PATHFIND_CELL_SIZE_F;
- newPos.x = xPos;
- newPos.y = yPos;
- valid = TheBuildAssistant->isLocationLegalToBuild( &newPos, bldgPlan, angle,
- BuildAssistant::CLEAR_PATH |
- BuildAssistant::TERRAIN_RESTRICTIONS |
- BuildAssistant::NO_OBJECT_OVERLAP,
- dozer, m_player ) == LBC_OK;
- if (valid) break;
- newPos.x = xPos+posOffset;
- valid = TheBuildAssistant->isLocationLegalToBuild( &newPos, bldgPlan, angle,
- BuildAssistant::CLEAR_PATH |
- BuildAssistant::TERRAIN_RESTRICTIONS |
- BuildAssistant::NO_OBJECT_OVERLAP,
- dozer, m_player ) == LBC_OK;
- }
- if (valid) break;
- }
- if (valid) pos = newPos;
- if (!valid) {
- valid = TheBuildAssistant->isLocationLegalToBuild( &pos, bldgPlan, angle,
- BuildAssistant::NO_ENEMY_OBJECT_OVERLAP,
- dozer, m_player ) == LBC_OK;
- if (!valid) {
- return NULL;
- }
- }
- }
- TheTerrainVisual->removeAllBibs(); // isLocationLegalToBuild adds bib feedback, turn it off. jba.
- if (!TheAI->pathfinder()->quickDoesPathExist(dozer->getAI()->getLocomotorSet(),
- dozer->getPosition(), &pos)) {
- AsciiString bldgName = bldgPlan->getName();
- bldgName.concat(" - Dozer unable to reach building. Teleporting.");
- TheScriptEngine->AppendDebugMessage(bldgName, false);
- dozer->setPosition(&pos);
- }
- Object *bldg = TheBuildAssistant->buildObjectNow( dozer,
- bldgPlan,
- &pos,
- angle,
- m_player );
- #if defined _DEBUG || defined _INTERNAL
- if (TheGlobalData->m_debugAI == AI_DEBUG_PATHS)
- {
- extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
- RGBColor color;
- color.blue = 0;
- color.red = 1;
- color.green = 0;
- Coord3D myPos;
- myPos = *dozer->getPosition();
- myPos.z = TheTerrainLogic->getGroundHeight( myPos.x, myPos.y ) + 0.5f;
- addIcon(&myPos, 2*PATHFIND_CELL_SIZE_F, 120, color);
- myPos = pos;
- myPos.z = TheTerrainLogic->getGroundHeight( myPos.x, myPos.y ) + 0.5f;
- addIcon(&myPos, 2*PATHFIND_CELL_SIZE_F, 120, color);
- Real dx, dy;
- dx = dozer->getPosition()->x - pos.x;
- dy = dozer->getPosition()->y - pos.y;
- Int count = sqrt(dx*dx+dy*dy)/(PATHFIND_CELL_SIZE_F/2);
- if (count<2) count = 2;
- Int i;
- color.green = 1;
- for (i=1; i<count; i++) {
- myPos.x = dozer->getPosition()->x + (pos.x-dozer->getPosition()->x)*i/count;
- myPos.y = dozer->getPosition()->y + (pos.y-dozer->getPosition()->y)*i/count;
- myPos.z = TheTerrainLogic->getGroundHeight( myPos.x, myPos.y ) + 0.5f;
- addIcon(&myPos, PATHFIND_CELL_SIZE_F/2, 120, color);
- }
- }
- #endif
- // store the object with the build order
- if (bldg)
- {
- ExitInterface *exitInterface = bldg->getObjectExitInterface();
- if( exitInterface )
- {
- Coord3D rallyPoint;
- Bool gotOffset = false;
- if (fabs(info->getRallyOffset()->x) > 1.0f || fabs(info->getRallyOffset()->y)>1.0f) {
- gotOffset;
- }
- if (!exitInterface->getNaturalRallyPoint(rallyPoint)) {
- rallyPoint = *info->getLocation();
- }
- if (gotOffset) {
- rallyPoint.x += info->getRallyOffset()->x;
- rallyPoint.y += info->getRallyOffset()->y;
- exitInterface->setRallyPoint(&rallyPoint);
- }
- }
- info->setObjectID( bldg->getID() );
- info->setObjectTimestamp( TheGameLogic->getFrame()+1 ); // Has to be non-zero, so add 1.
- info->setUnderConstruction(true);
- if (TheGlobalData->m_debugAI) {
- AsciiString bldgName = bldgPlan->getName();
- bldgName.concat(" - Building started.");
- TheScriptEngine->AppendDebugMessage(bldgName, false);
- }
- } // bldg built
- TheTerrainVisual->removeAllBibs(); // isLocationLegalToBuild adds bib feedback, turn it off. jba.
- return bldg;
- }
- // ------------------------------------------------------------------------------------------------
- /** Build our base. */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::processBaseBuilding( void )
- {
- //
- // Refresh base buildings. Scan through list, if a building is missing,
- // rebuild it, unless it's rebuild count is zero.
- //
- if (m_readyToBuildStructure)
- {
- for( BuildListInfo *info = m_player->getBuildList(); info; info = info->getNext() )
- {
- AsciiString name = info->getTemplateName();
- if (name.isEmpty()) continue;
- const ThingTemplate *bldgPlan = TheThingFactory->findTemplate( name );
- if (!bldgPlan) {
- DEBUG_LOG(("*** ERROR - Build list building '%s' doesn't exist.\n", name.str()));
- continue;
- }
- // check for hole.
- if (info->getObjectID() != INVALID_ID) {
- // used to have a building.
- Object *bldg = TheGameLogic->findObjectByID( info->getObjectID() );
- if (bldg==NULL) {
- // got destroyed.
- ObjectID priorID;
- priorID = info->getObjectID();
- info->setObjectID(INVALID_ID);
- info->setObjectTimestamp(TheGameLogic->getFrame()+1);
- // Scan for a GLA hole. KINDOF_REBUILD_HOLE
- Object *obj;
- for( obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() ) {
- if (!obj->isKindOf(KINDOF_REBUILD_HOLE)) continue;
- RebuildHoleBehaviorInterface *rhbi = RebuildHoleBehavior::getRebuildHoleBehaviorInterfaceFromObject( obj );
- if( rhbi ) {
- ObjectID spawnerID = rhbi->getSpawnerID();
- if (priorID == spawnerID) {
- DEBUG_LOG(("AI Found hole to rebuild %s\n", bldgPlan->getName().str()));
- info->setObjectID(obj->getID());
- }
- }
- }
- } else {
- if (bldg->getControllingPlayer() == m_player) {
- // Check for built or dozer missing.
- if( BitTest( bldg->getStatusBits(), OBJECT_STATUS_UNDER_CONSTRUCTION ) == TRUE) {
- // make sure dozer is working on him.
- ObjectID builder = bldg->getBuilderID();
- Object* myDozer = TheGameLogic->findObjectByID(builder);
- if (myDozer==NULL) {
- DEBUG_LOG(("AI's Dozer got killed. Find another dozer.\n"));
- myDozer = findDozer(bldg->getPosition());
- if (myDozer==NULL || myDozer->getAI()==NULL) {
- continue;
- }
- myDozer->getAI()->aiResumeConstruction(bldg, CMD_FROM_AI);
- } else {
- // make sure he is building.
- myDozer->getAI()->aiResumeConstruction(bldg, CMD_FROM_AI);
- }
- }
- } else {
- // oops, got captured.
- info->setObjectID(INVALID_ID);
- info->setObjectTimestamp(TheGameLogic->getFrame()+1);
- }
- }
- }
- if (info->getObjectID()==INVALID_ID && info->getObjectTimestamp()>0) {
- // this object was built at some time, and got destroyed at or near objectTimestamp.
- // Wait a few seconds before initiating a rebuild.
- if (info->getObjectTimestamp()+TheAI->getAiData()->m_rebuildDelaySeconds*LOGICFRAMES_PER_SECOND > TheGameLogic->getFrame()) {
- continue;
- } else {
- DEBUG_LOG(("Enabling rebuild for %s\n", info->getTemplateName().str()));
- info->setObjectTimestamp(0); // ready to build.
- }
- }
- // check if this building has any "rebuilds" left
- if (info->isBuildable())
- {
- Object *bldg = TheGameLogic->findObjectByID( info->getObjectID() );
- if (bldg == NULL)
- {
- #ifdef USE_DOZER
- // dozer-construct the building
- bldg = buildStructureWithDozer(bldgPlan, info);
- // store the object with the build order
- if (bldg)
- {
- info->setObjectID( bldg->getID() );
- info->decrementNumRebuilds();
- m_readyToBuildStructure = false;
- m_structureTimer = TheAI->getAiData()->m_structureSeconds*LOGICFRAMES_PER_SECOND;
- if (m_player->getMoney()->countMoney() < TheAI->getAiData()->m_resourcesPoor) {
- m_structureTimer = m_structureTimer/TheAI->getAiData()->m_structuresPoorMod;
- } else if (m_player->getMoney()->countMoney() > TheAI->getAiData()->m_resourcesWealthy) {
- m_structureTimer = m_structureTimer/TheAI->getAiData()->m_structuresWealthyMod;
- }
- m_frameLastBuildingBuilt = TheGameLogic->getFrame();
- // only build one building per delay loop
- break;
- } // bldg built
- #else
- // force delay between rebuilds
- if (TheGameLogic->getFrame() - m_frameLastBuildingBuilt < framesToBuild)
- {
- m_buildDelay = framesToBuild - (TheGameLogic->getFrame() - m_frameLastBuildingBuilt);
- return;
- } else {
- // building is missing, (re)build it
- // deduct money to build, if we have it
- Int cost = bldgPlan->calcCostToBuild( m_player );
- if (m_player->getMoney()->countMoney() >= cost)
- {
- // we have the money, deduct it
- m_player->getMoney()->withdraw( cost );
- // inst-construct the building
- bldg = buildStructureNow(bldgPlan, info, NULL);
- // store the object with the build order
- if (bldg)
- {
- info->setObjectID( bldg->getID() );
- info->decrementNumRebuilds();
- m_readyToBuildStructure = false;
- m_structureTimer = TheAI->getAiData()->m_structureSeconds*LOGICFRAMES_PER_SECOND;
- if (m_player->getMoney()->countMoney() < TheAI->getAiData()->m_resourcesPoor) {
- m_structureTimer = m_structureTimer/TheAI->getAiData()->m_structuresPoorMod;
- } else if (m_player->getMoney()->countMoney() > TheAI->getAiData()->m_resourcesWealthy) {
- m_structureTimer = m_structureTimer/TheAI->getAiData()->m_structuresWealthyMod;
- }
- m_frameLastBuildingBuilt = TheGameLogic->getFrame();
- // only build one building per delay loop
- break;
- } // bldg built
- } // have money
- } // rebuild delay ok
- #endif
- } // building missing
- } // is buildable
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** A team is about to be destroyed */
- //-------------------------------------------------------------------------------------------------
- void AIPlayer::aiPreTeamDestroy( const Team *deletedTeam )
- {
- {
- for (DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamBuildQueue(); !iter.done(); iter.advance())
- {
- TeamInQueue *team = iter.cur();
- if (team->m_team == deletedTeam) {
- // The members of the team all got killed before we could finish building the team.
- removeFrom_TeamBuildQueue(team);
- team->deleteInstance();
- iter = iterate_TeamBuildQueue();
- }
- }
- }
- {
- for ( DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamReadyQueue(); !iter.done(); iter.advance())
- {
- TeamInQueue *team = iter.cur();
- if (team->m_team == deletedTeam) {
- // The members of the team all got killed before we could activate the team.
- removeFrom_TeamReadyQueue(team);
- team->deleteInstance();
- iter = iterate_TeamReadyQueue();
- }
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Guard supply center */
- //-------------------------------------------------------------------------------------------------
- void AIPlayer::guardSupplyCenter( Team *team, Int minSupplies )
- {
- m_supplySourceAttackCheckFrame = 0; // force check.
- Object *warehouse = NULL;
- if (isSupplySourceAttacked()) {
- warehouse = TheGameLogic->findObjectByID(m_attackedSupplyCenter);
- }
- if (warehouse==NULL) {
- warehouse = findSupplyCenter(minSupplies);
- }
- if (warehouse) {
- AIGroup* theGroup = TheAI->createGroup();
- if (!theGroup) {
- return;
- }
- team->getTeamAsAIGroup(theGroup);
- Coord3D location = *warehouse->getPosition();
- // It's probably a defensive move - position towards the enemy.
- Region2D bounds;
- Int enemyNdx = TheScriptEngine->getSkirmishEnemyPlayer()->getPlayerIndex();
- getPlayerStructureBounds(&bounds, enemyNdx);
- Coord3D offset;
- offset.zero();
- offset.x = location.x - (bounds.lo.x+bounds.hi.x)*0.5f;
- offset.y = location.y - (bounds.lo.y+bounds.hi.y)*0.5f;
- offset.normalize();
- Real radius = warehouse->getGeometryInfo().getBoundingCircleRadius()*0.8f;
- location.x -= offset.x*radius;
- location.y -= offset.y*radius;
- theGroup->groupGuardPosition( &location, GUARDMODE_NORMAL, CMD_FROM_SCRIPT );
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Is a supply source attacked? */
- //-------------------------------------------------------------------------------------------------
- Bool AIPlayer::isSupplySourceAttacked( void )
- {
- const Int SCAN_RATE = 10; // don't scan more often than every 10 seconds.
- UnsignedInt curFrame = TheGameLogic->getFrame();
- if (curFrame==0) {
- m_supplySourceAttackCheckFrame = curFrame+SCAN_RATE;
- return false; // can't be attacked on first frame.
- }
- m_attackedSupplyCenter = INVALID_ID;
- if (curFrame < m_supplySourceAttackCheckFrame) {
- return false;
- }
- if (m_player->getAttackedFrame()+SCAN_RATE < curFrame) {
- return false; // haven't been attacked recently.
- }
- m_supplySourceAttackCheckFrame = curFrame+SCAN_RATE;
- // Scan my units.
- Player::PlayerTeamList::const_iterator it;
- for (it = m_player->getPlayerTeams()->begin(); it != m_player->getPlayerTeams()->end(); ++it) {
- for (DLINK_ITERATOR<Team> iter = (*it)->iterate_TeamInstanceList(); !iter.done(); iter.advance()) {
- Team *team = iter.cur();
- if (!team) {
- continue;
- }
- for (DLINK_ITERATOR<Object> objIter = team->iterate_TeamMemberList(); !objIter.done(); objIter.advance()) {
- Object *obj = objIter.cur();
- if (!obj) {
- continue;
- }
- if (!obj->isKindOf(KINDOF_CASH_GENERATOR) && !obj->isKindOf(KINDOF_DOZER) &&
- !obj->isKindOf(KINDOF_HARVESTER)) {
- continue;
- }
- // check for attacked.
- BodyModuleInterface *body = obj->getBodyModule();
- if (body) {
- const DamageInfo *info = body->getLastDamageInfo();
- if (info) {
- if (info->out.m_noEffect) {
- continue;
- }
- if (body->getLastDamageTimestamp() + SCAN_RATE > curFrame) {
- // winner.
- m_attackedSupplyCenter = obj->getID();
- return true;
- }
- }
- }
- }
- }
- }
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- /** Is the nearest supply source safe? */
- //-------------------------------------------------------------------------------------------------
- Bool AIPlayer::isSupplySourceSafe( Int minSupplies )
- {
- Object *warehouse = findSupplyCenter(minSupplies);
- if (warehouse==NULL) return true; // it's safe cause it doesn't exist.
- return (isLocationSafe(warehouse->getPosition(), warehouse->getTemplate()));
- }
- //-------------------------------------------------------------------------------------------------
- /** Is this location safe for building this thing? */
- //-------------------------------------------------------------------------------------------------
- Bool AIPlayer::isLocationSafe(const Coord3D *pos, const ThingTemplate *tthing )
- {
- if (tthing == NULL) return 0;
- // See if we have enemies.
- Real radius = TheAI->getAiData()->m_supplyCenterSafeRadius;
- radius += tthing->getTemplateGeometryInfo().getBoundingCircleRadius();
- // only consider enemies.
- PartitionFilterPlayerAffiliation filterTeam(m_player, (ALLOW_ALLIES|ALLOW_NEUTRAL), false);
- // and only stuff that is not dead
- PartitionFilterAlive filterAlive;
- // and only stuff that isn't stealthed (and not detected)
- // (note that stealthed allies aren't hidden from us, but we're only looking for enemies here)
- PartitionFilterRejectByObjectStatus filterStealth(OBJECT_STATUS_STEALTHED, OBJECT_STATUS_DETECTED);
- // (optional) only stuff that is significant
- PartitionFilterInsignificantBuildings filterInsignificant(true, false);
- PartitionFilterRejectByKindOf filterHarvesters(MAKE_KINDOF_MASK(KINDOF_HARVESTER), KINDOFMASK_NONE);
- PartitionFilterRejectByKindOf filterDozer(MAKE_KINDOF_MASK(KINDOF_DOZER), KINDOFMASK_NONE);
- PartitionFilter *filters[16];
- Int numFilters = 0;
- filters[numFilters++] = &filterTeam;
- filters[numFilters++] = &filterAlive;
- filters[numFilters++] = &filterStealth;
- filters[numFilters++] = &filterInsignificant;
- filters[numFilters++] = &filterHarvesters;
- filters[numFilters++] = &filterDozer;
- filters[numFilters] = NULL;
- Object *enemy = ThePartitionManager->getClosestObject( pos, radius, FROM_BOUNDINGSPHERE_2D, filters );
- if (enemy!=NULL) {
- return false;
- }
- return true;
- } // isSupplySourceSafe
- // ------------------------------------------------------------------------------------------------
- /** Invoked when a unit I am training comes into existence */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::onUnitProduced( Object *factory, Object *unit )
- {
- Bool found = false;
- Bool supplyTruck;
- // factory could be NULL at the start of the game.
- if (factory == NULL) {
- return;
- }
- for (DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamBuildQueue(); !iter.done(); iter.advance())
- {
- TeamInQueue *team = iter.cur();
- // find work order entry and delete it
- WorkOrder *order;
- if (found) break;
- for( order = team->m_workOrders; order; order = order->m_next )
- {
- if (order->m_factoryID == factory->getID() && order->m_numCompleted < order->m_numRequired && unit->getTemplate()->isEquivalentTo(order->m_thing))
- {
- // found associated order, mark it complete.
- order->m_numCompleted++;
- // put new unit into the team under construction
- if (team->m_team)
- unit->setTeam( team->m_team );
- if (team->m_reinforcement) {
- team->m_reinforcementID = unit->getID();
- }
- AIUpdateInterface *ai = unit->getAIUpdateInterface();
- if (team->m_team->getPrototype()->getTemplateInfo()->m_hasHomeLocation) {
- if (ai) {
- std::vector<Coord3D> path;
- path.push_back( *ai->getGoalPosition() );
- path.push_back(team->m_team->getPrototype()->getTemplateInfo()->m_homeLocation);
- ai->aiFollowExitProductionPath(&path, NULL, CMD_FROM_AI);
- }
- }
- order->m_factoryID = INVALID_ID; // no longer using this factory.
- if (ai) {
- // tell it to start gathering resources.
- // Here is the special bit for this exit style, force wanting on SupplyTruck types
- SupplyTruckAIInterface* supplyTruckAI = ai->getSupplyTruckAIInterface();
- if( supplyTruckAI ) {
- if (order->m_isResourceGatherer) {
- supplyTruck = true;
- } else {
- supplyTruck = false;
- }
- supplyTruckAI->setForceWantingState(supplyTruck);
- if (supplyTruck) {
- // assign to a supply depot.
- for( BuildListInfo *info = m_player->getBuildList(); info; info = info->getNext() )
- {
- if (info->isSupplyBuilding() && info->getDesiredGatherers()>0 &&
- info->getDesiredGatherers()>info->getCurrentGatherers()) {
- Object *obj = TheGameLogic->findObjectByID(info->getObjectID());
- if (obj) {
- info->setCurrentGatherers(info->getCurrentGatherers()+1);
- // Note - although this is the ai, we are sending in CMD_FROM_PLAYER.
- // This causes the dock object to stick in the docking interface.
- // The supply truck ai issues dock commands, and they become confused.
- // Thus, player. jba. ;(
- ai->aiDock(obj, CMD_FROM_PLAYER);
- }
- }
- }
- }
- }
- }
- found = true;
- break;
- }
- }
- }
- if (!supplyTruck && unit->isKindOf(KINDOF_DOZER)) {
- if (m_dozerQueuedForRepair) {
- m_repairDozer = unit->getID();
- m_dozerQueuedForRepair =false;
- } else {
- m_buildDelay = 0;
- m_structureTimer = 1;
- }
- }
- if (!found) {
- DEBUG_LOG(("***AI PLAYER-Unit not found in production queue.\n"));
- }
- m_teamDelay = 0; // Cause the update queues & selection to happen immediately.
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * Find a good spot to fire a superweapon.
- */
- void AIPlayer::computeSuperweaponTarget(const SpecialPowerTemplate *power, Coord3D *retPos, Int playerNdx, Real weaponRadius)
- {
- Region2D bounds;
- getPlayerStructureBounds(&bounds, playerNdx);
- if (weaponRadius<1.0f) {
- weaponRadius = 1.0f; // sanity to avoid divide by 0.
- }
- Int xCount, yCount;
- bounds.lo.x += weaponRadius;
- bounds.hi.x -= weaponRadius;
- if (bounds.hi.x<bounds.lo.x) {
- bounds.hi.x = bounds.lo.x = (bounds.hi.x+bounds.lo.x)/2.0f;
- }
- if (bounds.hi.y<bounds.lo.y) {
- bounds.hi.y = bounds.lo.y = (bounds.hi.y+bounds.lo.y)/2.0f;
- }
-
- xCount = REAL_TO_INT_CEIL(bounds.width()/weaponRadius)+1;
- yCount = REAL_TO_INT_CEIL(bounds.height()/weaponRadius)+1;
- if (xCount>10) xCount = 10;
- if (yCount>10) yCount = 10;
- Int cash = -1;
- Coord3D pos;
- Coord3D bestPos;
- Int i, j;
- for (i=0; i<xCount; i++) {
- for (j=0; j<yCount; j++) {
- pos.x = bounds.lo.x + (bounds.width()*i)/xCount;
- pos.y = bounds.lo.y + (bounds.height()*j)/yCount;
- pos.z = 0;
- Int curCash = getPlayerSuperweaponValue(&pos, playerNdx, 2*weaponRadius);
- if ( curCash > cash) {
- cash = curCash;
- bestPos = pos;
- }
- }
- }
- Coord3D veryBestPos;
- xCount = 11;
- yCount = 11;
- cash = -1;
- Int count = 0;
- for (i=0; i<xCount; i++) {
- for (j=0; j<yCount; j++) {
- pos.x = bestPos.x + (i-5)*(weaponRadius/10);
- pos.y = bestPos.y+ (j-5)*(weaponRadius/10);
- pos.z = 0;
- Int curCash = getPlayerSuperweaponValue(&pos, playerNdx, weaponRadius);
- if ( curCash > cash) {
- cash = curCash;
- veryBestPos = pos;
- count = 1;
- } else if (curCash==cash) {
- veryBestPos.x += pos.x;
- veryBestPos.y += pos.y;
- count++;
- }
- }
- }
- if (count>1) {
- veryBestPos.x /= count;
- veryBestPos.y /= count;
- }
- veryBestPos.z = TheTerrainLogic->getGroundHeight(veryBestPos.x, veryBestPos.y);
- *retPos = veryBestPos;
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * Get the target value for structures in an area.
- */
- Int AIPlayer::getPlayerSuperweaponValue(Coord3D *center, Int playerNdx, Real radius )
- {
- if (radius < 4*PATHFIND_CELL_SIZE_F) {
- radius = 4*PATHFIND_CELL_SIZE_F;
- }
- Player::PlayerTeamList::const_iterator it;
- Real cash = 0;
- Real radSqr = sqr(radius);
- Player* pPlayer = ThePlayerList->getNthPlayer(playerNdx);
- if (pPlayer == NULL) return 0;
- for (it = pPlayer->getPlayerTeams()->begin(); it != pPlayer->getPlayerTeams()->end(); ++it) {
- for (DLINK_ITERATOR<Team> iter = (*it)->iterate_TeamInstanceList(); !iter.done(); iter.advance()) {
- Team *team = iter.cur();
- if (!team) continue;
- for (DLINK_ITERATOR<Object> iter = team->iterate_TeamMemberList(); !iter.done(); iter.advance()) {
- Object *pObj = iter.cur();
- if (!pObj) continue;
- if (pObj->isKindOf(KINDOF_AIRCRAFT)) {
- if (pObj->isSignificantlyAboveTerrain()) {
- continue; // Don't target flying aircraft. OK if in the airstrip.
- }
- }
- Coord3D pos = *pObj->getPosition();
- Real dx = center->x - pos.x;
- Real dy = center->y - pos.y;
- if (dx*dx+dy*dy<radSqr) {
- Real dist = sqrt(dx*dx+dy*dy);
- Real factor = 1.0f - (dist/(2*radius)); // 1.0 in center, 0.5 on edges.
- Real value = pObj->getTemplate()->calcCostToBuild(pPlayer);
- if (pObj->isKindOf(KINDOF_COMMANDCENTER)) {
- value = value/10; // Command centers cannot be killed by any superweapon, so we don't want to target them as highly. jba.
- }
- if (value > 3000) {
- value = value/10; // Superweapons can't be killed by superweapons, so we don't want to value them highly.
- }
- cash += factor * value;
- }
- }
- }
- }
- return cash;
- }
- // ------------------------------------------------------------------------------------------------
- /** Search the computer player's buildings for one that can build the given request
- * and start training the unit.
- * If busyOK is true, it will queue a unit even if one is building. This lets
- * script invoked teams "push" to the front of the queue. */
- // ------------------------------------------------------------------------------------------------
- Bool AIPlayer::startTraining( WorkOrder *order, Bool busyOK, AsciiString teamName)
- {
- Object *factory = findFactory(order->m_thing, busyOK);
- if( factory )
- {
- ProductionUpdateInterface *pu = factory->getProductionUpdateInterface();
- if (pu && pu->queueCreateUnit( order->m_thing, pu->requestUniqueUnitID() )) {
- order->m_factoryID = factory->getID();
- if (TheGlobalData->m_debugAI) {
- AsciiString teamStr = "Queuing ";
- teamStr.concat(order->m_thing->getName());
- teamStr.concat(" for ");
- teamStr.concat(teamName);
- TheScriptEngine->AppendDebugMessage(teamStr, false);
- }
- return true;
- }
- } // end if
- return FALSE;
- }
- // ------------------------------------------------------------------------------------------------
- /** Search the computer player's buildings for one that can build the given request.
- * If busyOK is true, it will return a busy factory if there are no idle ones. This is
- * used for script invoked teams "push" to the front of the queue. */
- // ------------------------------------------------------------------------------------------------
- Object *AIPlayer::findFactory(const ThingTemplate *thing, Bool busyOK)
- {
- Object *busyFactory = NULL; // We prefer a factory that isn't busy.
- for( BuildListInfo *info = m_player->getBuildList(); info; info = info->getNext() )
- {
- Object *factory = TheGameLogic->findObjectByID( info->getObjectID() );
- if( factory )
- {
- if (factory->getControllingPlayer() != m_player) {
- info->setObjectID(INVALID_ID);
- continue;
- }
- // ignore buildings that are under construction.
- if (factory->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))
- continue;
- // also ignore buildings that are being sold.
- if (factory->testStatus(OBJECT_STATUS_SOLD))
- continue;
- ProductionUpdateInterface *pu = factory->getProductionUpdateInterface();
- // If it doesn't produce, continue.
- if (!pu) continue;
- // if we can't create the unit do nothing
- if( TheBuildAssistant->isPossibleToMakeUnit( factory, thing ) == FALSE )
- continue;
- // If the factory is not busy, return it.
- Bool busy = pu->getProductionCount()>0;
- if (!busy) return factory; // found a not busy factory.
- if (busyOK) busyFactory = factory;
- } // end if
- } // end for
- // We didn't find an idle factory, so return the busy one.
- if (busyOK) return busyFactory;
- return NULL;
- }
- // ------------------------------------------------------------------------------------------------
- /** Return true if team can be considered for building */
- // ------------------------------------------------------------------------------------------------
- Bool AIPlayer::isPossibleToBuildTeam( TeamPrototype *proto, Bool requireIdleFactory, Bool ¬EnoughMoney)
- {
- /* Make sure we have at least one idle factory, and factories for all unit types. */
- Bool anyIdle = false;
- Int cost=0;
- notEnoughMoney = false;
- for( int i=0; i<proto->getTemplateInfo()->m_numUnitsInfo; i++ )
- {
- const TCreateUnitsInfo *unitInfo = &proto->getTemplateInfo()->m_unitsInfo[0];
- const ThingTemplate *thing = TheThingFactory->findTemplate( unitInfo[i].unitThingName );
- if (thing) {
- Int thingCost = thing->calcCostToBuild(m_player);
- if (NULL == findFactory(thing, true)) {
- // Couldn't find a factory.
- return false;
- }
- if (NULL != findFactory(thing, false)) {
- // Found an idle factory.
- anyIdle = true;
- }
- cost += thingCost * ((unitInfo[i].maxUnits+unitInfo[i].minUnits)/2.0f);
- }
- }
- cost *= TheAI->getAiData()->m_teamResourcesToBuild;
- if (m_player->getMoney()->countMoney() < cost) {
- notEnoughMoney = true;
- return false; // too expensive
- }
- if (anyIdle)
- {
- return true;
- }
- if (!requireIdleFactory)
- {
- // Doesn't require an idle factory, so we're ok.
- return true;
- }
- return false;
- }
- // ------------------------------------------------------------------------------------------------
- /** Check if this team is buildable, doesn't exceed maximum limits, meets conditions,
- * and isn't under construction. */
- // ------------------------------------------------------------------------------------------------
- Bool AIPlayer::isAGoodIdeaToBuildTeam( TeamPrototype *proto )
- {
- // Check condition.
- if (!proto->evaluateProductionCondition()) {
- return false;
- }
- // check build limit
- if (proto->countTeamInstances() >= proto->getTemplateInfo()->m_maxInstances){
- if (TheGlobalData->m_debugAI) {
- AsciiString str;
- str.format("Team %s not chosen - %d already exist.", proto->getName().str(), proto->countTeamInstances());
- TheScriptEngine->AppendDebugMessage(str, false);
- }
- return false; // Max already built.
- }
- for ( DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamBuildQueue(); !iter.done(); iter.advance())
- {
- TeamInQueue *team = iter.cur();
- if (team->m_team->getPrototype() == proto) {
- return false; // currently building one of these.
- }
- }
- Bool needMoney;
- if (!isPossibleToBuildTeam( proto, true, needMoney)) {
- if (TheGlobalData->m_debugAI) {
- AsciiString str;
- if (needMoney) {
- str.format("Team %s not chosen - Not enough money.", proto->getName().str());
- } else {
- str.format("Team %s not chosen - Factory/tech missing or busy.", proto->getName().str());
- }
- TheScriptEngine->AppendDebugMessage(str, false);
- }
- return false;
- }
- return true;
- }
- // ------------------------------------------------------------------------------------------------
- /** See if any existing teams need reinforcements, and have higher priority. */
- // ------------------------------------------------------------------------------------------------
- Bool AIPlayer::selectTeamToReinforce( Int minPriority )
- {
- // Find a high production priority team that needs reinforcements.
- Player::PlayerTeamList::const_iterator t;
- Team *curTeam = NULL;
- Int curPriority = minPriority; // Don't reinforce a team unless it is above min priority.
- const ThingTemplate *curThing = NULL;
- for (t = m_player->getPlayerTeams()->begin(); t != m_player->getPlayerTeams()->end(); ++t)
- {
- TeamPrototype *proto = (*t);
- Bool busy = false;
- for ( DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamBuildQueue(); !iter.done(); iter.advance())
- {
- TeamInQueue *team = iter.cur();
- if (team->m_team->getPrototype() == proto) {
- busy = true; // currently building one of these.
- }
- }
- if (busy) continue;
- if (proto->getTemplateInfo()->m_automaticallyReinforce && proto->getTemplateInfo()->m_productionPriority>curPriority) {
- // Check the team instances.
- for (DLINK_ITERATOR<Team> iter = proto->iterate_TeamInstanceList(); !iter.done(); iter.advance())
- {
- Team *team = iter.cur();
- if (team->hasAnyUnits() == false)
- {
- continue; // empty.
- }
- const TCreateUnitsInfo *unitInfo = &team->getPrototype()->getTemplateInfo()->m_unitsInfo[0];
- for( int i=0; i<team->getPrototype()->getTemplateInfo()->m_numUnitsInfo; i++ )
- {
- if (unitInfo[i].maxUnits < 1) continue;
- const ThingTemplate *thing = TheThingFactory->findTemplate( unitInfo[i].unitThingName );
- if (thing==NULL) continue;
- Int count=0;
- team->countObjectsByThingTemplate(1, &thing, false, &count);
- if (count < unitInfo[i].maxUnits)
- {
- // See if there is a factory available.
- if (NULL != findFactory(thing, false))
- {
- curTeam = team;
- curPriority = proto->getTemplateInfo()->m_productionPriority;
- curThing = thing;
- }
- }
- }
- }
- }
- }
- if (curTeam && curThing)
- {
- /* We have something to build. */
- TeamInQueue *teamQ = newInstance(TeamInQueue);
- // Put in front of queue.
- prependTo_TeamBuildQueue(teamQ);
- teamQ->m_priorityBuild = false;
- teamQ->m_reinforcement = true;
- WorkOrder *order = newInstance(WorkOrder);
- order->m_thing = curThing;
- order->m_factoryID = INVALID_ID;
- order->m_numRequired = 1;
- order->m_required = true;
- // prepend to head of list
- order->m_next = NULL;
- teamQ->m_workOrders = order;
- teamQ->m_frameStarted = TheGameLogic->getFrame();
- teamQ->m_team = curTeam;
- AsciiString teamName = curTeam->getPrototype()->getName();
- teamName.concat(" - AutoReinforcing one ");
- teamName.concat(curThing->getName());
- TheScriptEngine->AppendDebugMessage(teamName, false);
- // start the creation of a new unit
- Coord3D origin;
- origin = curTeam->getPrototype()->getTemplateInfo()->m_homeLocation;
- if (curTeam->getFirstItemIn_TeamMemberList())
- {
- origin = *curTeam->getFirstItemIn_TeamMemberList()->getPosition();
- }
- Object *unit = curTeam->tryToRecruit(curThing, &origin, TheAI->getAiData()->m_maxRecruitDistance);
- if (unit)
- {
- order->m_numCompleted = 1;
- AsciiString teamStr = "Team '";
- teamStr.concat(curTeam->getPrototype()->getName());
- teamStr.concat("' recruits ");
- teamStr.concat(curThing->getName());
- teamStr.concat(" from team '");
- teamStr.concat(unit->getTeam()->getPrototype()->getName());
- teamStr.concat("'");
- TheScriptEngine->AppendDebugMessage(teamStr, false);
- unit->setTeam(curTeam);
- teamQ->m_reinforcementID = unit->getID();
- AIUpdateInterface *ai = unit->getAIUpdateInterface();
- if (ai)
- {
- ai->aiIdle(CMD_FROM_AI);
- }
- } else {
- startTraining( order, teamQ->m_priorityBuild, teamQ->m_team->getName());
- }
- m_teamDelay = 0;
- return true;
- }
- return false;
- }
- // ------------------------------------------------------------------------------------------------
- /** Determine the next team to build. Return true if one was selected. */
- // ------------------------------------------------------------------------------------------------
- Bool AIPlayer::selectTeamToBuild( void )
- {
- // find the highest priority of all teams
- Player::PlayerTeamList::const_iterator t;
- const Int invalidPri = -99999;
- Int hiPri = invalidPri;
- // collect all teams that are possible to build, and are at the highest priority
- Player::PlayerTeamList candidateList1;
- for (t = m_player->getPlayerTeams()->begin(); t != m_player->getPlayerTeams()->end(); ++t)
- {
- if (isAGoodIdeaToBuildTeam(*t))
- {
- candidateList1.push_back( (*t) );
- Int pri = (*t)->getTemplateInfo()->m_productionPriority;
- if (pri > hiPri)
- {
- hiPri = pri;
- }
- }
- }
- if (selectTeamToReinforce(hiPri)) {
- return true;
- }
- // check if no team prototypes are valid for production
- if (hiPri == invalidPri)
- return false;
- if (TheGlobalData->m_debugAI) {
- TheScriptEngine->AppendDebugMessage("**AI** Selecting team to build", false);
- }
- // collect all teams that are possible to build, and are at the highest priority
- Player::PlayerTeamList candidateList;
- Int count = 0;
- for (t = candidateList1.begin(); t != candidateList1.end(); ++t)
- {
- if ((*t)->getTemplateInfo()->m_productionPriority == hiPri)
- {
- candidateList.push_back( (*t) );
- count++;
- }
- }
- // pick a random team from the hi-priority set
- Int which = GameLogicRandomValue( 0, count-1 );
- TeamPrototype *teamProto = NULL;
- Int i = 0;
- for (t = candidateList.begin(); t != candidateList.end(); ++t)
- {
- if (i == which)
- {
- teamProto = (*t);
- break;
- }
- i++;
- }
- if (teamProto) {
- if (!teamProto->getTemplateInfo()->m_hasHomeLocation && !isSkirmishAI()) {
- AsciiString teamStr = "Error : team '";
- teamStr.concat(teamProto->getName());
- teamStr.concat("' has no Home Position (or Origin).");
- TheScriptEngine->AppendDebugMessage(teamStr, false);
- }
- // Build it at low priority, as we have selected it automagically.
- buildSpecificAITeam(teamProto, false);
- m_readyToBuildTeam = false;
- m_teamTimer = m_teamSeconds*LOGICFRAMES_PER_SECOND;
- if (m_player->getMoney()->countMoney() < TheAI->getAiData()->m_resourcesPoor) {
- m_teamTimer = m_teamTimer/TheAI->getAiData()->m_teamPoorMod;
- } else if (m_player->getMoney()->countMoney() > TheAI->getAiData()->m_resourcesWealthy) {
- m_teamTimer = m_teamTimer/TheAI->getAiData()->m_teamWealthyMod;
- }
- return true;
- }
- return false;
- }
- // ------------------------------------------------------------------------------------------------
- /** Build a specific team. If priorityBuild, put at front of queue with priority set. */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::buildSpecificAIBuilding(const AsciiString &thingName)
- {
- //
- AsciiString teamStr = "Error : Solo ai doesn't support BuildSpecificBuilding. '";
- teamStr.concat(thingName);
- teamStr.concat("' not built.");
- TheScriptEngine->AppendDebugMessage(teamStr, false);
- }
- // ------------------------------------------------------------------------------------------------
- /** Build an upgrade. */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::buildUpgrade(const AsciiString &upgrade)
- {
- const UpgradeTemplate *curUpgrade = TheUpgradeCenter->findUpgrade(upgrade);
- if (curUpgrade==NULL) {
- AsciiString msg = "Upgrade ";
- msg.concat(upgrade);
- msg.concat(" does not exist. Ignoring request.");
- TheScriptEngine->AppendDebugMessage( msg, false);
- return;
- }
- if (curUpgrade->getUpgradeType()==UPGRADE_TYPE_OBJECT) {
- AsciiString msg = "Player build upgrade: Upgrade ";
- msg.concat(upgrade);
- msg.concat(" is an object, not a player upgrade. Ignoring request.");
- TheScriptEngine->AppendDebugMessage( msg, false);
- return;
- }
- // See if it is in progress.
- if (m_player->hasUpgradeInProduction(curUpgrade)) {
- AsciiString msg = TheNameKeyGenerator->keyToName(m_player->getPlayerNameKey());
- msg.concat(" already has upgrade ");
- msg.concat(upgrade);
- msg.concat(" queued. Ignoring request.");
- TheScriptEngine->AppendDebugMessage( msg, false);
- return;
- }
- // See if it is in progress.
- if (m_player->hasUpgradeComplete(curUpgrade)) {
- AsciiString msg = TheNameKeyGenerator->keyToName(m_player->getPlayerNameKey());
- msg.concat(" already has upgrade ");
- msg.concat(upgrade);
- msg.concat(" completed. Ignoring request.");
- TheScriptEngine->AppendDebugMessage( msg, false);
- return;
- }
- // No money.
- if( TheUpgradeCenter->canAffordUpgrade( m_player, curUpgrade ) == FALSE ) {
- AsciiString msg = TheNameKeyGenerator->keyToName(m_player->getPlayerNameKey());
- msg.concat(" lacks money to build upgrade ");
- msg.concat(upgrade);
- msg.concat(" at this time. Ignoring request.");
- TheScriptEngine->AppendDebugMessage( msg, false);
- return;
- }
- // Find a production queue.
- for( const BuildListInfo *info = m_player->getBuildList(); info; info = info->getNext() )
- {
- Object *factory = TheGameLogic->findObjectByID( info->getObjectID() );
- if( factory )
- {
- if( BitTest( factory->getStatusBits(), OBJECT_STATUS_UNDER_CONSTRUCTION ) == TRUE )
- continue;
- if( BitTest( factory->getStatusBits(), OBJECT_STATUS_SOLD ) == TRUE )
- continue;
- Bool canUpgradeHere = false;
- const CommandSet *commandSet = TheControlBar->findCommandSet( factory->getCommandSetString() );
- if( commandSet == NULL) continue;
- for( Int j = 0; j < MAX_COMMANDS_PER_SET; j++ )
- {
- //Get the command button.
- const CommandButton *commandButton = commandSet->getCommandButton(j);
- if (commandButton==NULL) continue;
- if (commandButton->getName().isEmpty() ) continue;
- if (commandButton->getUpgradeTemplate() == NULL ) continue;
- if (commandButton->getUpgradeTemplate()->getUpgradeName() == curUpgrade->getUpgradeName()) {
- canUpgradeHere = true;
- }
- }
- if (!canUpgradeHere) continue;
- ProductionUpdateInterface *pu = factory->getProductionUpdateInterface();
- // If it doesn't produce, continue.
- if (!pu) continue;
- // Try to queue it.
- if (pu->queueUpgrade(curUpgrade)) {
- AsciiString msg = TheNameKeyGenerator->keyToName(m_player->getPlayerNameKey());
- msg.concat(" queues ");
- msg.concat(curUpgrade->getUpgradeName());
- msg.concat(" at ");
- msg.concat(factory->getTemplate()->getName());
- TheScriptEngine->AppendDebugMessage( msg, false);
- return;
- }
- } // end if
- } // end for
- AsciiString msg = TheNameKeyGenerator->keyToName(m_player->getPlayerNameKey());
- msg.concat(" lacks factory to build upgrade ");
- msg.concat(upgrade);
- msg.concat(" at this time. Ignoring request.");
- TheScriptEngine->AppendDebugMessage( msg, false);
- return;
- }
- // ------------------------------------------------------------------------------------------------
- /** Build a supply center near a supply source with minimumCash or more resources. */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::buildBySupplies(Int minimumCash, const AsciiString& thingName)
- {
- Object *bestSupplyWarehouse = findSupplyCenter(minimumCash);
- const ThingTemplate* tTemplate = TheThingFactory->findTemplate(thingName);
- if (!tTemplate->isKindOf(KINDOF_CASH_GENERATOR)) {
- // Build by the current warehouse.
- Object *curWarehouse = TheGameLogic->findObjectByID(m_curWarehouseID);
- if (curWarehouse) {
- bestSupplyWarehouse = curWarehouse;
- }
- }
- if (bestSupplyWarehouse && tTemplate) {
- Coord3D location;
- location = *bestSupplyWarehouse->getPosition();
- // offset back towards the base.
- Coord2D offset;
- offset.x = location.x - m_baseCenter.x;
- offset.y = location.y - m_baseCenter.y;
- offset.normalize();
- Real radius = 3*PATHFIND_CELL_SIZE_F;
- if (!tTemplate->isKindOf(KINDOF_CASH_GENERATOR)) {
- // It's probably a defensive structure - build towards the enemy.
- Region2D bounds;
- Int enemyNdx = TheScriptEngine->getSkirmishEnemyPlayer()->getPlayerIndex();
- getPlayerStructureBounds(&bounds, enemyNdx);
- offset.x = location.x - (bounds.lo.x+bounds.hi.x)*0.5f;
- offset.y = location.y - (bounds.lo.y+bounds.hi.y)*0.5f;
- offset.normalize();
- radius = bestSupplyWarehouse->getGeometryInfo().getBoundingCircleRadius();
- }
- location.x -= offset.x*radius;
- location.y -= offset.y*radius;
- Real angle = tTemplate->getPlacementViewAngle();
- // validate the the position to build at is valid
- Bool valid=false;
- Coord3D newPos = location;
- if( TheBuildAssistant->isLocationLegalToBuild( &location, tTemplate, angle,
- BuildAssistant::NO_OBJECT_OVERLAP,
- NULL, m_player ) != LBC_OK ) {
- // Warn.
- AsciiString bldgName = tTemplate->getName();
- bldgName.concat(" - buildAISupplyCenter unable to place. Attempting to adjust position.");
- TheScriptEngine->AppendDebugMessage(bldgName, false);
- // try to fix.
- Real posOffset;
- // Wiggle it a little :)
- for (posOffset = 0; posOffset<2*SUPPLY_CENTER_CLOSE_DIST; posOffset += 2*PATHFIND_CELL_SIZE_F) {
- Real offset = posOffset/2;
- Real xPos, yPos;
- yPos = location.y-offset;
- for (xPos = location.x-offset; xPos <= location.x+offset; xPos+=PATHFIND_CELL_SIZE_F) {
- newPos.x = xPos;
- newPos.y = yPos;
- valid = TheBuildAssistant->isLocationLegalToBuild( &newPos, tTemplate, angle,
- BuildAssistant::CLEAR_PATH |
- BuildAssistant::TERRAIN_RESTRICTIONS |
- BuildAssistant::NO_OBJECT_OVERLAP,
- NULL, m_player ) == LBC_OK;
- if (valid) break;
- newPos.y = yPos+posOffset;
- valid = TheBuildAssistant->isLocationLegalToBuild( &newPos, tTemplate, angle,
- BuildAssistant::CLEAR_PATH |
- BuildAssistant::TERRAIN_RESTRICTIONS |
- BuildAssistant::NO_OBJECT_OVERLAP,
- NULL, m_player ) == LBC_OK;
- }
- if (valid) break;
- xPos = location.x-offset;
- for (yPos = location.y-offset; yPos <= location.y+offset; yPos+=PATHFIND_CELL_SIZE_F) {
- newPos.x = xPos;
- newPos.y = yPos;
- valid = TheBuildAssistant->isLocationLegalToBuild( &newPos, tTemplate, angle,
- BuildAssistant::CLEAR_PATH |
- BuildAssistant::TERRAIN_RESTRICTIONS |
- BuildAssistant::NO_OBJECT_OVERLAP,
- NULL, m_player ) == LBC_OK;
- if (valid) break;
- newPos.x = xPos+posOffset;
- valid = TheBuildAssistant->isLocationLegalToBuild( &newPos, tTemplate, angle,
- BuildAssistant::CLEAR_PATH |
- BuildAssistant::TERRAIN_RESTRICTIONS |
- BuildAssistant::NO_OBJECT_OVERLAP,
- NULL, m_player ) == LBC_OK;
- }
- if (valid) break;
- }
- }
- if (valid) location = newPos;
- TheTerrainVisual->removeAllBibs(); // isLocationLegalToBuild adds bib feedback, turn it off. jba.
- location.z = 0; // All build list locations are ground relative.
- m_player->addToPriorityBuildList(thingName, &location, angle);
- m_curWarehouseID = bestSupplyWarehouse->getID();
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** Find a supply center we haven't built a supply depot near yet. */
- // ------------------------------------------------------------------------------------------------
- Object *AIPlayer::findSupplyCenter(Int minimumCash)
- {
- Object *bestSupplyWarehouse = NULL;
- Real bestDistSqr = 0;
- Object *obj;
- Coord3D enemyCenter;
- enemyCenter.zero();
- Region2D bounds;
- Player *enemy = getAiEnemy();
- if (enemy) {
- getPlayerStructureBounds(&bounds, enemy->getPlayerIndex());
- enemyCenter.set( (bounds.lo.x+bounds.hi.x)*0.5f, (bounds.lo.y+bounds.hi.y)*0.5f, 0);
- }
- do {
- for( obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() )
- {
- if (!obj->isKindOf(KINDOF_STRUCTURE)) continue;
- if (!obj->isKindOf(KINDOF_SUPPLY_SOURCE)) continue;
- static const NameKeyType key_warehouseUpdate = NAMEKEY("SupplyWarehouseDockUpdate");
- SupplyWarehouseDockUpdate *warehouseModule = (SupplyWarehouseDockUpdate*)obj->findUpdateModule( key_warehouseUpdate );
- if( warehouseModule ) {
- Int availableCash = warehouseModule->getBoxesStored()*TheGlobalData->m_baseValuePerSupplyBox;
- if (availableCash<minimumCash) continue;
- if( m_player->getRelationship(obj->getTeam()) == ENEMIES ) {
- continue;
- }
- // Make sure we don't have a supply center near it.
- Coord3D center = *obj->getPosition();
- Real radius = SUPPLY_CENTER_CLOSE_DIST + obj->getGeometryInfo().getBoundingCircleRadius();
- PartitionFilterAcceptByKindOf f1(MAKE_KINDOF_MASK(KINDOF_CASH_GENERATOR), KINDOFMASK_NONE);
- PartitionFilterPlayer f2(m_player, true); // Only find your own units.
- PartitionFilterOnMap filterMapStatus;
- PartitionFilter *filters[] = { &f1, &f2, &filterMapStatus, 0 };
- Object *supplyCenter = ThePartitionManager->getClosestObject(¢er, radius, FROM_BOUNDINGSPHERE_2D, filters);
- if (supplyCenter) {
- // We already have a supply center.
- continue;
- }
- Real dx, dy;
- dx = obj->getPosition()->x - m_baseCenter.x;
- dy = obj->getPosition()->y - m_baseCenter.y;
- Real distSqr = dx*dx + dy*dy;
- if (enemy) {
- // make sure this isn't closer to our enemy than us.
- dx = obj->getPosition()->x - enemyCenter.x;
- dy = obj->getPosition()->y - enemyCenter.y;
- if (distSqr*0.4>(dx*dx+dy*dy)*0.6f) {
- // closer than 60/40 to enemy than to us, probably not a good candidate for expansion.
- continue;
- }
- }
- if (bestSupplyWarehouse==NULL) {
- bestSupplyWarehouse = obj;
- bestDistSqr = distSqr;
- } else if (bestDistSqr>distSqr) {
- bestSupplyWarehouse = obj;
- bestDistSqr = distSqr;
- }
- }
- }
- if (bestSupplyWarehouse) break;
- minimumCash /= 2;
- } while (minimumCash > 100);
- return bestSupplyWarehouse;
- }
- // ------------------------------------------------------------------------------------------------
- /** Build a base defense. */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::buildAIBaseDefense(Bool flank)
- {
- //
- AsciiString teamStr = "Error : Solo ai doesn't support buildAIBaseDefense. '";
- TheScriptEngine->AppendDebugMessage(teamStr, false);
- }
- // ------------------------------------------------------------------------------------------------
- /** Build a base defense. */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::buildAIBaseDefenseStructure(const AsciiString &thingName, Bool flank)
- {
- //
- AsciiString teamStr = "Error : Solo ai doesn't support buildAIBaseDefenseStructure. '";
- TheScriptEngine->AppendDebugMessage(teamStr, false);
- }
- // ------------------------------------------------------------------------------------------------
- /** Repair a bridge or other structure. */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::repairStructure(ObjectID structure)
- {
- Object *structureObj = TheGameLogic->findObjectByID(structure);
- if (structureObj==NULL) return;
- if (structureObj->getBodyModule()==NULL) return;
- // If the structure is not noticably damaged, don't bother.
- enum BodyDamageType structureState = structureObj->getBodyModule()->getDamageState();
- if (structureState==BODY_PRISTINE) {
- return;
- }
- if (structureObj->isKindOf(KINDOF_BRIDGE)) {
- // Locate the correct post to repair.
- }
- Int i;
- for (i=0; i<m_structuresInQueue; i++) {
- if (m_structuresToRepair[i] == structureObj->getID()) {
- DEBUG_LOG(("info - Bridge already queued for repair.\n"));
- return;
- }
- }
- if (m_structuresInQueue==MAX_STRUCTURES_TO_REPAIR) {
- DEBUG_LOG(("Structure repair queue is full, ignoring repair request. JBA\n"));
- return;
- }
- m_structuresToRepair[m_structuresInQueue] = structureObj->getID();
- m_structuresInQueue++;
- }
- // ------------------------------------------------------------------------------------------------
- /** select a skillset for the player. */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::selectSkillset(Int skillset)
- {
- DEBUG_ASSERTCRASH(m_skillsetSelector == INVALID_SKILLSET_SELECTION,
- ("Selecting a skill set (%d) after one has already been chosen (%d) means some points have been incorrectly spent.\n", skillset + 1, m_skillsetSelector + 1));
- m_skillsetSelector = skillset;
- }
- // ------------------------------------------------------------------------------------------------
- /** Do per frame work (if any) repairing bridges. */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::updateBridgeRepair(void)
- {
- if (m_structuresInQueue == 0) return;
- // Check once a second.
- m_bridgeTimer--;
- if (m_bridgeTimer>0) return;
- m_bridgeTimer = LOGICFRAMES_PER_SECOND;
- Object *bridgeObj=NULL;
- while (bridgeObj==NULL && m_structuresInQueue>0) {
- bridgeObj = TheGameLogic->findObjectByID(m_structuresToRepair[0]);
- if (bridgeObj==NULL) {
- Int i;
- for (i=0; i<m_structuresInQueue-1; i++) {
- m_structuresToRepair[i] = m_structuresToRepair[i+1];
- }
- m_structuresInQueue--;
- }
- }
- if (m_structuresInQueue == 0) return;
- // Got a bridge to repair.
- Object *dozer = NULL;
- Coord3D bridgePos = *bridgeObj->getPosition();
- enum BodyDamageType bridgeState = bridgeObj->getBodyModule()->getDamageState();
- if (m_repairDozer==INVALID_ID) {
- m_dozerIsRepairing = false;
- // Need a dozer.
- if (m_dozerQueuedForRepair) {
- return; // we're waiting for one.
- }
- dozer = findDozer(&bridgePos);
- if (dozer) {
- m_repairDozer = dozer->getID();
- m_repairDozerOrigin = *dozer->getPosition();
- dozer->getAI()->aiRepair(bridgeObj, CMD_FROM_AI);
- DEBUG_LOG(("Telling dozer to repair\n"));
- m_dozerIsRepairing = true;
- return;
- }
- queueDozer();
- m_dozerQueuedForRepair = true;
- return;
- }
- dozer = TheGameLogic->findObjectByID(m_repairDozer);
- if (dozer==NULL) {
- m_repairDozer=INVALID_ID; // we got killed.
- m_bridgeTimer=0;
- return; // Just try to find a dozer next frame.
- }
- DozerAIInterface* dozerAI = dozer->getAI()->getDozerAIInterface();
- if (dozerAI==NULL) {
- DEBUG_CRASH(("Unexpected - dozer doesn't have dozer interface."));
- return;
- }
- if (m_dozerIsRepairing) {
- if (!dozerAI->isAnyTaskPending()) {
- // should be done repairing.
- if (bridgeState==BODY_PRISTINE) {
- DEBUG_LOG(("Dozer finished repairing structure.\n"));
- // we're done.
- Int i;
- for (i=0; i<m_structuresInQueue-1; i++) {
- m_structuresToRepair[i] = m_structuresToRepair[i+1];
- }
- m_structuresInQueue--;
- m_dozerIsRepairing = false;
- if (m_structuresInQueue==0) {
- // Go home.
- Coord3D pos = m_baseCenter;
- if (!m_baseCenterSet) {
- pos = m_repairDozerOrigin;
- }
- AIUpdateInterface *ai=dozer->getAI();
- TheAI->pathfinder()->adjustToPossibleDestination(dozer, ai->getLocomotorSet(), &pos);
- dozer->getAI()->aiMoveToPosition(&pos, CMD_FROM_AI);
- return;
- }
- }
- } else {
- // dozer should be working on the bridge.
- return;
- }
- }
- dozer->getAI()->aiRepair(bridgeObj, CMD_FROM_AI);
- m_dozerIsRepairing = true;
- DEBUG_LOG(("Telling dozer to repair\n"));
- }
- // ------------------------------------------------------------------------------------------------
- /** Build a specific team. If priorityBuild, put at front of queue with priority set. */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::buildSpecificAITeam( TeamPrototype *teamProto, Bool priorityBuild)
- {
- //
- // Create "Team in queue" based on team population
- //
- if (teamProto)
- {
- if (!m_player->getCanBuildUnits()) {
- AsciiString teamStr = "Can't build team '";
- teamStr.concat(teamProto->getName());
- teamStr.concat("' because build units is disabled.");
- TheScriptEngine->AppendDebugMessage(teamStr, false);
- return;
- }
- if (priorityBuild && teamProto->getIsSingleton()) {
- Team *singletonTeam = TheTeamFactory->findTeam( teamProto->getName() );
- if (singletonTeam && singletonTeam->hasAnyObjects()) {
- AsciiString teamStr = "Unable to build singleton team '";
- teamStr.concat("' because team already exists.");
- TheScriptEngine->AppendDebugMessage(teamStr, false);
- return;
- }
- }
- // Check & make sure we have factories.
- Bool needMoney;
- if (!isPossibleToBuildTeam(teamProto, false, needMoney)) {
- if (needMoney) {
- // Queue it up anyway.
- AsciiString teamStr = "Note - queueing team '";
- teamStr.concat(teamProto->getName());
- teamStr.concat("' but there is enough money.");
- TheScriptEngine->AppendDebugMessage(teamStr, false);
- } else {
- // Tech tree doesn't work.
- AsciiString teamStr = "Unable to build team '";
- teamStr.concat(teamProto->getName());
- if (needMoney) {
- teamStr.concat("' - Not enough money.");
- } else {
- teamStr.concat("' because required factories/tech don't exist.");
- }
- TheScriptEngine->AppendDebugMessage(teamStr, false);
- return;
- }
- }
- const TCreateUnitsInfo *unitInfo = &teamProto->getTemplateInfo()->m_unitsInfo[0];
- WorkOrder *orders = NULL;
- Int i;
- // Queue up optional units.
- for( i=0; i<teamProto->getTemplateInfo()->m_numUnitsInfo; i++ )
- {
- const ThingTemplate *thing = TheThingFactory->findTemplate( unitInfo[i].unitThingName );
- if (thing)
- {
- int count = unitInfo[i].maxUnits-unitInfo[i].minUnits;
- if (count>0) {
- WorkOrder *order = newInstance(WorkOrder);
- order->m_thing = thing;
- order->m_factoryID = INVALID_ID;
- order->m_numRequired = count;
- // prepend to head of list
- order->m_next = orders;
- orders = order;
- }
- }
- }
- // Queue up required units.
- for( i=0; i<teamProto->getTemplateInfo()->m_numUnitsInfo; i++ )
- {
- const ThingTemplate *thing = TheThingFactory->findTemplate( unitInfo[i].unitThingName );
- if (thing)
- {
- int count = unitInfo[i].minUnits;
- WorkOrder *order = newInstance(WorkOrder);
- order->m_thing = thing;
- order->m_factoryID = INVALID_ID;
- order->m_numRequired = count;
- order->m_required = true;
- // prepend to head of list
- order->m_next = orders;
- orders = order;
- }
- }
- if (orders)
- {
- /* We have something to build. */
- TeamInQueue *team = newInstance(TeamInQueue);
- if (priorityBuild) {
- // Put in front of queue.
- prependTo_TeamBuildQueue(team);
- team->m_priorityBuild = true;
- } else {
- // Put in back of queue.
- reverse_TeamBuildQueue();
- prependTo_TeamBuildQueue(team);
- reverse_TeamBuildQueue();
- team->m_priorityBuild = false;
- }
- team->m_workOrders = orders;
- team->m_frameStarted = TheGameLogic->getFrame();
- // create inactive team to place members into as they are built
- // when team is complete, the team is activated
- team->m_team = TheTeamFactory->createInactiveTeam( teamProto->getName() );
- AsciiString teamName = teamProto->getName();
- teamName.concat(" - starting team build.");
- TheScriptEngine->AppendDebugMessage(teamName, false);
- m_teamDelay = 0;
- if (team->m_team->getPrototype()->getTemplateInfo()->m_executeActions) {
- const Script *script = TheScriptEngine->findScriptByName(team->m_team->getPrototype()->getTemplateInfo()->m_productionCondition);
- if (script && script->getAction()) {
- TheScriptEngine->friend_executeAction(script->getAction(), team->m_team);
- }
- }
- } else {
- if (TheGlobalData->m_debugAI) {
- AsciiString teamName = teamProto->getName();
- teamName.concat(" - contains 0 buildable units.");
- TheScriptEngine->AppendDebugMessage(teamName, false);
- }
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** Recruit a specific team, within the specific radius of the home position. */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::recruitSpecificAITeam(TeamPrototype *teamProto, Real recruitRadius)
- {
- if (recruitRadius < 1) recruitRadius = 99999.0f;
- //
- // Create "Team in queue" based on team population
- //
- if (teamProto)
- {
- if (teamProto->getIsSingleton()) {
- Team *singletonTeam = TheTeamFactory->findTeam( teamProto->getName() );
- if (singletonTeam && singletonTeam->hasAnyObjects()) {
- AsciiString teamStr = "Unable to recruit singleton team '";
- teamStr.concat("' because team already exists.");
- TheScriptEngine->AppendDebugMessage(teamStr, false);
- return;
- }
- }
- if (!teamProto->getTemplateInfo()->m_hasHomeLocation && !isSkirmishAI())
- {
- AsciiString teamStr = "Error : team '";
- teamStr.concat(teamProto->getName());
- teamStr.concat("' has no Home Position (or Origin).");
- TheScriptEngine->AppendDebugMessage(teamStr, false);
- }
- // create inactive team to place members into as they are built
- // when team is complete, the team is activated
- Team *theTeam = TheTeamFactory->createInactiveTeam( teamProto->getName() );
- AsciiString teamName = teamProto->getName();
- teamName.concat(" - Recruiting.");
- TheScriptEngine->AppendDebugMessage(teamName, false);
- const TCreateUnitsInfo *unitInfo = &teamProto->getTemplateInfo()->m_unitsInfo[0];
- // WorkOrder *orders = NULL;
- Int i;
- Int unitsRecruited = 0;
- // Recruit.
- for( i=0; i<teamProto->getTemplateInfo()->m_numUnitsInfo; i++ )
- {
- const ThingTemplate *thing = TheThingFactory->findTemplate( unitInfo[i].unitThingName );
- if (thing)
- {
- int count = unitInfo[i].maxUnits;
- while (count>0) {
- Object *unit = theTeam->tryToRecruit(thing, &teamProto->getTemplateInfo()->m_homeLocation, recruitRadius);
- if (unit)
- {
- unitsRecruited++;
- AsciiString teamStr = "Team '";
- teamStr.concat(theTeam->getPrototype()->getName());
- teamStr.concat("' recruits ");
- teamStr.concat(thing->getName());
- teamStr.concat(" from team '");
- teamStr.concat(unit->getTeam()->getPrototype()->getName());
- teamStr.concat("'");
- TheScriptEngine->AppendDebugMessage(teamStr, false);
- unit->setTeam(theTeam);
- AIUpdateInterface *ai = unit->getAIUpdateInterface();
- if (ai)
- {
- #ifdef DEBUG_LOGGING
- Coord3D pos = *unit->getPosition();
- Coord3D to = teamProto->getTemplateInfo()->m_homeLocation;
- DEBUG_LOG(("Moving unit from %f,%f to %f,%f\n", pos.x, pos.y , to.x, to.y ));
- #endif
- ai->aiMoveToPosition( &teamProto->getTemplateInfo()->m_homeLocation, CMD_FROM_AI);
- }
- } else {
- break;
- }
- count--;
- }
- }
- }
- if (unitsRecruited>0)
- {
- /* We have something to build. */
- TeamInQueue *team = newInstance(TeamInQueue);
- // Put in front of queue.
- prependTo_TeamReadyQueue(team);
- team->m_priorityBuild = false;
- team->m_workOrders = NULL;
- team->m_frameStarted = TheGameLogic->getFrame();
- team->m_team = theTeam;
- AsciiString teamName = teamProto->getName();
- teamName.concat(" - Finished recruiting.");
- TheScriptEngine->AppendDebugMessage(teamName, false);
- } else {
- //disband.
- if (!theTeam->getPrototype()->getIsSingleton()) {
- theTeam->deleteInstance();
- theTeam = NULL;
- }
- AsciiString teamName = teamProto->getName();
- teamName.concat(" - Recruited 0 units, disbanding.");
- TheScriptEngine->AppendDebugMessage(teamName, false);
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** Train our teams. */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::processTeamBuilding( void )
- {
- // select a new team
- if (selectTeamToBuild()) {
- queueUnits();
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::queueUnits( void )
- {
- queueSupplyTruck();
- // For each member of the current team to build, try to find a faction building to build it.
- //
- for ( DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamBuildQueue(); !iter.done(); iter.advance())
- {
- TeamInQueue *team = iter.cur();
- for( WorkOrder *order = team->m_workOrders; order; order = order->m_next )
- {
- // check if there is a unit on the map that we can steal (recruit) instead of building
- // @todo: Should this try to alter the home location of the recruiting area to
- // the center of the team, or to the home area of this player?
- Coord3D home = team->m_team->getPrototype()->getTemplateInfo()->m_homeLocation;
- Bool hasHome = false;
- if (team->m_team->getPrototype()->getTemplateInfo()->m_hasHomeLocation) {
- hasHome = true;
- } else {
- hasHome = getBaseCenter(&home);
- }
- while (order->isWaitingToBuild()) {
-
- Object *unit = team->m_team->tryToRecruit(order->m_thing, &home, TheAI->getAiData()->m_maxRecruitDistance);
- if (unit)
- {
- order->m_numCompleted++;
- AsciiString teamStr = "Team '";
- teamStr.concat(team->m_team->getPrototype()->getName());
- teamStr.concat("' recruits ");
- teamStr.concat(order->m_thing->getName());
- teamStr.concat(" from team '");
- teamStr.concat(unit->getTeam()->getPrototype()->getName());
- teamStr.concat("'");
- TheScriptEngine->AppendDebugMessage(teamStr, false);
- unit->setTeam(team->m_team);
- AIUpdateInterface *ai = unit->getAIUpdateInterface();
- if (hasHome) {
- ai->aiMoveToPosition( &home, CMD_FROM_AI);
- } else {
- ai->aiIdle(CMD_FROM_AI); // stop, you've been recruited.
- }
- } else {
- break;
- }
- }
- if (order->isWaitingToBuild())
- {
- // start the creation of a new unit
- startTraining( order, team->m_priorityBuild, team->m_team->getName());
- }
- else
- {
- // we are under construction, verify our factory still exists
- order->validateFactory(m_player);
- }
- }
- }
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * See if it's time to build another base building.
- */
- void AIPlayer::doBaseBuilding( void )
- {
- if (m_player->getCanBuildBase()) {
- // See if we are ready to start trying a structure.
- if (!m_readyToBuildStructure) {
- m_structureTimer--;
- if (m_structureTimer<=0) {
- m_readyToBuildStructure = true;
- m_buildDelay = 0;
- }
- }
- // This timer is to keep from banging on the logic each frame. If something interesting
- // happens, like a building is added or a unit finished, the timers are shortcut.
- m_buildDelay--;
- if (m_buildDelay<1) {
- if (m_readyToBuildStructure) {
- processBaseBuilding();
- }
- if (m_buildDelay<1) { // processBaseBuilding may reset m_buildDelay.
- m_buildDelay = 2*LOGICFRAMES_PER_SECOND; // check again in 2 seconds.
- }
- // Note that this timer gets shortcut when a building is completed.
- }
- }
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * See if any ready teams have finished moving to the rally point.
- */
- void AIPlayer::checkReadyTeams( void )
- {
- // See if any ready teams are gathered at their rally point
- { // needed to scope iter. silly ms c++.
- for ( DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamReadyQueue(); !iter.done(); iter.advance())
- {
- TeamInQueue *team = iter.cur();
- // If 60 seconds passed, start anyway.
- Bool timeExpired = team->m_frameStarted+60*LOGICFRAMES_PER_SECOND < TheGameLogic->getFrame();
- Bool allIdle=TRUE;
- Bool anyIdle = FALSE;
- if (team->m_reinforcement) {
- Object *obj = TheGameLogic->findObjectByID(team->m_reinforcementID);
- if (obj && obj->getAIUpdateInterface()) {
- allIdle = obj->getAIUpdateInterface()->isIdle();
- anyIdle = allIdle;
- }
- } else {
- allIdle = team->m_team->isIdle();
- for (DLINK_ITERATOR<Object> iter = team->m_team->iterate_TeamMemberList(); !iter.done(); iter.advance()) {
- Object *obj = iter.cur();
- if (obj->getAI() && obj->getAI()->isIdle()) {
- anyIdle = true;
- }
- }
- }
- if (anyIdle && team->m_team->getPrototype()->getTemplateInfo()->m_executeActions) {
- const Script *script = TheScriptEngine->findScriptByName(team->m_team->getPrototype()->getTemplateInfo()->m_productionCondition);
- if (script && script->getAction()) {
- // we have a start action. So don't wait for allIdle as the team may be guarding.
- allIdle = true;
- }
- }
- if (timeExpired) allIdle = true;
- if (allIdle) {
- if (!team->m_sentToStartLocation) {
- team->m_sentToStartLocation = true;
- /*
- if (team->m_team->getPrototype()->getTemplateInfo()->m_hasHomeLocation &&
- !team->m_reinforcement) {
- AIGroup* theGroup = TheAI->createGroup();
- if (theGroup) {
- team->m_team->getTeamAsAIGroup(theGroup);
- Coord3D destination = team->m_team->getPrototype()->getTemplateInfo()->m_homeLocation;
- theGroup->groupTightenToPosition( &destination, false, CMD_FROM_AI );
- team->m_frameStarted = TheGameLogic->getFrame();
- continue;
- }
- }
- */
- }
- // Start the team up.
- removeFrom_TeamReadyQueue(team);
- if (team->m_reinforcement) {
- Object *obj = TheGameLogic->findObjectByID(team->m_reinforcementID);
- if (obj&&obj->getAIUpdateInterface()) {
- obj->getAIUpdateInterface()->joinTeam();
- }
- } else {
- // mark our completed team as "active" - this will invoke any OnCreate scripts, etc.
- team->m_team->setActive();
- if (isSkirmishAI()) {
- TheScriptEngine->clearTeamFlags();
- }
- if (TheGlobalData->m_debugAI) {
- AsciiString teamName = team->m_team->getPrototype()->getName();
- teamName.concat(" - team activated.");
- TheScriptEngine->AppendDebugMessage(teamName, false);
- }
- }
- team->deleteInstance();
- iter = iterate_TeamReadyQueue();
- }
- }
- }
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * See if any queued teams have finished building, or have run out of time.
- */
- void AIPlayer::checkQueuedTeams( void )
- {
- // See if any teams are expired.
- { // needed to scope iter. silly ms c++.
- for ( DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamBuildQueue(); !iter.done(); iter.advance())
- {
- TeamInQueue *team = iter.cur();
- if (team && team->isBuildTimeExpired()) {
- if (team->isMinimumBuilt())
- {
- if (team->areBuildsComplete()) {
- // Move to ready queue
- removeFrom_TeamBuildQueue(team);
- prependTo_TeamReadyQueue(team);
- } else {
- continue;
- }
- } else {
- // Disband.
- removeFrom_TeamBuildQueue(team);
- team->disband();
- team->deleteInstance();
- if (isSkirmishAI()) {
- TheScriptEngine->clearTeamFlags();
- }
- }
- iter = iterate_TeamBuildQueue();
- }
- }
- }
- // See if any teams are ready.
- { // needed to scope iter. silly ms c++.
- for ( DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamBuildQueue(); !iter.done(); iter.advance())
- {
- TeamInQueue *team = iter.cur();
- if (team && team->isAllBuilt())
- {
- // Move to ready queue
- removeFrom_TeamBuildQueue(team);
- prependTo_TeamReadyQueue(team);
- iter = iterate_TeamBuildQueue();
- continue;
- }
- Bool anyIdle = false;
- for (DLINK_ITERATOR<Object> iter = team->m_team->iterate_TeamMemberList(); !iter.done(); iter.advance()) {
- Object *obj = iter.cur();
- if (obj && obj->getAI() && obj->getAI()->isIdle()) {
- anyIdle = true;
- }
- }
- if (anyIdle) {
- if (team->m_team->getPrototype()->getTemplateInfo()->m_executeActions) {
- const Script *script = TheScriptEngine->findScriptByName(team->m_team->getPrototype()->getTemplateInfo()->m_productionCondition);
- if (script) {
- TheScriptEngine->friend_executeAction(script->getAction(), team->m_team);
- }
- }
- }
- }
- }
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * See if it is time to start another ai team building.
- */
- void AIPlayer::doTeamBuilding( void )
- {
- // See if any teams are expired.
- if (m_player->getCanBuildUnits()) {
- // See if we are ready to start trying a team.
- if (!m_readyToBuildTeam) {
- m_teamTimer--;
- if (m_teamTimer<=0) {
- m_readyToBuildTeam = true;
- m_teamDelay = 0;
- }
- }
- // This timer is to keep from banging on the logic each frame. If something interesting
- // happens, like a building is added or a unit finished, the timers are shortcut.
- m_teamDelay--;
- if (m_teamDelay<1) {
- queueUnits(); // update the queues.
- if (m_readyToBuildTeam) {
- processTeamBuilding();
- }
- m_teamDelay = 5*LOGICFRAMES_PER_SECOND; // check again in 5 seconds.
- // Note that this timer gets shortcut when a unit or building is completed.
- }
- }
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * See if it is time to start another upgrade or skill building.
- */
- void AIPlayer::doUpgradesAndSkills( void )
- {
- if (TheGameLogic->getFrame() < 2) {
- // can't do updates on the first few frames
- return;
- }
- Bool checkScience = m_player->getSciencePurchasePoints()>0;
- if (!checkScience) {
- return;
- }
- const AISideInfo *sideInfo = TheAI->getAiData()->m_sideInfo;
- while (sideInfo) {
- if (sideInfo->m_side == m_player->getSide()) {
- break;
- }
- sideInfo = sideInfo->m_next;
- }
- if (sideInfo == NULL) return;
- if (m_skillsetSelector == INVALID_SKILLSET_SELECTION) {
- Int limit = 0;
- // Pick randomly among the skillsets that have skills.
- // Designers sometimes only define skillset 1 & 2, or some such. jba.
- if (sideInfo->m_skillSet2.m_numSkills>0) {
- limit = 1;
- if (sideInfo->m_skillSet3.m_numSkills>0) {
- limit = 2;
- if (sideInfo->m_skillSet4.m_numSkills>0) {
- limit = 3;
- if (sideInfo->m_skillSet5.m_numSkills>0) {
- limit = 4;
- }
- }
- }
- }
- if (isSkirmishAI()) {
- m_skillsetSelector = GameLogicRandomValue(0, limit);
- } else {
- m_skillsetSelector = 0; // Non-skirmish default to 0. jba.
- }
- }
- // SKILLS
- if (m_player->getSciencePurchasePoints()>0) {
- const TSkillSet *skillset;
- switch(m_skillsetSelector) {
- default:
- case 0: skillset = &sideInfo->m_skillSet1; break;
- case 1: skillset = &sideInfo->m_skillSet2; break;
- case 2: skillset = &sideInfo->m_skillSet3; break;
- case 3: skillset = &sideInfo->m_skillSet4; break;
- case 4: skillset = &sideInfo->m_skillSet5; break;
- }
- Int i;
- for (i=0; i<skillset->m_numSkills; i++) {
- ScienceType science = skillset->m_skills[i];
- if (m_player->isCapableOfPurchasingScience(science)) {
- if (m_player->attemptToPurchaseScience(science)) {
- AsciiString msg = TheNameKeyGenerator->keyToName(m_player->getPlayerNameKey());
- msg.concat(" purchases from SkillSet");
- msg.concat('1'+m_skillsetSelector);
- msg.concat(' ');
- msg.concat(TheScienceStore->getInternalNameForScience(science));
- msg.concat(".");
- TheScriptEngine->AppendDebugMessage( msg, false);
- }
- }
- }
- }
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * Perform computer-controlled player AI
- */
- //DECLARE_PERF_TIMER(AIPlayer_update)
- void AIPlayer::update( void )
- {
- //USE_PERF_TIMER(AIPlayer_update)
- doBaseBuilding(); // See if it's time to build another building.
- checkReadyTeams(); // See if any teams are ready to start.
- checkQueuedTeams(); // See if any teams are complete.
- doTeamBuilding(); // See if it's time to start another team.
- doUpgradesAndSkills(); // See if it's time to build an upgrade or buy a skill.
- updateBridgeRepair(); // Handle any bridge repairs.
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * Find any things that build stuff & add them to the build list. Then build any initially built
- * buildings.
- */
- void AIPlayer::newMap( void )
- {
- BuildListInfo *info = m_player->getBuildList();
- // Add any factories placed to the build list.
- Object *obj;
- for( obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() )
- {
- Player *owner = obj->getControllingPlayer();
- if (owner==m_player) {
- // See if it's a factory.
- ProductionUpdateInterface *pu = obj->getProductionUpdateInterface();
- // If it doesn't produce, continue.
- if (!pu) continue;
- m_player->addToBuildList(obj);
- }
- }
- computeCenterAndRadiusOfBase(&m_baseCenter, &m_baseRadius);
- // Build any with the initially built flag.
- for( /* nothing */; info; info = info->getNext() )
- {
- AsciiString name = info->getTemplateName();
- if (name.isEmpty()) continue;
- const ThingTemplate *bldgPlan = TheThingFactory->findTemplate( name );
- if (!bldgPlan) {
- DEBUG_LOG(("*** ERROR - Build list building '%s' doesn't exist.\n", name.str()));
- continue;
- }
- if (info->isInitiallyBuilt()) {
- buildStructureNow(bldgPlan, info);
- } else {
- info->incrementNumRebuilds(); // the initial build in the normal build list consumes a rebuild, so add one.
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** Find the center of the base and the radius of buildings. */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::computeCenterAndRadiusOfBase(Coord3D *center, Real *radius)
- {
- //
- BuildListInfo *info;
- Coord2D totalPos;
- totalPos.x = 0;
- totalPos.y = 0;
- Int numBldg=0;
- for( info = m_player->getBuildList(); info; info = info->getNext() )
- {
- AsciiString name = info->getTemplateName();
- if (name.isEmpty()) continue;
- const ThingTemplate *bldgPlan = TheThingFactory->findTemplate( name );
- if (!bldgPlan) {
- continue;
- }
- Coord3D pos = *info->getLocation();
- totalPos.x += pos.x;
- totalPos.y += pos.y;
- numBldg++;
- }
- if (numBldg>0) {
- totalPos.x /= numBldg;
- totalPos.y /= numBldg;
- }
- m_baseCenterSet = numBldg>0;
- center->x = totalPos.x;
- center->y = totalPos.y;
- Real maxRadSqr = 0;
- //
- for( info = m_player->getBuildList(); info; info = info->getNext() )
- {
- AsciiString name = info->getTemplateName();
- if (name.isEmpty()) continue;
- const ThingTemplate *bldgPlan = TheThingFactory->findTemplate( name );
- if (!bldgPlan) {
- continue;
- }
- Coord3D pos = *info->getLocation();
- Real dx = pos.x-center->x;
- Real dy = pos.y-center->y;
- if (dx<0) dx = -dx;
- if (dy<0) dy = -dy;
- Real bldgRadius = bldgPlan->getTemplateGeometryInfo().getBoundingCircleRadius()*0.4f;
- dx += bldgRadius;
- dy += bldgRadius;
- Real radSqr = dx*dx+dy*dy;
- if (radSqr>maxRadSqr) maxRadSqr=radSqr;
- }
- *radius = sqrt(maxRadSqr);
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * Checks to see if we're building a dozer.
- */
- Bool AIPlayer::dozerInQueue( void )
- {
- { // needed to scope iter. silly ms c++.
- for ( DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamBuildQueue(); !iter.done(); iter.advance())
- {
- TeamInQueue *team = iter.cur();
- if (team && team->includesADozer() )
- {
- return true; // dozer is building already.
- }
- }
- }
- return false;
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * Queues up a dozer.
- */
- void AIPlayer::queueDozer( void )
- {
- if (dozerInQueue()) return;
- // Find a factory that can build a dozer.
- Bool canBuildUnits = m_player->getCanBuildUnits();
- // If we need a dozer, turn on unit building for a moment.
- m_player->setCanBuildUnits(true);
- const ThingTemplate *tTemplate = TheThingFactory->firstTemplate();
- while (tTemplate) {
- if (tTemplate->isKindOf(KINDOF_DOZER)) {
- Object *factory = findFactory(tTemplate, true);
- if (factory) {
- // we can build one.
- WorkOrder *order = newInstance(WorkOrder);
- order->m_thing = tTemplate;
- order->m_factoryID = INVALID_ID;
- order->m_numRequired = 1;
- order->m_required = true;
- order->m_isResourceGatherer = FALSE;
- // prepend to head of list
- order->m_next = NULL;
- TeamInQueue *team = newInstance(TeamInQueue);
- // Put in front of queue.
- prependTo_TeamBuildQueue(team);
- team->m_priorityBuild = true;
- team->m_workOrders = order;
- team->m_frameStarted = TheGameLogic->getFrame();
- // Stick it on the default team
- team->m_team = m_player->getDefaultTeam();
- AsciiString teamName = "DOZER - building one at the ";
- teamName.concat(factory->getTemplate()->getName());
- TheScriptEngine->AppendDebugMessage(teamName, false);
- m_teamDelay = 0;
- startTraining( order, team->m_priorityBuild, team->m_team->getName());
- break;
- }
- }
- tTemplate = tTemplate->friend_getNextTemplate();
- }
- // restore canbuildunits.
- m_player->setCanBuildUnits(canBuildUnits);
- }
- //-------------------------------------------------------------------------------------------------
- /** Difficulty level for this player */
- //-------------------------------------------------------------------------------------------------
- enum GameDifficulty AIPlayer::getAIDifficulty(void) const
- {
- return m_difficulty;
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * Finds a dozer that isn't building or collecting resources.
- */
- Object * AIPlayer::findDozer( const Coord3D *pos )
- {
- // Add any factories placed to the build list.
- Object *obj;
- Object *dozer = NULL;
- Bool needDozer = true;
- Object *closestDozer=NULL;
- Real closestDistSqr = 0;
- for( obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() )
- {
- Player *owner = obj->getControllingPlayer();
- if (owner==m_player) {
- // See if it's a dozer.
- if (obj->isKindOf(KINDOF_DOZER)) {
- AIUpdateInterface *ai = obj->getAIUpdateInterface();
- if (ai==NULL) {
- continue;
- }
- DozerAIInterface* dozerAI = ai->getDozerAIInterface();
- if (dozerAI) {
- // Since workers can be dozers, hmmm....
- SupplyTruckAIInterface* supplyTruckAI = ai->getSupplyTruckAIInterface();
- if( !dozerAI->isAnyTaskPending() && supplyTruckAI ) {
- // If it is gathering supplies, don't steal it.
- if (supplyTruckAI->isCurrentlyFerryingSupplies() || supplyTruckAI->isForcedIntoWantingState())
- {
- continue;
- }
- }
- if (obj->getID() == m_repairDozer) {
- continue; // don't steal the repair dozer.
- }
- needDozer = false; // dozer exists, may be busy.
- if (dozerAI->isTaskPending(DOZER_TASK_BUILD)) {
- continue; // already building.
- }
- if (!dozerAI->isAnyTaskPending()) {
- dozer = obj; // prefer an idle dozer
- }
- if (dozer==NULL) {
- dozer = obj; // but we'll take one doing stuff.
- }
- if (dozer && !dozerAI->isAnyTaskPending()) {
- // Got a good one, track closest.
- Real distSqr;
- Real dx, dy;
- dx = pos->x - dozer->getPosition()->x;
- dy = pos->y - dozer->getPosition()->y;
- distSqr = dx*dx+dy*dy;
- if (closestDozer == NULL) {
- closestDozer = dozer;
- closestDistSqr = distSqr;
- } else if (distSqr < closestDistSqr) {
- closestDozer = dozer;
- closestDistSqr = distSqr;
- }
- }
- }
- }
- }
- }
- if (needDozer) {
- queueDozer();
- }
- if (closestDozer) return closestDozer;
- return dozer;
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version
- * 2: added m_teamSeconds delay.
- * 3: Added m_curWarehouseID.
- * 1: Reset back to 1 with major save file changes.
- */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // team build queue count
- UnsignedShort teamBuildQueueCount = 0;
- for( DLINK_ITERATOR< TeamInQueue > teamInQueueIt = iterate_TeamBuildQueue();
- teamInQueueIt.done() == FALSE;
- teamInQueueIt.advance() )
- teamBuildQueueCount++;
- xfer->xferUnsignedShort( &teamBuildQueueCount );
- // team build queue data
- TeamInQueue *teamInQueue;
- if( xfer->getXferMode() == XFER_SAVE )
- {
- for( DLINK_ITERATOR< TeamInQueue > teamInQueueIt = iterate_TeamBuildQueue();
- teamInQueueIt.done() == FALSE;
- teamInQueueIt.advance() )
- {
-
- // get element data
- teamInQueue = teamInQueueIt.cur();
- // xfer it
- xfer->xferSnapshot( teamInQueue );
- } // end for, iterate team build queue
- } // end if, save
- else
- {
- // sanity, the list must be empty
- if( getFirstItemIn_TeamBuildQueue() != NULL )
- {
-
- DEBUG_CRASH(( "AIPlayer::xfer - TeamBuildQueue head is not NULL, you should delete it or something before loading a new list\n" ));
- throw SC_INVALID_DATA;
- } // end if
- // ready all data
- for( UnsignedShort i = 0; i < teamBuildQueueCount; ++i )
- {
- // allocate new team in queue instance
- teamInQueue = newInstance(TeamInQueue);
- // attach to end of list
- prependTo_TeamBuildQueue( teamInQueue );
- // xfer data
- xfer->xferSnapshot( teamInQueue );
- } // end for, i
- // the list was loaded in reverse order, reverse the list so it's in the same order as before
- reverse_TeamBuildQueue();
- } // end else, load
- // team ready queue count
- UnsignedShort teamReadyQueueCount = 0;
- for( DLINK_ITERATOR< TeamInQueue > teamReadyQueueIt = iterate_TeamReadyQueue();
- teamReadyQueueIt.done() == FALSE;
- teamReadyQueueIt.advance() )
- teamReadyQueueCount++;
- xfer->xferUnsignedShort( &teamReadyQueueCount );
- // team Ready queue data
- TeamInQueue *teamReadyQueue;
- if( xfer->getXferMode() == XFER_SAVE )
- {
- for( DLINK_ITERATOR< TeamInQueue > teamReadyQueueIt = iterate_TeamReadyQueue();
- teamReadyQueueIt.done() == FALSE;
- teamReadyQueueIt.advance() )
- {
-
- // get element
- teamReadyQueue = teamReadyQueueIt.cur();
-
- // xfer data
- xfer->xferSnapshot( teamReadyQueue );
- } // end for, iterate team ready queue
- } // end if, save
- else
- {
- // sanity, the list must be empty
- if( getFirstItemIn_TeamReadyQueue() != NULL )
- {
-
- DEBUG_CRASH(( "AIPlayer::xfer - TeamReadyQueue head is not NULL, you should delete it or something before loading a new list\n" ));
- throw SC_INVALID_DATA;
- } // end if
- // read all data
- for( UnsignedShort i = 0; i < teamReadyQueueCount; ++i )
- {
- // allocate new team in queue instance
- teamInQueue = newInstance(TeamInQueue);
- // attach to end of list
- prependTo_TeamReadyQueue( teamInQueue );
- // xfer data
- xfer->xferSnapshot( teamInQueue );
- } // end for, i
- // reverse the list since it was loaded in reverse order due to the prepend
- reverse_TeamReadyQueue();
- } // end else, load
- // xfer player index ... this is really just for sanity
- PlayerIndex playerIndex = m_player->getPlayerIndex();
- xfer->xferUser( &playerIndex, sizeof( PlayerIndex ) );
- if( playerIndex != m_player->getPlayerIndex() )
- {
- DEBUG_CRASH(( "AIPlayer::xfer - player index mismatch\n" ));
- throw SC_INVALID_DATA;
- } // end if
- // xfer the rest of the ai player data (it's pretty straight forward)
- xfer->xferBool( &m_readyToBuildTeam );
- xfer->xferBool( &m_readyToBuildStructure );
- xfer->xferInt( &m_teamTimer );
- xfer->xferInt( &m_structureTimer );
- xfer->xferInt( &m_buildDelay );
- xfer->xferInt( &m_teamDelay );
- xfer->xferInt(&m_teamSeconds);
- xfer->xferObjectID(&m_curWarehouseID);
- xfer->xferInt( &m_frameLastBuildingBuilt );
- xfer->xferUser( &m_difficulty, sizeof( GameDifficulty ) );
- xfer->xferInt( &m_skillsetSelector );
- xfer->xferCoord3D( &m_baseCenter );
- xfer->xferBool( &m_baseCenterSet );
- xfer->xferReal( &m_baseRadius );
- xfer->xferUser( m_structuresToRepair, sizeof( ObjectID ) * MAX_STRUCTURES_TO_REPAIR );
- xfer->xferObjectID( &m_repairDozer );
- xfer->xferInt( &m_structuresInQueue );
- xfer->xferBool( &m_dozerQueuedForRepair );
- xfer->xferBool( &m_dozerIsRepairing );
- xfer->xferInt( &m_bridgeTimer );
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIPlayer::loadPostProcess( void )
- {
- } // end loadPostProcess
- #endif
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- TeamInQueue::~TeamInQueue()
- {
- WorkOrder *order, *next;
- for( order = m_workOrders; order; order = next )
- {
- next = order->m_next;
- order->deleteInstance();
- }
- // If we have a team, activate it. If it is empty, Team.cpp will remove empty active teams.
- if (m_team) m_team->setActive();
- m_workOrders = NULL;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Bool TeamInQueue::isAllBuilt()
- {
- WorkOrder *order;
- Bool stillBuilding = false;
- for( order = m_workOrders; order; order = order->m_next )
- {
- if (order->m_numRequired>order->m_numCompleted)
- {
- stillBuilding = true;
- }
- }
- return !stillBuilding;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Bool TeamInQueue::isBuildTimeExpired()
- {
- if (m_team->getPrototype()->getTemplateInfo()->m_initialIdleFrames<1) {
- return false; // Unlimited time.
- }
- if (TheGameLogic->getFrame() > m_frameStarted + m_team->getPrototype()->getTemplateInfo()->m_initialIdleFrames) {
- return true;
- }
- return false;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Bool TeamInQueue::isMinimumBuilt()
- {
- WorkOrder *order;
- for( order = m_workOrders; order; order = order->m_next )
- {
- Int count = order->m_numCompleted;
- if (order->m_factoryID != INVALID_ID) {
- count++; // we have one building.
- }
- if (order->m_numRequired>count)
- {
- if (order->m_required) {
- return false; // required units not built.
- }
- }
- }
- return true;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Bool TeamInQueue::includesADozer()
- {
- WorkOrder *order;
- for( order = m_workOrders; order; order = order->m_next )
- {
- // GLA dozers (workers) are also resource gatherers, so make sure it isn't a gatherer. jba.
- if (order->m_thing->isKindOf(KINDOF_DOZER) && !order->m_isResourceGatherer) {
- return true;
- }
- }
- return false;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Bool TeamInQueue::areBuildsComplete()
- {
- WorkOrder *order;
- for( order = m_workOrders; order; order = order->m_next )
- {
- if (order->m_factoryID != INVALID_ID) {
- return false; // we have one building.
- }
- }
- return true;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void TeamInQueue::disband()
- {
- Team *newTeam = m_team->getPrototype()->getControllingPlayer()->getDefaultTeam();
- AsciiString teamName = m_team->getPrototype()->getName();
- teamName.concat(" - team disbanded, build time expired.");
- TheScriptEngine->AppendDebugMessage(teamName, false);
- if (m_team != newTeam) {
- m_team->transferUnitsTo(newTeam);
- if (!m_team->getPrototype()->getIsSingleton()) {
- m_team->deleteInstance();
- }
- m_team = NULL;
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void TeamInQueue::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void TeamInQueue::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;;
- xfer->xferVersion( &version, currentVersion );
- // xfer work order count
- UnsignedShort workOrderCount = 0;
- WorkOrder *workOrder;
- for( workOrder = m_workOrders; workOrder; workOrder = workOrder->m_next )
- workOrderCount++;
- xfer->xferUnsignedShort( &workOrderCount );
- // xfer work orders
- if( xfer->getXferMode() == XFER_SAVE )
- {
- // xfer each work order
- for( workOrder = m_workOrders; workOrder; workOrder = workOrder->m_next )
- {
- // xfer work order data
- xfer->xferSnapshot( workOrder );
- } // end for
- } // end if, save
- else
- {
- // sanity
- if( m_workOrders != NULL )
- {
- DEBUG_CRASH(( "TeamInQueue::xfer - m_workOrders should be NULL but isn't. Perhaps you should blow it away before loading\n" ));
- throw SC_INVALID_DATA;
- } // end if
- // load all work orders
- for( UnsignedShort i = 0; i < workOrderCount; ++i )
- {
- // allocate new work order
- workOrder = newInstance(WorkOrder);
- // attach to list at the end
- workOrder->m_next = NULL;
- if( m_workOrders == NULL )
- m_workOrders = workOrder;
- else
- {
- WorkOrder *last = m_workOrders;
- while( last->m_next != NULL )
- last = last->m_next;
- last->m_next = workOrder;
- } // end else
- // load work order data
- xfer->xferSnapshot( workOrder );
- } // end for, i
- } // end else, load
- // xfer the rest of the team in queue data
- xfer->xferBool( &m_priorityBuild );
- TeamID teamID = m_team ? m_team->getID() : TEAM_ID_INVALID;
- xfer->xferUser( &teamID, sizeof( TeamID ) );
- if( xfer->getXferMode() == XFER_LOAD )
- m_team = TheTeamFactory->findTeamByID( teamID );
- xfer->xferInt( &m_frameStarted );
- xfer->xferBool( &m_sentToStartLocation );
- xfer->xferBool( &m_stopQueueing );
- xfer->xferBool( &m_reinforcement );
- xfer->xferObjectID( &m_reinforcementID );
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void TeamInQueue::loadPostProcess( void )
- {
- } // end loadPostProcess
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- WorkOrder::~WorkOrder()
- {
- } // end WorkOrder
- // ------------------------------------------------------------------------------------------------
- /** Verify factoryID still refers to an active object */
- // ------------------------------------------------------------------------------------------------
- void WorkOrder::validateFactory( Player *thisPlayer )
- {
- if (m_factoryID == INVALID_ID)
- return;
- Object *factory = TheGameLogic->findObjectByID( m_factoryID );
- if ( factory == NULL) {
- m_factoryID = INVALID_ID;
- return;
- }
- if (factory->getControllingPlayer()!=thisPlayer) {
- m_factoryID = INVALID_ID;
- }
- } // end validateFactory
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void WorkOrder::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void WorkOrder::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // thing template
- AsciiString thingTemplateName = m_thing ? m_thing->getName() : AsciiString::TheEmptyString;
- xfer->xferAsciiString( &thingTemplateName );
- if( xfer->getXferMode() == XFER_LOAD )
- m_thing = TheThingFactory->findTemplate( thingTemplateName );
- // factory id
- xfer->xferObjectID( &m_factoryID );
- // num completed
- xfer->xferInt( &m_numCompleted );
- // num required
- xfer->xferInt( &m_numRequired );
- // is required
- xfer->xferBool( &m_required );
- // is resource gatherer
- xfer->xferBool( &m_isResourceGatherer );
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void WorkOrder::loadPostProcess( void )
- {
- } // end loadPostProcess
- //----------------------------------------------------------------------------------------------------------
- /**
- * Get the bounds for a player's structure.
- */
- void AIPlayer::getPlayerStructureBounds(Region2D *bounds, Int playerNdx )
- {
- Player::PlayerTeamList::const_iterator it;
- Bool firstObject = true;
- Bool firstStructure = true;
- bounds->hi.x = bounds->lo.x = bounds->hi.y = bounds->lo.x = 0;
- Region2D objBounds;
- objBounds.hi.x = objBounds.lo.x = objBounds.hi.y = objBounds.lo.x = 0;
- Player* pPlayer = ThePlayerList->getNthPlayer(playerNdx);
- if (pPlayer == NULL) return;
- for (it = pPlayer->getPlayerTeams()->begin(); it != pPlayer->getPlayerTeams()->end(); ++it) {
- for (DLINK_ITERATOR<Team> iter = (*it)->iterate_TeamInstanceList(); !iter.done(); iter.advance()) {
- Team *team = iter.cur();
- if (!team) continue;
- for (DLINK_ITERATOR<Object> iter = team->iterate_TeamMemberList(); !iter.done(); iter.advance()) {
- Object *pObj = iter.cur();
- if (!pObj) continue;
- if (pObj->isKindOf(KINDOF_STRUCTURE)) {
- Coord3D pos = *pObj->getPosition();
- if (firstObject) {
- objBounds.lo.x = objBounds.hi.x = pos.x;
- objBounds.lo.y = objBounds.hi.y = pos.y;
- firstObject = false;
- } else {
- if (objBounds.lo.x>pos.x) objBounds.lo.x = pos.x;
- if (objBounds.lo.y>pos.y) objBounds.lo.y = pos.y;
- if (objBounds.hi.x<pos.x) objBounds.hi.x = pos.x;
- if (objBounds.hi.y<pos.y) objBounds.hi.y = pos.y;
- }
- if (firstStructure) {
- bounds->lo.x = bounds->hi.x = pos.x;
- bounds->lo.y = bounds->hi.y = pos.y;
- firstStructure = false;
- } else {
- if (bounds->lo.x>pos.x) bounds->lo.x = pos.x;
- if (bounds->lo.y>pos.y) bounds->lo.y = pos.y;
- if (bounds->hi.x<pos.x) bounds->hi.x = pos.x;
- if (bounds->hi.y<pos.y) bounds->hi.y = pos.y;
- }
- }
- }
- }
- }
- if (!firstStructure) {
- // Player had no structures, so use unit bounds.
- *bounds = objBounds;
- }
- }
|