AIGroup.cpp 91 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318
  1. /*
  2. ** Command & Conquer Generals Zero Hour(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. // AIGroup.cpp
  24. // Encapsulation of a simple group of AI agents
  25. // Author: Michael S. Booth, January 2002
  26. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  27. #include "Common/ActionManager.h"
  28. #include "Common/BuildAssistant.h"
  29. #include "Common/CRCDebug.h"
  30. #include "Common/Player.h"
  31. #include "Common/SpecialPower.h"
  32. #include "Common/ThingTemplate.h"
  33. #include "Common/Upgrade.h"
  34. #include "Common/Xfer.h"
  35. #include "Common/XferCRC.h"
  36. #include "GameClient/ControlBar.h"
  37. #include "GameClient/Drawable.h"
  38. #include "GameClient/Line2D.h"
  39. #include "GameLogic/AI.h"
  40. #include "GameLogic/AIPathfind.h"
  41. #include "GameLogic/Locomotor.h"
  42. #include "GameLogic/Module/AIUpdate.h"
  43. #include "GameLogic/Module/BodyModule.h"
  44. #include "GameLogic/Module/ContainModule.h"
  45. #include "GameLogic/Module/OverchargeBehavior.h"
  46. #include "GameLogic/Module/ProductionUpdate.h"
  47. #include "GameLogic/Module/SpawnBehavior.h"
  48. #include "GameLogic/Module/SpecialPowerModule.h"
  49. #include "GameLogic/Module/StealthUpdate.h"
  50. #include "GameLogic/Module/SpecialPowerUpdateModule.h"
  51. #include "GameLogic/ObjectIter.h"
  52. #ifdef _INTERNAL
  53. // for occasional debugging...
  54. //#pragma optimize("", off)
  55. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  56. #endif
  57. /**
  58. * NOTE: Only AI objects (ie: having an AIUpdate module) can be in
  59. * AIGroups. It is ASSUMED that an object cannot morph from having
  60. * an AIUpdate module to not having one... (MSB)
  61. NOTE: This comment has been wrong for about ten years now. Any Object can be in an AIGroup
  62. */
  63. /**
  64. * Constructor
  65. */
  66. AIGroup::AIGroup( void )
  67. {
  68. // DEBUG_LOG(("***AIGROUP %x is being constructed.\n", this));
  69. m_groundPath = NULL;
  70. m_speed = 0.0f;
  71. m_dirty = false;
  72. m_id = TheAI->getNextGroupID();
  73. m_memberListSize = 0;
  74. m_memberList.clear();
  75. //DEBUG_LOG(( "AIGroup #%d created\n", m_id ));
  76. }
  77. /**
  78. * Destructor
  79. */
  80. AIGroup::~AIGroup()
  81. {
  82. // DEBUG_LOG(("***AIGROUP %x is being destructed.\n", this));
  83. // disassociate each member from the group
  84. std::list<Object *>::iterator i;
  85. for( i = m_memberList.begin(); i != m_memberList.end(); /* empty */ )
  86. {
  87. Object *member = *i;
  88. if (member)
  89. {
  90. member->leaveGroup();
  91. i = m_memberList.begin(); // jump back to the beginning, cause ai->leaveGroup will remove this element.
  92. }
  93. else
  94. {
  95. i = m_memberList.erase(i);
  96. }
  97. }
  98. if (m_groundPath) {
  99. m_groundPath->deleteInstance();
  100. m_groundPath = NULL;
  101. }
  102. //DEBUG_LOG(( "AIGroup #%d destroyed\n", m_id ));
  103. }
  104. /**
  105. * Return this group's unique ID
  106. */
  107. UnsignedInt AIGroup::getID( void )
  108. {
  109. return m_id;
  110. }
  111. /**
  112. * Return the group IDs for every member in this group
  113. */
  114. const VecObjectID& AIGroup::getAllIDs( void ) const
  115. {
  116. m_lastRequestedIDList.clear();
  117. for (std::list<Object *>::const_iterator cit = m_memberList.begin(); cit != m_memberList.end(); ++cit)
  118. {
  119. if ((*cit) == NULL)
  120. continue;
  121. m_lastRequestedIDList.push_back((*cit)->getID());
  122. }
  123. return m_lastRequestedIDList;
  124. }
  125. /**
  126. * Return the speed of the group's slowest member
  127. */
  128. Real AIGroup::getSpeed( void )
  129. {
  130. if (m_dirty)
  131. recompute();
  132. return m_speed;
  133. }
  134. /**
  135. * Return true if object is in this group
  136. */
  137. Bool AIGroup::isMember( Object *obj )
  138. {
  139. std::list<Object *>::iterator i = std::find( m_memberList.begin(), m_memberList.end(), obj );
  140. if (i == m_memberList.end())
  141. return false;
  142. return true;
  143. }
  144. /**
  145. * Add object to group.
  146. * Only allow AI agents into the group.
  147. */
  148. void AIGroup::add( Object *obj )
  149. {
  150. // DEBUG_LOG(("***AIGROUP %x is adding Object %x (%s).\n", this, obj, obj->getTemplate()->getName().str()));
  151. DEBUG_ASSERTCRASH(obj != NULL, ("trying to add null obj to AIGroup"));
  152. if (obj == NULL)
  153. return;
  154. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  155. //If this object doesn't have an AIUpdateInterface, then
  156. //don't add it to the group UNLESS it is a structure! Structures
  157. //with AIUpdateInterfaces also issue similar commands, but those
  158. //commands don't need AI updates... they are instant commands like
  159. //evacuate or triggering certain special powers...
  160. KindOfMaskType validNonAIKindofs;
  161. validNonAIKindofs.set(KINDOF_STRUCTURE);
  162. validNonAIKindofs.set(KINDOF_ALWAYS_SELECTABLE);
  163. if( ai == NULL && !obj->isAnyKindOf( validNonAIKindofs ) )
  164. {
  165. return;
  166. }
  167. // add to group's list of objects
  168. m_memberList.push_back( obj );
  169. ++m_memberListSize;
  170. // DEBUG_LOG(("***AIGROUP %x has size %u now.\n", this, m_memberListSize));
  171. obj->enterGroup( this );
  172. // list has changed, properties need recomputation
  173. m_dirty = true;
  174. }
  175. /**
  176. * Remove object from group
  177. */
  178. Bool AIGroup::remove( Object *obj )
  179. {
  180. // DEBUG_LOG(("***AIGROUP %x is removing Object %x (%s).\n", this, obj, obj->getTemplate()->getName().str()));
  181. std::list<Object *>::iterator i = std::find( m_memberList.begin(), m_memberList.end(), obj );
  182. // make sure object is actually in the group
  183. if (i == m_memberList.end())
  184. return FALSE;
  185. // remove it
  186. m_memberList.erase( i );
  187. --m_memberListSize;
  188. // DEBUG_LOG(("***AIGROUP %x has size %u now.\n", this, m_memberListSize));
  189. // tell object to forget about group
  190. obj->leaveGroup();
  191. // list has changed, properties need recomputation
  192. m_dirty = true;
  193. // if the group is empty, no-one is using it any longer, so destroy it
  194. if (isEmpty()) {
  195. TheAI->destroyGroup( this );
  196. return TRUE;
  197. }
  198. return FALSE;
  199. }
  200. /**
  201. * If the group contains any objects not owned by ownerPlayer, return TRUE.
  202. */
  203. Bool AIGroup::containsAnyObjectsNotOwnedByPlayer( const Player *ownerPlayer )
  204. {
  205. ListObjectPtrIt it;
  206. for (it = m_memberList.begin(); it != m_memberList.end(); ++it) {
  207. Object *obj = (*it);
  208. if (!obj) {
  209. continue;
  210. }
  211. if (obj->getControllingPlayer() != ownerPlayer) {
  212. return TRUE;
  213. }
  214. }
  215. return FALSE;
  216. }
  217. /**
  218. * Remove any objects that aren't owned by the player, and return true if the group was destroyed due to emptiness
  219. */
  220. Bool AIGroup::removeAnyObjectsNotOwnedByPlayer( const Player *ownerPlayer )
  221. {
  222. ListObjectPtrIt it;
  223. for (it = m_memberList.begin(); it != m_memberList.end(); /* empty */) {
  224. Object *obj = (*it);
  225. if (!obj) {
  226. continue;
  227. }
  228. if (obj->getControllingPlayer() != ownerPlayer) {
  229. // Advance the iterator first, its about to become invalid.
  230. ++it;
  231. if (remove(obj)) {
  232. return TRUE;
  233. }
  234. continue;
  235. }
  236. ++it;
  237. }
  238. return FALSE;
  239. }
  240. /**
  241. * Compute the centroid of the group
  242. */
  243. Bool AIGroup::getCenter( Coord3D *center )
  244. {
  245. Int count = 0;
  246. center->x = 0.0f;
  247. center->y = 0.0f;
  248. center->z = 0.0f;
  249. std::list<Object *>::iterator i;
  250. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  251. {
  252. if( (*i)->isDisabledByType( DISABLED_HELD) )
  253. {
  254. continue; // don't bother counting riders in the center calculation.
  255. }
  256. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  257. if (ai)
  258. {
  259. const Coord3D *objPos = (*i)->getPosition();
  260. center->x += objPos->x;
  261. center->y += objPos->y;
  262. center->z += objPos->z;
  263. ++count;
  264. }
  265. }
  266. if (count == 0 && !m_memberList.empty())
  267. {
  268. /*
  269. if there are no AIs (eg, the team consists of a faction bldg), we can get here.
  270. This was originally used to offset the centers of objects moving (still used for that) and non-ais can't move.
  271. So if you have a mix of ai's & not ai's, you want just the ais.
  272. But it seems reasonable that if there are no ai's, it returns the center of the other stuff. Cause they won't be moving anyway.
  273. */
  274. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  275. {
  276. if( (*i)->isDisabledByType( DISABLED_HELD) )
  277. {
  278. continue; // don't bother counting riders in the center calculation.
  279. }
  280. const Coord3D *objPos = (*i)->getPosition();
  281. center->x += objPos->x;
  282. center->y += objPos->y;
  283. center->z += objPos->z;
  284. ++count;
  285. }
  286. }
  287. center->x /= count;
  288. center->y /= count;
  289. center->z /= count;
  290. return count > 0;
  291. }
  292. Bool AIGroup::getMinMaxAndCenter( Coord2D *min, Coord2D *max, Coord3D *center )
  293. {
  294. Int count = 0;
  295. min->x = 1e10f;
  296. max->x = -1e10f;
  297. min->y = 1e10f;
  298. max->y = -1e10f;
  299. center->x = 0.0f;
  300. center->y = 0.0f;
  301. center->z = 0.0f;
  302. std::list<Object *>::iterator i;
  303. FormationID id= NO_FORMATION_ID;
  304. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  305. {
  306. if( (*i)->isDisabledByType( DISABLED_HELD) )
  307. {
  308. continue; // don't bother counting riders in the center calculation.
  309. }
  310. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  311. if (ai)
  312. {
  313. const Coord3D *objPos = (*i)->getPosition();
  314. center->x += objPos->x;
  315. center->y += objPos->y;
  316. center->z += objPos->z;
  317. //Calculate the bounding coordinates of all units
  318. min->x = min->x > objPos->x ? objPos->x : min->x;
  319. max->x = max->x < objPos->x ? objPos->x : max->x;
  320. min->y = min->y > objPos->y ? objPos->y : min->y;
  321. max->y = max->y < objPos->y ? objPos->y : max->y;
  322. FormationID curID = (*i)->getFormationID() ;
  323. if (count==0) {
  324. id = curID;
  325. } else {
  326. if (id == NO_FORMATION_ID) {
  327. id = NO_FORMATION_ID;
  328. }
  329. }
  330. count++;
  331. }
  332. }
  333. center->x /= count;
  334. center->y /= count;
  335. center->z /= count;
  336. Bool isFormation = (id!=NO_FORMATION_ID);
  337. if (count<2) isFormation = false;
  338. return isFormation;
  339. }
  340. /**
  341. * Compute the speed of the team (its slowest member's speed),
  342. * and find the leader (closest to center of group).
  343. */
  344. void AIGroup::recompute( void )
  345. {
  346. Real closeDist = 999999999.9f;
  347. Real dx, dy, dist;
  348. const Coord3D *objPos;
  349. Coord3D center;
  350. getCenter( &center );
  351. if (m_groundPath) {
  352. m_groundPath->deleteInstance();
  353. m_groundPath = NULL;
  354. }
  355. m_speed = 9999999999.9f;
  356. std::list<Object *>::iterator i;
  357. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  358. {
  359. // don't consider immobile things for leadership
  360. if ((*i)->isKindOf(KINDOF_IMMOBILE))
  361. continue;
  362. if( (*i)->isDisabledByType( DISABLED_HELD) )
  363. {
  364. continue; // don't bother counting riders in the max speed calculation.
  365. }
  366. Object *obj = (*i);
  367. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  368. if (ai)
  369. {
  370. //
  371. // speed is slowest speed ... we won't consider slow speeds from objects that
  372. // are being penalized by their damage state, if we did the whole group would slow
  373. // down to that slowest penalized speed, instead only those objects that are penalized
  374. // will fall out of "formation" ... bummer for them!
  375. //
  376. Real maxSpeed = ai->getCurLocomotorSpeed();
  377. if( m_speed > maxSpeed &&
  378. IS_CONDITION_BETTER( obj->getBodyModule()->getDamageState(), TheGlobalData->m_movementPenaltyDamageState ) )
  379. m_speed = maxSpeed;
  380. // leader is closest to the group's center
  381. objPos = obj->getPosition();
  382. dx = objPos->x - center.x;
  383. dy = objPos->y - center.y;
  384. dist = dx*dx + dy*dy;
  385. if (dist < closeDist)
  386. {
  387. closeDist = dist;
  388. }
  389. }
  390. }
  391. // clear "dirty bit" - data is up-to-date
  392. m_dirty = false;
  393. }
  394. /**
  395. * Return the number of objects in the group
  396. */
  397. Int AIGroup::getCount( void )
  398. {
  399. return m_memberListSize;
  400. }
  401. /**
  402. * Returns true if the group has no members
  403. */
  404. Bool AIGroup::isEmpty( void )
  405. {
  406. return m_memberList.empty();
  407. }
  408. /**
  409. * Given a destination location, compute the destination position for
  410. * this object such that it keeps its relative position with the group.
  411. */
  412. void AIGroup::computeIndividualDestination( Coord3D *dest, const Coord3D *groupDest,
  413. Object *obj, const Coord3D *center, Bool isFormation )
  414. {
  415. Coord2D v;
  416. // compute vector from "group center" to self
  417. const Coord3D *pos = obj->getPosition();
  418. if (isFormation) {
  419. obj->getFormationOffset(&v);
  420. } else {
  421. v.x = pos->x - center->x;
  422. v.y = pos->y - center->y;
  423. }
  424. Real length = v.length();
  425. if (length > 6*obj->getGeometryInfo().getBoundingCircleRadius()) {
  426. length = 6*obj->getGeometryInfo().getBoundingCircleRadius();
  427. }
  428. v.normalize();
  429. v.x *= length;
  430. v.y *= length;
  431. PathfindLayerEnum layer = TheTerrainLogic->getLayerForDestination(groupDest);
  432. // move to same offset at destination
  433. /// @todo use fast int->real type cast here later
  434. dest->x = groupDest->x + v.x;
  435. dest->y = groupDest->y + v.y;
  436. dest->z = TheTerrainLogic->getLayerHeight( dest->x, dest->y, layer );
  437. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  438. if (ai && ai->isDoingGroundMovement()) {
  439. if (isFormation) {
  440. TheAI->pathfinder()->adjustDestination(obj, ai->getLocomotorSet(), dest, NULL);
  441. } else {
  442. TheAI->pathfinder()->adjustDestination(obj, ai->getLocomotorSet(), dest, groupDest);
  443. }
  444. TheAI->pathfinder()->updateGoal(obj, dest, LAYER_GROUND);
  445. }
  446. }
  447. static const Int PATH_DIAMETER_IN_CELLS = 6;
  448. //-------------------------------------------------------------------------------------------------
  449. // Internal function for moving a group of infantry as a column.
  450. //
  451. /**
  452. * Move to given position(s)
  453. */
  454. Bool AIGroup::friend_computeGroundPath( const Coord3D *pos, CommandSourceType cmdSource )
  455. {
  456. if (m_dirty)
  457. recompute();
  458. std::list<Object *>::iterator i;
  459. // compute current centroid of the team
  460. Coord3D center;
  461. Coord2D min;
  462. Coord2D max;
  463. Real dx, dy;
  464. if (TheGlobalData->m_debugAI==AI_DEBUG_TERRAIN) return false;
  465. Bool closeEnough = false;
  466. getMinMaxAndCenter( &min, &max, &center );
  467. Real distSqr = 4*sqr(TheAI->getAiData()->m_distanceRequiresGroup);
  468. Int numInfantry = 0;
  469. Int numVehicles = 0;
  470. Object *centerVehicle = NULL;
  471. Real distSqrCenterVeh = distSqr*10;
  472. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  473. {
  474. Object *obj = (*i);
  475. TheAI->pathfinder()->removeGoal(obj);
  476. if (obj->isDisabledByType( DISABLED_HELD ) )
  477. {
  478. continue; // don't bother telling the occupants to move.
  479. }
  480. if( obj->getAI()==NULL )
  481. {
  482. continue;
  483. }
  484. if( obj->isKindOf( KINDOF_INFANTRY ) )
  485. {
  486. numInfantry++;
  487. } else if (obj->isKindOf( KINDOF_VEHICLE)) {
  488. if (obj->isKindOf(KINDOF_AIRCRAFT)) {
  489. continue;
  490. }
  491. numVehicles++;
  492. } else {
  493. continue;
  494. }
  495. // Note - we are getting the closest of ANY type of unit for later testing intentionally. jba.
  496. Coord3D unitPos = *((*i)->getPosition());
  497. dx = unitPos.x-pos->x;
  498. dy = unitPos.y-pos->y;
  499. if (dx*dx+dy*dy<distSqr) {
  500. distSqr = dx*dx+dy*dy;
  501. }
  502. // find object closest to the center.
  503. dx = unitPos.x-center.x;
  504. dy = unitPos.y-center.y;
  505. if (centerVehicle==NULL || dx*dx+dy*dy<distSqrCenterVeh) {
  506. centerVehicle = (*i);
  507. distSqrCenterVeh = dx*dx+dy*dy;
  508. }
  509. }
  510. if(centerVehicle==NULL) return false;
  511. center = *centerVehicle->getPosition();
  512. dx = max.x - min.x;
  513. dy = max.y - min.y;
  514. if (dx*dx + dy*dy > sqr(TheAI->getAiData()->m_distanceRequiresGroup)) {
  515. distSqr = dx*dx+dy*dy;
  516. }
  517. if (distSqr < sqr(TheAI->getAiData()->m_minDistanceForGroup)) {
  518. return false;
  519. }
  520. if (distSqr>sqr(TheAI->getAiData()->m_distanceRequiresGroup)) {
  521. closeEnough = true;
  522. }
  523. if (numInfantry>6) {
  524. closeEnough = true;
  525. }
  526. if (numVehicles>4) {
  527. closeEnough = true;
  528. }
  529. if (!closeEnough) {
  530. Bool isPassable = true;
  531. // see if all units have an unobstructed path to the center.
  532. // If so, then they are close enough.
  533. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  534. {
  535. Object *obj = (*i);
  536. if (!obj->isKindOf(KINDOF_INFANTRY)) {
  537. continue;
  538. }
  539. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  540. if (ai)
  541. {
  542. if (!TheAI->pathfinder()->isLinePassable(obj,
  543. ai->getLocomotorSet().getValidSurfaces(), obj->getLayer(), *obj->getPosition(),
  544. center, false, true)) {
  545. isPassable = false;
  546. }
  547. }
  548. }
  549. if (isPassable) closeEnough = true;
  550. }
  551. if (!closeEnough) return false;
  552. m_groundPath = TheAI->pathfinder()->findGroundPath(&center, pos, PATH_DIAMETER_IN_CELLS, false);
  553. return m_groundPath!=NULL;
  554. }
  555. static void clampToMap(Coord3D *dest, PlayerType pt)
  556. // Clamps to the player's current visible map area. jba. [8/28/2003]
  557. {
  558. Region3D extent;
  559. if (pt==PLAYER_COMPUTER) {
  560. // AI gets to operate inside the pathable shrouded area. [8/28/2003]
  561. TheTerrainLogic->getMaximumPathfindExtent(&extent);
  562. } else {
  563. // Human player has to stay within the visible map.
  564. TheTerrainLogic->getExtent(&extent);
  565. }
  566. extent.hi.x -= PATHFIND_CELL_SIZE_F;
  567. extent.hi.y -= PATHFIND_CELL_SIZE_F;
  568. extent.lo.x += PATHFIND_CELL_SIZE_F;
  569. extent.lo.y += PATHFIND_CELL_SIZE_F;
  570. if (!extent.isInRegionNoZ(dest)) {
  571. // clamp to in region. [8/28/2003]
  572. if (dest->x < extent.lo.x) {
  573. dest->x = extent.lo.x;
  574. }
  575. if (dest->y < extent.lo.y) {
  576. dest->y = extent.lo.y;
  577. }
  578. if (dest->x > extent.hi.x) {
  579. dest->x = extent.hi.x;
  580. }
  581. if (dest->y > extent.hi.y) {
  582. dest->y = extent.hi.y;
  583. }
  584. }
  585. }
  586. //-------------------------------------------------------------------------------------------------
  587. // Internal function for moving a group of infantry as a column.
  588. //
  589. /**
  590. * Move to given position(s)
  591. */
  592. Bool AIGroup::friend_moveInfantryToPos( const Coord3D *pos, CommandSourceType cmdSource )
  593. {
  594. if (m_groundPath==NULL) return false;
  595. Int numColumns = 3;
  596. Int halfNumColumns = numColumns/2;
  597. Real dx, dy;
  598. Coord3D center;
  599. if (!getCenter( &center )) return false;
  600. // Get the start & end vectors for the path.
  601. Coord3D startPoint = *m_groundPath->getFirstNode()->getPosition();
  602. Real farEnoughSqr = sqr(PATH_DIAMETER_IN_CELLS*PATHFIND_CELL_SIZE_F);
  603. PathNode *startNode = NULL;
  604. PathNode *node;
  605. for (node = m_groundPath->getFirstNode(); node; node=node->getNextOptimized()) {
  606. dx = node->getPosition()->x - startPoint.x;
  607. dy = node->getPosition()->y - startPoint.y;
  608. if (dx*dx+dy*dy>farEnoughSqr) {
  609. startNode = node;
  610. break;
  611. }
  612. }
  613. Coord3D endPoint = *m_groundPath->getLastNode()->getPosition();
  614. PathNode *endNode = NULL;
  615. for (node = m_groundPath->getFirstNode(); node; node=node->getNextOptimized()) {
  616. Real dx = node->getPosition()->x - endPoint.x;
  617. Real dy = node->getPosition()->y - endPoint.y;
  618. if (dx*dx+dy*dy>farEnoughSqr) {
  619. endNode = node;
  620. }
  621. }
  622. if (startNode==NULL || endNode==NULL) {
  623. m_groundPath->deleteInstance();
  624. m_groundPath = NULL;
  625. return false;
  626. }
  627. Coord2D startVector;
  628. startVector.x = startNode->getPosition()->x - startPoint.x;
  629. startVector.y = startNode->getPosition()->y - startPoint.y;
  630. startVector.normalize();
  631. Coord2D endVector;
  632. endVector.x = endPoint.x - endNode->getPosition()->x;
  633. endVector.y = endPoint.y - endNode->getPosition()->y;
  634. endVector.normalize();
  635. Coord2D startVectorNormal;
  636. startVectorNormal.x = -startVector.y;
  637. startVectorNormal.y = startVector.x;
  638. startVectorNormal.normalize();
  639. Coord2D endVectorNormal;
  640. endVectorNormal.x = -endVector.y;
  641. endVectorNormal.y = endVector.x;
  642. endVectorNormal.normalize();
  643. Bool useEndVector = false;
  644. Int unitsToPath = 0;
  645. // Move.
  646. MemoryPoolObjectHolder iterHolder;
  647. SimpleObjectIterator *iter = newInstance(SimpleObjectIterator);
  648. iterHolder.hold(iter);
  649. MemoryPoolObjectHolder iterHolder2;
  650. SimpleObjectIterator *iter2 = newInstance(SimpleObjectIterator);
  651. iterHolder2.hold(iter2);
  652. std::list<Object *>::iterator i;
  653. PlayerType controllingPlayerType = PLAYER_COMPUTER;
  654. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  655. {
  656. if ((*i)->isDisabledByType( DISABLED_HELD ) )
  657. {
  658. continue; // don't bother telling the occupants to move.
  659. }
  660. if( !(*i)->isKindOf( KINDOF_INFANTRY ) )
  661. {
  662. continue;
  663. }
  664. if( (*i)->getAI()==NULL )
  665. {
  666. continue;
  667. }
  668. if ( (*i)->isKindOf( KINDOF_MOB_NEXUS ) )
  669. {
  670. return FALSE;// means I did NOT do a column group pathfind,
  671. //so the nexus will have a far-away goal position for the mobsters to aim at
  672. }
  673. if ((*i)->getControllingPlayer()) {
  674. controllingPlayerType = (*i)->getControllingPlayer()->getPlayerType();
  675. }
  676. Coord3D unitPos = *((*i)->getPosition());
  677. TheAI->pathfinder()->removeGoal(*i);
  678. dx = unitPos.x - center.x;
  679. dy = unitPos.y - center.y;
  680. // Sort by the dot product of normal.
  681. iter->insert((*i), dx*startVectorNormal.x+dy*startVectorNormal.y);
  682. unitsToPath++;
  683. // If units are closer to the end vector than the start vector, use the end vector.
  684. Real distToEndSqr;
  685. Real distToStartSqr;
  686. dx = unitPos.x - endPoint.x;
  687. dy = unitPos.y - endPoint.y;
  688. distToEndSqr = dx*dx + dy*dy;
  689. dx = unitPos.x - startPoint.x;
  690. dy = unitPos.y - startPoint.y;
  691. distToStartSqr = dx*dx + dy*dy;
  692. if (distToStartSqr>distToEndSqr) {
  693. useEndVector = true;
  694. }
  695. }
  696. if (unitsToPath<TheAI->getAiData()->m_minInfantryForGroup) {
  697. return false;
  698. }
  699. Object *theUnit;
  700. if (useEndVector) {
  701. // resort unsing the end vector.
  702. startVector = endVector;
  703. startVectorNormal = endVectorNormal;
  704. for (theUnit = iter->first(); theUnit; theUnit = iter->next()) iter2->insert(theUnit);
  705. iter->makeEmpty();
  706. for (theUnit = iter2->first(); theUnit; theUnit = iter2->next())
  707. {
  708. Coord3D unitPos = *(theUnit->getPosition());
  709. dx = unitPos.x - center.x;
  710. dy = unitPos.y - center.y;
  711. // Sort by the dot product of normal.
  712. iter->insert(theUnit, dx*startVectorNormal.x+dy*startVectorNormal.y);
  713. }
  714. iter2->makeEmpty();
  715. }
  716. iter->sort(ITER_SORTED_FAR_TO_NEAR);
  717. Int curIndex = 0;
  718. for (theUnit = iter->first(); theUnit; theUnit = iter->next())
  719. {
  720. AIUpdateInterface *ai = theUnit->getAIUpdateInterface();
  721. Int divisor = ((unitsToPath+1)/numColumns);
  722. if (divisor<1) divisor=1;
  723. Int columnDelta = 1-(curIndex/divisor); // 1, 0, -1 from left to right across column. jba.
  724. if (columnDelta<-halfNumColumns) columnDelta=-halfNumColumns;
  725. divisor = ((unitsToPath+3)/5);
  726. if (divisor<1) divisor=1;
  727. Int fiveColumnDelta = 2-(curIndex/divisor); // 2, 1, 0, -1, -2 from left to right across column. jba.
  728. if (fiveColumnDelta<-2) fiveColumnDelta=-2;
  729. if (unitsToPath<16) {
  730. fiveColumnDelta = columnDelta;
  731. }
  732. ai->setTmpValue( (fiveColumnDelta<<16)|(columnDelta&0x00ffff));
  733. // Sort next pass by the dot product of start vector.
  734. dx, dy;
  735. dx = theUnit->getPosition()->x - center.x;
  736. dy = theUnit->getPosition()->y - center.y;
  737. Int adjust = 0;
  738. LocomotorPriority movePriority = LOCO_MOVES_FRONT;
  739. if (ai->getCurLocomotor()) {
  740. movePriority = ai->getCurLocomotor()->getMovePriority();
  741. if (movePriority == LOCO_MOVES_MIDDLE) {
  742. adjust = -100*PATHFIND_CELL_SIZE_F;
  743. } else if (movePriority == LOCO_MOVES_BACK) {
  744. adjust = -200*PATHFIND_CELL_SIZE_F;
  745. }
  746. }
  747. iter2->insert(theUnit, adjust + dx*startVector.x + dy*startVector.y);
  748. curIndex++;
  749. }
  750. iter2->sort(ITER_SORTED_FAR_TO_NEAR);
  751. // Even out columns by priority.
  752. Int group;
  753. Int column3[3] = {0,0,0};
  754. Int column5[5] = {0,0,0,0,0};
  755. for (group = LOCO_MOVES_FRONT; group>=LOCO_MOVES_BACK; group--) {
  756. for (theUnit = iter2->first(); theUnit; theUnit = iter2->next())
  757. {
  758. AIUpdateInterface *ai = theUnit->getAIUpdateInterface();
  759. Int tmp = ai->getTmpValue();
  760. LocomotorPriority movePriority = LOCO_MOVES_MIDDLE;
  761. if (ai->getCurLocomotor()) {
  762. movePriority = ai->getCurLocomotor()->getMovePriority();
  763. }
  764. if (group!=movePriority) continue;
  765. Int fiveColumnDelta = tmp>>16;
  766. Int columnDelta = (Short)(tmp & 0xFFFF);
  767. Int i;
  768. Int min3 = 10000;
  769. Int min5 = 10000;
  770. for (i=0; i<3; i++) if (column3[i]<min3) min3 = column3[i];
  771. for (i=0; i<5; i++) if (column5[i]<min5) min5 = column5[i];
  772. Int delta = 10000;
  773. Int best = -1;
  774. for (i=0; i<3; i++) {
  775. if (column3[i]==min3) {
  776. Int dx = (1+columnDelta)-i;
  777. if (dx<0) dx = -dx;
  778. if (dx<delta) {
  779. delta = dx;
  780. best = i;
  781. }
  782. }
  783. }
  784. if (best >= 0) {
  785. column3[best]++;
  786. columnDelta = best-1;
  787. }
  788. delta = 10000;
  789. best = -1;
  790. for (i=0; i<5; i++) {
  791. if (column5[i]==min5) {
  792. Int dx = (2+fiveColumnDelta)-i;
  793. if (dx<0) dx = -dx;
  794. if (dx<delta) {
  795. delta = dx;
  796. best = i;
  797. }
  798. }
  799. }
  800. if (best >= 0) {
  801. column5[best]++;
  802. fiveColumnDelta = best-2;
  803. }
  804. if (unitsToPath<16) {
  805. fiveColumnDelta = columnDelta;
  806. }
  807. ai->setTmpValue( (fiveColumnDelta<<16)|(columnDelta&0x00ffff));
  808. }
  809. }
  810. curIndex = 0;
  811. Int columnFactor[5] = {0,0,0,0,0};
  812. PathfindLayerEnum layer = TheTerrainLogic->getLayerForDestination(pos);
  813. for (theUnit = iter2->first(); theUnit; theUnit = iter2->next())
  814. {
  815. AIUpdateInterface *ai = theUnit->getAIUpdateInterface();
  816. Int tmp = ai->getTmpValue();
  817. Int fiveColumnDelta = tmp>>16;
  818. Int columnDelta = (Short)(tmp & 0xFFFF);
  819. Int factor = columnFactor[fiveColumnDelta+2];
  820. columnFactor[fiveColumnDelta+2] = factor+1;
  821. std::vector<Coord3D> path;
  822. PathNode *node = startNode;
  823. PathNode *previousNode = m_groundPath->getFirstNode();
  824. Coord3D prevPos = *theUnit->getPosition();
  825. while (node) {
  826. Coord3D dest = *node->getPosition();
  827. PathNode *tmpNode;
  828. PathNode *nextNode=NULL;
  829. for (tmpNode = node->getNextOptimized(); tmpNode; tmpNode=tmpNode->getNextOptimized()) {
  830. Real dx = tmpNode->getPosition()->x - dest.x;
  831. Real dy = tmpNode->getPosition()->y - dest.y;
  832. if (dx*dx+dy*dy>farEnoughSqr) {
  833. nextNode = tmpNode;
  834. break;
  835. }
  836. }
  837. if (nextNode==NULL) break;
  838. Coord2D cornerVectorNormal;
  839. cornerVectorNormal.y = nextNode->getPosition()->x - previousNode->getPosition()->x;
  840. cornerVectorNormal.x = -(nextNode->getPosition()->y - previousNode->getPosition()->y);
  841. cornerVectorNormal.normalize();
  842. Coord2D cornerVector;
  843. cornerVector.x = nextNode->getPosition()->x - previousNode->getPosition()->x;
  844. cornerVector.y = nextNode->getPosition()->y - previousNode->getPosition()->y;
  845. Real offset = PATHFIND_CELL_SIZE_F*2.1f/halfNumColumns;
  846. dest.x += offset * columnDelta * cornerVectorNormal.x;
  847. dest.y += offset * columnDelta * cornerVectorNormal.y;
  848. if (factor&1) {
  849. dest.x += 0.5f*PATHFIND_CELL_SIZE_F * cornerVectorNormal.x;
  850. dest.y += 0.5f*PATHFIND_CELL_SIZE_F * cornerVectorNormal.y;
  851. } else {
  852. dest.x -= 0.5f*PATHFIND_CELL_SIZE_F * cornerVectorNormal.x;
  853. dest.y -= 0.5f*PATHFIND_CELL_SIZE_F * cornerVectorNormal.y;
  854. }
  855. Coord2D curVector;
  856. curVector.x = dest.x-prevPos.x;
  857. curVector.y = dest.y-prevPos.y;
  858. clampToMap(&dest, controllingPlayerType);
  859. // Make sure that this dest is going in the same direction as the vector.
  860. if (cornerVector.x*curVector.x + cornerVector.y*curVector.y > 0) {
  861. path.push_back( dest );
  862. prevPos = dest;
  863. }
  864. node=node->getNextOptimized();
  865. for (tmpNode = previousNode->getNextOptimized(); tmpNode && tmpNode!=node; tmpNode=tmpNode->getNextOptimized()) {
  866. Real dx = tmpNode->getPosition()->x - node->getPosition()->x;
  867. Real dy = tmpNode->getPosition()->y - node->getPosition()->y;
  868. if (dx*dx+dy*dy>farEnoughSqr) {
  869. previousNode = tmpNode;
  870. }
  871. }
  872. }
  873. Coord3D dest = *pos;
  874. if (fiveColumnDelta<-2) fiveColumnDelta=-2;
  875. if (fiveColumnDelta>2) fiveColumnDelta=2;
  876. Real offset = PATHFIND_CELL_SIZE_F*2.2f;
  877. dest.x += offset * fiveColumnDelta * endVectorNormal.x;
  878. dest.y += offset * fiveColumnDelta * endVectorNormal.y;
  879. if (factor&1) {
  880. dest.x += PATHFIND_CELL_SIZE_F * endVectorNormal.x;
  881. dest.y += PATHFIND_CELL_SIZE_F * endVectorNormal.y;
  882. }
  883. LocomotorPriority movePriority = LOCO_MOVES_MIDDLE;
  884. if (ai->getCurLocomotor()) {
  885. movePriority = ai->getCurLocomotor()->getMovePriority();
  886. }
  887. Int delta = movePriority - LOCO_MOVES_FRONT;
  888. dest.x += delta*PATHFIND_CELL_SIZE_F*endVector.x;
  889. dest.y += delta*PATHFIND_CELL_SIZE_F*endVector.y;
  890. dest.x -= factor*offset*endVector.x;
  891. dest.y -= factor*offset*endVector.y;
  892. dest.z = TheTerrainLogic->getLayerHeight( dest.x, dest.y, layer );
  893. while (path.size()>0) {
  894. Coord2D curVector;
  895. prevPos = path[path.size()-1];
  896. curVector.x = dest.x-prevPos.x;
  897. curVector.y = dest.y-prevPos.y;
  898. // Make sure that this dest is going in the same direction as the vector.
  899. if (endVector.x*curVector.x + endVector.y*curVector.y <= 0) {
  900. path.pop_back();
  901. } else {
  902. break;
  903. }
  904. }
  905. clampToMap(&dest, controllingPlayerType);
  906. TheAI->pathfinder()->adjustDestination(theUnit, ai->getLocomotorSet(), &dest, NULL);
  907. TheAI->pathfinder()->updateGoal(theUnit, &dest, LAYER_GROUND);
  908. path.push_back(dest);
  909. ai->aiFollowPath( &path, NULL, cmdSource );
  910. }
  911. return true;
  912. }
  913. /**
  914. * Move to given position(s)
  915. */
  916. void AIGroup::friend_moveFormationToPos( const Coord3D *pos, CommandSourceType cmdSource )
  917. {
  918. Real dx, dy;
  919. Coord3D center;
  920. if (!getCenter( &center )) return;
  921. PathNode *startNode = NULL;
  922. PathNode *endNode = NULL;
  923. Coord3D endPoint = *pos;
  924. if (m_groundPath) {
  925. // Get the start & end vectors for the path.
  926. Coord3D startPoint = *m_groundPath->getFirstNode()->getPosition();
  927. Real farEnoughSqr = sqr(PATH_DIAMETER_IN_CELLS*PATHFIND_CELL_SIZE_F);
  928. PathNode *node;
  929. for (node = m_groundPath->getFirstNode(); node; node=node->getNextOptimized()) {
  930. dx = node->getPosition()->x - startPoint.x;
  931. dy = node->getPosition()->y - startPoint.y;
  932. if (dx*dx+dy*dy>farEnoughSqr) {
  933. startNode = node;
  934. break;
  935. }
  936. }
  937. endPoint = *m_groundPath->getLastNode()->getPosition();
  938. for (node = m_groundPath->getFirstNode(); node; node=node->getNextOptimized()) {
  939. dx = node->getPosition()->x - endPoint.x;
  940. dy = node->getPosition()->y - endPoint.y;
  941. if (dx*dx+dy*dy>farEnoughSqr) {
  942. endNode = node;
  943. }
  944. }
  945. PathNode *tmpNode = endNode;
  946. while (tmpNode) {
  947. if (tmpNode == startNode) {
  948. endNode = NULL;
  949. }
  950. tmpNode = tmpNode->getNextOptimized();
  951. }
  952. if (startNode==NULL || endNode==NULL) {
  953. m_groundPath->deleteInstance();
  954. m_groundPath = NULL;
  955. startNode = NULL;
  956. endNode = NULL;
  957. }
  958. }
  959. // Move.
  960. std::list<Object *>::iterator i;
  961. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  962. {
  963. if ((*i)->isDisabledByType( DISABLED_HELD ) )
  964. {
  965. continue; // don't bother telling the occupants to move.
  966. }
  967. Object *theUnit = (*i);
  968. AIUpdateInterface *ai = theUnit->getAIUpdateInterface();
  969. Bool isDifferentFormation = false;
  970. Coord2D offset;
  971. if (isDifferentFormation) {
  972. Coord3D pos = *theUnit->getPosition();
  973. offset.x = pos.x - center.x;
  974. offset.y = pos.y - center.y;
  975. theUnit->setFormationOffset(offset);
  976. }
  977. theUnit->getFormationOffset(&offset);
  978. if (startNode) {
  979. std::vector<Coord3D> path;
  980. PathNode *node = startNode;
  981. while (node) {
  982. Coord3D dest = *node->getPosition();
  983. dest.x += offset.x;
  984. dest.y += offset.y;
  985. path.push_back( dest );
  986. if (node==endNode) break;
  987. node=node->getNextOptimized();
  988. }
  989. Coord3D dest = endPoint;
  990. dest.x += offset.x;
  991. dest.y += offset.y;
  992. TheAI->pathfinder()->adjustDestination(theUnit, ai->getLocomotorSet(), &dest, NULL);
  993. TheAI->pathfinder()->updateGoal(theUnit, &dest, LAYER_GROUND);
  994. path.push_back(dest);
  995. ai->aiFollowPath( &path, NULL, cmdSource );
  996. } else {
  997. Coord3D dest = endPoint;
  998. dest.x += offset.x;
  999. dest.y += offset.y;
  1000. ai->aiMoveToPosition( &dest, cmdSource );
  1001. }
  1002. }
  1003. }
  1004. //-------------------------------------------------------------------------------------------------
  1005. // Internal function for moving a group of vehicles as a column.
  1006. //
  1007. /**
  1008. * Move to given position(s)
  1009. */
  1010. Bool AIGroup::friend_moveVehicleToPos( const Coord3D *pos, CommandSourceType cmdSource )
  1011. {
  1012. if (m_groundPath==NULL) return false;
  1013. Real dx, dy;
  1014. Coord3D center;
  1015. if (!getCenter( &center )) return false;
  1016. if (!m_groundPath) {
  1017. return false;
  1018. }
  1019. Int numColumns = 2;
  1020. // Get the start & end vectors for the path.
  1021. Coord3D startPoint = *m_groundPath->getFirstNode()->getPosition();
  1022. Real farEnoughSqr = sqr(PATH_DIAMETER_IN_CELLS*PATHFIND_CELL_SIZE_F);
  1023. PathNode *startNode = NULL;
  1024. PathNode *node;
  1025. for (node = m_groundPath->getFirstNode(); node; node=node->getNextOptimized()) {
  1026. Real dx = node->getPosition()->x - startPoint.x;
  1027. Real dy = node->getPosition()->y - startPoint.y;
  1028. if (dx*dx+dy*dy>farEnoughSqr) {
  1029. startNode = node;
  1030. break;
  1031. }
  1032. }
  1033. Coord3D endPoint = *m_groundPath->getLastNode()->getPosition();
  1034. PathNode *endNode = NULL;
  1035. for (node = m_groundPath->getFirstNode(); node; node=node->getNextOptimized()) {
  1036. Real dx = node->getPosition()->x - endPoint.x;
  1037. Real dy = node->getPosition()->y - endPoint.y;
  1038. if (dx*dx+dy*dy>farEnoughSqr) {
  1039. endNode = node;
  1040. }
  1041. }
  1042. if (endNode == m_groundPath->getFirstNode()) {
  1043. endNode = NULL;
  1044. }
  1045. if (startNode==NULL || endNode==NULL) {
  1046. m_groundPath->deleteInstance();
  1047. m_groundPath = NULL;
  1048. return false;
  1049. }
  1050. Coord2D startVector;
  1051. startVector.x = startNode->getPosition()->x - startPoint.x;
  1052. startVector.y = startNode->getPosition()->y - startPoint.y;
  1053. startVector.normalize();
  1054. Coord2D endVector;
  1055. endVector.x = endPoint.x - endNode->getPosition()->x;
  1056. endVector.y = endPoint.y - endNode->getPosition()->y;
  1057. endVector.normalize();
  1058. Coord2D startVectorNormal;
  1059. startVectorNormal.x = -startVector.y;
  1060. startVectorNormal.y = startVector.x;
  1061. startVectorNormal.normalize();
  1062. Coord2D endVectorNormal;
  1063. endVectorNormal.x = -endVector.y;
  1064. endVectorNormal.y = endVector.x;
  1065. endVectorNormal.normalize();
  1066. Int unitsToPath = 0;
  1067. Bool useEndVector = false;
  1068. // Move.
  1069. MemoryPoolObjectHolder iterHolder;
  1070. SimpleObjectIterator *iter = newInstance(SimpleObjectIterator);
  1071. iterHolder.hold(iter);
  1072. MemoryPoolObjectHolder iterHolder2;
  1073. SimpleObjectIterator *iter2 = newInstance(SimpleObjectIterator);
  1074. iterHolder2.hold(iter2);
  1075. PlayerType controllingPlayerType = PLAYER_COMPUTER;
  1076. std::list<Object *>::iterator i;
  1077. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  1078. {
  1079. if ((*i)->isDisabledByType( DISABLED_HELD ) )
  1080. {
  1081. continue; // don't bother telling the occupants to move.
  1082. }
  1083. if( !(*i)->isKindOf( KINDOF_VEHICLE ) )
  1084. {
  1085. continue;
  1086. }
  1087. if( (*i)->getAI()==NULL )
  1088. {
  1089. continue;
  1090. }
  1091. if( !(*i)->getAI()->isDoingGroundMovement() )
  1092. {
  1093. continue;
  1094. }
  1095. if ((*i)->getControllingPlayer()) {
  1096. controllingPlayerType = (*i)->getControllingPlayer()->getPlayerType();
  1097. }
  1098. Coord3D unitPos = *((*i)->getPosition());
  1099. TheAI->pathfinder()->removeGoal(*i);
  1100. Real dx, dy;
  1101. dx = unitPos.x - center.x;
  1102. dy = unitPos.y - center.y;
  1103. // Sort by the dot product of normal.
  1104. iter->insert((*i), dx*startVectorNormal.x+dy*startVectorNormal.y);
  1105. unitsToPath++;
  1106. // If units are closer to the end vector than the start vector, use the end vector.
  1107. Real distToEndSqr;
  1108. Real distToStartSqr;
  1109. dx = unitPos.x - endPoint.x;
  1110. dy = unitPos.y - endPoint.y;
  1111. distToEndSqr = dx*dx + dy*dy;
  1112. dx = unitPos.x - startPoint.x;
  1113. dy = unitPos.y - startPoint.y;
  1114. distToStartSqr = dx*dx + dy*dy;
  1115. if (distToStartSqr>distToEndSqr) {
  1116. useEndVector = true;
  1117. }
  1118. }
  1119. if (unitsToPath<TheAI->getAiData()->m_minVehiclesForGroup) {
  1120. return false;
  1121. }
  1122. Object *theUnit;
  1123. if (useEndVector) {
  1124. // resort unsing the end vector.
  1125. startVector = endVector;
  1126. startVectorNormal = endVectorNormal;
  1127. for (theUnit = iter->first(); theUnit; theUnit = iter->next()) iter2->insert(theUnit);
  1128. iter->makeEmpty();
  1129. for (theUnit = iter2->first(); theUnit; theUnit = iter2->next())
  1130. {
  1131. Coord3D unitPos = *(theUnit->getPosition());
  1132. dx = unitPos.x - center.x;
  1133. dy = unitPos.y - center.y;
  1134. // Sort by the dot product of normal.
  1135. iter->insert(theUnit, dx*startVectorNormal.x+dy*startVectorNormal.y);
  1136. }
  1137. iter2->makeEmpty();
  1138. }
  1139. iter->sort(ITER_SORTED_FAR_TO_NEAR);
  1140. Int curIndex = 0;
  1141. for (theUnit = iter->first(); theUnit; theUnit = iter->next())
  1142. {
  1143. AIUpdateInterface *ai = theUnit->getAIUpdateInterface();
  1144. Int divisor = ((unitsToPath+1)/numColumns);
  1145. if (divisor<1) divisor=1;
  1146. Int columnDelta = 1-(curIndex/divisor); // 0, 1 from left to right across column. jba.
  1147. if (columnDelta==0) columnDelta=-1;
  1148. divisor = ((unitsToPath+1)/3);
  1149. if (divisor<1) divisor=1;
  1150. Int threeColumnDelta = (curIndex/divisor); // 0, 1, 2 from left to right across column. jba.
  1151. threeColumnDelta = 1-threeColumnDelta; // 1, 0, -1
  1152. if (threeColumnDelta<-1) threeColumnDelta=-1;
  1153. if (unitsToPath<5) {
  1154. threeColumnDelta = columnDelta;
  1155. }
  1156. ai->setTmpValue( (threeColumnDelta<<16)|(columnDelta&0x00ffff));
  1157. // Sort next pass by the dot product of start vector.
  1158. Real dx, dy;
  1159. dx = theUnit->getPosition()->x - center.x;
  1160. dy = theUnit->getPosition()->y - center.y;
  1161. Int adjust = 0;
  1162. #if 0
  1163. LocomotorPriority movePriority = LOCO_MOVES_FRONT;
  1164. if (ai->getCurLocomotor()) {
  1165. movePriority = ai->getCurLocomotor()->getMovePriority();
  1166. if (movePriority == LOCO_MOVES_MIDDLE) {
  1167. adjust = -100*PATHFIND_CELL_SIZE_F;
  1168. } else if (movePriority == LOCO_MOVES_BACK) {
  1169. adjust = -200*PATHFIND_CELL_SIZE_F;
  1170. }
  1171. }
  1172. #endif
  1173. iter2->insert(theUnit, adjust + dx*startVector.x + dy*startVector.y);
  1174. curIndex++;
  1175. }
  1176. iter2->sort(ITER_SORTED_FAR_TO_NEAR);
  1177. // Even out columns by priority.
  1178. Int group;
  1179. Int column2[3] = {0,0,0};
  1180. Int column3[3] = {0,0,0};
  1181. for (group = LOCO_MOVES_FRONT; group>=LOCO_MOVES_BACK; group--) {
  1182. for (theUnit = iter2->first(); theUnit; theUnit = iter2->next())
  1183. {
  1184. AIUpdateInterface *ai = theUnit->getAIUpdateInterface();
  1185. Int tmp = ai->getTmpValue();
  1186. #if 0
  1187. LocomotorPriority movePriority = LOCO_MOVES_MIDDLE;
  1188. if (ai->getCurLocomotor()) {
  1189. movePriority = ai->getCurLocomotor()->getMovePriority();
  1190. }
  1191. if (group!=movePriority) continue;
  1192. #endif
  1193. Int threeColumnDelta = tmp>>16;
  1194. Int columnDelta = (Short)(tmp & 0xFFFF);
  1195. Int i;
  1196. Int min2 = 10000;
  1197. Int min3 = 10000;
  1198. for (i=0; i<3; i+=2) if (column2[i]<min2) min2 = column2[i];
  1199. for (i=0; i<3; i++) if (column3[i]<min3) min3 = column3[i];
  1200. Int delta = 10000;
  1201. Int best = -1;
  1202. for (i=0; i<3; i+=2) {
  1203. if (column2[i]==min2) {
  1204. Int dx = (1+columnDelta)-i;
  1205. if (dx<0) dx = -dx;
  1206. if (dx<delta) {
  1207. delta = dx;
  1208. best = i;
  1209. }
  1210. }
  1211. }
  1212. if (best >= 0) {
  1213. column2[best]++;
  1214. columnDelta = best-1;
  1215. }
  1216. delta = 10000;
  1217. best = -1;
  1218. for (i=0; i<3; i++) {
  1219. if (column3[i]==min3) {
  1220. Int dx = (1+threeColumnDelta)-i;
  1221. if (dx<0) dx = -dx;
  1222. if (dx<delta) {
  1223. delta = dx;
  1224. best = i;
  1225. }
  1226. }
  1227. }
  1228. if (best >= 0) {
  1229. column3[best]++;
  1230. threeColumnDelta = best-1;
  1231. }
  1232. if (unitsToPath<5) {
  1233. threeColumnDelta = columnDelta;
  1234. }
  1235. ai->setTmpValue( (threeColumnDelta<<16)|(columnDelta&0x00ffff));
  1236. }
  1237. }
  1238. curIndex = 0;
  1239. Int columnFactor[5] = {0,0,0,0,0};
  1240. PathfindLayerEnum layer = TheTerrainLogic->getLayerForDestination(pos);
  1241. for (theUnit = iter2->first(); theUnit; theUnit = iter2->next())
  1242. {
  1243. AIUpdateInterface *ai = theUnit->getAIUpdateInterface();
  1244. Int tmp = ai->getTmpValue();
  1245. Int threeColumnDelta = tmp>>16;
  1246. Int columnDelta = (Short)(tmp & 0xFFFF);
  1247. Int factor = columnFactor[threeColumnDelta+2];
  1248. columnFactor[threeColumnDelta+2] = factor+1;
  1249. std::vector<Coord3D> path;
  1250. PathNode *node = startNode;
  1251. PathNode *previousNode = m_groundPath->getFirstNode();
  1252. Coord3D prevPos = *theUnit->getPosition();
  1253. while (node) {
  1254. Coord3D dest = *node->getPosition();
  1255. PathNode *tmpNode;
  1256. PathNode *nextNode=NULL;
  1257. for (tmpNode = node->getNextOptimized(); tmpNode; tmpNode=tmpNode->getNextOptimized()) {
  1258. Real dx = tmpNode->getPosition()->x - dest.x;
  1259. Real dy = tmpNode->getPosition()->y - dest.y;
  1260. if (dx*dx+dy*dy>farEnoughSqr) {
  1261. nextNode = tmpNode;
  1262. break;
  1263. }
  1264. }
  1265. if (nextNode==NULL) break;
  1266. Coord2D cornerVectorNormal;
  1267. cornerVectorNormal.y = nextNode->getPosition()->x - previousNode->getPosition()->x;
  1268. cornerVectorNormal.x = -(nextNode->getPosition()->y - previousNode->getPosition()->y);
  1269. cornerVectorNormal.normalize();
  1270. Coord2D cornerVector;
  1271. cornerVector.x = nextNode->getPosition()->x - previousNode->getPosition()->x;
  1272. cornerVector.y = nextNode->getPosition()->y - previousNode->getPosition()->y;
  1273. Real offset = PATHFIND_CELL_SIZE_F*1.5f;
  1274. dest.x += offset * columnDelta * cornerVectorNormal.x;
  1275. dest.y += offset * columnDelta * cornerVectorNormal.y;
  1276. if (factor&1) {
  1277. dest.x += 0.5f*PATHFIND_CELL_SIZE_F * cornerVectorNormal.x;
  1278. dest.y += 0.5f*PATHFIND_CELL_SIZE_F * cornerVectorNormal.y;
  1279. } else {
  1280. dest.x -= 0.5f*PATHFIND_CELL_SIZE_F * cornerVectorNormal.x;
  1281. dest.y -= 0.5f*PATHFIND_CELL_SIZE_F * cornerVectorNormal.y;
  1282. }
  1283. Coord2D curVector;
  1284. curVector.x = dest.x-prevPos.x;
  1285. curVector.y = dest.y-prevPos.y;
  1286. clampToMap(&dest, controllingPlayerType);
  1287. // Make sure that this dest is going in the same direction as the vector.
  1288. if (cornerVector.x*curVector.x + cornerVector.y*curVector.y > 0) {
  1289. path.push_back( dest );
  1290. prevPos = dest;
  1291. }
  1292. node=node->getNextOptimized();
  1293. for (tmpNode = previousNode->getNextOptimized(); tmpNode && tmpNode!=node; tmpNode=tmpNode->getNextOptimized()) {
  1294. Real dx = tmpNode->getPosition()->x - node->getPosition()->x;
  1295. Real dy = tmpNode->getPosition()->y - node->getPosition()->y;
  1296. if (dx*dx+dy*dy>farEnoughSqr) {
  1297. previousNode = tmpNode;
  1298. }
  1299. }
  1300. }
  1301. Coord3D dest = *pos;
  1302. if (threeColumnDelta<-3) threeColumnDelta=-3;
  1303. if (threeColumnDelta>3) threeColumnDelta=3;
  1304. Real offset = PATHFIND_CELL_SIZE_F*3.2f;
  1305. if (unitsToPath<5) {
  1306. offset = PATHFIND_CELL_SIZE_F*1.5f;
  1307. }
  1308. dest.x += offset * threeColumnDelta * endVectorNormal.x;
  1309. dest.y += offset * threeColumnDelta * endVectorNormal.y;
  1310. if (factor&1) {
  1311. dest.x += PATHFIND_CELL_SIZE_F * endVectorNormal.x;
  1312. dest.y += PATHFIND_CELL_SIZE_F * endVectorNormal.y;
  1313. }
  1314. #if 0
  1315. LocomotorPriority movePriority = LOCO_MOVES_MIDDLE;
  1316. if (ai->getCurLocomotor()) {
  1317. movePriority = ai->getCurLocomotor()->getMovePriority();
  1318. }
  1319. Int delta = movePriority - LOCO_MOVES_FRONT;
  1320. dest.x += delta*PATHFIND_CELL_SIZE_F*endVector.x;
  1321. dest.y += delta*PATHFIND_CELL_SIZE_F*endVector.y;
  1322. #endif
  1323. dest.x -= factor*offset*endVector.x;
  1324. dest.y -= factor*offset*endVector.y;
  1325. dest.z = TheTerrainLogic->getLayerHeight( dest.x, dest.y, layer );
  1326. while (path.size()>0) {
  1327. Coord2D curVector;
  1328. prevPos = path[path.size()-1];
  1329. curVector.x = dest.x-prevPos.x;
  1330. curVector.y = dest.y-prevPos.y;
  1331. // Make sure that this dest is going in the same direction as the vector.
  1332. if (endVector.x*curVector.x + endVector.y*curVector.y <= 0) {
  1333. path.pop_back();
  1334. } else {
  1335. break;
  1336. }
  1337. }
  1338. clampToMap(&dest, controllingPlayerType);
  1339. TheAI->pathfinder()->adjustDestination(theUnit, ai->getLocomotorSet(), &dest, NULL);
  1340. TheAI->pathfinder()->updateGoal(theUnit, &dest, LAYER_GROUND);
  1341. path.push_back(dest);
  1342. ai->aiFollowPath( &path, NULL, cmdSource );
  1343. }
  1344. return true;
  1345. }
  1346. //-------------------------------------------------------------------------------------------------
  1347. // AI Command Interface implementation for AIGroup
  1348. //
  1349. const Int STD_WAYPOINT_CLAMP_MARGIN = ( PATHFIND_CELL_SIZE_F * 4.0f );
  1350. const Int STD_AIRCRAFT_EXTRA_MARGIN = ( PATHFIND_CELL_SIZE_F * 10.0f );
  1351. void clampWaypointPosition( Coord3D &position, Int margin )
  1352. {
  1353. Region3D mapExtent;
  1354. TheTerrainLogic->getExtent(&mapExtent);
  1355. // trim some fat off of all sides,
  1356. mapExtent.hi.x -= margin;
  1357. mapExtent.hi.y -= margin;
  1358. mapExtent.lo.x += margin;
  1359. mapExtent.lo.y += margin;
  1360. if ( mapExtent.isInRegionNoZ( &position ) == FALSE )
  1361. {
  1362. if ( position.x > mapExtent.hi.x )
  1363. position.x = mapExtent.hi.x;
  1364. else if ( position.x < mapExtent.lo.x )
  1365. position.x = mapExtent.lo.x;
  1366. if ( position.y > mapExtent.hi.y )
  1367. position.y = mapExtent.hi.y;
  1368. else if ( position.y < mapExtent.lo.y )
  1369. position.y = mapExtent.lo.y;
  1370. position.z = TheTerrainLogic->getGroundHeight( position.x, position.y );
  1371. }
  1372. }
  1373. /**
  1374. * Move to given position(s)
  1375. */
  1376. void AIGroup::groupMoveToPosition( const Coord3D *p_posIn, Bool addWaypoint, CommandSourceType cmdSource )
  1377. {
  1378. Coord3D position = *p_posIn;
  1379. Coord3D *pos = &position;
  1380. Bool didInfantry = false;
  1381. Bool didVehicles = false;
  1382. // compute current centroid of the team
  1383. Coord3D center;
  1384. Coord2D min;
  1385. Coord2D max;
  1386. Coord3D dest;
  1387. Bool tightenGroup = FALSE;
  1388. Bool isFormation = getMinMaxAndCenter( &min, &max, &center );
  1389. if (addWaypoint)
  1390. {
  1391. isFormation = false;
  1392. }
  1393. if (!addWaypoint && !isFormation) {
  1394. friend_computeGroundPath(pos, cmdSource);
  1395. didInfantry = friend_moveInfantryToPos(pos, cmdSource);
  1396. didVehicles = friend_moveVehicleToPos(pos, cmdSource);
  1397. }
  1398. if (m_dirty)
  1399. recompute();
  1400. std::list<Object *>::iterator i;
  1401. if( !isFormation && cmdSource == CMD_FROM_PLAYER && TheGlobalData->m_groupMoveClickToGatherFactor > 0.0f )
  1402. {
  1403. ScaleRect2D( &min, &max, TheGlobalData->m_groupMoveClickToGatherFactor );
  1404. if( Coord3DInsideRect2D( pos, &min, &max ) )
  1405. {
  1406. tightenGroup = TRUE;
  1407. }
  1408. }
  1409. Real extraMargin = 0.0f;
  1410. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  1411. {
  1412. const Object *groupMember = (*i);
  1413. if ( groupMember->isKindOf( KINDOF_PRODUCED_AT_HELIPAD ) )//helicopter
  1414. {
  1415. isFormation = FALSE;
  1416. extraMargin = MAX( extraMargin, groupMember->getGeometryInfo().getMajorRadius() );
  1417. }
  1418. else if ( groupMember->isKindOf( KINDOF_AIRCRAFT ) )// fixed wing aircraft only
  1419. {
  1420. if ( groupMember->getAI() && groupMember->getAI()->isDoingGroundMovement() == FALSE ) //if unit is airborne
  1421. {
  1422. tightenGroup = FALSE; // Don't tighten aircraft. It is a bad idea. jba.
  1423. isFormation = FALSE;//then keep spread formation after move
  1424. }
  1425. extraMargin = MAX( extraMargin, STD_AIRCRAFT_EXTRA_MARGIN );
  1426. }
  1427. }
  1428. Int margin = STD_WAYPOINT_CLAMP_MARGIN + extraMargin;
  1429. clampWaypointPosition( position, margin );
  1430. if (tightenGroup)
  1431. {
  1432. isFormation = false;
  1433. if (!addWaypoint) {
  1434. Int dx = (max.x-min.x)/PATHFIND_CELL_SIZE_F;
  1435. Int dy = (max.x-min.x)/PATHFIND_CELL_SIZE_F;
  1436. Int cells = (dx*dy);
  1437. if (cells<2000) {
  1438. groupTightenToPosition(pos, false, cmdSource);
  1439. return;
  1440. }
  1441. }
  1442. }
  1443. if (isFormation) {
  1444. friend_computeGroundPath(pos, cmdSource);
  1445. friend_moveFormationToPos(pos, cmdSource);
  1446. return;
  1447. }
  1448. // Move.
  1449. MemoryPoolObjectHolder iterHolder;
  1450. SimpleObjectIterator *iter = newInstance(SimpleObjectIterator);
  1451. iterHolder.hold(iter);
  1452. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  1453. {
  1454. Real dx, dy;
  1455. if ((*i)->isDisabledByType( DISABLED_HELD ) )
  1456. {
  1457. continue; // don't bother telling the occupants to move.
  1458. }
  1459. if( (*i)->isKindOf( KINDOF_IMMOBILE ) )
  1460. {
  1461. continue;
  1462. }
  1463. if( (*i)->getAI()==NULL )
  1464. {
  1465. continue;
  1466. }
  1467. if ((*i)->isKindOf(KINDOF_INFANTRY) && didInfantry) {
  1468. continue;
  1469. }
  1470. if ((*i)->isKindOf(KINDOF_VEHICLE) && didVehicles)
  1471. {
  1472. if( (*i)->getAI()->isDoingGroundMovement() )
  1473. {
  1474. Object *obj = (*i);
  1475. if( !obj->isKindOf( KINDOF_CLIFF_JUMPER ) )
  1476. {
  1477. //Not a cliff-jumper-offer unit.
  1478. continue;
  1479. }
  1480. }
  1481. }
  1482. Coord3D unitPos = *((*i)->getPosition());
  1483. TheAI->pathfinder()->removeGoal(*i);
  1484. dx = unitPos.x - pos->x;
  1485. dy = unitPos.y - pos->y;
  1486. // adjust so units are sorted first by move priority.
  1487. Real adjust = 0;
  1488. #if 0 // Nope. jba.
  1489. LocomotorPriority movePriority = LOCO_MOVES_FRONT;
  1490. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  1491. if (ai->getCurLocomotor()) {
  1492. movePriority = ai->getCurLocomotor()->getMovePriority();
  1493. if (movePriority == LOCO_MOVES_MIDDLE) {
  1494. adjust = 100*100*PATHFIND_CELL_SIZE_F*PATHFIND_CELL_SIZE_F;
  1495. } else if (movePriority == LOCO_MOVES_BACK) {
  1496. adjust = 200*200*PATHFIND_CELL_SIZE_F*PATHFIND_CELL_SIZE_F;
  1497. }
  1498. }
  1499. #endif
  1500. iter->insert((*i), adjust + dx*dx+dy*dy);
  1501. }
  1502. Coord3D goalPos = *pos;
  1503. iter->sort(ITER_SORTED_NEAR_TO_FAR);
  1504. // Works better if you let the near units get the first paths... jba.
  1505. // Move the ones nearest the goal first. Reduces collision problems later.
  1506. Object *theUnit;
  1507. Bool firstUnit = true;
  1508. for (theUnit = iter->first(); theUnit; theUnit = iter->next())
  1509. {
  1510. theUnit->setFormationID(NO_FORMATION_ID);
  1511. AIUpdateInterface *ai = theUnit->getAIUpdateInterface();
  1512. if (firstUnit) {
  1513. if (isFormation) {
  1514. Coord2D v;
  1515. theUnit->getFormationOffset(&v);
  1516. goalPos.x -= v.x;
  1517. goalPos.y -= v.y;
  1518. } else {
  1519. center = *theUnit->getPosition();
  1520. }
  1521. firstUnit = false;
  1522. }
  1523. computeIndividualDestination( &dest, &goalPos, theUnit, &center, isFormation );
  1524. if( cmdSource == CMD_FROM_PLAYER && theUnit->getStatusBits().test( OBJECT_STATUS_CAN_STEALTH ) && ai->canAutoAcquire() )
  1525. {
  1526. //When ordering a combat stealth unit to move, there is a single special case we want to handle.
  1527. //When a stealth unit is currently not stealthed and doesn't autoacquire while stealthed,
  1528. //then when the player specifically orders the unit to stop, we want to not autoacquire until
  1529. //he is able to stealth again. Of course, if he's detected, then don't bother trying.
  1530. if( !theUnit->getStatusBits().test( OBJECT_STATUS_STEALTHED ) && !theUnit->getStatusBits().test( OBJECT_STATUS_DETECTED ) )
  1531. {
  1532. //Not stealthed, not detected -- so do auto-acquire while stealthed?
  1533. if( !ai->canAutoAcquireWhileStealthed() )
  1534. {
  1535. StealthUpdate *stealth = theUnit->getStealth();
  1536. if( stealth )
  1537. {
  1538. //Delay the mood check time (for autoacquire) until after the unit can stealth again.
  1539. UnsignedInt stealthFrames = stealth->getStealthDelay();
  1540. //Skew it a little due to having a large group selected.
  1541. UnsignedInt randomFrames = GameLogicRandomValue( 0, LOGICFRAMES_PER_SECOND );
  1542. ai->setNextMoodCheckTime( TheGameLogic->getFrame() + stealthFrames + randomFrames );
  1543. }
  1544. }
  1545. }
  1546. }
  1547. if( !addWaypoint )
  1548. {
  1549. ai->aiMoveToPosition( &dest, cmdSource );
  1550. }
  1551. else
  1552. {
  1553. ai->aiFollowPathAppend(&dest, cmdSource);
  1554. }
  1555. }
  1556. }
  1557. //-------------------------------------------------------------------------------------------------
  1558. // AI Command Interface implementation for AIGroup
  1559. //
  1560. /**
  1561. * Scatter
  1562. */
  1563. void AIGroup::groupScatter( CommandSourceType cmdSource )
  1564. {
  1565. if (m_dirty)
  1566. recompute();
  1567. std::list<Object *>::iterator i;
  1568. // compute current centroid of the team
  1569. Coord3D center;
  1570. Coord2D min;
  1571. Coord2D max;
  1572. Coord3D dest;
  1573. getMinMaxAndCenter( &min, &max, &center );
  1574. // Move.
  1575. MemoryPoolObjectHolder iterHolder;
  1576. SimpleObjectIterator *iter = newInstance(SimpleObjectIterator);
  1577. iterHolder.hold(iter);
  1578. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  1579. {
  1580. Real dx, dy;
  1581. if ((*i)->isDisabledByType( DISABLED_HELD ) )
  1582. {
  1583. continue; // don't bother telling the occupants to move.
  1584. }
  1585. if( (*i)->isKindOf( KINDOF_IMMOBILE ) )
  1586. {
  1587. continue;
  1588. }
  1589. if( (*i)->getAI()==NULL )
  1590. {
  1591. continue;
  1592. }
  1593. Coord3D unitPos = *((*i)->getPosition());
  1594. TheAI->pathfinder()->removeGoal(*i);
  1595. dx = unitPos.x - center.x;
  1596. dy = unitPos.y - center.y;
  1597. iter->insert((*i), dx*dx+dy*dy);
  1598. }
  1599. iter->sort(ITER_SORTED_FAR_TO_NEAR);
  1600. Object *theUnit;
  1601. for (theUnit = iter->first(); theUnit; theUnit = iter->next())
  1602. {
  1603. center.x -= 0.01f;
  1604. AIUpdateInterface *ai = theUnit->getAIUpdateInterface();
  1605. Coord3D unitPos = *theUnit->getPosition();
  1606. Coord2D delta;
  1607. dest = unitPos;
  1608. delta.x = unitPos.x - center.x;
  1609. delta.y = unitPos.y - center.y;
  1610. delta.normalize();
  1611. dest.x += delta.x*4*theUnit->getGeometryInfo().getBoundingCircleRadius();
  1612. dest.y += delta.y*4*theUnit->getGeometryInfo().getBoundingCircleRadius();
  1613. ai->aiMoveToPosition( &dest, cmdSource );
  1614. }
  1615. }
  1616. const Real CIRCLE = ( 2.0f * PI );
  1617. void getHelicopterOffset( Coord3D& posOut, Int idx )
  1618. {
  1619. if (idx == 0)
  1620. return;
  1621. Real assumedHeliDiameter = 70.0f;
  1622. Real radius = assumedHeliDiameter;
  1623. Real circumference = radius * CIRCLE;
  1624. Real angle = 0;
  1625. Real angleBetweenEachChopper = assumedHeliDiameter / circumference * CIRCLE;
  1626. for (Int h = 1; h < idx; ++h )
  1627. {
  1628. angle += angleBetweenEachChopper;
  1629. if ( angle > CIRCLE )
  1630. {
  1631. radius += assumedHeliDiameter;
  1632. circumference = radius * CIRCLE;
  1633. angleBetweenEachChopper = assumedHeliDiameter / circumference * CIRCLE;
  1634. angle -= CIRCLE;
  1635. }
  1636. }
  1637. Coord3D tempCtr = posOut;
  1638. posOut.x = tempCtr.x + (sin(angle) * radius);
  1639. posOut.y = tempCtr.y + (cos(angle) * radius);
  1640. }
  1641. /**
  1642. * Move to given position(s), tightening the formation
  1643. */
  1644. void AIGroup::groupTightenToPosition( const Coord3D *pos, Bool addWaypoint, CommandSourceType cmdSource )
  1645. {
  1646. //Kris: Disabled (because its not used to make a logical difference)
  1647. //Bool outsideOfBounds = true;
  1648. Coord3D center;
  1649. Coord2D min;
  1650. Coord2D max;
  1651. if( cmdSource == CMD_FROM_PLAYER && TheGlobalData->m_groupMoveClickToGatherFactor > 0.0f )
  1652. {
  1653. getMinMaxAndCenter( &min, &max, &center );
  1654. //Kris: Disabled (because its not used to make a logical difference)
  1655. //if( Coord3DInsideRect2D( pos, &min, &max ) )
  1656. //{
  1657. // outsideOfBounds = FALSE;
  1658. //}
  1659. }
  1660. // Tighten.
  1661. MemoryPoolObjectHolder iterHolder;
  1662. SimpleObjectIterator *iter = newInstance(SimpleObjectIterator);
  1663. iterHolder.hold(iter);
  1664. std::list<Object *>::iterator i;
  1665. for( i = m_memberList.begin(); i != m_memberList.end(); ++i ) {
  1666. Real dx, dy;
  1667. Coord3D unitPos = *((*i)->getPosition());
  1668. if ((*i)->isDisabledByType( DISABLED_HELD ) )
  1669. {
  1670. continue; // don't bother telling the occupants to move.
  1671. }
  1672. if( (*i)->isKindOf( KINDOF_IMMOBILE ) )
  1673. {
  1674. continue;
  1675. }
  1676. if( (*i)->getAI()==NULL )
  1677. {
  1678. continue;
  1679. }
  1680. dx = unitPos.x - pos->x;
  1681. dy = unitPos.y - pos->y;
  1682. iter->insert((*i), dx*dx+dy*dy);
  1683. }
  1684. iter->sort(ITER_SORTED_NEAR_TO_FAR);
  1685. // Works better if you let the near units get the first paths... jba.
  1686. // Need a special case for helicopters, which do tighten when in groups
  1687. // but who do not reserve ground when they pathfind
  1688. // so we will send each new helicopter found in this list to a discrete
  1689. // offset from 'pos' from the s_helicopterFormation table
  1690. // a more elegant solution should have been added to AIPathfind, but given
  1691. // the late date, this is much safer.
  1692. Int heliIdx = 0;
  1693. Object *theUnit;
  1694. for (theUnit = iter->first(); theUnit; theUnit = iter->next())
  1695. {
  1696. AIUpdateInterface *ai = theUnit->getAIUpdateInterface();
  1697. if( !addWaypoint )
  1698. {
  1699. if ( theUnit->isKindOf( KINDOF_PRODUCED_AT_HELIPAD ) ) //NEW
  1700. {
  1701. Coord3D heliOffs = *pos;
  1702. getHelicopterOffset( heliOffs, heliIdx++ );
  1703. ai->aiTightenToPosition( &heliOffs, CMD_FROM_AI );//NEW
  1704. }
  1705. else
  1706. ai->aiTightenToPosition( pos, cmdSource );
  1707. }
  1708. else
  1709. {
  1710. ai->aiFollowPathAppend(pos, cmdSource);
  1711. }
  1712. }
  1713. for (theUnit = iter->first(); theUnit; theUnit = iter->next())
  1714. {
  1715. Coord3D unitPos = *theUnit->getPosition();
  1716. TheAI->pathfinder()->updatePos(theUnit, &unitPos);
  1717. }
  1718. }
  1719. /**
  1720. * Start following the path from the given point
  1721. */
  1722. void AIGroup::groupFollowWaypointPath( const Waypoint *way, CommandSourceType cmdSource )
  1723. {
  1724. std::list<Object *>::iterator i;
  1725. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  1726. {
  1727. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  1728. if (ai)
  1729. {
  1730. ai->aiFollowWaypointPath( way, cmdSource );
  1731. }
  1732. }
  1733. }
  1734. /**
  1735. * Start following the path from the given point
  1736. */
  1737. void AIGroup::groupFollowWaypointPathExact( const Waypoint *way, CommandSourceType cmdSource )
  1738. {
  1739. std::list<Object *>::iterator i;
  1740. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  1741. {
  1742. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  1743. if (ai)
  1744. {
  1745. ai->aiFollowWaypointPathExact( way, cmdSource );
  1746. }
  1747. }
  1748. }
  1749. /**
  1750. * Move to given position and unload transports.
  1751. */
  1752. void AIGroup::groupMoveToAndEvacuate( const Coord3D *pos, CommandSourceType cmdSource )
  1753. {
  1754. std::list<Object *>::iterator i;
  1755. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  1756. {
  1757. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  1758. if (ai)
  1759. {
  1760. ai->aiMoveToAndEvacuate( pos, cmdSource );
  1761. }
  1762. }
  1763. }
  1764. /**
  1765. * Move to given position and unload transports.
  1766. * transport returns and deletes itself.
  1767. */
  1768. void AIGroup::groupMoveToAndEvacuateAndExit( const Coord3D *pos, CommandSourceType cmdSource )
  1769. {
  1770. std::list<Object *>::iterator i;
  1771. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  1772. {
  1773. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  1774. if (ai)
  1775. {
  1776. ai->aiMoveToAndEvacuateAndExit( pos, cmdSource );
  1777. }
  1778. }
  1779. }
  1780. /**
  1781. * Start following the path from the given point
  1782. */
  1783. void AIGroup::groupFollowWaypointPathAsTeam( const Waypoint *way, CommandSourceType cmdSource )
  1784. {
  1785. std::list<Object *>::iterator i;
  1786. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  1787. {
  1788. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  1789. if (ai)
  1790. {
  1791. ai->aiFollowWaypointPathAsTeam( way, cmdSource );
  1792. }
  1793. }
  1794. }
  1795. /**
  1796. * Start following the path from the given point
  1797. */
  1798. void AIGroup::groupFollowWaypointPathAsTeamExact( const Waypoint *way, CommandSourceType cmdSource )
  1799. {
  1800. std::list<Object *>::iterator i;
  1801. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  1802. {
  1803. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  1804. if (ai)
  1805. {
  1806. ai->aiFollowWaypointPathExactAsTeam( way, cmdSource );
  1807. }
  1808. }
  1809. }
  1810. //Callback for groupIdle -- contained buildings.
  1811. void makeMemberStop( Object *obj, void* userData )
  1812. {
  1813. CommandSourceType cmdSource = *((CommandSourceType*)userData);
  1814. if( obj )
  1815. {
  1816. AIUpdateInterface *ai = obj->getAI();
  1817. if( ai )
  1818. {
  1819. ai->aiIdle( cmdSource );
  1820. }
  1821. }
  1822. }
  1823. /**
  1824. * Enter the idle state.
  1825. */
  1826. void AIGroup::groupIdle(CommandSourceType cmdSource)
  1827. {
  1828. std::list<Object *>::iterator i;
  1829. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  1830. {
  1831. Object *obj = *i;
  1832. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  1833. if (ai)
  1834. {
  1835. ai->aiIdle(cmdSource);
  1836. if( cmdSource == CMD_FROM_PLAYER && obj->getStatusBits().test( OBJECT_STATUS_CAN_STEALTH ) && ai->canAutoAcquire() )
  1837. {
  1838. //When ordering a combat stealth unit to stop, there is a single special case we want to handle.
  1839. //When a stealth unit is currently not stealthed and doesn't autoacquire while stealthed,
  1840. //then when the player specifically orders the unit to stop, we want to not autoacquire until
  1841. //he is able to stealth again. Of course, if he's detected, then don't bother trying.
  1842. if( !obj->getStatusBits().test( OBJECT_STATUS_STEALTHED ) && !obj->getStatusBits().test( OBJECT_STATUS_DETECTED ) )
  1843. {
  1844. //Not stealthed, not detected -- so do auto-acquire while stealthed?
  1845. if( !ai->canAutoAcquireWhileStealthed() )
  1846. {
  1847. StealthUpdate *stealth = obj->getStealth();
  1848. if( stealth )
  1849. {
  1850. //Delay the mood check time (for autoacquire) until after the unit can stealth again.
  1851. UnsignedInt stealthFrames = stealth->getStealthDelay();
  1852. //Skew it a little due to having a large group selected.
  1853. UnsignedInt randomFrames = GameLogicRandomValue( 0, LOGICFRAMES_PER_SECOND );
  1854. ai->setNextMoodCheckTime( TheGameLogic->getFrame() + stealthFrames + randomFrames );
  1855. }
  1856. }
  1857. }
  1858. }
  1859. }
  1860. else
  1861. {
  1862. //Handle garrisoned buildings.
  1863. ContainModuleInterface *contain = obj->getContain();
  1864. if( contain )
  1865. {
  1866. contain->iterateContained( makeMemberStop, &cmdSource, false );
  1867. }
  1868. }
  1869. //Also handle slaves. If we have slaves, then order them to stop too!
  1870. SpawnBehaviorInterface *spawnInterface = obj->getSpawnBehaviorInterface();
  1871. if( spawnInterface )
  1872. {
  1873. spawnInterface->orderSlavesToGoIdle( cmdSource );
  1874. //Do we need to delay mood check?
  1875. }
  1876. }
  1877. }
  1878. /**
  1879. * Follow the path defined by the given array of points
  1880. */
  1881. void AIGroup::groupFollowPath( const std::vector<Coord3D>* path, Object *ignoreObject, CommandSourceType cmdSource )
  1882. {
  1883. }
  1884. /**
  1885. * Attack given object
  1886. */
  1887. /**
  1888. * Attack given object
  1889. */
  1890. void AIGroup::groupAttackObjectPrivate( Bool forced, Object *victim, Int maxShotsToFire, CommandSourceType cmdSource )
  1891. {
  1892. if (!victim) {
  1893. // Hard to kill em if they're already dead. jba
  1894. return;
  1895. }
  1896. Coord3D victimPos = *victim->getPosition();
  1897. MemoryPoolObjectHolder iterHolder;
  1898. SimpleObjectIterator *iter = newInstance(SimpleObjectIterator);
  1899. iterHolder.hold(iter);
  1900. std::list<Object *>::iterator i;
  1901. for( i = m_memberList.begin(); i != m_memberList.end(); ++i ) {
  1902. Real dx, dy;
  1903. Coord3D unitPos = *((*i)->getPosition());
  1904. if ((*i)->isDisabledByType( DISABLED_HELD ) )
  1905. {
  1906. continue; // don't bother telling the occupants to move.
  1907. }
  1908. dx = unitPos.x - victimPos.x;
  1909. dy = unitPos.y - victimPos.y;
  1910. iter->insert((*i), dx*dx+dy*dy);
  1911. }
  1912. iter->sort(ITER_SORTED_NEAR_TO_FAR);
  1913. // Works better if you let the near units get the first paths... jba.
  1914. Object *theUnit;
  1915. for (theUnit = iter->first(); theUnit; theUnit = iter->next())
  1916. {
  1917. //Determine if this object is a garrisoned container capable of firing!
  1918. //If so, order everyone inside to attack as well!
  1919. ContainModuleInterface *contain = theUnit->getContain();
  1920. if( contain && contain->isPassengerAllowedToFire() )
  1921. {
  1922. //Loop through each member and order them to attack the same target (if possible)
  1923. const ContainedItemsList* items = contain->getContainedItemsList();
  1924. if (items)
  1925. {
  1926. for( ContainedItemsList::const_iterator it = items->begin(); it != items->end(); ++it )
  1927. {
  1928. Object* garrisonedMember = *it;
  1929. CanAttackResult result = garrisonedMember->getAbleToAttackSpecificObject( forced ? ATTACK_NEW_TARGET_FORCED : ATTACK_NEW_TARGET, victim, cmdSource );
  1930. if( result == ATTACKRESULT_POSSIBLE || result == ATTACKRESULT_POSSIBLE_AFTER_MOVING )
  1931. {
  1932. AIUpdateInterface *memberAI = garrisonedMember->getAI();
  1933. if( memberAI )
  1934. {
  1935. if (forced)
  1936. memberAI->aiForceAttackObject( victim, maxShotsToFire, cmdSource );
  1937. else
  1938. memberAI->aiAttackObject( victim, maxShotsToFire, cmdSource );
  1939. }
  1940. }
  1941. }
  1942. }
  1943. }
  1944. //Do a check to see if we have a hive object that has slaved objects.
  1945. SpawnBehaviorInterface *spawnInterface = theUnit->getSpawnBehaviorInterface();
  1946. if( spawnInterface && !spawnInterface->doSlavesHaveFreedom() )
  1947. {
  1948. spawnInterface->orderSlavesToAttackTarget( victim, maxShotsToFire, cmdSource );
  1949. }
  1950. //Order the specific group object to attack!
  1951. AIUpdateInterface *ai = theUnit->getAIUpdateInterface();
  1952. if( ai && theUnit != victim )
  1953. {
  1954. if (forced)
  1955. ai->aiForceAttackObject( victim, maxShotsToFire, cmdSource );
  1956. else
  1957. ai->aiAttackObject( victim, maxShotsToFire, cmdSource );
  1958. }
  1959. }
  1960. }
  1961. /**
  1962. * Attack the given team
  1963. */
  1964. void AIGroup::groupAttackTeam( const Team *team, Int maxShotsToFire, CommandSourceType cmdSource )
  1965. {
  1966. if (!team) {
  1967. // Hard to kill em if they're already dead. jba
  1968. return;
  1969. }
  1970. std::list<Object *>::iterator i;
  1971. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  1972. {
  1973. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  1974. if (ai)
  1975. {
  1976. ai->aiAttackTeam( team, maxShotsToFire, cmdSource );
  1977. }
  1978. }
  1979. }
  1980. /**
  1981. * Attack given spot
  1982. */
  1983. void AIGroup::groupAttackPosition( const Coord3D *pos, Int maxShotsToFire, CommandSourceType cmdSource )
  1984. {
  1985. Coord3D attackPos;
  1986. if( pos )
  1987. {
  1988. attackPos = *pos;
  1989. }
  1990. std::list<Object *>::iterator i;
  1991. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  1992. {
  1993. if( !pos )
  1994. {
  1995. //If you specify a NULL position, it means you are attacking your own location.
  1996. attackPos.set( (*i)->getPosition() );
  1997. }
  1998. //This code allows garrisoned buildings to force attack a ground position
  1999. //-----------------------------------------------------------------------
  2000. //Determine if this object is a garrisoned container capable of firing!
  2001. //If so, order everyone inside to attack as well!
  2002. ContainModuleInterface *contain = (*i)->getContain();
  2003. if( contain && contain->isPassengerAllowedToFire() )
  2004. {
  2005. //Loop through each member and order them to attack the same target (if possible)
  2006. const ContainedItemsList* items = contain->getContainedItemsList();
  2007. if (items)
  2008. {
  2009. for( ContainedItemsList::const_iterator it = items->begin(); it != items->end(); ++it )
  2010. {
  2011. Object* garrisonedMember = *it;
  2012. CanAttackResult result = garrisonedMember->getAbleToUseWeaponAgainstTarget( ATTACK_NEW_TARGET, NULL, &attackPos, cmdSource ) ;
  2013. if( result == ATTACKRESULT_POSSIBLE || result == ATTACKRESULT_POSSIBLE_AFTER_MOVING )
  2014. {
  2015. AIUpdateInterface *memberAI = garrisonedMember->getAI();
  2016. if( memberAI )
  2017. {
  2018. memberAI->aiAttackPosition( &attackPos, maxShotsToFire, cmdSource );
  2019. }
  2020. }
  2021. }
  2022. }
  2023. }
  2024. //Also handle slaves. If we have slaves, then order them to stop too!
  2025. SpawnBehaviorInterface *spawnInterface = (*i)->getSpawnBehaviorInterface();
  2026. if( spawnInterface && !spawnInterface->doSlavesHaveFreedom() )
  2027. {
  2028. spawnInterface->orderSlavesToAttackPosition( &attackPos, maxShotsToFire, cmdSource );
  2029. }
  2030. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2031. if (ai)
  2032. {
  2033. ai->aiAttackPosition( &attackPos, maxShotsToFire, cmdSource );
  2034. }
  2035. }
  2036. }
  2037. /**
  2038. * Attack move to a location
  2039. */
  2040. void AIGroup::groupAttackMoveToPosition( const Coord3D *pos, Int maxShotsToFire, CommandSourceType cmdSource )
  2041. {
  2042. std::list<Object *>::iterator i;
  2043. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2044. {
  2045. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2046. if (ai)
  2047. {
  2048. if ((*i)->isAbleToAttack())
  2049. ai->aiAttackMoveToPosition( pos, maxShotsToFire, cmdSource );
  2050. else
  2051. ai->aiMoveToPosition( pos, cmdSource );
  2052. }
  2053. }
  2054. }
  2055. /**
  2056. * Begin "seek and destroy"
  2057. */
  2058. void AIGroup::groupHunt( CommandSourceType cmdSource )
  2059. {
  2060. std::list<Object *>::iterator i;
  2061. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2062. {
  2063. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2064. if (ai)
  2065. {
  2066. ai->aiHunt( cmdSource );
  2067. }
  2068. }
  2069. }
  2070. /**
  2071. * Repair the given object
  2072. */
  2073. void AIGroup::groupRepair( Object *obj, CommandSourceType cmdSource )
  2074. {
  2075. std::list<Object *>::iterator i;
  2076. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2077. {
  2078. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2079. if (ai)
  2080. {
  2081. ai->aiRepair( obj, cmdSource );
  2082. }
  2083. }
  2084. }
  2085. /**
  2086. * Resume construction on object
  2087. */
  2088. void AIGroup::groupResumeConstruction( Object *obj, CommandSourceType cmdSource )
  2089. {
  2090. std::list<Object *>::iterator i;
  2091. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2092. {
  2093. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2094. if (ai)
  2095. {
  2096. ai->aiResumeConstruction( obj, cmdSource );
  2097. }
  2098. }
  2099. }
  2100. /**
  2101. * Get healed at the heal depot
  2102. */
  2103. void AIGroup::groupGetHealed( Object *healDepot, CommandSourceType cmdSource )
  2104. {
  2105. std::list<Object *>::iterator i;
  2106. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2107. {
  2108. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2109. if (ai)
  2110. {
  2111. ai->aiGetHealed( healDepot, cmdSource );
  2112. }
  2113. }
  2114. }
  2115. /**
  2116. * Get repaired at the repair depot
  2117. */
  2118. void AIGroup::groupGetRepaired( Object *repairDepot, CommandSourceType cmdSource )
  2119. {
  2120. std::list<Object *>::iterator i;
  2121. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2122. {
  2123. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2124. if (ai)
  2125. {
  2126. ai->aiGetRepaired( repairDepot, cmdSource );
  2127. }
  2128. }
  2129. }
  2130. /**
  2131. * Enter the given object
  2132. */
  2133. void AIGroup::groupEnter( Object *obj, CommandSourceType cmdSource )
  2134. {
  2135. std::list<Object *>::iterator i;
  2136. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2137. {
  2138. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2139. if (ai)
  2140. {
  2141. ai->aiEnter( obj, cmdSource );
  2142. }
  2143. }
  2144. }
  2145. /**
  2146. * Get near given object and wait for enter clearance
  2147. */
  2148. void AIGroup::groupDock( Object *obj, CommandSourceType cmdSource )
  2149. {
  2150. std::list<Object *>::iterator i;
  2151. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2152. {
  2153. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2154. if (ai)
  2155. {
  2156. ai->aiDock( obj, cmdSource );
  2157. }
  2158. }
  2159. }
  2160. /**
  2161. * Get out of whatever it is inside of
  2162. */
  2163. void AIGroup::groupExit( Object *objectToExit, CommandSourceType cmdSource )
  2164. {
  2165. std::list<Object *>::iterator i;
  2166. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2167. {
  2168. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2169. if (ai)
  2170. {
  2171. ai->aiExit( objectToExit, cmdSource );
  2172. }
  2173. }
  2174. }
  2175. /**
  2176. * Empty its contents
  2177. */
  2178. void AIGroup::groupEvacuate( CommandSourceType cmdSource )
  2179. {
  2180. std::list<Object *>::iterator i;
  2181. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2182. {
  2183. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2184. if (ai)
  2185. {
  2186. if( (*i)->isKindOf( KINDOF_AIRCRAFT ) && (*i)->isAirborneTarget() )
  2187. {
  2188. //Calculate the highest point on the ground to drop off troops (chinook or other air transports)
  2189. Coord3D pos = *((*i)->getPosition());
  2190. PathfindLayerEnum layerAtDest = TheTerrainLogic->getHighestLayerForDestination( &pos );
  2191. pos.z = TheTerrainLogic->getLayerHeight( pos.x, pos.y, layerAtDest );
  2192. ai->aiMoveToAndEvacuate( &pos, cmdSource );
  2193. }
  2194. else
  2195. {
  2196. ai->aiEvacuate( FALSE, cmdSource );
  2197. }
  2198. }
  2199. else if( (*i)->isKindOf( KINDOF_STRUCTURE ) )
  2200. {
  2201. //Buildings don't normally have AIUpdateInterfaces. In this special
  2202. //case, simple call the function directly. Special powers work in a similar
  2203. //manner.
  2204. ContainModuleInterface *contain = (*i)->getContain();
  2205. if( contain )
  2206. {
  2207. contain->orderAllPassengersToExit( cmdSource, FALSE );
  2208. }
  2209. }
  2210. }
  2211. }
  2212. /**
  2213. * Execute railed transport behavior
  2214. */
  2215. void AIGroup::groupExecuteRailedTransport( CommandSourceType cmdSource )
  2216. {
  2217. std::list<Object *>::iterator i;
  2218. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2219. {
  2220. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2221. if( ai )
  2222. ai->aiExecuteRailedTransport( cmdSource );
  2223. } // end for i
  2224. } // end groupExecuteRailedTransport
  2225. ///< life altering state change, if this AI can do it
  2226. void AIGroup::groupGoProne( const DamageInfo *damageInfo, CommandSourceType cmdSource )
  2227. {
  2228. std::list<Object *>::iterator i;
  2229. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2230. {
  2231. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2232. if (ai)
  2233. {
  2234. ai->aiGoProne( damageInfo, cmdSource );
  2235. }
  2236. }
  2237. }
  2238. /**
  2239. * Guard the given spot
  2240. */
  2241. void AIGroup::groupGuardPosition( const Coord3D *pos, GuardMode guardMode, CommandSourceType cmdSource )
  2242. {
  2243. if (!pos) {
  2244. return;
  2245. }
  2246. std::list<Object *>::iterator i;
  2247. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2248. {
  2249. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2250. if (ai)
  2251. {
  2252. ai->aiGuardPosition( pos, guardMode, cmdSource );
  2253. }
  2254. }
  2255. }
  2256. /**
  2257. * Guard the given object
  2258. */
  2259. void AIGroup::groupGuardObject( Object *objToGuard, GuardMode guardMode, CommandSourceType cmdSource )
  2260. {
  2261. if (!objToGuard) {
  2262. return;
  2263. }
  2264. std::list<Object *>::iterator i;
  2265. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2266. {
  2267. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2268. if (ai)
  2269. {
  2270. ai->aiGuardObject( objToGuard, guardMode, cmdSource );
  2271. }
  2272. }
  2273. }
  2274. /**
  2275. * Guard the given area
  2276. */
  2277. void AIGroup::groupGuardArea( const PolygonTrigger *areaToGuard, GuardMode guardMode, CommandSourceType cmdSource )
  2278. {
  2279. if (!areaToGuard) {
  2280. return;
  2281. }
  2282. std::list<Object *>::iterator i;
  2283. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2284. {
  2285. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2286. if (ai)
  2287. {
  2288. ai->aiGuardArea( areaToGuard, guardMode, cmdSource );
  2289. }
  2290. }
  2291. }
  2292. /**
  2293. * Attack the given area
  2294. */
  2295. void AIGroup::groupAttackArea( const PolygonTrigger *areaToGuard, CommandSourceType cmdSource )
  2296. {
  2297. if (!areaToGuard) {
  2298. return;
  2299. }
  2300. std::list<Object *>::iterator i;
  2301. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2302. {
  2303. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2304. if (ai)
  2305. {
  2306. ai->aiAttackArea( areaToGuard, cmdSource );
  2307. }
  2308. }
  2309. }
  2310. void AIGroup::groupHackInternet( CommandSourceType cmdSource ) ///< Begin hacking the internet for free cash from the heavens.
  2311. {
  2312. std::list<Object *>::iterator i;
  2313. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2314. {
  2315. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2316. if (ai)
  2317. {
  2318. ai->aiHackInternet( cmdSource );
  2319. }
  2320. }
  2321. }
  2322. void AIGroup::groupCreateFormation( CommandSourceType cmdSource ) ///< Create a formation.
  2323. {
  2324. Coord3D center;
  2325. Coord2D min;
  2326. Coord2D max;
  2327. Bool isFormation = getMinMaxAndCenter( &min, &max, &center );
  2328. std::list<Object *>::iterator i;
  2329. FormationID id = TheAI->getNextFormationID();
  2330. Int count = 0;
  2331. FormationID countID = NO_FORMATION_ID;
  2332. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2333. {
  2334. count++;
  2335. countID = (*i)->getFormationID();
  2336. }
  2337. if (count==1 && countID!=NO_FORMATION_ID) {
  2338. isFormation = true;
  2339. }
  2340. if (isFormation) {
  2341. id = NO_FORMATION_ID;
  2342. }
  2343. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2344. {
  2345. Object *obj = (*i);
  2346. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2347. if (ai)
  2348. {
  2349. Coord3D pos = *obj->getPosition();
  2350. Coord2D offset;
  2351. offset.x = pos.x - center.x;
  2352. offset.y = pos.y - center.y;
  2353. obj->setFormationID(id);
  2354. obj->setFormationOffset(offset);
  2355. }
  2356. }
  2357. }
  2358. /**
  2359. * The unit(s)/structure will perform it's special power -- special powers triggered by buildings
  2360. * don't use AIUpdateInterfaces!!! No special power uses an AIUpdateInterface immediately, but special
  2361. * abilities, which are derived from special powers do... and are unit triggered. Those do have AI.
  2362. */
  2363. void AIGroup::groupDoSpecialPower( UnsignedInt specialPowerID, UnsignedInt commandOptions )
  2364. {
  2365. //This is the no target, no position version.
  2366. std::list<Object *>::iterator i;
  2367. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2368. {
  2369. //Special powers do a lot of different things, but the top level stuff doesn't use
  2370. //ai interface code. It finds the special power module and calls it directly for each object.
  2371. Object *object = (*i);
  2372. const SpecialPowerTemplate *spTemplate = TheSpecialPowerStore->findSpecialPowerTemplateByID( specialPowerID );
  2373. if( spTemplate )
  2374. {
  2375. // Have to justify the execution in case someone changed their button
  2376. if( spTemplate->getRequiredScience() != SCIENCE_INVALID )
  2377. {
  2378. if( !object->getControllingPlayer()->hasScience(spTemplate->getRequiredScience()) )
  2379. continue;// Nice try, smacktard.
  2380. }
  2381. SpecialPowerModuleInterface *mod = object->getSpecialPowerModule( spTemplate );
  2382. if( mod )
  2383. {
  2384. if( TheActionManager->canDoSpecialPower( object, spTemplate, CMD_FROM_PLAYER, commandOptions ) )
  2385. {
  2386. mod->doSpecialPower( commandOptions );
  2387. object->friend_setUndetectedDefector( FALSE );// My secret is out
  2388. }
  2389. }
  2390. }
  2391. }
  2392. }
  2393. /**
  2394. * The unit(s)/structure will perform it's special power -- special powers triggered by buildings
  2395. * don't use AIUpdateInterfaces!!! No special power uses an AIUpdateInterface immediately, but special
  2396. * abilities, which are derived from special powers do... and are unit triggered. Those do have AI.
  2397. */
  2398. void AIGroup::groupDoSpecialPowerAtLocation( UnsignedInt specialPowerID, const Coord3D *location, Real angle, const Object *objectInWay, UnsignedInt commandOptions )
  2399. {
  2400. //This one requires a position
  2401. std::list<Object *>::iterator i;
  2402. for( i = m_memberList.begin(); i != m_memberList.end(); )
  2403. {
  2404. //Special powers do a lot of different things, but the top level stuff doesn't use
  2405. //ai interface code. It finds the special power module and calls it directly for each object.
  2406. Object *object = (*i);
  2407. ++i; // just in case the act of specialpowering changes this list,
  2408. // like when the rebelambush happens over the ocean, and all the rebels drown
  2409. // and, of course, their slowdeath behavior calls deselect(), which naturally
  2410. // destroys the AIGroup list, in order to keep the selection sync'ed with the group.
  2411. // M Lorenzen... 8/23/03
  2412. const SpecialPowerTemplate *spTemplate = TheSpecialPowerStore->findSpecialPowerTemplateByID( specialPowerID );
  2413. if( spTemplate )
  2414. {
  2415. // Have to justify the execution in case someone changed their button
  2416. if( spTemplate->getRequiredScience() != SCIENCE_INVALID )
  2417. {
  2418. if( !object->getControllingPlayer()->hasScience(spTemplate->getRequiredScience()) )
  2419. continue;// Nice try, smacktard.
  2420. }
  2421. SpecialPowerModuleInterface *mod = object->getSpecialPowerModule( spTemplate );
  2422. if( mod )
  2423. {
  2424. if( TheActionManager->canDoSpecialPowerAtLocation( object, location, CMD_FROM_PLAYER, spTemplate, objectInWay, commandOptions ) )
  2425. {
  2426. mod->doSpecialPowerAtLocation( location, angle, commandOptions );
  2427. object->friend_setUndetectedDefector( FALSE );// My secret is out
  2428. }
  2429. }
  2430. }
  2431. }
  2432. }
  2433. /**
  2434. * The unit(s)/structure will perform it's special power -- special powers triggered by buildings
  2435. * don't use AIUpdateInterfaces!!! No special power uses an AIUpdateInterface immediately, but special
  2436. * abilities, which are derived from special powers do... and are unit triggered. Those do have AI.
  2437. */
  2438. void AIGroup::groupDoSpecialPowerAtObject( UnsignedInt specialPowerID, Object *target, UnsignedInt commandOptions )
  2439. {
  2440. //This one requires a target
  2441. std::list<Object *>::iterator i;
  2442. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2443. {
  2444. //Special powers do a lot of different things, but the top level stuff doesn't use
  2445. //ai interface code. It finds the special power module and calls it directly for each object.
  2446. Object *object = (*i);
  2447. const SpecialPowerTemplate *spTemplate = TheSpecialPowerStore->findSpecialPowerTemplateByID( specialPowerID );
  2448. if( spTemplate )
  2449. {
  2450. // Have to justify the execution in case someone changed their button
  2451. if( spTemplate->getRequiredScience() != SCIENCE_INVALID )
  2452. {
  2453. if( !object->getControllingPlayer()->hasScience(spTemplate->getRequiredScience()) )
  2454. continue;// Nice try, smacktard.
  2455. }
  2456. SpecialPowerModuleInterface *mod = object->getSpecialPowerModule( spTemplate );
  2457. if( mod )
  2458. {
  2459. if( TheActionManager->canDoSpecialPowerAtObject( object, target, CMD_FROM_PLAYER, spTemplate, commandOptions ) )
  2460. {
  2461. mod->doSpecialPowerAtObject( target, commandOptions );
  2462. object->friend_setUndetectedDefector( FALSE );// My secret is out
  2463. }
  2464. }
  2465. }
  2466. }
  2467. }
  2468. #ifdef ALLOW_SURRENDER
  2469. void AIGroup::groupSurrender( const Object *objWeSurrenderedTo, Bool surrender, CommandSourceType cmdSource )
  2470. {
  2471. //This is currently only activated via test key
  2472. std::list<Object *>::iterator i;
  2473. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2474. {
  2475. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2476. if (ai)
  2477. {
  2478. ai->setSurrendered(objWeSurrenderedTo, surrender);
  2479. }
  2480. }
  2481. }
  2482. #endif
  2483. void AIGroup::groupCheer( CommandSourceType cmdSource )
  2484. {
  2485. //This is currently only activated via test key
  2486. std::list<Object *>::iterator i;
  2487. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2488. {
  2489. Object *object = (*i);
  2490. //This allows all special conditions states to reset after a specified delay. Assume
  2491. //only one works at a time (it'll clear any others).
  2492. object->setSpecialModelConditionState( MODELCONDITION_SPECIAL_CHEERING, LOGICFRAMES_PER_SECOND * 3 );
  2493. }
  2494. }
  2495. /**
  2496. * Sell all things in the group ... if possible
  2497. */
  2498. void AIGroup::groupSell( CommandSourceType cmdSource )
  2499. {
  2500. std::list<Object *>::iterator i, thisIterator;
  2501. Object *obj;
  2502. for( i = m_memberList.begin(); i != m_memberList.end(); /*empty*/ )
  2503. {
  2504. // work off of 'thisIterator' as we may change the contents of this list
  2505. thisIterator = i;
  2506. ++i;
  2507. // get object
  2508. obj = *thisIterator;
  2509. // try to sell object
  2510. TheBuildAssistant->sellObject( obj );
  2511. } // end for, i
  2512. }
  2513. /**
  2514. * Tell all things in the group to toggle overcharge ... if possible
  2515. */
  2516. void AIGroup::groupToggleOvercharge( CommandSourceType cmdSource )
  2517. {
  2518. std::list<Object *>::iterator i;
  2519. Object *obj;
  2520. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2521. {
  2522. // get object
  2523. obj = *i;
  2524. OverchargeBehaviorInterface *obi;
  2525. for( BehaviorModule **bmi = obj->getBehaviorModules(); *bmi; ++bmi )
  2526. {
  2527. obi = (*bmi)->getOverchargeBehaviorInterface();
  2528. if( obi )
  2529. obi->toggle();
  2530. } // end for
  2531. } // end for, i
  2532. }
  2533. #ifdef ALLOW_SURRENDER
  2534. /**
  2535. * Pick up prisoners of war
  2536. */
  2537. void AIGroup::groupPickUpPrisoner( Object *prisoner, enum CommandSourceType cmdSource )
  2538. {
  2539. std::list<Object *>::iterator i;
  2540. Object *obj;
  2541. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2542. {
  2543. // get object
  2544. obj = *i;
  2545. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  2546. if( ai )
  2547. ai->aiPickUpPrisoner( prisoner, cmdSource );
  2548. } // end for, i
  2549. }
  2550. #endif
  2551. #ifdef ALLOW_SURRENDER
  2552. /**
  2553. * Return to prison
  2554. */
  2555. void AIGroup::groupReturnToPrison( Object *prison, enum CommandSourceType cmdSource )
  2556. {
  2557. std::list<Object *>::iterator i;
  2558. Object *obj;
  2559. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2560. {
  2561. // get object
  2562. obj = *i;
  2563. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  2564. if( ai )
  2565. ai->aiReturnPrisoners( prison, cmdSource );
  2566. } // end for, i
  2567. }
  2568. #endif
  2569. /**
  2570. * Combat drop
  2571. */
  2572. void AIGroup::groupCombatDrop( Object *target, const Coord3D &pos, CommandSourceType cmdSource )
  2573. {
  2574. std::list<Object *>::iterator i;
  2575. Object *obj;
  2576. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2577. {
  2578. // get object
  2579. obj = *i;
  2580. // do action
  2581. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  2582. if( ai )
  2583. ai->aiCombatDrop( target, pos, cmdSource );
  2584. } // end for, i
  2585. }
  2586. //-------------------------------------------------------------------------------------
  2587. // Used by scripts to issue a command button order - Note that it's possible that some
  2588. // commands are not AI commands!
  2589. //-------------------------------------------------------------------------------------
  2590. void AIGroup::groupDoCommandButton( const CommandButton *commandButton, CommandSourceType cmdSource )
  2591. {
  2592. std::list<Object *>::iterator i;
  2593. Object *source;
  2594. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2595. {
  2596. // get object
  2597. source = *i;
  2598. source->doCommandButton( commandButton, cmdSource );
  2599. } // end for, i
  2600. }
  2601. //-------------------------------------------------------------------------------------
  2602. // Used by scripts to issue a command button order - Note that it's possible that some
  2603. // commands are not AI commands!
  2604. //-------------------------------------------------------------------------------------
  2605. void AIGroup::groupDoCommandButtonAtPosition( const CommandButton *commandButton, const Coord3D *pos, CommandSourceType cmdSource )
  2606. {
  2607. std::list<Object *>::iterator i;
  2608. Object *source;
  2609. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2610. {
  2611. // get object
  2612. source = *i;
  2613. source->doCommandButtonAtPosition( commandButton, pos, cmdSource );
  2614. } // end for, i
  2615. }
  2616. //-------------------------------------------------------------------------------------
  2617. // Used by scripts to issue a command button order - Note that it's possible that some
  2618. // commands are not AI commands!
  2619. //-------------------------------------------------------------------------------------
  2620. void AIGroup::groupDoCommandButtonUsingWaypoints( const CommandButton *commandButton, const Waypoint *way, CommandSourceType cmdSource )
  2621. {
  2622. std::list<Object *>::iterator i;
  2623. Object *source;
  2624. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2625. {
  2626. // get object
  2627. source = *i;
  2628. source->doCommandButtonUsingWaypoints( commandButton, way, cmdSource );
  2629. } // end for, i
  2630. }
  2631. //-------------------------------------------------------------------------------------
  2632. // Used by scripts to issue a command button order - Note that it's possible that some
  2633. // commands are not AI commands!
  2634. //-------------------------------------------------------------------------------------
  2635. void AIGroup::groupDoCommandButtonAtObject( const CommandButton *commandButton, Object *obj, CommandSourceType cmdSource )
  2636. {
  2637. std::list<Object *>::iterator i;
  2638. Object *source;
  2639. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2640. {
  2641. // get object
  2642. source = *i;
  2643. source->doCommandButtonAtObject( commandButton, obj, cmdSource );
  2644. } // end for, i
  2645. }
  2646. /**
  2647. * Set the behavior modifier for this agent
  2648. */
  2649. void AIGroup::setAttitude( AttitudeType tude )
  2650. {
  2651. std::list<Object *>::iterator i;
  2652. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2653. {
  2654. AIUpdateInterface *ai = (*i)->getAIUpdateInterface();
  2655. if (ai)
  2656. {
  2657. ai->setAttitude( tude );
  2658. }
  2659. }
  2660. }
  2661. /**
  2662. * Get the current behavior modifier state
  2663. */
  2664. AttitudeType AIGroup::getAttitude( void ) const
  2665. {
  2666. return AI_PASSIVE;
  2667. }
  2668. void AIGroup::setMineClearingDetail( Bool set )
  2669. {
  2670. std::list<Object *>::iterator i;
  2671. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2672. {
  2673. if (set)
  2674. (*i)->setWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);
  2675. else
  2676. (*i)->clearWeaponSetFlag(WEAPONSET_MINE_CLEARING_DETAIL);
  2677. }
  2678. }
  2679. Bool AIGroup::setWeaponLockForGroup( WeaponSlotType weaponSlot, WeaponLockType lockType )
  2680. {
  2681. Bool any = false;
  2682. std::list<Object *>::iterator i;
  2683. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2684. {
  2685. if ((*i)->setWeaponLock( weaponSlot, lockType ))
  2686. any = true;
  2687. }
  2688. return any;
  2689. }
  2690. void AIGroup::releaseWeaponLockForGroup(WeaponLockType lockType)
  2691. {
  2692. std::list<Object *>::iterator i;
  2693. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2694. {
  2695. (*i)->releaseWeaponLock(lockType);
  2696. }
  2697. }
  2698. //This function loops through the AIGroup setting the weaponset only for those units that
  2699. //have the specified weaponset. If a member doesn't have the weaponset, nothing happens for
  2700. //that unit.
  2701. void AIGroup::setWeaponSetFlag( WeaponSetType wst )
  2702. {
  2703. std::list<Object *>::iterator i;
  2704. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2705. {
  2706. Object *obj = (*i);
  2707. //First check to see if our object even has the specified weaponset. It's very
  2708. //likely that a selected group won't all have the same weaponset options, so
  2709. //only set it for those members that have it.
  2710. WeaponSetFlags flags;
  2711. flags.set( wst );
  2712. const WeaponTemplateSet* set = obj->getTemplate()->findWeaponTemplateSet( flags );
  2713. if( set )
  2714. {
  2715. obj->setWeaponSetFlag( wst );
  2716. }
  2717. }
  2718. }
  2719. void AIGroup::queueUpgrade( const UpgradeTemplate *upgrade )
  2720. {
  2721. if (!upgrade)
  2722. return;
  2723. std::list<Object *>::iterator i;
  2724. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2725. {
  2726. Object *thisMember = (*i);
  2727. // make sure that the this object can actually build the upgrade
  2728. // There is an extra check for Object type only. These are the same checks as in
  2729. // ControlCommandProcessing when the message was going out. We are just revalidating on the
  2730. // way in to stop cheaters.
  2731. if( ! TheUpgradeCenter->canAffordUpgrade( thisMember->getControllingPlayer(), upgrade, FALSE ) )
  2732. {
  2733. continue;
  2734. }
  2735. if( upgrade->getUpgradeType() == UPGRADE_TYPE_OBJECT )
  2736. {
  2737. if( thisMember->hasUpgrade( upgrade ) || !thisMember->affectedByUpgrade( upgrade ) )
  2738. continue;
  2739. }
  2740. // Ever think to check if this thing can actually build the upgrade to "stop cheaters"?
  2741. if( !thisMember->canProduceUpgrade(upgrade) )
  2742. continue;// They have faked their button; go out of sync. (Cheater will execute it, non cheater will not execute it.)
  2743. // producer must have a production update
  2744. ProductionUpdateInterface *pu = thisMember->getProductionUpdateInterface();
  2745. if( pu == NULL )
  2746. continue;
  2747. if ( pu->canQueueUpgrade( upgrade ) == CANMAKE_QUEUE_FULL )
  2748. continue;//So we don't charge them for something that we can't build... happy happy
  2749. // queue the upgrade "research"
  2750. pu->queueUpgrade( upgrade );
  2751. }
  2752. }
  2753. //------------------------------------------------------------------------------------------------------------
  2754. Bool AIGroup::isIdle( void ) const
  2755. {
  2756. Bool isIdle = true;
  2757. std::list<Object *>::const_iterator i;
  2758. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2759. {
  2760. Object *obj = *i;
  2761. if (!obj) {
  2762. continue;
  2763. }
  2764. const AIUpdateInterface *ai = obj->getAIUpdateInterface();
  2765. if (!ai) {
  2766. continue;
  2767. }
  2768. //Kris: Optimization
  2769. isIdle = ai->isIdle() || obj->isEffectivelyDead();
  2770. if( !isIdle )
  2771. {
  2772. //Don't bother continuing if even one of our members is not idle.
  2773. return false;
  2774. }
  2775. }
  2776. return isIdle;
  2777. }
  2778. //------------------------------------------------------------------------------------------------------------
  2779. //Definition of busy -- when explicitly in the busy state. Moving or attacking is not considered busy!
  2780. //------------------------------------------------------------------------------------------------------------
  2781. Bool AIGroup::isBusy( void ) const
  2782. {
  2783. Bool isBusy = true;
  2784. std::list<Object *>::const_iterator i;
  2785. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2786. {
  2787. Object *obj = *i;
  2788. if( !obj )
  2789. {
  2790. continue;
  2791. }
  2792. const AIUpdateInterface *ai = obj->getAIUpdateInterface();
  2793. if( !ai )
  2794. {
  2795. continue;
  2796. }
  2797. //Kris: Optimization
  2798. isBusy = ai->isBusy() && !obj->isEffectivelyDead();
  2799. if( !isBusy )
  2800. {
  2801. //Don't bother continuing if even one of our members is not busy.
  2802. return false;
  2803. }
  2804. }
  2805. return isBusy;
  2806. }
  2807. //------------------------------------------------------------------------------------------------------------
  2808. // return true iff all group members are dead
  2809. //------------------------------------------------------------------------------------------------------------
  2810. Bool AIGroup::isGroupAiDead( void ) const
  2811. {
  2812. Bool isDead = true;
  2813. std::list<Object *>::const_iterator i;
  2814. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2815. {
  2816. Object *obj = *i;
  2817. if (!obj) {
  2818. continue;
  2819. }
  2820. isDead = (isDead && obj->isEffectivelyDead());
  2821. }
  2822. return isDead;
  2823. }
  2824. // Returns an object that can perform the special power. Useful for making queries on the Action Manager
  2825. //------------------------------------------------------------------------------------------------------------
  2826. Object *AIGroup::getSpecialPowerSourceObject( UnsignedInt specialPowerID )
  2827. {
  2828. std::list<Object *>::iterator i;
  2829. const SpecialPowerTemplate *spTemplate = TheSpecialPowerStore->findSpecialPowerTemplateByID( specialPowerID );
  2830. if( spTemplate )
  2831. {
  2832. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2833. {
  2834. Object *object = (*i);
  2835. SpecialPowerModuleInterface *mod = object->getSpecialPowerModule( spTemplate );
  2836. if( mod )
  2837. return object;
  2838. }
  2839. }
  2840. return NULL;
  2841. }
  2842. // Returns an object that has a command button for the GUI command type.
  2843. //------------------------------------------------------------------------------------------------------------
  2844. Object *AIGroup::getCommandButtonSourceObject( GUICommandType type )
  2845. {
  2846. std::list<Object *>::iterator it;
  2847. for( it = m_memberList.begin(); it != m_memberList.end(); ++it )
  2848. {
  2849. Object *object = (*it);
  2850. if (!object) {
  2851. continue;
  2852. }
  2853. const CommandSet *commandSet = TheControlBar->findCommandSet( object->getCommandSetString() );
  2854. if (!commandSet) {
  2855. continue;
  2856. }
  2857. const CommandButton *commandButton;
  2858. for(Int i = 0; i < MAX_COMMANDS_PER_SET; ++i)
  2859. {
  2860. commandButton = commandSet->getCommandButton(i);
  2861. if(commandButton && (commandButton->getCommandType() == type)) {
  2862. return object;
  2863. }
  2864. }
  2865. }
  2866. return NULL;
  2867. }
  2868. //------------------------------------------------------------------------------------------------------------
  2869. void AIGroup::groupSetEmoticon( const AsciiString &name, Int duration )
  2870. {
  2871. std::list<Object *>::iterator i;
  2872. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2873. {
  2874. Object *object = (*i);
  2875. Drawable *draw = object->getDrawable();
  2876. if( draw )
  2877. {
  2878. draw->setEmoticon( name, duration );
  2879. }
  2880. }
  2881. }
  2882. //-----------------------------------------------------------------------------
  2883. void AIGroup::groupOverrideSpecialPowerDestination( SpecialPowerType spType, const Coord3D *loc, CommandSourceType cmdSource )
  2884. {
  2885. std::list<Object *>::iterator i;
  2886. for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
  2887. {
  2888. Object *object = (*i);
  2889. if( object )
  2890. {
  2891. SpecialPowerUpdateInterface *spuInterface = object->findSpecialPowerWithOverridableDestinationActive( spType );
  2892. if( spuInterface )
  2893. {
  2894. spuInterface->setSpecialPowerOverridableDestination( loc );
  2895. }
  2896. }
  2897. }
  2898. }
  2899. //-----------------------------------------------------------------------------
  2900. void AIGroup::crc( Xfer *xfer )
  2901. {
  2902. ObjectID id = INVALID_ID;
  2903. for (std::list<Object *>::iterator it = m_memberList.begin(); it != m_memberList.end(); ++it)
  2904. {
  2905. if (*it)
  2906. id = (*it)->getID();
  2907. xfer->xferUser(&id, sizeof(ObjectID));
  2908. CRCGEN_LOG(("CRC after AI AIGroup m_memberList for frame %d is 0x%8.8X\n", TheGameLogic->getFrame(), ((XferCRC *)xfer)->getCRC()));
  2909. }
  2910. xfer->xferUnsignedInt( &m_memberListSize );
  2911. CRCGEN_LOG(("CRC after AI AIGroup m_memberListSize for frame %d is 0x%8.8X\n", TheGameLogic->getFrame(), ((XferCRC *)xfer)->getCRC()));
  2912. id = INVALID_ID; // Used to be leader id, unused now. jba.
  2913. xfer->xferObjectID( &id );
  2914. CRCGEN_LOG(("CRC after AI AIGroup m_leader for frame %d is 0x%8.8X\n", TheGameLogic->getFrame(), ((XferCRC *)xfer)->getCRC()));
  2915. xfer->xferReal( &m_speed );
  2916. CRCGEN_LOG(("CRC after AI AIGroup m_speed for frame %d is 0x%8.8X\n", TheGameLogic->getFrame(), ((XferCRC *)xfer)->getCRC()));
  2917. xfer->xferBool( &m_dirty );
  2918. CRCGEN_LOG(("CRC after AI AIGroup m_dirty for frame %d is 0x%8.8X\n", TheGameLogic->getFrame(), ((XferCRC *)xfer)->getCRC()));
  2919. xfer->xferUnsignedInt( &m_id );
  2920. CRCGEN_LOG(("CRC after AI AIGroup m_id (%d) for frame %d is 0x%8.8X\n", m_id, TheGameLogic->getFrame(), ((XferCRC *)xfer)->getCRC()));
  2921. } // end crc
  2922. //-----------------------------------------------------------------------------
  2923. void AIGroup::xfer( Xfer *xfer )
  2924. {
  2925. // version
  2926. XferVersion currentVersion = 1;
  2927. XferVersion version = currentVersion;
  2928. xfer->xferVersion( &version, currentVersion );
  2929. } // end xfer
  2930. //-----------------------------------------------------------------------------
  2931. void AIGroup::loadPostProcess( void )
  2932. {
  2933. } // end loadPostProcess