AIPlayer.cpp 117 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // AIPlayer.cpp ///////////////////////////////////////////////////////////////////////////////////
  24. // Computerized opponent
  25. // Author: Michael S. Booth, January 2002
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/GameMemory.h"
  30. #include "Common/GameState.h"
  31. #include "Common/GlobalData.h"
  32. #include "Common/PerfTimer.h"
  33. #include "Common/Player.h"
  34. #include "Common/Team.h"
  35. #include "Common/ThingFactory.h"
  36. #include "Common/PlayerList.h"
  37. #include "Common/BuildAssistant.h"
  38. #include "Common/ThingTemplate.h"
  39. #include "Common/Upgrade.h"
  40. #include "Common/WellKnownKeys.h"
  41. #include "Common/Xfer.h"
  42. #include "GameClient/ControlBar.h"
  43. #include "GameClient/TerrainVisual.h"
  44. #include "GameLogic/GameLogic.h"
  45. #include "GameLogic/Object.h"
  46. #include "GameLogic/AIPlayer.h"
  47. #include "GameLogic/SidesList.h"
  48. #include "GameLogic/AI.h"
  49. #include "GameLogic/AIPathfind.h"
  50. #include "GameLogic/TerrainLogic.h"
  51. #include "GameLogic/Module/AIUpdate.h"
  52. #include "GameLogic/Module/DozerAIUpdate.h"
  53. #include "GameLogic/Module/UpdateModule.h"
  54. #include "GameLogic/ScriptEngine.h"
  55. #include "GameLogic/Module/ProductionUpdate.h"
  56. #include "GameLogic/Module/RebuildHoleBehavior.h"
  57. #include "GameLogic/Module/SupplyTruckAIUpdate.h"
  58. #include "GameLogic/Module/SupplyWarehouseDockUpdate.h"
  59. #include "GameLogic/PartitionManager.h"
  60. #if !defined(_PLAYTEST)
  61. #ifdef _INTERNAL
  62. // for occasional debugging...
  63. //#pragma optimize("", off)
  64. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  65. #endif
  66. #define SUPPLY_CENTER_CLOSE_DIST (20*PATHFIND_CELL_SIZE_F)
  67. #define USE_DOZER 1
  68. // ------------------------------------------------------------------------------------------------
  69. // ------------------------------------------------------------------------------------------------
  70. AIPlayer::AIPlayer( Player *p ) :
  71. m_player(p),
  72. m_buildDelay(0),
  73. m_teamDelay(0),
  74. m_teamTimer(2), // Important - don't start building teams until frame 1.
  75. m_structureTimer(2), // Important - don't start building structures until frame 1.
  76. m_readyToBuildTeam(false),
  77. m_readyToBuildStructure(false),
  78. m_structuresInQueue(0),
  79. m_repairDozer(INVALID_ID),
  80. m_skillsetSelector(INVALID_SKILLSET_SELECTION),
  81. m_dozerQueuedForRepair(false),
  82. m_supplySourceAttackCheckFrame(0),
  83. m_attackedSupplyCenter(INVALID_ID),
  84. m_teamSeconds(10),
  85. m_curWarehouseID(INVALID_ID)
  86. {
  87. m_frameLastBuildingBuilt = TheGameLogic->getFrame();
  88. p->setCanBuildUnits(false); // turn off ai production by default.
  89. Int i;
  90. for (i=0; i<MAX_STRUCTURES_TO_REPAIR; i++) {
  91. m_structuresToRepair[i] = INVALID_ID;
  92. }
  93. m_repairDozerOrigin.zero();
  94. m_baseCenter.zero();
  95. m_baseCenterSet = false;
  96. m_difficulty = TheScriptEngine->getGlobalDifficulty();
  97. m_teamSeconds = TheAI->getAiData()->m_teamSeconds;
  98. }
  99. // ------------------------------------------------------------------------------------------------
  100. // ------------------------------------------------------------------------------------------------
  101. AIPlayer::~AIPlayer()
  102. {
  103. clearTeamsInQueue();
  104. }
  105. // ------------------------------------------------------------------------------------------------
  106. /** Invoked when a structure I am building is finished building. */
  107. // ------------------------------------------------------------------------------------------------
  108. void AIPlayer::onStructureProduced( Object *factory, Object *bldg )
  109. {
  110. m_teamDelay = 0; // Cause the update queues & selection to happen immediately.
  111. m_buildDelay = 0; // Cause
  112. /* Find the info building this. */
  113. BuildListInfo *info;
  114. for( info = m_player->getBuildList(); info; info = info->getNext() )
  115. {
  116. if (info->getObjectID() != bldg->getID()) continue;
  117. Dict d;
  118. d.setAsciiString(TheKey_objectName, info->getBuildingName());
  119. d.setAsciiString(TheKey_objectScriptAttachment, info->getScript());
  120. d.setInt(TheKey_objectInitialHealth, info->getHealth());
  121. d.setBool(TheKey_objectUnsellable, info->getUnsellable());
  122. info->setUnderConstruction(false);
  123. bldg->updateObjValuesFromMapProperties(&d);
  124. // clear the under construction status
  125. bldg->clearStatus( OBJECT_STATUS_UNDER_CONSTRUCTION );
  126. bldg->clearStatus( OBJECT_STATUS_RECONSTRUCTING );
  127. TheScriptEngine->addObjectToCache(bldg);
  128. TheScriptEngine->runObjectScript(info->getScript(), bldg);
  129. if (TheGlobalData->m_debugAI) {
  130. AsciiString bldgName = bldg->getTemplate()->getName();
  131. bldgName.concat(" - Building completed.");
  132. TheScriptEngine->AppendDebugMessage(bldgName, false);
  133. }
  134. checkForSupplyCenter(info, bldg);
  135. return;
  136. }
  137. // Look in build list & see if this is spawned from a hole.
  138. for( info = m_player->getBuildList(); info; info = info->getNext() )
  139. {
  140. const ThingTemplate *bldgPlan = TheThingFactory->findTemplate( info->getTemplateName() );
  141. if (!bldgPlan) {
  142. continue;
  143. }
  144. if (!bldgPlan->isEquivalentTo(bldg->getTemplate())) {
  145. continue; // not the same kind of building we're looking for.
  146. }
  147. // check for hole.
  148. if (info->getObjectID() != INVALID_ID) {
  149. // used to have a building.
  150. Object *obj = TheGameLogic->findObjectByID( info->getObjectID() );
  151. if (obj!=NULL) {
  152. if (obj->isKindOf(KINDOF_REBUILD_HOLE)) {
  153. RebuildHoleBehaviorInterface *rhbi = RebuildHoleBehavior::getRebuildHoleBehaviorInterfaceFromObject( obj );
  154. if( rhbi ) {
  155. ObjectID spawnedID = rhbi->getReconstructedBuildingID();
  156. if (bldg->getID() == spawnedID) {
  157. DEBUG_LOG(("AI got rebuilt %s\n", bldgPlan->getName().str()));
  158. info->setObjectID(bldg->getID());
  159. return;
  160. }
  161. }
  162. }
  163. }
  164. }
  165. }
  166. if (TheGameLogic->getFrame()>0) {
  167. DEBUG_LOG(("***AI PLAYER-Structure not found in production queue.\n"));
  168. }
  169. }
  170. // ------------------------------------------------------------------------------------------------
  171. /** See if the building is a supply center, and see how many supply trucks we want. */
  172. // ------------------------------------------------------------------------------------------------
  173. void AIPlayer::checkForSupplyCenter( BuildListInfo *info, Object *bldg )
  174. {
  175. class SupplyCenterDockUpdate;
  176. // if it is a supply center, I must have boxes
  177. static const NameKeyType key_centerUpdate = NAMEKEY("SupplyCenterDockUpdate");
  178. SupplyCenterDockUpdate *centerModule = (SupplyCenterDockUpdate*)bldg->findUpdateModule( key_centerUpdate );
  179. if( centerModule )
  180. {
  181. info->setSupplyBuilding(true);
  182. Int desiredGatherers = 0;
  183. const AISideInfo *resInfo = TheAI->getAiData()->m_sideInfo;
  184. while (resInfo) {
  185. if (resInfo->m_side == m_player->getSide()) {
  186. GameDifficulty difficulty = m_difficulty;
  187. if (difficulty == DIFFICULTY_EASY) {
  188. desiredGatherers = resInfo->m_easy;
  189. }
  190. if (difficulty == DIFFICULTY_NORMAL) {
  191. desiredGatherers = resInfo->m_normal;
  192. }
  193. if (difficulty == DIFFICULTY_HARD) {
  194. desiredGatherers = resInfo->m_hard;
  195. }
  196. }
  197. resInfo = resInfo->m_next;
  198. }
  199. info->setSupplyBuilding(true);
  200. info->setCurrentGatherers(-1);
  201. info->setDesiredGatherers(desiredGatherers+1); // get a freebie with the supply depots.
  202. }
  203. }
  204. // ------------------------------------------------------------------------------------------------
  205. /** Queue up a supply truck to be built. */
  206. // ------------------------------------------------------------------------------------------------
  207. void AIPlayer::queueSupplyTruck( void )
  208. {
  209. Bool truckInQueue = false;
  210. for ( DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamBuildQueue(); !iter.done(); iter.advance())
  211. {
  212. TeamInQueue *team = iter.cur();
  213. WorkOrder *order;
  214. for( order = team->m_workOrders; order; order = order->m_next )
  215. {
  216. // GLA dozers (workers) are also resource gatherers, so make sure it isn't a worker. jba.
  217. if (order->m_isResourceGatherer) {
  218. truckInQueue = true;
  219. }
  220. }
  221. }
  222. if (truckInQueue) {
  223. return; // already building a supply truck.
  224. }
  225. Int totalHarvesters = 0;
  226. // See how many harvesters we have servicing this supply src.
  227. // Scan my units.
  228. Player::PlayerTeamList::const_iterator it;
  229. for (it = m_player->getPlayerTeams()->begin(); it != m_player->getPlayerTeams()->end(); ++it) {
  230. for (DLINK_ITERATOR<Team> iter = (*it)->iterate_TeamInstanceList(); !iter.done(); iter.advance()) {
  231. Team *team = iter.cur();
  232. if (!team) {
  233. continue;
  234. }
  235. for (DLINK_ITERATOR<Object> objIter = team->iterate_TeamMemberList(); !objIter.done(); objIter.advance()) {
  236. Object *obj = objIter.cur();
  237. if (!obj) continue;
  238. if (!obj->isKindOf(KINDOF_HARVESTER)) continue;
  239. if (!obj->getAI()) continue;
  240. SupplyTruckAIInterface* supplyTruckAI = obj->getAI()->getSupplyTruckAIInterface();
  241. if( supplyTruckAI ) {
  242. totalHarvesters++;
  243. }
  244. }
  245. }
  246. }
  247. /* Find the info building this. */
  248. for( BuildListInfo *info = m_player->getBuildList(); info; info = info->getNext() )
  249. {
  250. if (info->isSupplyBuilding() == false) continue;
  251. Int desiredGatherers = info->getDesiredGatherers();
  252. Int curGatherers = info->getCurrentGatherers();
  253. if (curGatherers>=desiredGatherers) {
  254. // Check & see if any have died.
  255. Object *supplyCenter = TheGameLogic->findObjectByID(info->getObjectID());
  256. // Check for supplies.
  257. if (supplyCenter) {
  258. if (supplyCenter->isKindOf(KINDOF_REBUILD_HOLE)) {
  259. continue; // don't consider rebuild holes.
  260. }
  261. // Make sure we have a supplies near it.
  262. Coord3D center = *supplyCenter->getPosition();
  263. Real radius = SUPPLY_CENTER_CLOSE_DIST + supplyCenter->getGeometryInfo().getBoundingCircleRadius();
  264. PartitionFilterAcceptByKindOf f1(MAKE_KINDOF_MASK(KINDOF_SUPPLY_SOURCE), KINDOFMASK_NONE);
  265. PartitionFilterPlayer f2(m_player, false); // Only find other.
  266. PartitionFilterOnMap filterMapStatus;
  267. PartitionFilter *filters[] = { &f1, &f2, &filterMapStatus, 0 };
  268. Object *supplySource = ThePartitionManager->getClosestObject(&center, radius, FROM_BOUNDINGSPHERE_2D, filters);
  269. if (!supplySource) {
  270. // No supplies.
  271. continue;
  272. }
  273. static const NameKeyType key_warehouseUpdate = NAMEKEY("SupplyWarehouseDockUpdate");
  274. SupplyWarehouseDockUpdate *warehouseModule = (SupplyWarehouseDockUpdate*)supplySource->findUpdateModule( key_warehouseUpdate );
  275. if( warehouseModule ) {
  276. Int availableCash = warehouseModule->getBoxesStored()*TheGlobalData->m_baseValuePerSupplyBox;
  277. if (availableCash<=0) continue;
  278. if( m_player->getRelationship(supplySource->getTeam()) == ENEMIES ) {
  279. continue;
  280. }
  281. }
  282. // Ok, it has supplies available near it.
  283. checkForSupplyCenter(info, supplyCenter);
  284. Int curGatherers = 0;
  285. // See how many harvesters we have servicing this supply src.
  286. // Scan my units.
  287. Player::PlayerTeamList::const_iterator it;
  288. for (it = m_player->getPlayerTeams()->begin(); it != m_player->getPlayerTeams()->end(); ++it) {
  289. for (DLINK_ITERATOR<Team> iter = (*it)->iterate_TeamInstanceList(); !iter.done(); iter.advance()) {
  290. Team *team = iter.cur();
  291. if (!team) {
  292. continue;
  293. }
  294. for (DLINK_ITERATOR<Object> objIter = team->iterate_TeamMemberList(); !objIter.done(); objIter.advance()) {
  295. Object *obj = objIter.cur();
  296. if (!obj) continue;
  297. if (!obj->isKindOf(KINDOF_HARVESTER)) continue;
  298. if (!obj->getAI()) continue;
  299. SupplyTruckAIInterface* supplyTruckAI = obj->getAI()->getSupplyTruckAIInterface();
  300. if( supplyTruckAI ) {
  301. ObjectID dock = supplyTruckAI->getPreferredDockID();
  302. if (dock == supplyCenter->getID()) {
  303. curGatherers++;
  304. if (!supplyTruckAI->isCurrentlyFerryingSupplies()) {
  305. // Note - although this is the ai, we are sending in CMD_FROM_PLAYER.
  306. // This causes the dock object to stick in the docking interface.
  307. // The supply truck ai issues dock commands, and they become confused.
  308. // Thus, player. jba. ;(
  309. obj->getAI()->aiDock(supplyCenter, CMD_FROM_PLAYER);
  310. }
  311. }
  312. }
  313. }
  314. }
  315. }
  316. //DEBUG_LOG(("Expected %d harvesters, found %d, need %d\n", info->getDesiredGatherers(),
  317. // curGatherers, info->getDesiredGatherers()-curGatherers) );
  318. info->setCurrentGatherers(curGatherers);
  319. }
  320. } else {
  321. /* See if we have any "loose" harvesters (cause my supply center got nuked.) */
  322. Player::PlayerTeamList::const_iterator it;
  323. for (it = m_player->getPlayerTeams()->begin(); it != m_player->getPlayerTeams()->end(); ++it) {
  324. for (DLINK_ITERATOR<Team> iter = (*it)->iterate_TeamInstanceList(); !iter.done(); iter.advance()) {
  325. Team *team = iter.cur();
  326. if (!team) continue;
  327. for (DLINK_ITERATOR<Object> objIter = team->iterate_TeamMemberList(); !objIter.done(); objIter.advance()) {
  328. Object *obj = objIter.cur();
  329. if (!obj) continue;
  330. if (!obj->isKindOf(KINDOF_HARVESTER)) continue;
  331. if (!obj->getAI()) continue;
  332. SupplyTruckAIInterface* supplyTruckAI = obj->getAI()->getSupplyTruckAIInterface();
  333. if( supplyTruckAI ) {
  334. ObjectID dock = supplyTruckAI->getPreferredDockID();
  335. if (TheGameLogic->findObjectByID(dock)!=NULL) continue;
  336. if (supplyTruckAI->isCurrentlyFerryingSupplies() || supplyTruckAI->isForcedIntoWantingState())
  337. {
  338. // This thinks he is a gatherer, but doesn't have a preferred dock id.
  339. Object *center = TheGameLogic->findObjectByID(info->getObjectID());
  340. if (center) {
  341. info->setCurrentGatherers(info->getCurrentGatherers()+1);
  342. // Note - although this is the ai, we are sending in CMD_FROM_PLAYER.
  343. // This causes the dock object to stick in the docking interface.
  344. // The supply truck ai issues dock commands, and they become confused.
  345. // Thus, player. jba. ;(
  346. obj->getAI()->aiDock(center, CMD_FROM_PLAYER);
  347. DEBUG_LOG(("Re-attaching supply truck to supply center.\n"));
  348. return;
  349. }
  350. }
  351. }
  352. }
  353. }
  354. }
  355. if (totalHarvesters >= desiredGatherers*3) {
  356. continue; // we got lotsa gatherers.
  357. }
  358. Bool canBuildUnits = m_player->getCanBuildUnits();
  359. // If we need a supply truck thingy, turn on unit building for a moment.
  360. m_player->setCanBuildUnits(true);
  361. const ThingTemplate *tTemplate = TheThingFactory->firstTemplate();
  362. while (tTemplate) {
  363. Bool isSupplyTruck = tTemplate->isKindOf(KINDOF_HARVESTER);;
  364. if (isSupplyTruck) {
  365. Object *factory = findFactory(tTemplate, false);
  366. if (factory) {
  367. // we can build one.
  368. WorkOrder *order = newInstance(WorkOrder);
  369. order->m_thing = tTemplate;
  370. order->m_factoryID = INVALID_ID;
  371. order->m_numRequired = 1;
  372. order->m_required = true;
  373. order->m_isResourceGatherer =true;
  374. // prepend to head of list
  375. order->m_next = NULL;
  376. TeamInQueue *team = newInstance(TeamInQueue);
  377. // Put in front of queue.
  378. prependTo_TeamBuildQueue(team);
  379. team->m_priorityBuild = true;
  380. team->m_workOrders = order;
  381. team->m_frameStarted = TheGameLogic->getFrame();
  382. // Stick it on the default team
  383. team->m_team = m_player->getDefaultTeam();
  384. AsciiString teamName = "Supply truck - building one at the ";
  385. teamName.concat(factory->getTemplate()->getName());
  386. TheScriptEngine->AppendDebugMessage(teamName, false);
  387. m_teamDelay = 0;
  388. if (info->getCurrentGatherers()==-1) {
  389. // First one is automatic. jba.
  390. order->m_factoryID = factory->getID();
  391. info->setCurrentGatherers(0);
  392. } else {
  393. startTraining( order, team->m_priorityBuild, team->m_team->getName());
  394. }
  395. break;
  396. }
  397. }
  398. tTemplate = tTemplate->friend_getNextTemplate();
  399. }
  400. m_player->setCanBuildUnits(canBuildUnits);
  401. }
  402. }
  403. }
  404. // ------------------------------------------------------------------------------------------------
  405. // ------------------------------------------------------------------------------------------------
  406. static void deleteQueue(TeamInQueue* o)
  407. {
  408. if (o)
  409. {
  410. o->deleteInstance();
  411. }
  412. }
  413. // ------------------------------------------------------------------------------------------------
  414. /** Clear the current work order */
  415. // ------------------------------------------------------------------------------------------------
  416. void AIPlayer::clearTeamsInQueue( void )
  417. {
  418. removeAll_TeamBuildQueue(deleteQueue);
  419. removeAll_TeamReadyQueue(deleteQueue);
  420. }
  421. // ------------------------------------------------------------------------------------------------
  422. // ------------------------------------------------------------------------------------------------
  423. Object *AIPlayer::buildStructureNow(const ThingTemplate *bldgPlan, BuildListInfo *info)
  424. {
  425. // inst-construct the building
  426. Object *bldg = TheBuildAssistant->buildObjectNow( NULL,
  427. bldgPlan,
  428. info->getLocation(),
  429. info->getAngle(),
  430. m_player );
  431. // store the object with the build order
  432. if (bldg)
  433. {
  434. Dict d;
  435. d.setAsciiString(TheKey_objectName, info->getBuildingName());
  436. d.setAsciiString(TheKey_objectScriptAttachment, info->getScript());
  437. d.setInt(TheKey_objectInitialHealth, info->getHealth());
  438. d.setBool(TheKey_objectUnsellable, info->getUnsellable());
  439. bldg->updateObjValuesFromMapProperties(&d);
  440. info->setObjectID( bldg->getID() );
  441. info->setObjectTimestamp( TheGameLogic->getFrame()+1 ); // has to be non-zero, so just add 1.
  442. // clear the under construction status
  443. bldg->clearStatus( OBJECT_STATUS_UNDER_CONSTRUCTION );
  444. bldg->clearStatus( OBJECT_STATUS_RECONSTRUCTING );
  445. if (TheGlobalData->m_debugAI) {
  446. AsciiString bldgName = bldgPlan->getName();
  447. bldgName.concat(" - Building completed.");
  448. TheScriptEngine->AppendDebugMessage(bldgName, false);
  449. }
  450. TheScriptEngine->addObjectToCache(bldg);
  451. TheScriptEngine->runObjectScript(info->getScript(), bldg);
  452. checkForSupplyCenter(info, bldg);
  453. ExitInterface *exitInterface = bldg->getObjectExitInterface();
  454. if( exitInterface )
  455. {
  456. Coord3D rallyPoint;
  457. Bool gotOffset = false;
  458. if (fabs(info->getRallyOffset()->x) > 1.0f || fabs(info->getRallyOffset()->y)>1.0f) {
  459. gotOffset;
  460. }
  461. if (!exitInterface->getNaturalRallyPoint(rallyPoint)) {
  462. rallyPoint = *info->getLocation();
  463. }
  464. if (gotOffset) {
  465. rallyPoint.x += info->getRallyOffset()->x;
  466. rallyPoint.y += info->getRallyOffset()->y;
  467. exitInterface->setRallyPoint(&rallyPoint);
  468. }
  469. }
  470. } // bldg built
  471. return bldg;
  472. }
  473. // ------------------------------------------------------------------------------------------------
  474. // ------------------------------------------------------------------------------------------------
  475. Object *AIPlayer::buildStructureWithDozer(const ThingTemplate *bldgPlan, BuildListInfo *info)
  476. {
  477. // Find a dozer.
  478. Object *dozer = findDozer(info->getLocation());
  479. if (dozer==NULL) {
  480. return NULL;
  481. }
  482. // Check available funds.
  483. Money *money = m_player->getMoney();
  484. if (money->countMoney()<bldgPlan->calcCostToBuild(m_player)) {
  485. return NULL;
  486. }
  487. // construct the building
  488. Coord3D pos = *info->getLocation();
  489. pos.z += TheTerrainLogic->getGroundHeight(pos.x, pos.y);
  490. if( !dozer->getAIUpdateInterface() )
  491. {
  492. return NULL;
  493. }
  494. Real angle = info->getAngle();
  495. if( TheBuildAssistant->isLocationLegalToBuild( &pos, bldgPlan, angle,
  496. BuildAssistant::NO_ENEMY_OBJECT_OVERLAP,
  497. dozer, m_player ) != LBC_OK ) {
  498. // If there's enemy units or structures, don't build/rebuild.
  499. TheTerrainVisual->removeAllBibs(); // isLocationLegalToBuild adds bib feedback, turn it off. jba.
  500. return NULL;
  501. }
  502. // validate the the position to build at is valid
  503. if( TheBuildAssistant->isLocationLegalToBuild( &pos, bldgPlan, angle,
  504. BuildAssistant::CLEAR_PATH |
  505. BuildAssistant::TERRAIN_RESTRICTIONS |
  506. BuildAssistant::NO_OBJECT_OVERLAP,
  507. dozer, m_player ) != LBC_OK ) {
  508. // Warn.
  509. AsciiString bldgName = bldgPlan->getName();
  510. bldgName.concat(" - Dozer unable to place. Attempting to adjust position.");
  511. TheScriptEngine->AppendDebugMessage(bldgName, false);
  512. // try to fix.
  513. Real posOffset;
  514. Bool valid = false;
  515. // Wiggle it a little :)
  516. Real limit = 10*PATHFIND_CELL_SIZE_F;
  517. if (isSkirmishAI()) {
  518. limit = 120*PATHFIND_CELL_SIZE_F;
  519. }
  520. Coord3D newPos = pos;
  521. for (posOffset = 0; posOffset<limit; posOffset += 2*PATHFIND_CELL_SIZE_F) {
  522. if (isSkirmishAI()) {
  523. posOffset += 2*PATHFIND_CELL_SIZE_F;
  524. }
  525. Real offset = posOffset/2;
  526. Real xPos, yPos;
  527. yPos = pos.y-offset;
  528. for (xPos = pos.x-offset; xPos <= pos.x+offset; xPos+=PATHFIND_CELL_SIZE_F) {
  529. if (isSkirmishAI()) xPos += PATHFIND_CELL_SIZE_F;
  530. newPos.x = xPos;
  531. newPos.y = yPos;
  532. valid = TheBuildAssistant->isLocationLegalToBuild( &newPos, bldgPlan, angle,
  533. BuildAssistant::CLEAR_PATH |
  534. BuildAssistant::TERRAIN_RESTRICTIONS |
  535. BuildAssistant::NO_OBJECT_OVERLAP,
  536. dozer, m_player ) == LBC_OK;
  537. if (valid) break;
  538. newPos.y = yPos+posOffset;
  539. valid = TheBuildAssistant->isLocationLegalToBuild( &newPos, bldgPlan, angle,
  540. BuildAssistant::CLEAR_PATH |
  541. BuildAssistant::TERRAIN_RESTRICTIONS |
  542. BuildAssistant::NO_OBJECT_OVERLAP,
  543. dozer, m_player ) == LBC_OK;
  544. }
  545. if (valid) break;
  546. xPos = pos.x-offset;
  547. for (yPos = pos.y-offset; yPos <= pos.y+offset; yPos+=PATHFIND_CELL_SIZE_F) {
  548. if (isSkirmishAI()) yPos += PATHFIND_CELL_SIZE_F;
  549. newPos.x = xPos;
  550. newPos.y = yPos;
  551. valid = TheBuildAssistant->isLocationLegalToBuild( &newPos, bldgPlan, angle,
  552. BuildAssistant::CLEAR_PATH |
  553. BuildAssistant::TERRAIN_RESTRICTIONS |
  554. BuildAssistant::NO_OBJECT_OVERLAP,
  555. dozer, m_player ) == LBC_OK;
  556. if (valid) break;
  557. newPos.x = xPos+posOffset;
  558. valid = TheBuildAssistant->isLocationLegalToBuild( &newPos, bldgPlan, angle,
  559. BuildAssistant::CLEAR_PATH |
  560. BuildAssistant::TERRAIN_RESTRICTIONS |
  561. BuildAssistant::NO_OBJECT_OVERLAP,
  562. dozer, m_player ) == LBC_OK;
  563. }
  564. if (valid) break;
  565. }
  566. if (valid) pos = newPos;
  567. if (!valid) {
  568. valid = TheBuildAssistant->isLocationLegalToBuild( &pos, bldgPlan, angle,
  569. BuildAssistant::NO_ENEMY_OBJECT_OVERLAP,
  570. dozer, m_player ) == LBC_OK;
  571. if (!valid) {
  572. return NULL;
  573. }
  574. }
  575. }
  576. TheTerrainVisual->removeAllBibs(); // isLocationLegalToBuild adds bib feedback, turn it off. jba.
  577. if (!TheAI->pathfinder()->quickDoesPathExist(dozer->getAI()->getLocomotorSet(),
  578. dozer->getPosition(), &pos)) {
  579. AsciiString bldgName = bldgPlan->getName();
  580. bldgName.concat(" - Dozer unable to reach building. Teleporting.");
  581. TheScriptEngine->AppendDebugMessage(bldgName, false);
  582. dozer->setPosition(&pos);
  583. }
  584. Object *bldg = TheBuildAssistant->buildObjectNow( dozer,
  585. bldgPlan,
  586. &pos,
  587. angle,
  588. m_player );
  589. #if defined _DEBUG || defined _INTERNAL
  590. if (TheGlobalData->m_debugAI == AI_DEBUG_PATHS)
  591. {
  592. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  593. RGBColor color;
  594. color.blue = 0;
  595. color.red = 1;
  596. color.green = 0;
  597. Coord3D myPos;
  598. myPos = *dozer->getPosition();
  599. myPos.z = TheTerrainLogic->getGroundHeight( myPos.x, myPos.y ) + 0.5f;
  600. addIcon(&myPos, 2*PATHFIND_CELL_SIZE_F, 120, color);
  601. myPos = pos;
  602. myPos.z = TheTerrainLogic->getGroundHeight( myPos.x, myPos.y ) + 0.5f;
  603. addIcon(&myPos, 2*PATHFIND_CELL_SIZE_F, 120, color);
  604. Real dx, dy;
  605. dx = dozer->getPosition()->x - pos.x;
  606. dy = dozer->getPosition()->y - pos.y;
  607. Int count = sqrt(dx*dx+dy*dy)/(PATHFIND_CELL_SIZE_F/2);
  608. if (count<2) count = 2;
  609. Int i;
  610. color.green = 1;
  611. for (i=1; i<count; i++) {
  612. myPos.x = dozer->getPosition()->x + (pos.x-dozer->getPosition()->x)*i/count;
  613. myPos.y = dozer->getPosition()->y + (pos.y-dozer->getPosition()->y)*i/count;
  614. myPos.z = TheTerrainLogic->getGroundHeight( myPos.x, myPos.y ) + 0.5f;
  615. addIcon(&myPos, PATHFIND_CELL_SIZE_F/2, 120, color);
  616. }
  617. }
  618. #endif
  619. // store the object with the build order
  620. if (bldg)
  621. {
  622. ExitInterface *exitInterface = bldg->getObjectExitInterface();
  623. if( exitInterface )
  624. {
  625. Coord3D rallyPoint;
  626. Bool gotOffset = false;
  627. if (fabs(info->getRallyOffset()->x) > 1.0f || fabs(info->getRallyOffset()->y)>1.0f) {
  628. gotOffset;
  629. }
  630. if (!exitInterface->getNaturalRallyPoint(rallyPoint)) {
  631. rallyPoint = *info->getLocation();
  632. }
  633. if (gotOffset) {
  634. rallyPoint.x += info->getRallyOffset()->x;
  635. rallyPoint.y += info->getRallyOffset()->y;
  636. exitInterface->setRallyPoint(&rallyPoint);
  637. }
  638. }
  639. info->setObjectID( bldg->getID() );
  640. info->setObjectTimestamp( TheGameLogic->getFrame()+1 ); // Has to be non-zero, so add 1.
  641. info->setUnderConstruction(true);
  642. if (TheGlobalData->m_debugAI) {
  643. AsciiString bldgName = bldgPlan->getName();
  644. bldgName.concat(" - Building started.");
  645. TheScriptEngine->AppendDebugMessage(bldgName, false);
  646. }
  647. } // bldg built
  648. TheTerrainVisual->removeAllBibs(); // isLocationLegalToBuild adds bib feedback, turn it off. jba.
  649. return bldg;
  650. }
  651. // ------------------------------------------------------------------------------------------------
  652. /** Build our base. */
  653. // ------------------------------------------------------------------------------------------------
  654. void AIPlayer::processBaseBuilding( void )
  655. {
  656. //
  657. // Refresh base buildings. Scan through list, if a building is missing,
  658. // rebuild it, unless it's rebuild count is zero.
  659. //
  660. if (m_readyToBuildStructure)
  661. {
  662. for( BuildListInfo *info = m_player->getBuildList(); info; info = info->getNext() )
  663. {
  664. AsciiString name = info->getTemplateName();
  665. if (name.isEmpty()) continue;
  666. const ThingTemplate *bldgPlan = TheThingFactory->findTemplate( name );
  667. if (!bldgPlan) {
  668. DEBUG_LOG(("*** ERROR - Build list building '%s' doesn't exist.\n", name.str()));
  669. continue;
  670. }
  671. // check for hole.
  672. if (info->getObjectID() != INVALID_ID) {
  673. // used to have a building.
  674. Object *bldg = TheGameLogic->findObjectByID( info->getObjectID() );
  675. if (bldg==NULL) {
  676. // got destroyed.
  677. ObjectID priorID;
  678. priorID = info->getObjectID();
  679. info->setObjectID(INVALID_ID);
  680. info->setObjectTimestamp(TheGameLogic->getFrame()+1);
  681. // Scan for a GLA hole. KINDOF_REBUILD_HOLE
  682. Object *obj;
  683. for( obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() ) {
  684. if (!obj->isKindOf(KINDOF_REBUILD_HOLE)) continue;
  685. RebuildHoleBehaviorInterface *rhbi = RebuildHoleBehavior::getRebuildHoleBehaviorInterfaceFromObject( obj );
  686. if( rhbi ) {
  687. ObjectID spawnerID = rhbi->getSpawnerID();
  688. if (priorID == spawnerID) {
  689. DEBUG_LOG(("AI Found hole to rebuild %s\n", bldgPlan->getName().str()));
  690. info->setObjectID(obj->getID());
  691. }
  692. }
  693. }
  694. } else {
  695. if (bldg->getControllingPlayer() == m_player) {
  696. // Check for built or dozer missing.
  697. if( BitTest( bldg->getStatusBits(), OBJECT_STATUS_UNDER_CONSTRUCTION ) == TRUE) {
  698. // make sure dozer is working on him.
  699. ObjectID builder = bldg->getBuilderID();
  700. Object* myDozer = TheGameLogic->findObjectByID(builder);
  701. if (myDozer==NULL) {
  702. DEBUG_LOG(("AI's Dozer got killed. Find another dozer.\n"));
  703. myDozer = findDozer(bldg->getPosition());
  704. if (myDozer==NULL || myDozer->getAI()==NULL) {
  705. continue;
  706. }
  707. myDozer->getAI()->aiResumeConstruction(bldg, CMD_FROM_AI);
  708. } else {
  709. // make sure he is building.
  710. myDozer->getAI()->aiResumeConstruction(bldg, CMD_FROM_AI);
  711. }
  712. }
  713. } else {
  714. // oops, got captured.
  715. info->setObjectID(INVALID_ID);
  716. info->setObjectTimestamp(TheGameLogic->getFrame()+1);
  717. }
  718. }
  719. }
  720. if (info->getObjectID()==INVALID_ID && info->getObjectTimestamp()>0) {
  721. // this object was built at some time, and got destroyed at or near objectTimestamp.
  722. // Wait a few seconds before initiating a rebuild.
  723. if (info->getObjectTimestamp()+TheAI->getAiData()->m_rebuildDelaySeconds*LOGICFRAMES_PER_SECOND > TheGameLogic->getFrame()) {
  724. continue;
  725. } else {
  726. DEBUG_LOG(("Enabling rebuild for %s\n", info->getTemplateName().str()));
  727. info->setObjectTimestamp(0); // ready to build.
  728. }
  729. }
  730. // check if this building has any "rebuilds" left
  731. if (info->isBuildable())
  732. {
  733. Object *bldg = TheGameLogic->findObjectByID( info->getObjectID() );
  734. if (bldg == NULL)
  735. {
  736. #ifdef USE_DOZER
  737. // dozer-construct the building
  738. bldg = buildStructureWithDozer(bldgPlan, info);
  739. // store the object with the build order
  740. if (bldg)
  741. {
  742. info->setObjectID( bldg->getID() );
  743. info->decrementNumRebuilds();
  744. m_readyToBuildStructure = false;
  745. m_structureTimer = TheAI->getAiData()->m_structureSeconds*LOGICFRAMES_PER_SECOND;
  746. if (m_player->getMoney()->countMoney() < TheAI->getAiData()->m_resourcesPoor) {
  747. m_structureTimer = m_structureTimer/TheAI->getAiData()->m_structuresPoorMod;
  748. } else if (m_player->getMoney()->countMoney() > TheAI->getAiData()->m_resourcesWealthy) {
  749. m_structureTimer = m_structureTimer/TheAI->getAiData()->m_structuresWealthyMod;
  750. }
  751. m_frameLastBuildingBuilt = TheGameLogic->getFrame();
  752. // only build one building per delay loop
  753. break;
  754. } // bldg built
  755. #else
  756. // force delay between rebuilds
  757. if (TheGameLogic->getFrame() - m_frameLastBuildingBuilt < framesToBuild)
  758. {
  759. m_buildDelay = framesToBuild - (TheGameLogic->getFrame() - m_frameLastBuildingBuilt);
  760. return;
  761. } else {
  762. // building is missing, (re)build it
  763. // deduct money to build, if we have it
  764. Int cost = bldgPlan->calcCostToBuild( m_player );
  765. if (m_player->getMoney()->countMoney() >= cost)
  766. {
  767. // we have the money, deduct it
  768. m_player->getMoney()->withdraw( cost );
  769. // inst-construct the building
  770. bldg = buildStructureNow(bldgPlan, info, NULL);
  771. // store the object with the build order
  772. if (bldg)
  773. {
  774. info->setObjectID( bldg->getID() );
  775. info->decrementNumRebuilds();
  776. m_readyToBuildStructure = false;
  777. m_structureTimer = TheAI->getAiData()->m_structureSeconds*LOGICFRAMES_PER_SECOND;
  778. if (m_player->getMoney()->countMoney() < TheAI->getAiData()->m_resourcesPoor) {
  779. m_structureTimer = m_structureTimer/TheAI->getAiData()->m_structuresPoorMod;
  780. } else if (m_player->getMoney()->countMoney() > TheAI->getAiData()->m_resourcesWealthy) {
  781. m_structureTimer = m_structureTimer/TheAI->getAiData()->m_structuresWealthyMod;
  782. }
  783. m_frameLastBuildingBuilt = TheGameLogic->getFrame();
  784. // only build one building per delay loop
  785. break;
  786. } // bldg built
  787. } // have money
  788. } // rebuild delay ok
  789. #endif
  790. } // building missing
  791. } // is buildable
  792. }
  793. }
  794. }
  795. //-------------------------------------------------------------------------------------------------
  796. /** A team is about to be destroyed */
  797. //-------------------------------------------------------------------------------------------------
  798. void AIPlayer::aiPreTeamDestroy( const Team *deletedTeam )
  799. {
  800. {
  801. for (DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamBuildQueue(); !iter.done(); iter.advance())
  802. {
  803. TeamInQueue *team = iter.cur();
  804. if (team->m_team == deletedTeam) {
  805. // The members of the team all got killed before we could finish building the team.
  806. removeFrom_TeamBuildQueue(team);
  807. team->deleteInstance();
  808. iter = iterate_TeamBuildQueue();
  809. }
  810. }
  811. }
  812. {
  813. for ( DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamReadyQueue(); !iter.done(); iter.advance())
  814. {
  815. TeamInQueue *team = iter.cur();
  816. if (team->m_team == deletedTeam) {
  817. // The members of the team all got killed before we could activate the team.
  818. removeFrom_TeamReadyQueue(team);
  819. team->deleteInstance();
  820. iter = iterate_TeamReadyQueue();
  821. }
  822. }
  823. }
  824. }
  825. //-------------------------------------------------------------------------------------------------
  826. /** Guard supply center */
  827. //-------------------------------------------------------------------------------------------------
  828. void AIPlayer::guardSupplyCenter( Team *team, Int minSupplies )
  829. {
  830. m_supplySourceAttackCheckFrame = 0; // force check.
  831. Object *warehouse = NULL;
  832. if (isSupplySourceAttacked()) {
  833. warehouse = TheGameLogic->findObjectByID(m_attackedSupplyCenter);
  834. }
  835. if (warehouse==NULL) {
  836. warehouse = findSupplyCenter(minSupplies);
  837. }
  838. if (warehouse) {
  839. AIGroup* theGroup = TheAI->createGroup();
  840. if (!theGroup) {
  841. return;
  842. }
  843. team->getTeamAsAIGroup(theGroup);
  844. Coord3D location = *warehouse->getPosition();
  845. // It's probably a defensive move - position towards the enemy.
  846. Region2D bounds;
  847. Int enemyNdx = TheScriptEngine->getSkirmishEnemyPlayer()->getPlayerIndex();
  848. getPlayerStructureBounds(&bounds, enemyNdx);
  849. Coord3D offset;
  850. offset.zero();
  851. offset.x = location.x - (bounds.lo.x+bounds.hi.x)*0.5f;
  852. offset.y = location.y - (bounds.lo.y+bounds.hi.y)*0.5f;
  853. offset.normalize();
  854. Real radius = warehouse->getGeometryInfo().getBoundingCircleRadius()*0.8f;
  855. location.x -= offset.x*radius;
  856. location.y -= offset.y*radius;
  857. theGroup->groupGuardPosition( &location, GUARDMODE_NORMAL, CMD_FROM_SCRIPT );
  858. }
  859. }
  860. //-------------------------------------------------------------------------------------------------
  861. /** Is a supply source attacked? */
  862. //-------------------------------------------------------------------------------------------------
  863. Bool AIPlayer::isSupplySourceAttacked( void )
  864. {
  865. const Int SCAN_RATE = 10; // don't scan more often than every 10 seconds.
  866. UnsignedInt curFrame = TheGameLogic->getFrame();
  867. if (curFrame==0) {
  868. m_supplySourceAttackCheckFrame = curFrame+SCAN_RATE;
  869. return false; // can't be attacked on first frame.
  870. }
  871. m_attackedSupplyCenter = INVALID_ID;
  872. if (curFrame < m_supplySourceAttackCheckFrame) {
  873. return false;
  874. }
  875. if (m_player->getAttackedFrame()+SCAN_RATE < curFrame) {
  876. return false; // haven't been attacked recently.
  877. }
  878. m_supplySourceAttackCheckFrame = curFrame+SCAN_RATE;
  879. // Scan my units.
  880. Player::PlayerTeamList::const_iterator it;
  881. for (it = m_player->getPlayerTeams()->begin(); it != m_player->getPlayerTeams()->end(); ++it) {
  882. for (DLINK_ITERATOR<Team> iter = (*it)->iterate_TeamInstanceList(); !iter.done(); iter.advance()) {
  883. Team *team = iter.cur();
  884. if (!team) {
  885. continue;
  886. }
  887. for (DLINK_ITERATOR<Object> objIter = team->iterate_TeamMemberList(); !objIter.done(); objIter.advance()) {
  888. Object *obj = objIter.cur();
  889. if (!obj) {
  890. continue;
  891. }
  892. if (!obj->isKindOf(KINDOF_CASH_GENERATOR) && !obj->isKindOf(KINDOF_DOZER) &&
  893. !obj->isKindOf(KINDOF_HARVESTER)) {
  894. continue;
  895. }
  896. // check for attacked.
  897. BodyModuleInterface *body = obj->getBodyModule();
  898. if (body) {
  899. const DamageInfo *info = body->getLastDamageInfo();
  900. if (info) {
  901. if (info->out.m_noEffect) {
  902. continue;
  903. }
  904. if (body->getLastDamageTimestamp() + SCAN_RATE > curFrame) {
  905. // winner.
  906. m_attackedSupplyCenter = obj->getID();
  907. return true;
  908. }
  909. }
  910. }
  911. }
  912. }
  913. }
  914. return false;
  915. }
  916. //-------------------------------------------------------------------------------------------------
  917. /** Is the nearest supply source safe? */
  918. //-------------------------------------------------------------------------------------------------
  919. Bool AIPlayer::isSupplySourceSafe( Int minSupplies )
  920. {
  921. Object *warehouse = findSupplyCenter(minSupplies);
  922. if (warehouse==NULL) return true; // it's safe cause it doesn't exist.
  923. return (isLocationSafe(warehouse->getPosition(), warehouse->getTemplate()));
  924. }
  925. //-------------------------------------------------------------------------------------------------
  926. /** Is this location safe for building this thing? */
  927. //-------------------------------------------------------------------------------------------------
  928. Bool AIPlayer::isLocationSafe(const Coord3D *pos, const ThingTemplate *tthing )
  929. {
  930. if (tthing == NULL) return 0;
  931. // See if we have enemies.
  932. Real radius = TheAI->getAiData()->m_supplyCenterSafeRadius;
  933. radius += tthing->getTemplateGeometryInfo().getBoundingCircleRadius();
  934. // only consider enemies.
  935. PartitionFilterPlayerAffiliation filterTeam(m_player, (ALLOW_ALLIES|ALLOW_NEUTRAL), false);
  936. // and only stuff that is not dead
  937. PartitionFilterAlive filterAlive;
  938. // and only stuff that isn't stealthed (and not detected)
  939. // (note that stealthed allies aren't hidden from us, but we're only looking for enemies here)
  940. PartitionFilterRejectByObjectStatus filterStealth(OBJECT_STATUS_STEALTHED, OBJECT_STATUS_DETECTED);
  941. // (optional) only stuff that is significant
  942. PartitionFilterInsignificantBuildings filterInsignificant(true, false);
  943. PartitionFilterRejectByKindOf filterHarvesters(MAKE_KINDOF_MASK(KINDOF_HARVESTER), KINDOFMASK_NONE);
  944. PartitionFilterRejectByKindOf filterDozer(MAKE_KINDOF_MASK(KINDOF_DOZER), KINDOFMASK_NONE);
  945. PartitionFilter *filters[16];
  946. Int numFilters = 0;
  947. filters[numFilters++] = &filterTeam;
  948. filters[numFilters++] = &filterAlive;
  949. filters[numFilters++] = &filterStealth;
  950. filters[numFilters++] = &filterInsignificant;
  951. filters[numFilters++] = &filterHarvesters;
  952. filters[numFilters++] = &filterDozer;
  953. filters[numFilters] = NULL;
  954. Object *enemy = ThePartitionManager->getClosestObject( pos, radius, FROM_BOUNDINGSPHERE_2D, filters );
  955. if (enemy!=NULL) {
  956. return false;
  957. }
  958. return true;
  959. } // isSupplySourceSafe
  960. // ------------------------------------------------------------------------------------------------
  961. /** Invoked when a unit I am training comes into existence */
  962. // ------------------------------------------------------------------------------------------------
  963. void AIPlayer::onUnitProduced( Object *factory, Object *unit )
  964. {
  965. Bool found = false;
  966. Bool supplyTruck;
  967. // factory could be NULL at the start of the game.
  968. if (factory == NULL) {
  969. return;
  970. }
  971. for (DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamBuildQueue(); !iter.done(); iter.advance())
  972. {
  973. TeamInQueue *team = iter.cur();
  974. // find work order entry and delete it
  975. WorkOrder *order;
  976. if (found) break;
  977. for( order = team->m_workOrders; order; order = order->m_next )
  978. {
  979. if (order->m_factoryID == factory->getID() && order->m_numCompleted < order->m_numRequired && unit->getTemplate()->isEquivalentTo(order->m_thing))
  980. {
  981. // found associated order, mark it complete.
  982. order->m_numCompleted++;
  983. // put new unit into the team under construction
  984. if (team->m_team)
  985. unit->setTeam( team->m_team );
  986. if (team->m_reinforcement) {
  987. team->m_reinforcementID = unit->getID();
  988. }
  989. AIUpdateInterface *ai = unit->getAIUpdateInterface();
  990. if (team->m_team->getPrototype()->getTemplateInfo()->m_hasHomeLocation) {
  991. if (ai) {
  992. std::vector<Coord3D> path;
  993. path.push_back( *ai->getGoalPosition() );
  994. path.push_back(team->m_team->getPrototype()->getTemplateInfo()->m_homeLocation);
  995. ai->aiFollowExitProductionPath(&path, NULL, CMD_FROM_AI);
  996. }
  997. }
  998. order->m_factoryID = INVALID_ID; // no longer using this factory.
  999. if (ai) {
  1000. // tell it to start gathering resources.
  1001. // Here is the special bit for this exit style, force wanting on SupplyTruck types
  1002. SupplyTruckAIInterface* supplyTruckAI = ai->getSupplyTruckAIInterface();
  1003. if( supplyTruckAI ) {
  1004. if (order->m_isResourceGatherer) {
  1005. supplyTruck = true;
  1006. } else {
  1007. supplyTruck = false;
  1008. }
  1009. supplyTruckAI->setForceWantingState(supplyTruck);
  1010. if (supplyTruck) {
  1011. // assign to a supply depot.
  1012. for( BuildListInfo *info = m_player->getBuildList(); info; info = info->getNext() )
  1013. {
  1014. if (info->isSupplyBuilding() && info->getDesiredGatherers()>0 &&
  1015. info->getDesiredGatherers()>info->getCurrentGatherers()) {
  1016. Object *obj = TheGameLogic->findObjectByID(info->getObjectID());
  1017. if (obj) {
  1018. info->setCurrentGatherers(info->getCurrentGatherers()+1);
  1019. // Note - although this is the ai, we are sending in CMD_FROM_PLAYER.
  1020. // This causes the dock object to stick in the docking interface.
  1021. // The supply truck ai issues dock commands, and they become confused.
  1022. // Thus, player. jba. ;(
  1023. ai->aiDock(obj, CMD_FROM_PLAYER);
  1024. }
  1025. }
  1026. }
  1027. }
  1028. }
  1029. }
  1030. found = true;
  1031. break;
  1032. }
  1033. }
  1034. }
  1035. if (!supplyTruck && unit->isKindOf(KINDOF_DOZER)) {
  1036. if (m_dozerQueuedForRepair) {
  1037. m_repairDozer = unit->getID();
  1038. m_dozerQueuedForRepair =false;
  1039. } else {
  1040. m_buildDelay = 0;
  1041. m_structureTimer = 1;
  1042. }
  1043. }
  1044. if (!found) {
  1045. DEBUG_LOG(("***AI PLAYER-Unit not found in production queue.\n"));
  1046. }
  1047. m_teamDelay = 0; // Cause the update queues & selection to happen immediately.
  1048. }
  1049. //----------------------------------------------------------------------------------------------------------
  1050. /**
  1051. * Find a good spot to fire a superweapon.
  1052. */
  1053. void AIPlayer::computeSuperweaponTarget(const SpecialPowerTemplate *power, Coord3D *retPos, Int playerNdx, Real weaponRadius)
  1054. {
  1055. Region2D bounds;
  1056. getPlayerStructureBounds(&bounds, playerNdx);
  1057. if (weaponRadius<1.0f) {
  1058. weaponRadius = 1.0f; // sanity to avoid divide by 0.
  1059. }
  1060. Int xCount, yCount;
  1061. bounds.lo.x += weaponRadius;
  1062. bounds.hi.x -= weaponRadius;
  1063. if (bounds.hi.x<bounds.lo.x) {
  1064. bounds.hi.x = bounds.lo.x = (bounds.hi.x+bounds.lo.x)/2.0f;
  1065. }
  1066. if (bounds.hi.y<bounds.lo.y) {
  1067. bounds.hi.y = bounds.lo.y = (bounds.hi.y+bounds.lo.y)/2.0f;
  1068. }
  1069. xCount = REAL_TO_INT_CEIL(bounds.width()/weaponRadius)+1;
  1070. yCount = REAL_TO_INT_CEIL(bounds.height()/weaponRadius)+1;
  1071. if (xCount>10) xCount = 10;
  1072. if (yCount>10) yCount = 10;
  1073. Int cash = -1;
  1074. Coord3D pos;
  1075. Coord3D bestPos;
  1076. Int i, j;
  1077. for (i=0; i<xCount; i++) {
  1078. for (j=0; j<yCount; j++) {
  1079. pos.x = bounds.lo.x + (bounds.width()*i)/xCount;
  1080. pos.y = bounds.lo.y + (bounds.height()*j)/yCount;
  1081. pos.z = 0;
  1082. Int curCash = getPlayerSuperweaponValue(&pos, playerNdx, 2*weaponRadius);
  1083. if ( curCash > cash) {
  1084. cash = curCash;
  1085. bestPos = pos;
  1086. }
  1087. }
  1088. }
  1089. Coord3D veryBestPos;
  1090. xCount = 11;
  1091. yCount = 11;
  1092. cash = -1;
  1093. Int count = 0;
  1094. for (i=0; i<xCount; i++) {
  1095. for (j=0; j<yCount; j++) {
  1096. pos.x = bestPos.x + (i-5)*(weaponRadius/10);
  1097. pos.y = bestPos.y+ (j-5)*(weaponRadius/10);
  1098. pos.z = 0;
  1099. Int curCash = getPlayerSuperweaponValue(&pos, playerNdx, weaponRadius);
  1100. if ( curCash > cash) {
  1101. cash = curCash;
  1102. veryBestPos = pos;
  1103. count = 1;
  1104. } else if (curCash==cash) {
  1105. veryBestPos.x += pos.x;
  1106. veryBestPos.y += pos.y;
  1107. count++;
  1108. }
  1109. }
  1110. }
  1111. if (count>1) {
  1112. veryBestPos.x /= count;
  1113. veryBestPos.y /= count;
  1114. }
  1115. veryBestPos.z = TheTerrainLogic->getGroundHeight(veryBestPos.x, veryBestPos.y);
  1116. *retPos = veryBestPos;
  1117. }
  1118. //----------------------------------------------------------------------------------------------------------
  1119. /**
  1120. * Get the target value for structures in an area.
  1121. */
  1122. Int AIPlayer::getPlayerSuperweaponValue(Coord3D *center, Int playerNdx, Real radius )
  1123. {
  1124. if (radius < 4*PATHFIND_CELL_SIZE_F) {
  1125. radius = 4*PATHFIND_CELL_SIZE_F;
  1126. }
  1127. Player::PlayerTeamList::const_iterator it;
  1128. Real cash = 0;
  1129. Real radSqr = sqr(radius);
  1130. Player* pPlayer = ThePlayerList->getNthPlayer(playerNdx);
  1131. if (pPlayer == NULL) return 0;
  1132. for (it = pPlayer->getPlayerTeams()->begin(); it != pPlayer->getPlayerTeams()->end(); ++it) {
  1133. for (DLINK_ITERATOR<Team> iter = (*it)->iterate_TeamInstanceList(); !iter.done(); iter.advance()) {
  1134. Team *team = iter.cur();
  1135. if (!team) continue;
  1136. for (DLINK_ITERATOR<Object> iter = team->iterate_TeamMemberList(); !iter.done(); iter.advance()) {
  1137. Object *pObj = iter.cur();
  1138. if (!pObj) continue;
  1139. if (pObj->isKindOf(KINDOF_AIRCRAFT)) {
  1140. if (pObj->isSignificantlyAboveTerrain()) {
  1141. continue; // Don't target flying aircraft. OK if in the airstrip.
  1142. }
  1143. }
  1144. Coord3D pos = *pObj->getPosition();
  1145. Real dx = center->x - pos.x;
  1146. Real dy = center->y - pos.y;
  1147. if (dx*dx+dy*dy<radSqr) {
  1148. Real dist = sqrt(dx*dx+dy*dy);
  1149. Real factor = 1.0f - (dist/(2*radius)); // 1.0 in center, 0.5 on edges.
  1150. Real value = pObj->getTemplate()->calcCostToBuild(pPlayer);
  1151. if (pObj->isKindOf(KINDOF_COMMANDCENTER)) {
  1152. value = value/10; // Command centers cannot be killed by any superweapon, so we don't want to target them as highly. jba.
  1153. }
  1154. if (value > 3000) {
  1155. value = value/10; // Superweapons can't be killed by superweapons, so we don't want to value them highly.
  1156. }
  1157. cash += factor * value;
  1158. }
  1159. }
  1160. }
  1161. }
  1162. return cash;
  1163. }
  1164. // ------------------------------------------------------------------------------------------------
  1165. /** Search the computer player's buildings for one that can build the given request
  1166. * and start training the unit.
  1167. * If busyOK is true, it will queue a unit even if one is building. This lets
  1168. * script invoked teams "push" to the front of the queue. */
  1169. // ------------------------------------------------------------------------------------------------
  1170. Bool AIPlayer::startTraining( WorkOrder *order, Bool busyOK, AsciiString teamName)
  1171. {
  1172. Object *factory = findFactory(order->m_thing, busyOK);
  1173. if( factory )
  1174. {
  1175. ProductionUpdateInterface *pu = factory->getProductionUpdateInterface();
  1176. if (pu && pu->queueCreateUnit( order->m_thing, pu->requestUniqueUnitID() )) {
  1177. order->m_factoryID = factory->getID();
  1178. if (TheGlobalData->m_debugAI) {
  1179. AsciiString teamStr = "Queuing ";
  1180. teamStr.concat(order->m_thing->getName());
  1181. teamStr.concat(" for ");
  1182. teamStr.concat(teamName);
  1183. TheScriptEngine->AppendDebugMessage(teamStr, false);
  1184. }
  1185. return true;
  1186. }
  1187. } // end if
  1188. return FALSE;
  1189. }
  1190. // ------------------------------------------------------------------------------------------------
  1191. /** Search the computer player's buildings for one that can build the given request.
  1192. * If busyOK is true, it will return a busy factory if there are no idle ones. This is
  1193. * used for script invoked teams "push" to the front of the queue. */
  1194. // ------------------------------------------------------------------------------------------------
  1195. Object *AIPlayer::findFactory(const ThingTemplate *thing, Bool busyOK)
  1196. {
  1197. Object *busyFactory = NULL; // We prefer a factory that isn't busy.
  1198. for( BuildListInfo *info = m_player->getBuildList(); info; info = info->getNext() )
  1199. {
  1200. Object *factory = TheGameLogic->findObjectByID( info->getObjectID() );
  1201. if( factory )
  1202. {
  1203. if (factory->getControllingPlayer() != m_player) {
  1204. info->setObjectID(INVALID_ID);
  1205. continue;
  1206. }
  1207. // ignore buildings that are under construction.
  1208. if (factory->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))
  1209. continue;
  1210. // also ignore buildings that are being sold.
  1211. if (factory->testStatus(OBJECT_STATUS_SOLD))
  1212. continue;
  1213. ProductionUpdateInterface *pu = factory->getProductionUpdateInterface();
  1214. // If it doesn't produce, continue.
  1215. if (!pu) continue;
  1216. // if we can't create the unit do nothing
  1217. if( TheBuildAssistant->isPossibleToMakeUnit( factory, thing ) == FALSE )
  1218. continue;
  1219. // If the factory is not busy, return it.
  1220. Bool busy = pu->getProductionCount()>0;
  1221. if (!busy) return factory; // found a not busy factory.
  1222. if (busyOK) busyFactory = factory;
  1223. } // end if
  1224. } // end for
  1225. // We didn't find an idle factory, so return the busy one.
  1226. if (busyOK) return busyFactory;
  1227. return NULL;
  1228. }
  1229. // ------------------------------------------------------------------------------------------------
  1230. /** Return true if team can be considered for building */
  1231. // ------------------------------------------------------------------------------------------------
  1232. Bool AIPlayer::isPossibleToBuildTeam( TeamPrototype *proto, Bool requireIdleFactory, Bool &notEnoughMoney)
  1233. {
  1234. /* Make sure we have at least one idle factory, and factories for all unit types. */
  1235. Bool anyIdle = false;
  1236. Int cost=0;
  1237. notEnoughMoney = false;
  1238. for( int i=0; i<proto->getTemplateInfo()->m_numUnitsInfo; i++ )
  1239. {
  1240. const TCreateUnitsInfo *unitInfo = &proto->getTemplateInfo()->m_unitsInfo[0];
  1241. const ThingTemplate *thing = TheThingFactory->findTemplate( unitInfo[i].unitThingName );
  1242. if (thing) {
  1243. Int thingCost = thing->calcCostToBuild(m_player);
  1244. if (NULL == findFactory(thing, true)) {
  1245. // Couldn't find a factory.
  1246. return false;
  1247. }
  1248. if (NULL != findFactory(thing, false)) {
  1249. // Found an idle factory.
  1250. anyIdle = true;
  1251. }
  1252. cost += thingCost * ((unitInfo[i].maxUnits+unitInfo[i].minUnits)/2.0f);
  1253. }
  1254. }
  1255. cost *= TheAI->getAiData()->m_teamResourcesToBuild;
  1256. if (m_player->getMoney()->countMoney() < cost) {
  1257. notEnoughMoney = true;
  1258. return false; // too expensive
  1259. }
  1260. if (anyIdle)
  1261. {
  1262. return true;
  1263. }
  1264. if (!requireIdleFactory)
  1265. {
  1266. // Doesn't require an idle factory, so we're ok.
  1267. return true;
  1268. }
  1269. return false;
  1270. }
  1271. // ------------------------------------------------------------------------------------------------
  1272. /** Check if this team is buildable, doesn't exceed maximum limits, meets conditions,
  1273. * and isn't under construction. */
  1274. // ------------------------------------------------------------------------------------------------
  1275. Bool AIPlayer::isAGoodIdeaToBuildTeam( TeamPrototype *proto )
  1276. {
  1277. // Check condition.
  1278. if (!proto->evaluateProductionCondition()) {
  1279. return false;
  1280. }
  1281. // check build limit
  1282. if (proto->countTeamInstances() >= proto->getTemplateInfo()->m_maxInstances){
  1283. if (TheGlobalData->m_debugAI) {
  1284. AsciiString str;
  1285. str.format("Team %s not chosen - %d already exist.", proto->getName().str(), proto->countTeamInstances());
  1286. TheScriptEngine->AppendDebugMessage(str, false);
  1287. }
  1288. return false; // Max already built.
  1289. }
  1290. for ( DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamBuildQueue(); !iter.done(); iter.advance())
  1291. {
  1292. TeamInQueue *team = iter.cur();
  1293. if (team->m_team->getPrototype() == proto) {
  1294. return false; // currently building one of these.
  1295. }
  1296. }
  1297. Bool needMoney;
  1298. if (!isPossibleToBuildTeam( proto, true, needMoney)) {
  1299. if (TheGlobalData->m_debugAI) {
  1300. AsciiString str;
  1301. if (needMoney) {
  1302. str.format("Team %s not chosen - Not enough money.", proto->getName().str());
  1303. } else {
  1304. str.format("Team %s not chosen - Factory/tech missing or busy.", proto->getName().str());
  1305. }
  1306. TheScriptEngine->AppendDebugMessage(str, false);
  1307. }
  1308. return false;
  1309. }
  1310. return true;
  1311. }
  1312. // ------------------------------------------------------------------------------------------------
  1313. /** See if any existing teams need reinforcements, and have higher priority. */
  1314. // ------------------------------------------------------------------------------------------------
  1315. Bool AIPlayer::selectTeamToReinforce( Int minPriority )
  1316. {
  1317. // Find a high production priority team that needs reinforcements.
  1318. Player::PlayerTeamList::const_iterator t;
  1319. Team *curTeam = NULL;
  1320. Int curPriority = minPriority; // Don't reinforce a team unless it is above min priority.
  1321. const ThingTemplate *curThing = NULL;
  1322. for (t = m_player->getPlayerTeams()->begin(); t != m_player->getPlayerTeams()->end(); ++t)
  1323. {
  1324. TeamPrototype *proto = (*t);
  1325. Bool busy = false;
  1326. for ( DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamBuildQueue(); !iter.done(); iter.advance())
  1327. {
  1328. TeamInQueue *team = iter.cur();
  1329. if (team->m_team->getPrototype() == proto) {
  1330. busy = true; // currently building one of these.
  1331. }
  1332. }
  1333. if (busy) continue;
  1334. if (proto->getTemplateInfo()->m_automaticallyReinforce && proto->getTemplateInfo()->m_productionPriority>curPriority) {
  1335. // Check the team instances.
  1336. for (DLINK_ITERATOR<Team> iter = proto->iterate_TeamInstanceList(); !iter.done(); iter.advance())
  1337. {
  1338. Team *team = iter.cur();
  1339. if (team->hasAnyUnits() == false)
  1340. {
  1341. continue; // empty.
  1342. }
  1343. const TCreateUnitsInfo *unitInfo = &team->getPrototype()->getTemplateInfo()->m_unitsInfo[0];
  1344. for( int i=0; i<team->getPrototype()->getTemplateInfo()->m_numUnitsInfo; i++ )
  1345. {
  1346. if (unitInfo[i].maxUnits < 1) continue;
  1347. const ThingTemplate *thing = TheThingFactory->findTemplate( unitInfo[i].unitThingName );
  1348. if (thing==NULL) continue;
  1349. Int count=0;
  1350. team->countObjectsByThingTemplate(1, &thing, false, &count);
  1351. if (count < unitInfo[i].maxUnits)
  1352. {
  1353. // See if there is a factory available.
  1354. if (NULL != findFactory(thing, false))
  1355. {
  1356. curTeam = team;
  1357. curPriority = proto->getTemplateInfo()->m_productionPriority;
  1358. curThing = thing;
  1359. }
  1360. }
  1361. }
  1362. }
  1363. }
  1364. }
  1365. if (curTeam && curThing)
  1366. {
  1367. /* We have something to build. */
  1368. TeamInQueue *teamQ = newInstance(TeamInQueue);
  1369. // Put in front of queue.
  1370. prependTo_TeamBuildQueue(teamQ);
  1371. teamQ->m_priorityBuild = false;
  1372. teamQ->m_reinforcement = true;
  1373. WorkOrder *order = newInstance(WorkOrder);
  1374. order->m_thing = curThing;
  1375. order->m_factoryID = INVALID_ID;
  1376. order->m_numRequired = 1;
  1377. order->m_required = true;
  1378. // prepend to head of list
  1379. order->m_next = NULL;
  1380. teamQ->m_workOrders = order;
  1381. teamQ->m_frameStarted = TheGameLogic->getFrame();
  1382. teamQ->m_team = curTeam;
  1383. AsciiString teamName = curTeam->getPrototype()->getName();
  1384. teamName.concat(" - AutoReinforcing one ");
  1385. teamName.concat(curThing->getName());
  1386. TheScriptEngine->AppendDebugMessage(teamName, false);
  1387. // start the creation of a new unit
  1388. Coord3D origin;
  1389. origin = curTeam->getPrototype()->getTemplateInfo()->m_homeLocation;
  1390. if (curTeam->getFirstItemIn_TeamMemberList())
  1391. {
  1392. origin = *curTeam->getFirstItemIn_TeamMemberList()->getPosition();
  1393. }
  1394. Object *unit = curTeam->tryToRecruit(curThing, &origin, TheAI->getAiData()->m_maxRecruitDistance);
  1395. if (unit)
  1396. {
  1397. order->m_numCompleted = 1;
  1398. AsciiString teamStr = "Team '";
  1399. teamStr.concat(curTeam->getPrototype()->getName());
  1400. teamStr.concat("' recruits ");
  1401. teamStr.concat(curThing->getName());
  1402. teamStr.concat(" from team '");
  1403. teamStr.concat(unit->getTeam()->getPrototype()->getName());
  1404. teamStr.concat("'");
  1405. TheScriptEngine->AppendDebugMessage(teamStr, false);
  1406. unit->setTeam(curTeam);
  1407. teamQ->m_reinforcementID = unit->getID();
  1408. AIUpdateInterface *ai = unit->getAIUpdateInterface();
  1409. if (ai)
  1410. {
  1411. ai->aiIdle(CMD_FROM_AI);
  1412. }
  1413. } else {
  1414. startTraining( order, teamQ->m_priorityBuild, teamQ->m_team->getName());
  1415. }
  1416. m_teamDelay = 0;
  1417. return true;
  1418. }
  1419. return false;
  1420. }
  1421. // ------------------------------------------------------------------------------------------------
  1422. /** Determine the next team to build. Return true if one was selected. */
  1423. // ------------------------------------------------------------------------------------------------
  1424. Bool AIPlayer::selectTeamToBuild( void )
  1425. {
  1426. // find the highest priority of all teams
  1427. Player::PlayerTeamList::const_iterator t;
  1428. const Int invalidPri = -99999;
  1429. Int hiPri = invalidPri;
  1430. // collect all teams that are possible to build, and are at the highest priority
  1431. Player::PlayerTeamList candidateList1;
  1432. for (t = m_player->getPlayerTeams()->begin(); t != m_player->getPlayerTeams()->end(); ++t)
  1433. {
  1434. if (isAGoodIdeaToBuildTeam(*t))
  1435. {
  1436. candidateList1.push_back( (*t) );
  1437. Int pri = (*t)->getTemplateInfo()->m_productionPriority;
  1438. if (pri > hiPri)
  1439. {
  1440. hiPri = pri;
  1441. }
  1442. }
  1443. }
  1444. if (selectTeamToReinforce(hiPri)) {
  1445. return true;
  1446. }
  1447. // check if no team prototypes are valid for production
  1448. if (hiPri == invalidPri)
  1449. return false;
  1450. if (TheGlobalData->m_debugAI) {
  1451. TheScriptEngine->AppendDebugMessage("**AI** Selecting team to build", false);
  1452. }
  1453. // collect all teams that are possible to build, and are at the highest priority
  1454. Player::PlayerTeamList candidateList;
  1455. Int count = 0;
  1456. for (t = candidateList1.begin(); t != candidateList1.end(); ++t)
  1457. {
  1458. if ((*t)->getTemplateInfo()->m_productionPriority == hiPri)
  1459. {
  1460. candidateList.push_back( (*t) );
  1461. count++;
  1462. }
  1463. }
  1464. // pick a random team from the hi-priority set
  1465. Int which = GameLogicRandomValue( 0, count-1 );
  1466. TeamPrototype *teamProto = NULL;
  1467. Int i = 0;
  1468. for (t = candidateList.begin(); t != candidateList.end(); ++t)
  1469. {
  1470. if (i == which)
  1471. {
  1472. teamProto = (*t);
  1473. break;
  1474. }
  1475. i++;
  1476. }
  1477. if (teamProto) {
  1478. if (!teamProto->getTemplateInfo()->m_hasHomeLocation && !isSkirmishAI()) {
  1479. AsciiString teamStr = "Error : team '";
  1480. teamStr.concat(teamProto->getName());
  1481. teamStr.concat("' has no Home Position (or Origin).");
  1482. TheScriptEngine->AppendDebugMessage(teamStr, false);
  1483. }
  1484. // Build it at low priority, as we have selected it automagically.
  1485. buildSpecificAITeam(teamProto, false);
  1486. m_readyToBuildTeam = false;
  1487. m_teamTimer = m_teamSeconds*LOGICFRAMES_PER_SECOND;
  1488. if (m_player->getMoney()->countMoney() < TheAI->getAiData()->m_resourcesPoor) {
  1489. m_teamTimer = m_teamTimer/TheAI->getAiData()->m_teamPoorMod;
  1490. } else if (m_player->getMoney()->countMoney() > TheAI->getAiData()->m_resourcesWealthy) {
  1491. m_teamTimer = m_teamTimer/TheAI->getAiData()->m_teamWealthyMod;
  1492. }
  1493. return true;
  1494. }
  1495. return false;
  1496. }
  1497. // ------------------------------------------------------------------------------------------------
  1498. /** Build a specific team. If priorityBuild, put at front of queue with priority set. */
  1499. // ------------------------------------------------------------------------------------------------
  1500. void AIPlayer::buildSpecificAIBuilding(const AsciiString &thingName)
  1501. {
  1502. //
  1503. AsciiString teamStr = "Error : Solo ai doesn't support BuildSpecificBuilding. '";
  1504. teamStr.concat(thingName);
  1505. teamStr.concat("' not built.");
  1506. TheScriptEngine->AppendDebugMessage(teamStr, false);
  1507. }
  1508. // ------------------------------------------------------------------------------------------------
  1509. /** Build an upgrade. */
  1510. // ------------------------------------------------------------------------------------------------
  1511. void AIPlayer::buildUpgrade(const AsciiString &upgrade)
  1512. {
  1513. const UpgradeTemplate *curUpgrade = TheUpgradeCenter->findUpgrade(upgrade);
  1514. if (curUpgrade==NULL) {
  1515. AsciiString msg = "Upgrade ";
  1516. msg.concat(upgrade);
  1517. msg.concat(" does not exist. Ignoring request.");
  1518. TheScriptEngine->AppendDebugMessage( msg, false);
  1519. return;
  1520. }
  1521. if (curUpgrade->getUpgradeType()==UPGRADE_TYPE_OBJECT) {
  1522. AsciiString msg = "Player build upgrade: Upgrade ";
  1523. msg.concat(upgrade);
  1524. msg.concat(" is an object, not a player upgrade. Ignoring request.");
  1525. TheScriptEngine->AppendDebugMessage( msg, false);
  1526. return;
  1527. }
  1528. // See if it is in progress.
  1529. if (m_player->hasUpgradeInProduction(curUpgrade)) {
  1530. AsciiString msg = TheNameKeyGenerator->keyToName(m_player->getPlayerNameKey());
  1531. msg.concat(" already has upgrade ");
  1532. msg.concat(upgrade);
  1533. msg.concat(" queued. Ignoring request.");
  1534. TheScriptEngine->AppendDebugMessage( msg, false);
  1535. return;
  1536. }
  1537. // See if it is in progress.
  1538. if (m_player->hasUpgradeComplete(curUpgrade)) {
  1539. AsciiString msg = TheNameKeyGenerator->keyToName(m_player->getPlayerNameKey());
  1540. msg.concat(" already has upgrade ");
  1541. msg.concat(upgrade);
  1542. msg.concat(" completed. Ignoring request.");
  1543. TheScriptEngine->AppendDebugMessage( msg, false);
  1544. return;
  1545. }
  1546. // No money.
  1547. if( TheUpgradeCenter->canAffordUpgrade( m_player, curUpgrade ) == FALSE ) {
  1548. AsciiString msg = TheNameKeyGenerator->keyToName(m_player->getPlayerNameKey());
  1549. msg.concat(" lacks money to build upgrade ");
  1550. msg.concat(upgrade);
  1551. msg.concat(" at this time. Ignoring request.");
  1552. TheScriptEngine->AppendDebugMessage( msg, false);
  1553. return;
  1554. }
  1555. // Find a production queue.
  1556. for( const BuildListInfo *info = m_player->getBuildList(); info; info = info->getNext() )
  1557. {
  1558. Object *factory = TheGameLogic->findObjectByID( info->getObjectID() );
  1559. if( factory )
  1560. {
  1561. if( BitTest( factory->getStatusBits(), OBJECT_STATUS_UNDER_CONSTRUCTION ) == TRUE )
  1562. continue;
  1563. if( BitTest( factory->getStatusBits(), OBJECT_STATUS_SOLD ) == TRUE )
  1564. continue;
  1565. Bool canUpgradeHere = false;
  1566. const CommandSet *commandSet = TheControlBar->findCommandSet( factory->getCommandSetString() );
  1567. if( commandSet == NULL) continue;
  1568. for( Int j = 0; j < MAX_COMMANDS_PER_SET; j++ )
  1569. {
  1570. //Get the command button.
  1571. const CommandButton *commandButton = commandSet->getCommandButton(j);
  1572. if (commandButton==NULL) continue;
  1573. if (commandButton->getName().isEmpty() ) continue;
  1574. if (commandButton->getUpgradeTemplate() == NULL ) continue;
  1575. if (commandButton->getUpgradeTemplate()->getUpgradeName() == curUpgrade->getUpgradeName()) {
  1576. canUpgradeHere = true;
  1577. }
  1578. }
  1579. if (!canUpgradeHere) continue;
  1580. ProductionUpdateInterface *pu = factory->getProductionUpdateInterface();
  1581. // If it doesn't produce, continue.
  1582. if (!pu) continue;
  1583. // Try to queue it.
  1584. if (pu->queueUpgrade(curUpgrade)) {
  1585. AsciiString msg = TheNameKeyGenerator->keyToName(m_player->getPlayerNameKey());
  1586. msg.concat(" queues ");
  1587. msg.concat(curUpgrade->getUpgradeName());
  1588. msg.concat(" at ");
  1589. msg.concat(factory->getTemplate()->getName());
  1590. TheScriptEngine->AppendDebugMessage( msg, false);
  1591. return;
  1592. }
  1593. } // end if
  1594. } // end for
  1595. AsciiString msg = TheNameKeyGenerator->keyToName(m_player->getPlayerNameKey());
  1596. msg.concat(" lacks factory to build upgrade ");
  1597. msg.concat(upgrade);
  1598. msg.concat(" at this time. Ignoring request.");
  1599. TheScriptEngine->AppendDebugMessage( msg, false);
  1600. return;
  1601. }
  1602. // ------------------------------------------------------------------------------------------------
  1603. /** Build a supply center near a supply source with minimumCash or more resources. */
  1604. // ------------------------------------------------------------------------------------------------
  1605. void AIPlayer::buildBySupplies(Int minimumCash, const AsciiString& thingName)
  1606. {
  1607. Object *bestSupplyWarehouse = findSupplyCenter(minimumCash);
  1608. const ThingTemplate* tTemplate = TheThingFactory->findTemplate(thingName);
  1609. if (!tTemplate->isKindOf(KINDOF_CASH_GENERATOR)) {
  1610. // Build by the current warehouse.
  1611. Object *curWarehouse = TheGameLogic->findObjectByID(m_curWarehouseID);
  1612. if (curWarehouse) {
  1613. bestSupplyWarehouse = curWarehouse;
  1614. }
  1615. }
  1616. if (bestSupplyWarehouse && tTemplate) {
  1617. Coord3D location;
  1618. location = *bestSupplyWarehouse->getPosition();
  1619. // offset back towards the base.
  1620. Coord2D offset;
  1621. offset.x = location.x - m_baseCenter.x;
  1622. offset.y = location.y - m_baseCenter.y;
  1623. offset.normalize();
  1624. Real radius = 3*PATHFIND_CELL_SIZE_F;
  1625. if (!tTemplate->isKindOf(KINDOF_CASH_GENERATOR)) {
  1626. // It's probably a defensive structure - build towards the enemy.
  1627. Region2D bounds;
  1628. Int enemyNdx = TheScriptEngine->getSkirmishEnemyPlayer()->getPlayerIndex();
  1629. getPlayerStructureBounds(&bounds, enemyNdx);
  1630. offset.x = location.x - (bounds.lo.x+bounds.hi.x)*0.5f;
  1631. offset.y = location.y - (bounds.lo.y+bounds.hi.y)*0.5f;
  1632. offset.normalize();
  1633. radius = bestSupplyWarehouse->getGeometryInfo().getBoundingCircleRadius();
  1634. }
  1635. location.x -= offset.x*radius;
  1636. location.y -= offset.y*radius;
  1637. Real angle = tTemplate->getPlacementViewAngle();
  1638. // validate the the position to build at is valid
  1639. Bool valid=false;
  1640. Coord3D newPos = location;
  1641. if( TheBuildAssistant->isLocationLegalToBuild( &location, tTemplate, angle,
  1642. BuildAssistant::NO_OBJECT_OVERLAP,
  1643. NULL, m_player ) != LBC_OK ) {
  1644. // Warn.
  1645. AsciiString bldgName = tTemplate->getName();
  1646. bldgName.concat(" - buildAISupplyCenter unable to place. Attempting to adjust position.");
  1647. TheScriptEngine->AppendDebugMessage(bldgName, false);
  1648. // try to fix.
  1649. Real posOffset;
  1650. // Wiggle it a little :)
  1651. for (posOffset = 0; posOffset<2*SUPPLY_CENTER_CLOSE_DIST; posOffset += 2*PATHFIND_CELL_SIZE_F) {
  1652. Real offset = posOffset/2;
  1653. Real xPos, yPos;
  1654. yPos = location.y-offset;
  1655. for (xPos = location.x-offset; xPos <= location.x+offset; xPos+=PATHFIND_CELL_SIZE_F) {
  1656. newPos.x = xPos;
  1657. newPos.y = yPos;
  1658. valid = TheBuildAssistant->isLocationLegalToBuild( &newPos, tTemplate, angle,
  1659. BuildAssistant::CLEAR_PATH |
  1660. BuildAssistant::TERRAIN_RESTRICTIONS |
  1661. BuildAssistant::NO_OBJECT_OVERLAP,
  1662. NULL, m_player ) == LBC_OK;
  1663. if (valid) break;
  1664. newPos.y = yPos+posOffset;
  1665. valid = TheBuildAssistant->isLocationLegalToBuild( &newPos, tTemplate, angle,
  1666. BuildAssistant::CLEAR_PATH |
  1667. BuildAssistant::TERRAIN_RESTRICTIONS |
  1668. BuildAssistant::NO_OBJECT_OVERLAP,
  1669. NULL, m_player ) == LBC_OK;
  1670. }
  1671. if (valid) break;
  1672. xPos = location.x-offset;
  1673. for (yPos = location.y-offset; yPos <= location.y+offset; yPos+=PATHFIND_CELL_SIZE_F) {
  1674. newPos.x = xPos;
  1675. newPos.y = yPos;
  1676. valid = TheBuildAssistant->isLocationLegalToBuild( &newPos, tTemplate, angle,
  1677. BuildAssistant::CLEAR_PATH |
  1678. BuildAssistant::TERRAIN_RESTRICTIONS |
  1679. BuildAssistant::NO_OBJECT_OVERLAP,
  1680. NULL, m_player ) == LBC_OK;
  1681. if (valid) break;
  1682. newPos.x = xPos+posOffset;
  1683. valid = TheBuildAssistant->isLocationLegalToBuild( &newPos, tTemplate, angle,
  1684. BuildAssistant::CLEAR_PATH |
  1685. BuildAssistant::TERRAIN_RESTRICTIONS |
  1686. BuildAssistant::NO_OBJECT_OVERLAP,
  1687. NULL, m_player ) == LBC_OK;
  1688. }
  1689. if (valid) break;
  1690. }
  1691. }
  1692. if (valid) location = newPos;
  1693. TheTerrainVisual->removeAllBibs(); // isLocationLegalToBuild adds bib feedback, turn it off. jba.
  1694. location.z = 0; // All build list locations are ground relative.
  1695. m_player->addToPriorityBuildList(thingName, &location, angle);
  1696. m_curWarehouseID = bestSupplyWarehouse->getID();
  1697. }
  1698. }
  1699. // ------------------------------------------------------------------------------------------------
  1700. /** Find a supply center we haven't built a supply depot near yet. */
  1701. // ------------------------------------------------------------------------------------------------
  1702. Object *AIPlayer::findSupplyCenter(Int minimumCash)
  1703. {
  1704. Object *bestSupplyWarehouse = NULL;
  1705. Real bestDistSqr = 0;
  1706. Object *obj;
  1707. Coord3D enemyCenter;
  1708. enemyCenter.zero();
  1709. Region2D bounds;
  1710. Player *enemy = getAiEnemy();
  1711. if (enemy) {
  1712. getPlayerStructureBounds(&bounds, enemy->getPlayerIndex());
  1713. enemyCenter.set( (bounds.lo.x+bounds.hi.x)*0.5f, (bounds.lo.y+bounds.hi.y)*0.5f, 0);
  1714. }
  1715. do {
  1716. for( obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() )
  1717. {
  1718. if (!obj->isKindOf(KINDOF_STRUCTURE)) continue;
  1719. if (!obj->isKindOf(KINDOF_SUPPLY_SOURCE)) continue;
  1720. static const NameKeyType key_warehouseUpdate = NAMEKEY("SupplyWarehouseDockUpdate");
  1721. SupplyWarehouseDockUpdate *warehouseModule = (SupplyWarehouseDockUpdate*)obj->findUpdateModule( key_warehouseUpdate );
  1722. if( warehouseModule ) {
  1723. Int availableCash = warehouseModule->getBoxesStored()*TheGlobalData->m_baseValuePerSupplyBox;
  1724. if (availableCash<minimumCash) continue;
  1725. if( m_player->getRelationship(obj->getTeam()) == ENEMIES ) {
  1726. continue;
  1727. }
  1728. // Make sure we don't have a supply center near it.
  1729. Coord3D center = *obj->getPosition();
  1730. Real radius = SUPPLY_CENTER_CLOSE_DIST + obj->getGeometryInfo().getBoundingCircleRadius();
  1731. PartitionFilterAcceptByKindOf f1(MAKE_KINDOF_MASK(KINDOF_CASH_GENERATOR), KINDOFMASK_NONE);
  1732. PartitionFilterPlayer f2(m_player, true); // Only find your own units.
  1733. PartitionFilterOnMap filterMapStatus;
  1734. PartitionFilter *filters[] = { &f1, &f2, &filterMapStatus, 0 };
  1735. Object *supplyCenter = ThePartitionManager->getClosestObject(&center, radius, FROM_BOUNDINGSPHERE_2D, filters);
  1736. if (supplyCenter) {
  1737. // We already have a supply center.
  1738. continue;
  1739. }
  1740. Real dx, dy;
  1741. dx = obj->getPosition()->x - m_baseCenter.x;
  1742. dy = obj->getPosition()->y - m_baseCenter.y;
  1743. Real distSqr = dx*dx + dy*dy;
  1744. if (enemy) {
  1745. // make sure this isn't closer to our enemy than us.
  1746. dx = obj->getPosition()->x - enemyCenter.x;
  1747. dy = obj->getPosition()->y - enemyCenter.y;
  1748. if (distSqr*0.4>(dx*dx+dy*dy)*0.6f) {
  1749. // closer than 60/40 to enemy than to us, probably not a good candidate for expansion.
  1750. continue;
  1751. }
  1752. }
  1753. if (bestSupplyWarehouse==NULL) {
  1754. bestSupplyWarehouse = obj;
  1755. bestDistSqr = distSqr;
  1756. } else if (bestDistSqr>distSqr) {
  1757. bestSupplyWarehouse = obj;
  1758. bestDistSqr = distSqr;
  1759. }
  1760. }
  1761. }
  1762. if (bestSupplyWarehouse) break;
  1763. minimumCash /= 2;
  1764. } while (minimumCash > 100);
  1765. return bestSupplyWarehouse;
  1766. }
  1767. // ------------------------------------------------------------------------------------------------
  1768. /** Build a base defense. */
  1769. // ------------------------------------------------------------------------------------------------
  1770. void AIPlayer::buildAIBaseDefense(Bool flank)
  1771. {
  1772. //
  1773. AsciiString teamStr = "Error : Solo ai doesn't support buildAIBaseDefense. '";
  1774. TheScriptEngine->AppendDebugMessage(teamStr, false);
  1775. }
  1776. // ------------------------------------------------------------------------------------------------
  1777. /** Build a base defense. */
  1778. // ------------------------------------------------------------------------------------------------
  1779. void AIPlayer::buildAIBaseDefenseStructure(const AsciiString &thingName, Bool flank)
  1780. {
  1781. //
  1782. AsciiString teamStr = "Error : Solo ai doesn't support buildAIBaseDefenseStructure. '";
  1783. TheScriptEngine->AppendDebugMessage(teamStr, false);
  1784. }
  1785. // ------------------------------------------------------------------------------------------------
  1786. /** Repair a bridge or other structure. */
  1787. // ------------------------------------------------------------------------------------------------
  1788. void AIPlayer::repairStructure(ObjectID structure)
  1789. {
  1790. Object *structureObj = TheGameLogic->findObjectByID(structure);
  1791. if (structureObj==NULL) return;
  1792. if (structureObj->getBodyModule()==NULL) return;
  1793. // If the structure is not noticably damaged, don't bother.
  1794. enum BodyDamageType structureState = structureObj->getBodyModule()->getDamageState();
  1795. if (structureState==BODY_PRISTINE) {
  1796. return;
  1797. }
  1798. if (structureObj->isKindOf(KINDOF_BRIDGE)) {
  1799. // Locate the correct post to repair.
  1800. }
  1801. Int i;
  1802. for (i=0; i<m_structuresInQueue; i++) {
  1803. if (m_structuresToRepair[i] == structureObj->getID()) {
  1804. DEBUG_LOG(("info - Bridge already queued for repair.\n"));
  1805. return;
  1806. }
  1807. }
  1808. if (m_structuresInQueue==MAX_STRUCTURES_TO_REPAIR) {
  1809. DEBUG_LOG(("Structure repair queue is full, ignoring repair request. JBA\n"));
  1810. return;
  1811. }
  1812. m_structuresToRepair[m_structuresInQueue] = structureObj->getID();
  1813. m_structuresInQueue++;
  1814. }
  1815. // ------------------------------------------------------------------------------------------------
  1816. /** select a skillset for the player. */
  1817. // ------------------------------------------------------------------------------------------------
  1818. void AIPlayer::selectSkillset(Int skillset)
  1819. {
  1820. DEBUG_ASSERTCRASH(m_skillsetSelector == INVALID_SKILLSET_SELECTION,
  1821. ("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));
  1822. m_skillsetSelector = skillset;
  1823. }
  1824. // ------------------------------------------------------------------------------------------------
  1825. /** Do per frame work (if any) repairing bridges. */
  1826. // ------------------------------------------------------------------------------------------------
  1827. void AIPlayer::updateBridgeRepair(void)
  1828. {
  1829. if (m_structuresInQueue == 0) return;
  1830. // Check once a second.
  1831. m_bridgeTimer--;
  1832. if (m_bridgeTimer>0) return;
  1833. m_bridgeTimer = LOGICFRAMES_PER_SECOND;
  1834. Object *bridgeObj=NULL;
  1835. while (bridgeObj==NULL && m_structuresInQueue>0) {
  1836. bridgeObj = TheGameLogic->findObjectByID(m_structuresToRepair[0]);
  1837. if (bridgeObj==NULL) {
  1838. Int i;
  1839. for (i=0; i<m_structuresInQueue-1; i++) {
  1840. m_structuresToRepair[i] = m_structuresToRepair[i+1];
  1841. }
  1842. m_structuresInQueue--;
  1843. }
  1844. }
  1845. if (m_structuresInQueue == 0) return;
  1846. // Got a bridge to repair.
  1847. Object *dozer = NULL;
  1848. Coord3D bridgePos = *bridgeObj->getPosition();
  1849. enum BodyDamageType bridgeState = bridgeObj->getBodyModule()->getDamageState();
  1850. if (m_repairDozer==INVALID_ID) {
  1851. m_dozerIsRepairing = false;
  1852. // Need a dozer.
  1853. if (m_dozerQueuedForRepair) {
  1854. return; // we're waiting for one.
  1855. }
  1856. dozer = findDozer(&bridgePos);
  1857. if (dozer) {
  1858. m_repairDozer = dozer->getID();
  1859. m_repairDozerOrigin = *dozer->getPosition();
  1860. dozer->getAI()->aiRepair(bridgeObj, CMD_FROM_AI);
  1861. DEBUG_LOG(("Telling dozer to repair\n"));
  1862. m_dozerIsRepairing = true;
  1863. return;
  1864. }
  1865. queueDozer();
  1866. m_dozerQueuedForRepair = true;
  1867. return;
  1868. }
  1869. dozer = TheGameLogic->findObjectByID(m_repairDozer);
  1870. if (dozer==NULL) {
  1871. m_repairDozer=INVALID_ID; // we got killed.
  1872. m_bridgeTimer=0;
  1873. return; // Just try to find a dozer next frame.
  1874. }
  1875. DozerAIInterface* dozerAI = dozer->getAI()->getDozerAIInterface();
  1876. if (dozerAI==NULL) {
  1877. DEBUG_CRASH(("Unexpected - dozer doesn't have dozer interface."));
  1878. return;
  1879. }
  1880. if (m_dozerIsRepairing) {
  1881. if (!dozerAI->isAnyTaskPending()) {
  1882. // should be done repairing.
  1883. if (bridgeState==BODY_PRISTINE) {
  1884. DEBUG_LOG(("Dozer finished repairing structure.\n"));
  1885. // we're done.
  1886. Int i;
  1887. for (i=0; i<m_structuresInQueue-1; i++) {
  1888. m_structuresToRepair[i] = m_structuresToRepair[i+1];
  1889. }
  1890. m_structuresInQueue--;
  1891. m_dozerIsRepairing = false;
  1892. if (m_structuresInQueue==0) {
  1893. // Go home.
  1894. Coord3D pos = m_baseCenter;
  1895. if (!m_baseCenterSet) {
  1896. pos = m_repairDozerOrigin;
  1897. }
  1898. AIUpdateInterface *ai=dozer->getAI();
  1899. TheAI->pathfinder()->adjustToPossibleDestination(dozer, ai->getLocomotorSet(), &pos);
  1900. dozer->getAI()->aiMoveToPosition(&pos, CMD_FROM_AI);
  1901. return;
  1902. }
  1903. }
  1904. } else {
  1905. // dozer should be working on the bridge.
  1906. return;
  1907. }
  1908. }
  1909. dozer->getAI()->aiRepair(bridgeObj, CMD_FROM_AI);
  1910. m_dozerIsRepairing = true;
  1911. DEBUG_LOG(("Telling dozer to repair\n"));
  1912. }
  1913. // ------------------------------------------------------------------------------------------------
  1914. /** Build a specific team. If priorityBuild, put at front of queue with priority set. */
  1915. // ------------------------------------------------------------------------------------------------
  1916. void AIPlayer::buildSpecificAITeam( TeamPrototype *teamProto, Bool priorityBuild)
  1917. {
  1918. //
  1919. // Create "Team in queue" based on team population
  1920. //
  1921. if (teamProto)
  1922. {
  1923. if (!m_player->getCanBuildUnits()) {
  1924. AsciiString teamStr = "Can't build team '";
  1925. teamStr.concat(teamProto->getName());
  1926. teamStr.concat("' because build units is disabled.");
  1927. TheScriptEngine->AppendDebugMessage(teamStr, false);
  1928. return;
  1929. }
  1930. if (priorityBuild && teamProto->getIsSingleton()) {
  1931. Team *singletonTeam = TheTeamFactory->findTeam( teamProto->getName() );
  1932. if (singletonTeam && singletonTeam->hasAnyObjects()) {
  1933. AsciiString teamStr = "Unable to build singleton team '";
  1934. teamStr.concat("' because team already exists.");
  1935. TheScriptEngine->AppendDebugMessage(teamStr, false);
  1936. return;
  1937. }
  1938. }
  1939. // Check & make sure we have factories.
  1940. Bool needMoney;
  1941. if (!isPossibleToBuildTeam(teamProto, false, needMoney)) {
  1942. if (needMoney) {
  1943. // Queue it up anyway.
  1944. AsciiString teamStr = "Note - queueing team '";
  1945. teamStr.concat(teamProto->getName());
  1946. teamStr.concat("' but there is enough money.");
  1947. TheScriptEngine->AppendDebugMessage(teamStr, false);
  1948. } else {
  1949. // Tech tree doesn't work.
  1950. AsciiString teamStr = "Unable to build team '";
  1951. teamStr.concat(teamProto->getName());
  1952. if (needMoney) {
  1953. teamStr.concat("' - Not enough money.");
  1954. } else {
  1955. teamStr.concat("' because required factories/tech don't exist.");
  1956. }
  1957. TheScriptEngine->AppendDebugMessage(teamStr, false);
  1958. return;
  1959. }
  1960. }
  1961. const TCreateUnitsInfo *unitInfo = &teamProto->getTemplateInfo()->m_unitsInfo[0];
  1962. WorkOrder *orders = NULL;
  1963. Int i;
  1964. // Queue up optional units.
  1965. for( i=0; i<teamProto->getTemplateInfo()->m_numUnitsInfo; i++ )
  1966. {
  1967. const ThingTemplate *thing = TheThingFactory->findTemplate( unitInfo[i].unitThingName );
  1968. if (thing)
  1969. {
  1970. int count = unitInfo[i].maxUnits-unitInfo[i].minUnits;
  1971. if (count>0) {
  1972. WorkOrder *order = newInstance(WorkOrder);
  1973. order->m_thing = thing;
  1974. order->m_factoryID = INVALID_ID;
  1975. order->m_numRequired = count;
  1976. // prepend to head of list
  1977. order->m_next = orders;
  1978. orders = order;
  1979. }
  1980. }
  1981. }
  1982. // Queue up required units.
  1983. for( i=0; i<teamProto->getTemplateInfo()->m_numUnitsInfo; i++ )
  1984. {
  1985. const ThingTemplate *thing = TheThingFactory->findTemplate( unitInfo[i].unitThingName );
  1986. if (thing)
  1987. {
  1988. int count = unitInfo[i].minUnits;
  1989. WorkOrder *order = newInstance(WorkOrder);
  1990. order->m_thing = thing;
  1991. order->m_factoryID = INVALID_ID;
  1992. order->m_numRequired = count;
  1993. order->m_required = true;
  1994. // prepend to head of list
  1995. order->m_next = orders;
  1996. orders = order;
  1997. }
  1998. }
  1999. if (orders)
  2000. {
  2001. /* We have something to build. */
  2002. TeamInQueue *team = newInstance(TeamInQueue);
  2003. if (priorityBuild) {
  2004. // Put in front of queue.
  2005. prependTo_TeamBuildQueue(team);
  2006. team->m_priorityBuild = true;
  2007. } else {
  2008. // Put in back of queue.
  2009. reverse_TeamBuildQueue();
  2010. prependTo_TeamBuildQueue(team);
  2011. reverse_TeamBuildQueue();
  2012. team->m_priorityBuild = false;
  2013. }
  2014. team->m_workOrders = orders;
  2015. team->m_frameStarted = TheGameLogic->getFrame();
  2016. // create inactive team to place members into as they are built
  2017. // when team is complete, the team is activated
  2018. team->m_team = TheTeamFactory->createInactiveTeam( teamProto->getName() );
  2019. AsciiString teamName = teamProto->getName();
  2020. teamName.concat(" - starting team build.");
  2021. TheScriptEngine->AppendDebugMessage(teamName, false);
  2022. m_teamDelay = 0;
  2023. if (team->m_team->getPrototype()->getTemplateInfo()->m_executeActions) {
  2024. const Script *script = TheScriptEngine->findScriptByName(team->m_team->getPrototype()->getTemplateInfo()->m_productionCondition);
  2025. if (script && script->getAction()) {
  2026. TheScriptEngine->friend_executeAction(script->getAction(), team->m_team);
  2027. }
  2028. }
  2029. } else {
  2030. if (TheGlobalData->m_debugAI) {
  2031. AsciiString teamName = teamProto->getName();
  2032. teamName.concat(" - contains 0 buildable units.");
  2033. TheScriptEngine->AppendDebugMessage(teamName, false);
  2034. }
  2035. }
  2036. }
  2037. }
  2038. // ------------------------------------------------------------------------------------------------
  2039. /** Recruit a specific team, within the specific radius of the home position. */
  2040. // ------------------------------------------------------------------------------------------------
  2041. void AIPlayer::recruitSpecificAITeam(TeamPrototype *teamProto, Real recruitRadius)
  2042. {
  2043. if (recruitRadius < 1) recruitRadius = 99999.0f;
  2044. //
  2045. // Create "Team in queue" based on team population
  2046. //
  2047. if (teamProto)
  2048. {
  2049. if (teamProto->getIsSingleton()) {
  2050. Team *singletonTeam = TheTeamFactory->findTeam( teamProto->getName() );
  2051. if (singletonTeam && singletonTeam->hasAnyObjects()) {
  2052. AsciiString teamStr = "Unable to recruit singleton team '";
  2053. teamStr.concat("' because team already exists.");
  2054. TheScriptEngine->AppendDebugMessage(teamStr, false);
  2055. return;
  2056. }
  2057. }
  2058. if (!teamProto->getTemplateInfo()->m_hasHomeLocation && !isSkirmishAI())
  2059. {
  2060. AsciiString teamStr = "Error : team '";
  2061. teamStr.concat(teamProto->getName());
  2062. teamStr.concat("' has no Home Position (or Origin).");
  2063. TheScriptEngine->AppendDebugMessage(teamStr, false);
  2064. }
  2065. // create inactive team to place members into as they are built
  2066. // when team is complete, the team is activated
  2067. Team *theTeam = TheTeamFactory->createInactiveTeam( teamProto->getName() );
  2068. AsciiString teamName = teamProto->getName();
  2069. teamName.concat(" - Recruiting.");
  2070. TheScriptEngine->AppendDebugMessage(teamName, false);
  2071. const TCreateUnitsInfo *unitInfo = &teamProto->getTemplateInfo()->m_unitsInfo[0];
  2072. // WorkOrder *orders = NULL;
  2073. Int i;
  2074. Int unitsRecruited = 0;
  2075. // Recruit.
  2076. for( i=0; i<teamProto->getTemplateInfo()->m_numUnitsInfo; i++ )
  2077. {
  2078. const ThingTemplate *thing = TheThingFactory->findTemplate( unitInfo[i].unitThingName );
  2079. if (thing)
  2080. {
  2081. int count = unitInfo[i].maxUnits;
  2082. while (count>0) {
  2083. Object *unit = theTeam->tryToRecruit(thing, &teamProto->getTemplateInfo()->m_homeLocation, recruitRadius);
  2084. if (unit)
  2085. {
  2086. unitsRecruited++;
  2087. AsciiString teamStr = "Team '";
  2088. teamStr.concat(theTeam->getPrototype()->getName());
  2089. teamStr.concat("' recruits ");
  2090. teamStr.concat(thing->getName());
  2091. teamStr.concat(" from team '");
  2092. teamStr.concat(unit->getTeam()->getPrototype()->getName());
  2093. teamStr.concat("'");
  2094. TheScriptEngine->AppendDebugMessage(teamStr, false);
  2095. unit->setTeam(theTeam);
  2096. AIUpdateInterface *ai = unit->getAIUpdateInterface();
  2097. if (ai)
  2098. {
  2099. #ifdef DEBUG_LOGGING
  2100. Coord3D pos = *unit->getPosition();
  2101. Coord3D to = teamProto->getTemplateInfo()->m_homeLocation;
  2102. DEBUG_LOG(("Moving unit from %f,%f to %f,%f\n", pos.x, pos.y , to.x, to.y ));
  2103. #endif
  2104. ai->aiMoveToPosition( &teamProto->getTemplateInfo()->m_homeLocation, CMD_FROM_AI);
  2105. }
  2106. } else {
  2107. break;
  2108. }
  2109. count--;
  2110. }
  2111. }
  2112. }
  2113. if (unitsRecruited>0)
  2114. {
  2115. /* We have something to build. */
  2116. TeamInQueue *team = newInstance(TeamInQueue);
  2117. // Put in front of queue.
  2118. prependTo_TeamReadyQueue(team);
  2119. team->m_priorityBuild = false;
  2120. team->m_workOrders = NULL;
  2121. team->m_frameStarted = TheGameLogic->getFrame();
  2122. team->m_team = theTeam;
  2123. AsciiString teamName = teamProto->getName();
  2124. teamName.concat(" - Finished recruiting.");
  2125. TheScriptEngine->AppendDebugMessage(teamName, false);
  2126. } else {
  2127. //disband.
  2128. if (!theTeam->getPrototype()->getIsSingleton()) {
  2129. theTeam->deleteInstance();
  2130. theTeam = NULL;
  2131. }
  2132. AsciiString teamName = teamProto->getName();
  2133. teamName.concat(" - Recruited 0 units, disbanding.");
  2134. TheScriptEngine->AppendDebugMessage(teamName, false);
  2135. }
  2136. }
  2137. }
  2138. // ------------------------------------------------------------------------------------------------
  2139. /** Train our teams. */
  2140. // ------------------------------------------------------------------------------------------------
  2141. void AIPlayer::processTeamBuilding( void )
  2142. {
  2143. // select a new team
  2144. if (selectTeamToBuild()) {
  2145. queueUnits();
  2146. }
  2147. }
  2148. // ------------------------------------------------------------------------------------------------
  2149. // ------------------------------------------------------------------------------------------------
  2150. void AIPlayer::queueUnits( void )
  2151. {
  2152. queueSupplyTruck();
  2153. // For each member of the current team to build, try to find a faction building to build it.
  2154. //
  2155. for ( DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamBuildQueue(); !iter.done(); iter.advance())
  2156. {
  2157. TeamInQueue *team = iter.cur();
  2158. for( WorkOrder *order = team->m_workOrders; order; order = order->m_next )
  2159. {
  2160. // check if there is a unit on the map that we can steal (recruit) instead of building
  2161. // @todo: Should this try to alter the home location of the recruiting area to
  2162. // the center of the team, or to the home area of this player?
  2163. Coord3D home = team->m_team->getPrototype()->getTemplateInfo()->m_homeLocation;
  2164. Bool hasHome = false;
  2165. if (team->m_team->getPrototype()->getTemplateInfo()->m_hasHomeLocation) {
  2166. hasHome = true;
  2167. } else {
  2168. hasHome = getBaseCenter(&home);
  2169. }
  2170. while (order->isWaitingToBuild()) {
  2171. Object *unit = team->m_team->tryToRecruit(order->m_thing, &home, TheAI->getAiData()->m_maxRecruitDistance);
  2172. if (unit)
  2173. {
  2174. order->m_numCompleted++;
  2175. AsciiString teamStr = "Team '";
  2176. teamStr.concat(team->m_team->getPrototype()->getName());
  2177. teamStr.concat("' recruits ");
  2178. teamStr.concat(order->m_thing->getName());
  2179. teamStr.concat(" from team '");
  2180. teamStr.concat(unit->getTeam()->getPrototype()->getName());
  2181. teamStr.concat("'");
  2182. TheScriptEngine->AppendDebugMessage(teamStr, false);
  2183. unit->setTeam(team->m_team);
  2184. AIUpdateInterface *ai = unit->getAIUpdateInterface();
  2185. if (hasHome) {
  2186. ai->aiMoveToPosition( &home, CMD_FROM_AI);
  2187. } else {
  2188. ai->aiIdle(CMD_FROM_AI); // stop, you've been recruited.
  2189. }
  2190. } else {
  2191. break;
  2192. }
  2193. }
  2194. if (order->isWaitingToBuild())
  2195. {
  2196. // start the creation of a new unit
  2197. startTraining( order, team->m_priorityBuild, team->m_team->getName());
  2198. }
  2199. else
  2200. {
  2201. // we are under construction, verify our factory still exists
  2202. order->validateFactory(m_player);
  2203. }
  2204. }
  2205. }
  2206. }
  2207. //----------------------------------------------------------------------------------------------------------
  2208. /**
  2209. * See if it's time to build another base building.
  2210. */
  2211. void AIPlayer::doBaseBuilding( void )
  2212. {
  2213. if (m_player->getCanBuildBase()) {
  2214. // See if we are ready to start trying a structure.
  2215. if (!m_readyToBuildStructure) {
  2216. m_structureTimer--;
  2217. if (m_structureTimer<=0) {
  2218. m_readyToBuildStructure = true;
  2219. m_buildDelay = 0;
  2220. }
  2221. }
  2222. // This timer is to keep from banging on the logic each frame. If something interesting
  2223. // happens, like a building is added or a unit finished, the timers are shortcut.
  2224. m_buildDelay--;
  2225. if (m_buildDelay<1) {
  2226. if (m_readyToBuildStructure) {
  2227. processBaseBuilding();
  2228. }
  2229. if (m_buildDelay<1) { // processBaseBuilding may reset m_buildDelay.
  2230. m_buildDelay = 2*LOGICFRAMES_PER_SECOND; // check again in 2 seconds.
  2231. }
  2232. // Note that this timer gets shortcut when a building is completed.
  2233. }
  2234. }
  2235. }
  2236. //----------------------------------------------------------------------------------------------------------
  2237. /**
  2238. * See if any ready teams have finished moving to the rally point.
  2239. */
  2240. void AIPlayer::checkReadyTeams( void )
  2241. {
  2242. // See if any ready teams are gathered at their rally point
  2243. { // needed to scope iter. silly ms c++.
  2244. for ( DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamReadyQueue(); !iter.done(); iter.advance())
  2245. {
  2246. TeamInQueue *team = iter.cur();
  2247. // If 60 seconds passed, start anyway.
  2248. Bool timeExpired = team->m_frameStarted+60*LOGICFRAMES_PER_SECOND < TheGameLogic->getFrame();
  2249. Bool allIdle=TRUE;
  2250. Bool anyIdle = FALSE;
  2251. if (team->m_reinforcement) {
  2252. Object *obj = TheGameLogic->findObjectByID(team->m_reinforcementID);
  2253. if (obj && obj->getAIUpdateInterface()) {
  2254. allIdle = obj->getAIUpdateInterface()->isIdle();
  2255. anyIdle = allIdle;
  2256. }
  2257. } else {
  2258. allIdle = team->m_team->isIdle();
  2259. for (DLINK_ITERATOR<Object> iter = team->m_team->iterate_TeamMemberList(); !iter.done(); iter.advance()) {
  2260. Object *obj = iter.cur();
  2261. if (obj->getAI() && obj->getAI()->isIdle()) {
  2262. anyIdle = true;
  2263. }
  2264. }
  2265. }
  2266. if (anyIdle && team->m_team->getPrototype()->getTemplateInfo()->m_executeActions) {
  2267. const Script *script = TheScriptEngine->findScriptByName(team->m_team->getPrototype()->getTemplateInfo()->m_productionCondition);
  2268. if (script && script->getAction()) {
  2269. // we have a start action. So don't wait for allIdle as the team may be guarding.
  2270. allIdle = true;
  2271. }
  2272. }
  2273. if (timeExpired) allIdle = true;
  2274. if (allIdle) {
  2275. if (!team->m_sentToStartLocation) {
  2276. team->m_sentToStartLocation = true;
  2277. /*
  2278. if (team->m_team->getPrototype()->getTemplateInfo()->m_hasHomeLocation &&
  2279. !team->m_reinforcement) {
  2280. AIGroup* theGroup = TheAI->createGroup();
  2281. if (theGroup) {
  2282. team->m_team->getTeamAsAIGroup(theGroup);
  2283. Coord3D destination = team->m_team->getPrototype()->getTemplateInfo()->m_homeLocation;
  2284. theGroup->groupTightenToPosition( &destination, false, CMD_FROM_AI );
  2285. team->m_frameStarted = TheGameLogic->getFrame();
  2286. continue;
  2287. }
  2288. }
  2289. */
  2290. }
  2291. // Start the team up.
  2292. removeFrom_TeamReadyQueue(team);
  2293. if (team->m_reinforcement) {
  2294. Object *obj = TheGameLogic->findObjectByID(team->m_reinforcementID);
  2295. if (obj&&obj->getAIUpdateInterface()) {
  2296. obj->getAIUpdateInterface()->joinTeam();
  2297. }
  2298. } else {
  2299. // mark our completed team as "active" - this will invoke any OnCreate scripts, etc.
  2300. team->m_team->setActive();
  2301. if (isSkirmishAI()) {
  2302. TheScriptEngine->clearTeamFlags();
  2303. }
  2304. if (TheGlobalData->m_debugAI) {
  2305. AsciiString teamName = team->m_team->getPrototype()->getName();
  2306. teamName.concat(" - team activated.");
  2307. TheScriptEngine->AppendDebugMessage(teamName, false);
  2308. }
  2309. }
  2310. team->deleteInstance();
  2311. iter = iterate_TeamReadyQueue();
  2312. }
  2313. }
  2314. }
  2315. }
  2316. //----------------------------------------------------------------------------------------------------------
  2317. /**
  2318. * See if any queued teams have finished building, or have run out of time.
  2319. */
  2320. void AIPlayer::checkQueuedTeams( void )
  2321. {
  2322. // See if any teams are expired.
  2323. { // needed to scope iter. silly ms c++.
  2324. for ( DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamBuildQueue(); !iter.done(); iter.advance())
  2325. {
  2326. TeamInQueue *team = iter.cur();
  2327. if (team && team->isBuildTimeExpired()) {
  2328. if (team->isMinimumBuilt())
  2329. {
  2330. if (team->areBuildsComplete()) {
  2331. // Move to ready queue
  2332. removeFrom_TeamBuildQueue(team);
  2333. prependTo_TeamReadyQueue(team);
  2334. } else {
  2335. continue;
  2336. }
  2337. } else {
  2338. // Disband.
  2339. removeFrom_TeamBuildQueue(team);
  2340. team->disband();
  2341. team->deleteInstance();
  2342. if (isSkirmishAI()) {
  2343. TheScriptEngine->clearTeamFlags();
  2344. }
  2345. }
  2346. iter = iterate_TeamBuildQueue();
  2347. }
  2348. }
  2349. }
  2350. // See if any teams are ready.
  2351. { // needed to scope iter. silly ms c++.
  2352. for ( DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamBuildQueue(); !iter.done(); iter.advance())
  2353. {
  2354. TeamInQueue *team = iter.cur();
  2355. if (team && team->isAllBuilt())
  2356. {
  2357. // Move to ready queue
  2358. removeFrom_TeamBuildQueue(team);
  2359. prependTo_TeamReadyQueue(team);
  2360. iter = iterate_TeamBuildQueue();
  2361. continue;
  2362. }
  2363. Bool anyIdle = false;
  2364. for (DLINK_ITERATOR<Object> iter = team->m_team->iterate_TeamMemberList(); !iter.done(); iter.advance()) {
  2365. Object *obj = iter.cur();
  2366. if (obj && obj->getAI() && obj->getAI()->isIdle()) {
  2367. anyIdle = true;
  2368. }
  2369. }
  2370. if (anyIdle) {
  2371. if (team->m_team->getPrototype()->getTemplateInfo()->m_executeActions) {
  2372. const Script *script = TheScriptEngine->findScriptByName(team->m_team->getPrototype()->getTemplateInfo()->m_productionCondition);
  2373. if (script) {
  2374. TheScriptEngine->friend_executeAction(script->getAction(), team->m_team);
  2375. }
  2376. }
  2377. }
  2378. }
  2379. }
  2380. }
  2381. //----------------------------------------------------------------------------------------------------------
  2382. /**
  2383. * See if it is time to start another ai team building.
  2384. */
  2385. void AIPlayer::doTeamBuilding( void )
  2386. {
  2387. // See if any teams are expired.
  2388. if (m_player->getCanBuildUnits()) {
  2389. // See if we are ready to start trying a team.
  2390. if (!m_readyToBuildTeam) {
  2391. m_teamTimer--;
  2392. if (m_teamTimer<=0) {
  2393. m_readyToBuildTeam = true;
  2394. m_teamDelay = 0;
  2395. }
  2396. }
  2397. // This timer is to keep from banging on the logic each frame. If something interesting
  2398. // happens, like a building is added or a unit finished, the timers are shortcut.
  2399. m_teamDelay--;
  2400. if (m_teamDelay<1) {
  2401. queueUnits(); // update the queues.
  2402. if (m_readyToBuildTeam) {
  2403. processTeamBuilding();
  2404. }
  2405. m_teamDelay = 5*LOGICFRAMES_PER_SECOND; // check again in 5 seconds.
  2406. // Note that this timer gets shortcut when a unit or building is completed.
  2407. }
  2408. }
  2409. }
  2410. //----------------------------------------------------------------------------------------------------------
  2411. /**
  2412. * See if it is time to start another upgrade or skill building.
  2413. */
  2414. void AIPlayer::doUpgradesAndSkills( void )
  2415. {
  2416. if (TheGameLogic->getFrame() < 2) {
  2417. // can't do updates on the first few frames
  2418. return;
  2419. }
  2420. Bool checkScience = m_player->getSciencePurchasePoints()>0;
  2421. if (!checkScience) {
  2422. return;
  2423. }
  2424. const AISideInfo *sideInfo = TheAI->getAiData()->m_sideInfo;
  2425. while (sideInfo) {
  2426. if (sideInfo->m_side == m_player->getSide()) {
  2427. break;
  2428. }
  2429. sideInfo = sideInfo->m_next;
  2430. }
  2431. if (sideInfo == NULL) return;
  2432. if (m_skillsetSelector == INVALID_SKILLSET_SELECTION) {
  2433. Int limit = 0;
  2434. // Pick randomly among the skillsets that have skills.
  2435. // Designers sometimes only define skillset 1 & 2, or some such. jba.
  2436. if (sideInfo->m_skillSet2.m_numSkills>0) {
  2437. limit = 1;
  2438. if (sideInfo->m_skillSet3.m_numSkills>0) {
  2439. limit = 2;
  2440. if (sideInfo->m_skillSet4.m_numSkills>0) {
  2441. limit = 3;
  2442. if (sideInfo->m_skillSet5.m_numSkills>0) {
  2443. limit = 4;
  2444. }
  2445. }
  2446. }
  2447. }
  2448. if (isSkirmishAI()) {
  2449. m_skillsetSelector = GameLogicRandomValue(0, limit);
  2450. } else {
  2451. m_skillsetSelector = 0; // Non-skirmish default to 0. jba.
  2452. }
  2453. }
  2454. // SKILLS
  2455. if (m_player->getSciencePurchasePoints()>0) {
  2456. const TSkillSet *skillset;
  2457. switch(m_skillsetSelector) {
  2458. default:
  2459. case 0: skillset = &sideInfo->m_skillSet1; break;
  2460. case 1: skillset = &sideInfo->m_skillSet2; break;
  2461. case 2: skillset = &sideInfo->m_skillSet3; break;
  2462. case 3: skillset = &sideInfo->m_skillSet4; break;
  2463. case 4: skillset = &sideInfo->m_skillSet5; break;
  2464. }
  2465. Int i;
  2466. for (i=0; i<skillset->m_numSkills; i++) {
  2467. ScienceType science = skillset->m_skills[i];
  2468. if (m_player->isCapableOfPurchasingScience(science)) {
  2469. if (m_player->attemptToPurchaseScience(science)) {
  2470. AsciiString msg = TheNameKeyGenerator->keyToName(m_player->getPlayerNameKey());
  2471. msg.concat(" purchases from SkillSet");
  2472. msg.concat('1'+m_skillsetSelector);
  2473. msg.concat(' ');
  2474. msg.concat(TheScienceStore->getInternalNameForScience(science));
  2475. msg.concat(".");
  2476. TheScriptEngine->AppendDebugMessage( msg, false);
  2477. }
  2478. }
  2479. }
  2480. }
  2481. }
  2482. //----------------------------------------------------------------------------------------------------------
  2483. /**
  2484. * Perform computer-controlled player AI
  2485. */
  2486. //DECLARE_PERF_TIMER(AIPlayer_update)
  2487. void AIPlayer::update( void )
  2488. {
  2489. //USE_PERF_TIMER(AIPlayer_update)
  2490. doBaseBuilding(); // See if it's time to build another building.
  2491. checkReadyTeams(); // See if any teams are ready to start.
  2492. checkQueuedTeams(); // See if any teams are complete.
  2493. doTeamBuilding(); // See if it's time to start another team.
  2494. doUpgradesAndSkills(); // See if it's time to build an upgrade or buy a skill.
  2495. updateBridgeRepair(); // Handle any bridge repairs.
  2496. }
  2497. //----------------------------------------------------------------------------------------------------------
  2498. /**
  2499. * Find any things that build stuff & add them to the build list. Then build any initially built
  2500. * buildings.
  2501. */
  2502. void AIPlayer::newMap( void )
  2503. {
  2504. BuildListInfo *info = m_player->getBuildList();
  2505. // Add any factories placed to the build list.
  2506. Object *obj;
  2507. for( obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() )
  2508. {
  2509. Player *owner = obj->getControllingPlayer();
  2510. if (owner==m_player) {
  2511. // See if it's a factory.
  2512. ProductionUpdateInterface *pu = obj->getProductionUpdateInterface();
  2513. // If it doesn't produce, continue.
  2514. if (!pu) continue;
  2515. m_player->addToBuildList(obj);
  2516. }
  2517. }
  2518. computeCenterAndRadiusOfBase(&m_baseCenter, &m_baseRadius);
  2519. // Build any with the initially built flag.
  2520. for( /* nothing */; info; info = info->getNext() )
  2521. {
  2522. AsciiString name = info->getTemplateName();
  2523. if (name.isEmpty()) continue;
  2524. const ThingTemplate *bldgPlan = TheThingFactory->findTemplate( name );
  2525. if (!bldgPlan) {
  2526. DEBUG_LOG(("*** ERROR - Build list building '%s' doesn't exist.\n", name.str()));
  2527. continue;
  2528. }
  2529. if (info->isInitiallyBuilt()) {
  2530. buildStructureNow(bldgPlan, info);
  2531. } else {
  2532. info->incrementNumRebuilds(); // the initial build in the normal build list consumes a rebuild, so add one.
  2533. }
  2534. }
  2535. }
  2536. // ------------------------------------------------------------------------------------------------
  2537. /** Find the center of the base and the radius of buildings. */
  2538. // ------------------------------------------------------------------------------------------------
  2539. void AIPlayer::computeCenterAndRadiusOfBase(Coord3D *center, Real *radius)
  2540. {
  2541. //
  2542. BuildListInfo *info;
  2543. Coord2D totalPos;
  2544. totalPos.x = 0;
  2545. totalPos.y = 0;
  2546. Int numBldg=0;
  2547. for( info = m_player->getBuildList(); info; info = info->getNext() )
  2548. {
  2549. AsciiString name = info->getTemplateName();
  2550. if (name.isEmpty()) continue;
  2551. const ThingTemplate *bldgPlan = TheThingFactory->findTemplate( name );
  2552. if (!bldgPlan) {
  2553. continue;
  2554. }
  2555. Coord3D pos = *info->getLocation();
  2556. totalPos.x += pos.x;
  2557. totalPos.y += pos.y;
  2558. numBldg++;
  2559. }
  2560. if (numBldg>0) {
  2561. totalPos.x /= numBldg;
  2562. totalPos.y /= numBldg;
  2563. }
  2564. m_baseCenterSet = numBldg>0;
  2565. center->x = totalPos.x;
  2566. center->y = totalPos.y;
  2567. Real maxRadSqr = 0;
  2568. //
  2569. for( info = m_player->getBuildList(); info; info = info->getNext() )
  2570. {
  2571. AsciiString name = info->getTemplateName();
  2572. if (name.isEmpty()) continue;
  2573. const ThingTemplate *bldgPlan = TheThingFactory->findTemplate( name );
  2574. if (!bldgPlan) {
  2575. continue;
  2576. }
  2577. Coord3D pos = *info->getLocation();
  2578. Real dx = pos.x-center->x;
  2579. Real dy = pos.y-center->y;
  2580. if (dx<0) dx = -dx;
  2581. if (dy<0) dy = -dy;
  2582. Real bldgRadius = bldgPlan->getTemplateGeometryInfo().getBoundingCircleRadius()*0.4f;
  2583. dx += bldgRadius;
  2584. dy += bldgRadius;
  2585. Real radSqr = dx*dx+dy*dy;
  2586. if (radSqr>maxRadSqr) maxRadSqr=radSqr;
  2587. }
  2588. *radius = sqrt(maxRadSqr);
  2589. }
  2590. //----------------------------------------------------------------------------------------------------------
  2591. /**
  2592. * Checks to see if we're building a dozer.
  2593. */
  2594. Bool AIPlayer::dozerInQueue( void )
  2595. {
  2596. { // needed to scope iter. silly ms c++.
  2597. for ( DLINK_ITERATOR<TeamInQueue> iter = iterate_TeamBuildQueue(); !iter.done(); iter.advance())
  2598. {
  2599. TeamInQueue *team = iter.cur();
  2600. if (team && team->includesADozer() )
  2601. {
  2602. return true; // dozer is building already.
  2603. }
  2604. }
  2605. }
  2606. return false;
  2607. }
  2608. //----------------------------------------------------------------------------------------------------------
  2609. /**
  2610. * Queues up a dozer.
  2611. */
  2612. void AIPlayer::queueDozer( void )
  2613. {
  2614. if (dozerInQueue()) return;
  2615. // Find a factory that can build a dozer.
  2616. Bool canBuildUnits = m_player->getCanBuildUnits();
  2617. // If we need a dozer, turn on unit building for a moment.
  2618. m_player->setCanBuildUnits(true);
  2619. const ThingTemplate *tTemplate = TheThingFactory->firstTemplate();
  2620. while (tTemplate) {
  2621. if (tTemplate->isKindOf(KINDOF_DOZER)) {
  2622. Object *factory = findFactory(tTemplate, true);
  2623. if (factory) {
  2624. // we can build one.
  2625. WorkOrder *order = newInstance(WorkOrder);
  2626. order->m_thing = tTemplate;
  2627. order->m_factoryID = INVALID_ID;
  2628. order->m_numRequired = 1;
  2629. order->m_required = true;
  2630. order->m_isResourceGatherer = FALSE;
  2631. // prepend to head of list
  2632. order->m_next = NULL;
  2633. TeamInQueue *team = newInstance(TeamInQueue);
  2634. // Put in front of queue.
  2635. prependTo_TeamBuildQueue(team);
  2636. team->m_priorityBuild = true;
  2637. team->m_workOrders = order;
  2638. team->m_frameStarted = TheGameLogic->getFrame();
  2639. // Stick it on the default team
  2640. team->m_team = m_player->getDefaultTeam();
  2641. AsciiString teamName = "DOZER - building one at the ";
  2642. teamName.concat(factory->getTemplate()->getName());
  2643. TheScriptEngine->AppendDebugMessage(teamName, false);
  2644. m_teamDelay = 0;
  2645. startTraining( order, team->m_priorityBuild, team->m_team->getName());
  2646. break;
  2647. }
  2648. }
  2649. tTemplate = tTemplate->friend_getNextTemplate();
  2650. }
  2651. // restore canbuildunits.
  2652. m_player->setCanBuildUnits(canBuildUnits);
  2653. }
  2654. //-------------------------------------------------------------------------------------------------
  2655. /** Difficulty level for this player */
  2656. //-------------------------------------------------------------------------------------------------
  2657. enum GameDifficulty AIPlayer::getAIDifficulty(void) const
  2658. {
  2659. return m_difficulty;
  2660. }
  2661. //----------------------------------------------------------------------------------------------------------
  2662. /**
  2663. * Finds a dozer that isn't building or collecting resources.
  2664. */
  2665. Object * AIPlayer::findDozer( const Coord3D *pos )
  2666. {
  2667. // Add any factories placed to the build list.
  2668. Object *obj;
  2669. Object *dozer = NULL;
  2670. Bool needDozer = true;
  2671. Object *closestDozer=NULL;
  2672. Real closestDistSqr = 0;
  2673. for( obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() )
  2674. {
  2675. Player *owner = obj->getControllingPlayer();
  2676. if (owner==m_player) {
  2677. // See if it's a dozer.
  2678. if (obj->isKindOf(KINDOF_DOZER)) {
  2679. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  2680. if (ai==NULL) {
  2681. continue;
  2682. }
  2683. DozerAIInterface* dozerAI = ai->getDozerAIInterface();
  2684. if (dozerAI) {
  2685. // Since workers can be dozers, hmmm....
  2686. SupplyTruckAIInterface* supplyTruckAI = ai->getSupplyTruckAIInterface();
  2687. if( !dozerAI->isAnyTaskPending() && supplyTruckAI ) {
  2688. // If it is gathering supplies, don't steal it.
  2689. if (supplyTruckAI->isCurrentlyFerryingSupplies() || supplyTruckAI->isForcedIntoWantingState())
  2690. {
  2691. continue;
  2692. }
  2693. }
  2694. if (obj->getID() == m_repairDozer) {
  2695. continue; // don't steal the repair dozer.
  2696. }
  2697. needDozer = false; // dozer exists, may be busy.
  2698. if (dozerAI->isTaskPending(DOZER_TASK_BUILD)) {
  2699. continue; // already building.
  2700. }
  2701. if (!dozerAI->isAnyTaskPending()) {
  2702. dozer = obj; // prefer an idle dozer
  2703. }
  2704. if (dozer==NULL) {
  2705. dozer = obj; // but we'll take one doing stuff.
  2706. }
  2707. if (dozer && !dozerAI->isAnyTaskPending()) {
  2708. // Got a good one, track closest.
  2709. Real distSqr;
  2710. Real dx, dy;
  2711. dx = pos->x - dozer->getPosition()->x;
  2712. dy = pos->y - dozer->getPosition()->y;
  2713. distSqr = dx*dx+dy*dy;
  2714. if (closestDozer == NULL) {
  2715. closestDozer = dozer;
  2716. closestDistSqr = distSqr;
  2717. } else if (distSqr < closestDistSqr) {
  2718. closestDozer = dozer;
  2719. closestDistSqr = distSqr;
  2720. }
  2721. }
  2722. }
  2723. }
  2724. }
  2725. }
  2726. if (needDozer) {
  2727. queueDozer();
  2728. }
  2729. if (closestDozer) return closestDozer;
  2730. return dozer;
  2731. }
  2732. // ------------------------------------------------------------------------------------------------
  2733. /** CRC */
  2734. // ------------------------------------------------------------------------------------------------
  2735. void AIPlayer::crc( Xfer *xfer )
  2736. {
  2737. } // end crc
  2738. // ------------------------------------------------------------------------------------------------
  2739. /** Xfer method
  2740. * Version Info:
  2741. * 1: Initial version
  2742. * 2: added m_teamSeconds delay.
  2743. * 3: Added m_curWarehouseID.
  2744. * 1: Reset back to 1 with major save file changes.
  2745. */
  2746. // ------------------------------------------------------------------------------------------------
  2747. void AIPlayer::xfer( Xfer *xfer )
  2748. {
  2749. // version
  2750. XferVersion currentVersion = 1;
  2751. XferVersion version = currentVersion;
  2752. xfer->xferVersion( &version, currentVersion );
  2753. // team build queue count
  2754. UnsignedShort teamBuildQueueCount = 0;
  2755. for( DLINK_ITERATOR< TeamInQueue > teamInQueueIt = iterate_TeamBuildQueue();
  2756. teamInQueueIt.done() == FALSE;
  2757. teamInQueueIt.advance() )
  2758. teamBuildQueueCount++;
  2759. xfer->xferUnsignedShort( &teamBuildQueueCount );
  2760. // team build queue data
  2761. TeamInQueue *teamInQueue;
  2762. if( xfer->getXferMode() == XFER_SAVE )
  2763. {
  2764. for( DLINK_ITERATOR< TeamInQueue > teamInQueueIt = iterate_TeamBuildQueue();
  2765. teamInQueueIt.done() == FALSE;
  2766. teamInQueueIt.advance() )
  2767. {
  2768. // get element data
  2769. teamInQueue = teamInQueueIt.cur();
  2770. // xfer it
  2771. xfer->xferSnapshot( teamInQueue );
  2772. } // end for, iterate team build queue
  2773. } // end if, save
  2774. else
  2775. {
  2776. // sanity, the list must be empty
  2777. if( getFirstItemIn_TeamBuildQueue() != NULL )
  2778. {
  2779. DEBUG_CRASH(( "AIPlayer::xfer - TeamBuildQueue head is not NULL, you should delete it or something before loading a new list\n" ));
  2780. throw SC_INVALID_DATA;
  2781. } // end if
  2782. // ready all data
  2783. for( UnsignedShort i = 0; i < teamBuildQueueCount; ++i )
  2784. {
  2785. // allocate new team in queue instance
  2786. teamInQueue = newInstance(TeamInQueue);
  2787. // attach to end of list
  2788. prependTo_TeamBuildQueue( teamInQueue );
  2789. // xfer data
  2790. xfer->xferSnapshot( teamInQueue );
  2791. } // end for, i
  2792. // the list was loaded in reverse order, reverse the list so it's in the same order as before
  2793. reverse_TeamBuildQueue();
  2794. } // end else, load
  2795. // team ready queue count
  2796. UnsignedShort teamReadyQueueCount = 0;
  2797. for( DLINK_ITERATOR< TeamInQueue > teamReadyQueueIt = iterate_TeamReadyQueue();
  2798. teamReadyQueueIt.done() == FALSE;
  2799. teamReadyQueueIt.advance() )
  2800. teamReadyQueueCount++;
  2801. xfer->xferUnsignedShort( &teamReadyQueueCount );
  2802. // team Ready queue data
  2803. TeamInQueue *teamReadyQueue;
  2804. if( xfer->getXferMode() == XFER_SAVE )
  2805. {
  2806. for( DLINK_ITERATOR< TeamInQueue > teamReadyQueueIt = iterate_TeamReadyQueue();
  2807. teamReadyQueueIt.done() == FALSE;
  2808. teamReadyQueueIt.advance() )
  2809. {
  2810. // get element
  2811. teamReadyQueue = teamReadyQueueIt.cur();
  2812. // xfer data
  2813. xfer->xferSnapshot( teamReadyQueue );
  2814. } // end for, iterate team ready queue
  2815. } // end if, save
  2816. else
  2817. {
  2818. // sanity, the list must be empty
  2819. if( getFirstItemIn_TeamReadyQueue() != NULL )
  2820. {
  2821. DEBUG_CRASH(( "AIPlayer::xfer - TeamReadyQueue head is not NULL, you should delete it or something before loading a new list\n" ));
  2822. throw SC_INVALID_DATA;
  2823. } // end if
  2824. // read all data
  2825. for( UnsignedShort i = 0; i < teamReadyQueueCount; ++i )
  2826. {
  2827. // allocate new team in queue instance
  2828. teamInQueue = newInstance(TeamInQueue);
  2829. // attach to end of list
  2830. prependTo_TeamReadyQueue( teamInQueue );
  2831. // xfer data
  2832. xfer->xferSnapshot( teamInQueue );
  2833. } // end for, i
  2834. // reverse the list since it was loaded in reverse order due to the prepend
  2835. reverse_TeamReadyQueue();
  2836. } // end else, load
  2837. // xfer player index ... this is really just for sanity
  2838. PlayerIndex playerIndex = m_player->getPlayerIndex();
  2839. xfer->xferUser( &playerIndex, sizeof( PlayerIndex ) );
  2840. if( playerIndex != m_player->getPlayerIndex() )
  2841. {
  2842. DEBUG_CRASH(( "AIPlayer::xfer - player index mismatch\n" ));
  2843. throw SC_INVALID_DATA;
  2844. } // end if
  2845. // xfer the rest of the ai player data (it's pretty straight forward)
  2846. xfer->xferBool( &m_readyToBuildTeam );
  2847. xfer->xferBool( &m_readyToBuildStructure );
  2848. xfer->xferInt( &m_teamTimer );
  2849. xfer->xferInt( &m_structureTimer );
  2850. xfer->xferInt( &m_buildDelay );
  2851. xfer->xferInt( &m_teamDelay );
  2852. xfer->xferInt(&m_teamSeconds);
  2853. xfer->xferObjectID(&m_curWarehouseID);
  2854. xfer->xferInt( &m_frameLastBuildingBuilt );
  2855. xfer->xferUser( &m_difficulty, sizeof( GameDifficulty ) );
  2856. xfer->xferInt( &m_skillsetSelector );
  2857. xfer->xferCoord3D( &m_baseCenter );
  2858. xfer->xferBool( &m_baseCenterSet );
  2859. xfer->xferReal( &m_baseRadius );
  2860. xfer->xferUser( m_structuresToRepair, sizeof( ObjectID ) * MAX_STRUCTURES_TO_REPAIR );
  2861. xfer->xferObjectID( &m_repairDozer );
  2862. xfer->xferInt( &m_structuresInQueue );
  2863. xfer->xferBool( &m_dozerQueuedForRepair );
  2864. xfer->xferBool( &m_dozerIsRepairing );
  2865. xfer->xferInt( &m_bridgeTimer );
  2866. } // end xfer
  2867. // ------------------------------------------------------------------------------------------------
  2868. /** Load post process */
  2869. // ------------------------------------------------------------------------------------------------
  2870. void AIPlayer::loadPostProcess( void )
  2871. {
  2872. } // end loadPostProcess
  2873. #endif
  2874. // ------------------------------------------------------------------------------------------------
  2875. // ------------------------------------------------------------------------------------------------
  2876. TeamInQueue::~TeamInQueue()
  2877. {
  2878. WorkOrder *order, *next;
  2879. for( order = m_workOrders; order; order = next )
  2880. {
  2881. next = order->m_next;
  2882. order->deleteInstance();
  2883. }
  2884. // If we have a team, activate it. If it is empty, Team.cpp will remove empty active teams.
  2885. if (m_team) m_team->setActive();
  2886. m_workOrders = NULL;
  2887. }
  2888. // ------------------------------------------------------------------------------------------------
  2889. // ------------------------------------------------------------------------------------------------
  2890. Bool TeamInQueue::isAllBuilt()
  2891. {
  2892. WorkOrder *order;
  2893. Bool stillBuilding = false;
  2894. for( order = m_workOrders; order; order = order->m_next )
  2895. {
  2896. if (order->m_numRequired>order->m_numCompleted)
  2897. {
  2898. stillBuilding = true;
  2899. }
  2900. }
  2901. return !stillBuilding;
  2902. }
  2903. // ------------------------------------------------------------------------------------------------
  2904. // ------------------------------------------------------------------------------------------------
  2905. Bool TeamInQueue::isBuildTimeExpired()
  2906. {
  2907. if (m_team->getPrototype()->getTemplateInfo()->m_initialIdleFrames<1) {
  2908. return false; // Unlimited time.
  2909. }
  2910. if (TheGameLogic->getFrame() > m_frameStarted + m_team->getPrototype()->getTemplateInfo()->m_initialIdleFrames) {
  2911. return true;
  2912. }
  2913. return false;
  2914. }
  2915. // ------------------------------------------------------------------------------------------------
  2916. // ------------------------------------------------------------------------------------------------
  2917. Bool TeamInQueue::isMinimumBuilt()
  2918. {
  2919. WorkOrder *order;
  2920. for( order = m_workOrders; order; order = order->m_next )
  2921. {
  2922. Int count = order->m_numCompleted;
  2923. if (order->m_factoryID != INVALID_ID) {
  2924. count++; // we have one building.
  2925. }
  2926. if (order->m_numRequired>count)
  2927. {
  2928. if (order->m_required) {
  2929. return false; // required units not built.
  2930. }
  2931. }
  2932. }
  2933. return true;
  2934. }
  2935. // ------------------------------------------------------------------------------------------------
  2936. // ------------------------------------------------------------------------------------------------
  2937. Bool TeamInQueue::includesADozer()
  2938. {
  2939. WorkOrder *order;
  2940. for( order = m_workOrders; order; order = order->m_next )
  2941. {
  2942. // GLA dozers (workers) are also resource gatherers, so make sure it isn't a gatherer. jba.
  2943. if (order->m_thing->isKindOf(KINDOF_DOZER) && !order->m_isResourceGatherer) {
  2944. return true;
  2945. }
  2946. }
  2947. return false;
  2948. }
  2949. // ------------------------------------------------------------------------------------------------
  2950. // ------------------------------------------------------------------------------------------------
  2951. Bool TeamInQueue::areBuildsComplete()
  2952. {
  2953. WorkOrder *order;
  2954. for( order = m_workOrders; order; order = order->m_next )
  2955. {
  2956. if (order->m_factoryID != INVALID_ID) {
  2957. return false; // we have one building.
  2958. }
  2959. }
  2960. return true;
  2961. }
  2962. // ------------------------------------------------------------------------------------------------
  2963. // ------------------------------------------------------------------------------------------------
  2964. void TeamInQueue::disband()
  2965. {
  2966. Team *newTeam = m_team->getPrototype()->getControllingPlayer()->getDefaultTeam();
  2967. AsciiString teamName = m_team->getPrototype()->getName();
  2968. teamName.concat(" - team disbanded, build time expired.");
  2969. TheScriptEngine->AppendDebugMessage(teamName, false);
  2970. if (m_team != newTeam) {
  2971. m_team->transferUnitsTo(newTeam);
  2972. if (!m_team->getPrototype()->getIsSingleton()) {
  2973. m_team->deleteInstance();
  2974. }
  2975. m_team = NULL;
  2976. }
  2977. }
  2978. // ------------------------------------------------------------------------------------------------
  2979. /** CRC */
  2980. // ------------------------------------------------------------------------------------------------
  2981. void TeamInQueue::crc( Xfer *xfer )
  2982. {
  2983. } // end crc
  2984. // ------------------------------------------------------------------------------------------------
  2985. /** Xfer method
  2986. * Version Info:
  2987. * 1: Initial version */
  2988. // ------------------------------------------------------------------------------------------------
  2989. void TeamInQueue::xfer( Xfer *xfer )
  2990. {
  2991. // version
  2992. XferVersion currentVersion = 1;
  2993. XferVersion version = currentVersion;;
  2994. xfer->xferVersion( &version, currentVersion );
  2995. // xfer work order count
  2996. UnsignedShort workOrderCount = 0;
  2997. WorkOrder *workOrder;
  2998. for( workOrder = m_workOrders; workOrder; workOrder = workOrder->m_next )
  2999. workOrderCount++;
  3000. xfer->xferUnsignedShort( &workOrderCount );
  3001. // xfer work orders
  3002. if( xfer->getXferMode() == XFER_SAVE )
  3003. {
  3004. // xfer each work order
  3005. for( workOrder = m_workOrders; workOrder; workOrder = workOrder->m_next )
  3006. {
  3007. // xfer work order data
  3008. xfer->xferSnapshot( workOrder );
  3009. } // end for
  3010. } // end if, save
  3011. else
  3012. {
  3013. // sanity
  3014. if( m_workOrders != NULL )
  3015. {
  3016. DEBUG_CRASH(( "TeamInQueue::xfer - m_workOrders should be NULL but isn't. Perhaps you should blow it away before loading\n" ));
  3017. throw SC_INVALID_DATA;
  3018. } // end if
  3019. // load all work orders
  3020. for( UnsignedShort i = 0; i < workOrderCount; ++i )
  3021. {
  3022. // allocate new work order
  3023. workOrder = newInstance(WorkOrder);
  3024. // attach to list at the end
  3025. workOrder->m_next = NULL;
  3026. if( m_workOrders == NULL )
  3027. m_workOrders = workOrder;
  3028. else
  3029. {
  3030. WorkOrder *last = m_workOrders;
  3031. while( last->m_next != NULL )
  3032. last = last->m_next;
  3033. last->m_next = workOrder;
  3034. } // end else
  3035. // load work order data
  3036. xfer->xferSnapshot( workOrder );
  3037. } // end for, i
  3038. } // end else, load
  3039. // xfer the rest of the team in queue data
  3040. xfer->xferBool( &m_priorityBuild );
  3041. TeamID teamID = m_team ? m_team->getID() : TEAM_ID_INVALID;
  3042. xfer->xferUser( &teamID, sizeof( TeamID ) );
  3043. if( xfer->getXferMode() == XFER_LOAD )
  3044. m_team = TheTeamFactory->findTeamByID( teamID );
  3045. xfer->xferInt( &m_frameStarted );
  3046. xfer->xferBool( &m_sentToStartLocation );
  3047. xfer->xferBool( &m_stopQueueing );
  3048. xfer->xferBool( &m_reinforcement );
  3049. xfer->xferObjectID( &m_reinforcementID );
  3050. } // end xfer
  3051. // ------------------------------------------------------------------------------------------------
  3052. /** Load post process */
  3053. // ------------------------------------------------------------------------------------------------
  3054. void TeamInQueue::loadPostProcess( void )
  3055. {
  3056. } // end loadPostProcess
  3057. ///////////////////////////////////////////////////////////////////////////////////////////////////
  3058. ///////////////////////////////////////////////////////////////////////////////////////////////////
  3059. ///////////////////////////////////////////////////////////////////////////////////////////////////
  3060. // ------------------------------------------------------------------------------------------------
  3061. // ------------------------------------------------------------------------------------------------
  3062. WorkOrder::~WorkOrder()
  3063. {
  3064. } // end WorkOrder
  3065. // ------------------------------------------------------------------------------------------------
  3066. /** Verify factoryID still refers to an active object */
  3067. // ------------------------------------------------------------------------------------------------
  3068. void WorkOrder::validateFactory( Player *thisPlayer )
  3069. {
  3070. if (m_factoryID == INVALID_ID)
  3071. return;
  3072. Object *factory = TheGameLogic->findObjectByID( m_factoryID );
  3073. if ( factory == NULL) {
  3074. m_factoryID = INVALID_ID;
  3075. return;
  3076. }
  3077. if (factory->getControllingPlayer()!=thisPlayer) {
  3078. m_factoryID = INVALID_ID;
  3079. }
  3080. } // end validateFactory
  3081. // ------------------------------------------------------------------------------------------------
  3082. /** CRC */
  3083. // ------------------------------------------------------------------------------------------------
  3084. void WorkOrder::crc( Xfer *xfer )
  3085. {
  3086. } // end crc
  3087. // ------------------------------------------------------------------------------------------------
  3088. /** Xfer method
  3089. * Version Info:
  3090. * 1: Initial version */
  3091. // ------------------------------------------------------------------------------------------------
  3092. void WorkOrder::xfer( Xfer *xfer )
  3093. {
  3094. // version
  3095. XferVersion currentVersion = 1;
  3096. XferVersion version = currentVersion;
  3097. xfer->xferVersion( &version, currentVersion );
  3098. // thing template
  3099. AsciiString thingTemplateName = m_thing ? m_thing->getName() : AsciiString::TheEmptyString;
  3100. xfer->xferAsciiString( &thingTemplateName );
  3101. if( xfer->getXferMode() == XFER_LOAD )
  3102. m_thing = TheThingFactory->findTemplate( thingTemplateName );
  3103. // factory id
  3104. xfer->xferObjectID( &m_factoryID );
  3105. // num completed
  3106. xfer->xferInt( &m_numCompleted );
  3107. // num required
  3108. xfer->xferInt( &m_numRequired );
  3109. // is required
  3110. xfer->xferBool( &m_required );
  3111. // is resource gatherer
  3112. xfer->xferBool( &m_isResourceGatherer );
  3113. } // end xfer
  3114. // ------------------------------------------------------------------------------------------------
  3115. /** Load post process */
  3116. // ------------------------------------------------------------------------------------------------
  3117. void WorkOrder::loadPostProcess( void )
  3118. {
  3119. } // end loadPostProcess
  3120. //----------------------------------------------------------------------------------------------------------
  3121. /**
  3122. * Get the bounds for a player's structure.
  3123. */
  3124. void AIPlayer::getPlayerStructureBounds(Region2D *bounds, Int playerNdx )
  3125. {
  3126. Player::PlayerTeamList::const_iterator it;
  3127. Bool firstObject = true;
  3128. Bool firstStructure = true;
  3129. bounds->hi.x = bounds->lo.x = bounds->hi.y = bounds->lo.x = 0;
  3130. Region2D objBounds;
  3131. objBounds.hi.x = objBounds.lo.x = objBounds.hi.y = objBounds.lo.x = 0;
  3132. Player* pPlayer = ThePlayerList->getNthPlayer(playerNdx);
  3133. if (pPlayer == NULL) return;
  3134. for (it = pPlayer->getPlayerTeams()->begin(); it != pPlayer->getPlayerTeams()->end(); ++it) {
  3135. for (DLINK_ITERATOR<Team> iter = (*it)->iterate_TeamInstanceList(); !iter.done(); iter.advance()) {
  3136. Team *team = iter.cur();
  3137. if (!team) continue;
  3138. for (DLINK_ITERATOR<Object> iter = team->iterate_TeamMemberList(); !iter.done(); iter.advance()) {
  3139. Object *pObj = iter.cur();
  3140. if (!pObj) continue;
  3141. if (pObj->isKindOf(KINDOF_STRUCTURE)) {
  3142. Coord3D pos = *pObj->getPosition();
  3143. if (firstObject) {
  3144. objBounds.lo.x = objBounds.hi.x = pos.x;
  3145. objBounds.lo.y = objBounds.hi.y = pos.y;
  3146. firstObject = false;
  3147. } else {
  3148. if (objBounds.lo.x>pos.x) objBounds.lo.x = pos.x;
  3149. if (objBounds.lo.y>pos.y) objBounds.lo.y = pos.y;
  3150. if (objBounds.hi.x<pos.x) objBounds.hi.x = pos.x;
  3151. if (objBounds.hi.y<pos.y) objBounds.hi.y = pos.y;
  3152. }
  3153. if (firstStructure) {
  3154. bounds->lo.x = bounds->hi.x = pos.x;
  3155. bounds->lo.y = bounds->hi.y = pos.y;
  3156. firstStructure = false;
  3157. } else {
  3158. if (bounds->lo.x>pos.x) bounds->lo.x = pos.x;
  3159. if (bounds->lo.y>pos.y) bounds->lo.y = pos.y;
  3160. if (bounds->hi.x<pos.x) bounds->hi.x = pos.x;
  3161. if (bounds->hi.y<pos.y) bounds->hi.y = pos.y;
  3162. }
  3163. }
  3164. }
  3165. }
  3166. }
  3167. if (!firstStructure) {
  3168. // Player had no structures, so use unit bounds.
  3169. *bounds = objBounds;
  3170. }
  3171. }