common.cpp 98 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764
  1. #include "pch.h"
  2. #include <math.h>
  3. const char* c_pszWingName[c_widMax] =
  4. { "command",
  5. "attack",
  6. "defend",
  7. "escort",
  8. "search",
  9. "alpha",
  10. "bravo",
  11. "charlie",
  12. "delta",
  13. "echo",
  14. //"foxtrot",
  15. //"golf",
  16. //"hotel",
  17. //"india"//,
  18. //"juliet"
  19. };
  20. const CommandData c_cdAllCommands[c_cidMax] =
  21. {
  22. { "goto", "acdefaultbmp", "qudefaultbmp" },
  23. { "attack", "ackillbmp", "qukillbmp" },
  24. { "capture", "accptbmp", "qucptbmp" },
  25. { "defend", "acdefendbmp", "qudefendbmp" },
  26. { "pickup", "acpickupbmp", "qupickupbmp" },
  27. { "goto", "acgotobmp", "qugotobmp" },
  28. { "repair", "acrepairbmp", "qurepairbmp" },
  29. { "join", "acjoinbmp", "qujoinbmp" },
  30. { "mine", "acminebmp", "quminebmp" },
  31. { "build", "acbuildbmp", "qubuildbmp" }
  32. };
  33. const int c_ttTypebits[OT_modelEnd+1] =
  34. { c_ttShip, c_ttStation, c_ttMissile,
  35. c_ttMine, c_ttProbe, c_ttAsteroid, 0, c_ttWarp, c_ttTreasure, c_ttBuoy, 0, 0};
  36. float solveForImpact(const Vector& deltaP,
  37. const Vector& deltaV,
  38. float speed,
  39. float radius,
  40. Vector* direction)
  41. {
  42. assert (speed >= 0.0f);
  43. assert (direction);
  44. float t;
  45. float c = deltaP * deltaP - radius * radius;
  46. if (c <= 0.0f)
  47. {
  48. t = 0.0f;
  49. *direction = deltaP.Normalize();
  50. }
  51. else
  52. {
  53. //Solve for the time when the projectile is t * speed + radius units away from the target.
  54. // distance(t) = radius + speed * t = |deltaP + t * deltaV|
  55. // distance(t)^2 = radius ^2 + 2 * radius * speed * t + speed ^2 * t^2 = deltaP * deltaP + 2 * t * deltaV * deltaP+ t^2 * deltaV * deltaV
  56. //
  57. // 0 = deltaP^2 - radius^2 + 2 * t * (deltaV * deltaP - radius * speed) + t^2 * (deltaV^2 - speed^2)
  58. //
  59. float a = deltaV * deltaV - (speed * speed);
  60. float b = 2.0f * (deltaV * deltaP - radius * speed);
  61. float b24ac = b * b - 4.0f * a * c;
  62. if ((a == 0.0f) || ((a > 0.0f) && (b > 0.0f)) || (b24ac < 0.0f))
  63. {
  64. //No valid solution
  65. t = FLT_MAX;
  66. }
  67. else
  68. {
  69. //quadratic formula ... we only care about the smallest root > 0
  70. t = (b + (float)sqrt(b24ac)) / (-2.0f * a); //(-b-sqrt(b24ac))/(2a) Only or smallest possible positive root
  71. if (t > 0.0f) //should always be the case, but round-off error can cause problems
  72. {
  73. //Found a solution: now find the direction in which to shoot:
  74. *direction = (deltaP + t * deltaV) / (t * speed + radius); //Should be a unit vector
  75. }
  76. else
  77. {
  78. t = 0.0f;
  79. *direction = deltaP.Normalize();
  80. }
  81. }
  82. }
  83. return t;
  84. }
  85. float solveForLead(ImodelIGC* shooter,
  86. ImodelIGC* target,
  87. IweaponIGC* weapon,
  88. Vector* direction,
  89. float skill)
  90. {
  91. assert (shooter);
  92. assert (target);
  93. assert (weapon);
  94. assert (direction);
  95. const Vector& myPosition = shooter->GetPosition();
  96. const Vector& myVelocity = shooter->GetVelocity();
  97. const Orientation& myOrientation = shooter->GetOrientation();
  98. const Vector& hisPosition = target->GetPosition();
  99. const Vector& hisVelocity = target->GetVelocity() * skill;
  100. IprojectileTypeIGC* pt = weapon->GetProjectileType();
  101. assert (pt);
  102. return solveForImpact(hisPosition - (myPosition + weapon->GetPosition() * myOrientation),
  103. pt->GetAbsoluteF()
  104. ? hisVelocity
  105. : (hisVelocity - myVelocity),
  106. pt->GetSpeed(), target->GetRadius(),
  107. direction);
  108. }
  109. float turnToFace(const Vector& deltaTarget,
  110. float dt,
  111. IshipIGC* pship,
  112. ControlData* controls,
  113. float skill)
  114. {
  115. float deltaAngle;
  116. const IhullTypeIGC* pht = pship->GetHullType();
  117. assert (controls);
  118. controls->jsValues[c_axisRoll] = 0.0f; //Ships never try to roll
  119. const Orientation& myOrientation = pship->GetOrientation();
  120. double cosTurn = myOrientation.CosForward(deltaTarget);
  121. if (cosTurn <= -0.999)
  122. {
  123. //Target is almost exactly behind, just yaw
  124. //(and assume the time increment is small enough
  125. //that yawing at max is appropriate).
  126. controls->jsValues[c_axisYaw] = 1.0f;
  127. controls->jsValues[c_axisPitch] = 0.0f;
  128. deltaAngle = pi;
  129. }
  130. else
  131. {
  132. float yaw;
  133. float pitch;
  134. if (cosTurn < 0.98)
  135. {
  136. //Target is somewhere other than in directly in front
  137. deltaAngle = (float)acos(cosTurn);
  138. //Find the vector we'd like to rotate about
  139. Vector twist = CrossProduct(myOrientation.GetBackward(), deltaTarget).Normalize();
  140. yaw = -(twist * myOrientation.GetUp()) * deltaAngle;
  141. pitch = (twist * myOrientation.GetRight()) * deltaAngle;
  142. }
  143. else
  144. {
  145. //The target is almost directly in front of us (within 11 degrees or so)
  146. //which would make getting the twist axis above a little dicey
  147. //So ... instead ... get the yaw and pitch off of the angles with the right * up
  148. yaw = acos(myOrientation.CosRight(deltaTarget)) - 0.5f * pi;
  149. pitch = acos(myOrientation.CosUp(deltaTarget)) - 0.5f * pi;
  150. deltaAngle = (float)sqrt(yaw * yaw + pitch * pitch);
  151. }
  152. //Adjust the yaw and pitch by the amount we are going to drift (due to our current
  153. //turning rates).
  154. {
  155. float tm = pship->GetTorqueMultiplier();
  156. float mass = pship->GetMass();
  157. assert (mass > 0.0f);
  158. {
  159. float yawRate = pship->GetCurrentTurnRate(c_axisYaw);
  160. yaw -= (float)(skill * fabs(yawRate) * (0.5f * yawRate * mass / (tm * pht->GetTurnTorque(c_axisYaw))));
  161. }
  162. {
  163. float pitchRate = pship->GetCurrentTurnRate(c_axisPitch);
  164. pitch -= (float)(skill * fabs(pitchRate) * (0.5f * pitchRate * mass / (tm * pht->GetTurnTorque(c_axisPitch))));
  165. }
  166. }
  167. //How do we want to set the controls so that we will turn to face the desired goal
  168. {
  169. float maxYaw = dt * pht->GetMaxTurnRate(c_axisYaw);
  170. float maxPitch = dt * pht->GetMaxTurnRate(c_axisPitch);
  171. float y = yaw / maxYaw;
  172. float p = pitch / maxPitch;
  173. float d2 = (y * y + p * p);
  174. if (d2 > 1.0f)
  175. {
  176. float f = (float)(1.0 / sqrt(d2));
  177. controls->jsValues[c_axisYaw] = y * f;
  178. controls->jsValues[c_axisPitch] = p * f;
  179. }
  180. else
  181. {
  182. controls->jsValues[c_axisYaw] = y;
  183. controls->jsValues[c_axisPitch] = p;
  184. }
  185. }
  186. }
  187. return deltaAngle;
  188. }
  189. bool FindableModel(ImodelIGC* m,
  190. IsideIGC* pside,
  191. int ttMask,
  192. AbilityBitMask abmAbilities)
  193. {
  194. bool okF = false;
  195. //You never target yourself or something marked as hidden
  196. ObjectType type = m->GetObjectType();
  197. if (GetTypebits(type) & ttMask)
  198. {
  199. IsideIGC* pHisSide = m->GetSide();
  200. int sidebits = (pHisSide == NULL)
  201. ? c_ttNeutral
  202. : ((pside == pHisSide)
  203. ? c_ttFriendly
  204. : c_ttEnemy);
  205. if ((sidebits & ttMask) ||
  206. ((type == OT_probe) && ((abmAbilities & c_eabmRescueAny) != 0) &&
  207. ((IprobeIGC*)m)->GetProbeType()->HasCapability(c_eabmRescueAny)))
  208. {
  209. if (abmAbilities != 0)
  210. {
  211. switch (type)
  212. {
  213. case OT_ship:
  214. {
  215. if (((IshipIGC*)m)->GetParentShip() == NULL)
  216. {
  217. IhullTypeIGC* pht = ((IshipIGC*)m)->GetBaseHullType();
  218. okF = pht && pht->HasCapability(abmAbilities);
  219. }
  220. else
  221. okF = false;
  222. }
  223. break;
  224. case OT_station:
  225. {
  226. okF = ((IstationIGC*)m)->GetStationType()->HasCapability(abmAbilities);
  227. }
  228. break;
  229. case OT_asteroid:
  230. {
  231. okF = ((IasteroidIGC*)m)->HasCapability(abmAbilities);
  232. }
  233. break;
  234. case OT_treasure:
  235. {
  236. okF = ((ItreasureIGC*)m)->GetTreasureCode() == c_tcFlag;
  237. }
  238. break;
  239. case OT_probe:
  240. {
  241. okF = ((IprobeIGC*)m)->GetProbeType()->HasCapability(abmAbilities);
  242. }
  243. break;
  244. default:
  245. assert (false);
  246. }
  247. }
  248. else
  249. okF = ((type != OT_ship) || (((IshipIGC*)m)->GetParentShip() == NULL));
  250. }
  251. }
  252. return okF;
  253. }
  254. static bool IsFriendlyCluster(IclusterIGC* pcluster, IsideIGC* pside)
  255. {
  256. StationLinkIGC* psl = pcluster->GetStations()->first();
  257. if (psl == NULL)
  258. return false; //No stations == unfriendly
  259. bool rc = false;
  260. do
  261. {
  262. IstationIGC* ps = psl->data();
  263. if ((!ps->GetStationType()->HasCapability(c_sabmPedestal)) &&
  264. ps->SeenBySide(pside))
  265. {
  266. if (pside != ps->GetSide())
  267. return false; //enemy has a station == unfriendly
  268. rc = true;
  269. }
  270. psl = psl->next();
  271. }
  272. while (psl != NULL);
  273. //It has stations but no enemy stations ... therefore at least one friendly station
  274. return rc;
  275. }
  276. struct ClusterPosition
  277. {
  278. IclusterIGC* pcluster;
  279. const Vector* pposition;
  280. };
  281. typedef Slist_utl<ClusterPosition> CPList;
  282. typedef Slink_utl<ClusterPosition> CPLink;
  283. static bool UniqueCP(CPList* cpl, IclusterIGC* pc)
  284. {
  285. for (CPLink* l = cpl->first(); (l != NULL); l = l->next())
  286. {
  287. if (l->data().pcluster == pc)
  288. return false;
  289. }
  290. return true;
  291. }
  292. static void NewCP(CPList* cpl, IclusterIGC* pc, const Vector* pposition)
  293. {
  294. CPLink* l = new CPLink;
  295. l->data().pcluster = pc;
  296. l->data().pposition = pposition;
  297. cpl->last(l);
  298. }
  299. ImodelIGC* FindTarget(IshipIGC* pship,
  300. int ttMask,
  301. ImodelIGC* pmodelCurrent,
  302. IclusterIGC* pcluster,
  303. const Vector* pposition,
  304. const Orientation* porientation,
  305. AbilityBitMask abmAbilities,
  306. int maxDistance)
  307. {
  308. if (!pcluster)
  309. pcluster = pship->GetCluster();
  310. if (!pcluster)
  311. return NULL;
  312. IsideIGC* pside = pship ? pship->GetSide() : NULL;
  313. if (pmodelCurrent && ((pmodelCurrent == pship) ||
  314. (!FindableModel(pmodelCurrent, pside, ttMask, abmAbilities)) ||
  315. (!pship->CanSee(pmodelCurrent))))
  316. pmodelCurrent = NULL;
  317. if (((ttMask & (c_ttFront | c_ttNearest)) == 0) && !pmodelCurrent && (pship || pposition))
  318. ttMask |= c_ttNearest;
  319. //This is a hack (which works only because the lists are lists of interface pointers)
  320. //so that, if we are only looking for a single type of target, we only search the list
  321. //of those types of targets.
  322. const ModelListIGC* models = ((ttMask & c_ttAllTypes) == c_ttShip)
  323. ? (const ModelListIGC*)(pcluster->GetShips())
  324. : (((ttMask & c_ttAllTypes) == c_ttStation)
  325. ? (const ModelListIGC*)(pcluster->GetStations())
  326. : (((ttMask & c_ttAllTypes) == c_ttWarp)
  327. ? (const ModelListIGC*)(pcluster->GetWarps())
  328. : ((ttMask & c_ttAllTypes) == c_ttTreasure)
  329. ? (const ModelListIGC*)(pcluster->GetTreasures())
  330. : pcluster->GetPickableModels()));
  331. assert (models);
  332. ImodelIGC* pmodelTarget = NULL;
  333. if (models->n() != 0)
  334. {
  335. int ttBest = (ttMask & (c_ttFront | c_ttNearest | c_ttLeastTargeted));
  336. if (ttMask & (c_ttFront | c_ttNearest))
  337. {
  338. if (!pposition)
  339. pposition = &(pship->GetPosition());
  340. if ((ttMask & c_ttFront) && !porientation)
  341. porientation = &(pship->GetOrientation());
  342. }
  343. ModelLinkIGC* mLink = models->first();
  344. if (pmodelCurrent)
  345. {
  346. //Find the link (in the model list) that corresponds to the current target
  347. while ((mLink != NULL) && (mLink->data() != pmodelCurrent))
  348. mLink = mLink->next();
  349. // mLink = mLink && (mLink != models->last()) ? mLink->next() : models->first();
  350. if (ttMask&c_ttPrevious)
  351. mLink = (mLink && (mLink != models->first())) ? mLink->txen() : models->last();
  352. else
  353. mLink = (mLink && (mLink != models->last())) ? mLink->next() : models->first();
  354. }
  355. assert (mLink);
  356. float capacity;
  357. if ((abmAbilities & c_aabmMineHe3) != 0)
  358. capacity = pside->GetMission()->GetFloatConstant(c_fcidCapacityHe3);
  359. //Search through the entire container list, starting at the link beyond mLink, wrapping around
  360. //until back at mLink for a ship or station hub in the same sector as the ship.
  361. int nTargeting = 0x7fff; //surely we can do better than this
  362. float distance = FLT_MAX;
  363. ModelLinkIGC* l = mLink;
  364. do
  365. {
  366. ImodelIGC* m = l->data();
  367. //You never target yourself or something marked as hidden
  368. if ((m != pship) && ((!pship) || pship->CanSee(m)) && FindableModel(m, pside, ttMask, abmAbilities))
  369. {
  370. if (ttBest)
  371. {
  372. int n;
  373. float d;
  374. if (ttMask & (c_ttFront | c_ttNearest))
  375. {
  376. //intentionally backwards so low cosforwards == in front
  377. Vector dp = (*pposition) - m->GetPosition();
  378. d = (ttMask & c_ttFront)
  379. ? porientation->CosForward2(dp)
  380. : dp.LengthSquared();
  381. }
  382. bool bReplace;
  383. if (ttMask & c_ttLeastTargeted)
  384. {
  385. assert ((ttMask & c_ttAllTypes) == c_ttAsteroid);
  386. assert (m->GetObjectType() == OT_asteroid);
  387. n = 0;
  388. {
  389. //Count the number of drones on this side building or mining this asteroid
  390. for (ShipLinkIGC* psl = pside->GetShips()->first(); (psl != NULL); psl = psl->next())
  391. {
  392. IshipIGC* ps = psl->data();
  393. if (ps->GetPilotType() < c_ptPlayer)
  394. {
  395. if ((ps->GetCommandTarget(c_cmdAccepted) == m) &&
  396. (ps->GetCommandID(c_cmdAccepted) >= c_cidMine))
  397. {
  398. n++;
  399. }
  400. }
  401. }
  402. }
  403. bReplace = ((n < nTargeting) || ((n == nTargeting) && (d < distance)));
  404. if (bReplace && ((abmAbilities & c_aabmMineHe3) != 0))
  405. {
  406. float ore = ((IasteroidIGC*)m)->GetOre() - float(n) * capacity;
  407. if (ore <= capacity * 0.25f)
  408. bReplace = false;
  409. }
  410. }
  411. else
  412. bReplace = (d < distance);
  413. if (bReplace)
  414. {
  415. nTargeting = n;
  416. distance = d;
  417. //Don't replace a valid (if less optimal) target with the
  418. //current one unless there is no target (pmodelCurrent is
  419. //always checked last).
  420. if ((m != pmodelCurrent) || (pmodelTarget == NULL))
  421. pmodelTarget = m;
  422. }
  423. }
  424. else
  425. {
  426. //and that is all we need ...
  427. pmodelTarget = m;
  428. break;
  429. }
  430. }
  431. if (ttMask & c_ttPrevious)
  432. l = (l == models->first()) ? models->last() : l->txen();
  433. else
  434. l = (l == models->last()) ? models->first() : l->next();
  435. }
  436. while (l != mLink);
  437. }
  438. if ((pmodelTarget) ||
  439. ((ttMask & c_ttAnyCluster) == 0) ||
  440. ((ttMask & (c_ttStation | c_ttAsteroid | c_ttTreasure | c_ttWarp)) == 0) ||
  441. (maxDistance == 0))
  442. return pmodelTarget;
  443. IclusterIGC* pclusterStart = pcluster;
  444. int distance = 0;
  445. {
  446. //Search adjacent clusters for an appropriate target
  447. WarpListIGC warpsOne;
  448. WarpListIGC warpsTwo;
  449. ClusterListIGC clustersVisited;
  450. WarpListIGC* pwlOneAway = &warpsOne;
  451. WarpListIGC* pwlTwoAway = &warpsTwo;
  452. clustersVisited.first(pcluster); //We've already visited this cluster
  453. //None of these bits make any sense when searching a remote cluster
  454. ttMask &= ~(c_ttAnyCluster | c_ttFront | c_ttShip | c_ttBuoy | c_ttMissile);
  455. bool firstMatchF = false;
  456. do
  457. {
  458. assert (pcluster);
  459. //Push the destinations of the warps in pcluster onto the end the list of
  460. //warps that are an extra jump away
  461. {
  462. for (WarpLinkIGC* l = pcluster->GetWarps()->first(); (l != NULL); l = l->next())
  463. {
  464. IwarpIGC* w = l->data();
  465. if ((!pship) || pship->CanSee(w))
  466. {
  467. IwarpIGC* pwarpDestination = w->GetDestination();
  468. if (pwarpDestination)
  469. {
  470. IclusterIGC* pclusterOther = pwarpDestination->GetCluster();
  471. //Have we visited pclusterOther?
  472. if (clustersVisited.find(pclusterOther) == NULL)
  473. {
  474. //No
  475. if (((ttMask & c_ttCowardly) == 0) || IsFriendlyCluster(pclusterOther, pside))
  476. pwlTwoAway->last(pwarpDestination);
  477. }
  478. }
  479. }
  480. }
  481. }
  482. //Find the next cluster to search
  483. if (pwlOneAway->n() == 0)
  484. {
  485. if ((pwlTwoAway->n() == 0) || (distance++ >= maxDistance))
  486. {
  487. //No place left to search
  488. break;
  489. }
  490. else
  491. {
  492. //No clusters in the current distance bracket ... start on the clusters in the next distance bracket
  493. WarpListIGC* pwl = pwlOneAway;
  494. pwlOneAway = pwlTwoAway;
  495. pwlTwoAway = pwl;
  496. }
  497. }
  498. assert (pwlOneAway->n() > 0);
  499. WarpLinkIGC* plink = (ttMask & c_ttPrevious) ? pwlOneAway->last() : pwlOneAway->first();
  500. IwarpIGC* pwarp = plink->data();
  501. delete plink;
  502. pposition = &(pwarp->GetPosition());
  503. pcluster = pwarp->GetCluster();
  504. clustersVisited.first(pcluster);
  505. pmodelTarget = FindTarget(pship, ttMask, NULL, pcluster, pposition, NULL, abmAbilities);
  506. //Skip over the existing target once
  507. if ((pmodelTarget != NULL) &&
  508. (pmodelTarget == pmodelCurrent) &&
  509. firstMatchF)
  510. {
  511. pmodelTarget = NULL;
  512. firstMatchF = false;
  513. }
  514. }
  515. while (pmodelTarget == NULL);
  516. }
  517. if (!pship || (ttMask & c_ttNoRipcord) ||
  518. (pship->GetBaseHullType() == NULL) ||
  519. (pship->GetFlag() != NA))
  520. {
  521. return pmodelTarget;
  522. }
  523. const IhullTypeIGC* pht = pship->GetHullType();
  524. HullAbilityBitMask habmShip = pht->GetCapabilities();
  525. if (habmShip & c_habmNoRipcord)
  526. return pmodelTarget;
  527. float ripcordSpeed = pht->GetRipcordSpeed();
  528. //OK ... the ship can rip. See if there is something "closer" via a ripcord.
  529. //First, make a list of all clusters that contain a ripcord
  530. CPList clustersRipcord;
  531. {
  532. for (StationLinkIGC* psl = pside->GetStations()->first(); (psl != NULL); psl = psl->next())
  533. {
  534. IstationIGC* ps = psl->data();
  535. if (ps->GetStationType()->HasCapability(c_sabmRipcord))
  536. {
  537. IclusterIGC* pc = ps->GetCluster();
  538. if ((pc != pclusterStart) && UniqueCP(&clustersRipcord, pc) &&
  539. (((ttMask & c_ttCowardly) == 0) || IsFriendlyCluster(pc, pside)))
  540. NewCP(&clustersRipcord, ps->GetCluster(), &(ps->GetPosition()));
  541. }
  542. }
  543. }
  544. if (pship->GetPilotType() >= c_ptPlayer) //non-players don't tp to probes or ships
  545. {
  546. HullAbilityBitMask habm = (habmShip & c_habmCanLtRipcord)
  547. ? (c_habmIsRipcordTarget | c_habmIsLtRipcordTarget)
  548. : c_habmIsRipcordTarget;
  549. ImissionIGC* pmission = pship->GetMission();
  550. IIgcSite* pigc = pmission->GetIgcSite();
  551. for (ShipLinkIGC* psl = pside->GetShips()->first(); (psl != NULL); psl = psl->next())
  552. {
  553. IshipIGC* ps = psl->data();
  554. if (ps != pship)
  555. {
  556. IclusterIGC* pc = pigc->GetRipcordCluster(ps, habm);
  557. if (pc && (pc != pclusterStart) && UniqueCP(&clustersRipcord, pc) &&
  558. (((ttMask & c_ttCowardly) == 0) || IsFriendlyCluster(pc, pside)))
  559. NewCP(&clustersRipcord, pc, NULL);
  560. }
  561. }
  562. for (ClusterLinkIGC* pcl = pmission->GetClusters()->first(); (pcl != NULL); pcl = pcl->next())
  563. {
  564. IclusterIGC* pc = pcl->data();
  565. if ((pc != pclusterStart) && UniqueCP(&clustersRipcord, pc) &&
  566. (((ttMask & c_ttCowardly) == 0) || IsFriendlyCluster(pc, pside)))
  567. {
  568. for (ProbeLinkIGC* ppl = pc->GetProbes()->first(); (ppl != NULL); ppl = ppl->next())
  569. {
  570. IprobeIGC* pprobe = ppl->data();
  571. if ((pprobe->GetSide() == pside) && pprobe->GetCanRipcord(ripcordSpeed))
  572. {
  573. NewCP(&clustersRipcord, pc, &(pprobe->GetPosition()));
  574. break;
  575. }
  576. }
  577. }
  578. }
  579. }
  580. //Now ... check each cluster to see if there is something closer
  581. ttMask |= c_ttNoRipcord | c_ttAnyCluster;
  582. distance--;
  583. int distanceBest = 0x7fffffff;
  584. for (CPLink* pcl = clustersRipcord.first(); (pcl != NULL); pcl = pcl->next())
  585. {
  586. ImodelIGC* pmodel = FindTarget(pship, ttMask, NULL, pcl->data().pcluster, pcl->data().pposition, NULL, abmAbilities, distance);
  587. if (pmodel)
  588. {
  589. IclusterIGC* pc = pmodel->GetCluster();
  590. assert (pc);
  591. int d = GetDistance(pship, pclusterStart, pc, distanceBest);
  592. if (d < distanceBest)
  593. {
  594. distanceBest = d;
  595. pmodelTarget = pmodel;
  596. }
  597. }
  598. }
  599. return pmodelTarget;
  600. }
  601. struct Path
  602. {
  603. IwarpIGC* pwarpStart;
  604. IwarpIGC* pwarp;
  605. float distance;
  606. };
  607. typedef Slist_utl<Path> PathList;
  608. typedef Slink_utl<Path> PathLink;
  609. IwarpIGC* FindPath(IshipIGC* pship,
  610. IclusterIGC* pclusterTarget,
  611. bool bCowardly)
  612. {
  613. assert (pship);
  614. IsideIGC* pside = pship->GetSide();
  615. IclusterIGC* pclusterCurrent = pship->GetCluster();
  616. assert (pclusterCurrent);
  617. if (pclusterCurrent == pclusterTarget)
  618. return NULL;
  619. const Vector& positionShip = pship->GetPosition();
  620. ClusterListIGC explored;
  621. PathList unexplored;
  622. explored.last(pclusterCurrent);
  623. {
  624. //Add the initial warps ... distance from the player to the aleph
  625. const WarpListIGC* pwarps = pclusterCurrent->GetWarps();
  626. for (WarpLinkIGC* wLink = pwarps->first(); (wLink != NULL); wLink = wLink->next())
  627. {
  628. IwarpIGC* pwarp = wLink->data();
  629. if (pship->CanSee(pwarp))
  630. {
  631. assert (pwarp->GetDestination());
  632. IclusterIGC* pclusterDestination = pwarp->GetDestination()->GetCluster();
  633. if ((!bCowardly) ||
  634. (pclusterTarget == pclusterDestination) ||
  635. IsFriendlyCluster(pclusterDestination, pside))
  636. {
  637. PathLink* pl = new PathLink;
  638. assert (pl);
  639. Path& path = pl->data();
  640. path.distance = (pwarp->GetPosition() - positionShip).LengthSquared();
  641. path.pwarpStart = path.pwarp = pwarp;
  642. //Keep the list sorted
  643. PathLink* p = unexplored.first();
  644. while (true)
  645. {
  646. if (p == NULL)
  647. {
  648. //Nothing left ... to the end of the list
  649. unexplored.last(pl);
  650. break;
  651. }
  652. else if (path.distance < p->data().distance)
  653. {
  654. //Insert in front of the existing link (which has a greater distance)
  655. p->txen(pl);
  656. break;
  657. }
  658. p = p->next();
  659. }
  660. }
  661. }
  662. }
  663. if (unexplored.n() == 0)
  664. return NULL;
  665. }
  666. while (true)
  667. {
  668. PathLink* plinkClosest = unexplored.first();
  669. if (!plinkClosest)
  670. return NULL; //Never found a path
  671. const Path& path = plinkClosest->data();
  672. IwarpIGC* pwarp = path.pwarp;
  673. IclusterIGC* pclusterNext = pwarp->GetDestination()->GetCluster();
  674. if (pclusterNext == pclusterTarget)
  675. {
  676. //Found a path to the target
  677. return path.pwarpStart;
  678. }
  679. explored.last(pclusterNext);
  680. //Add warps for the warp in the new cluster (that do not lead to a previously explored cluster)
  681. //Add all of the warps in this cluster to the unexplored list
  682. for (WarpLinkIGC* pwl = pclusterNext->GetWarps()->first(); (pwl != NULL); pwl = pwl->next())
  683. {
  684. IwarpIGC* pwarp = pwl->data();
  685. if (pship->CanSee(pwarp))
  686. {
  687. IclusterIGC* pclusterDestination = pwarp->GetDestination()->GetCluster();
  688. if ((!bCowardly) ||
  689. (pclusterDestination == pclusterTarget) ||
  690. IsFriendlyCluster(pclusterDestination, pside))
  691. {
  692. if (explored.find(pclusterDestination) == NULL)
  693. {
  694. PathLink* ppl = new PathLink;
  695. assert (ppl);
  696. Path& ppathNew = ppl->data();
  697. ppathNew.pwarpStart = path.pwarpStart;
  698. ppathNew.pwarp = pwarp;
  699. unexplored.last(ppl);
  700. }
  701. }
  702. }
  703. }
  704. delete plinkClosest;
  705. }
  706. }
  707. IwarpIGC* FindPath(IshipIGC* pShip,
  708. ImodelIGC* pTarget,
  709. bool bCowardly)
  710. {
  711. assert (pShip);
  712. assert (pTarget);
  713. IclusterIGC* pclusterTarget = pTarget->GetMission()->GetIgcSite()->GetCluster(pShip, pTarget);
  714. return pclusterTarget ? FindPath(pShip, pclusterTarget, bCowardly) : NULL;
  715. }
  716. bool SearchClusters(ImodelIGC* pmodel,
  717. void* pdata,
  718. bool (*pfnCluster)(IclusterIGC* pcluster,
  719. void* pdata))
  720. {
  721. assert (pmodel);
  722. bool rc;
  723. IclusterIGC* pcluster = pmodel->GetCluster();
  724. if (pcluster)
  725. {
  726. if (pfnCluster(pcluster, pdata))
  727. {
  728. //That was easy: success on the first cluster checked
  729. rc = true;
  730. }
  731. else
  732. {
  733. }
  734. }
  735. else
  736. rc = false;
  737. return rc;
  738. }
  739. const char* GetModelType(ImodelIGC* pmodel)
  740. {
  741. switch (pmodel->GetObjectType())
  742. {
  743. case OT_ship:
  744. return ((IshipIGC*)pmodel)->GetHullType()->GetName();
  745. case OT_projectile:
  746. return "";
  747. return "minefield";
  748. case OT_station:
  749. return ((IstationIGC*)pmodel)->GetStationType()->GetName();
  750. case OT_buoy:
  751. return "";
  752. case OT_asteroid:
  753. return IasteroidIGC::GetTypeName(((IasteroidIGC*)pmodel)->GetCapabilities());
  754. case OT_warp:
  755. return "aleph";
  756. case OT_treasure:
  757. {
  758. IbuyableIGC* pb = ((ItreasureIGC*)pmodel)->GetBuyable();
  759. if (pb)
  760. return pb->GetName();
  761. else
  762. {
  763. static const char* szNames[] = {"", "", "Powerup", "", "Cash", ""};
  764. TreasureCode tc = ((ItreasureIGC*)pmodel)->GetTreasureCode();
  765. assert ((tc == c_tcPowerup) || (tc == c_tcCash) || (tc == c_tcFlag));
  766. return szNames[tc];
  767. }
  768. }
  769. case OT_mine:
  770. case OT_probe:
  771. return pmodel->GetName() + 1; //Special hack where probes & mines always have a secondary name
  772. default:
  773. {
  774. assert (pmodel->GetObjectType() == OT_missile);
  775. return ((ImissileIGC*)pmodel)->GetMissileType()->GetName();
  776. }
  777. }
  778. }
  779. const char* GetModelName(ImodelIGC* pmodel)
  780. {
  781. assert (pmodel);
  782. const char* n = pmodel->GetName();
  783. if (n[0] != '\0')
  784. return n;
  785. else
  786. {
  787. ObjectType type = pmodel->GetObjectType();
  788. return ((type == OT_asteroid) || (type == OT_buoy)) ? (n + 1) : GetModelType(pmodel);
  789. }
  790. }
  791. AmmoState GetAmmoState(IshipIGC* pship)
  792. {
  793. assert (pship);
  794. //speed hack ... assume that 500 rounds is enough for anything without checking guns
  795. short ammo = pship->GetAmmo();
  796. if (ammo >= 500)
  797. return c_asFull;
  798. float consumption = 0.0f;
  799. //Go through the ship's mounted weapons and count up the number of bullets it needs/second
  800. for (Mount i = pship->GetHullType()->GetMaxWeapons() - 1; (i >= 0); i--)
  801. {
  802. IweaponIGC* w = (IweaponIGC*)(pship->GetMountedPart(ET_Weapon, i));
  803. if (w)
  804. {
  805. short aps = w->GetAmmoPerShot();
  806. ammo -= aps;
  807. consumption += ((float)aps) / w->GetDtBurst();
  808. }
  809. }
  810. return (consumption == 0.0f)
  811. ? c_asFull
  812. : ((ammo <= 0)
  813. ? c_asEmpty
  814. : (((float)ammo) / consumption) < 10.0f ? c_asLow : c_asFull);
  815. }
  816. static const GotoPositionMask c_gpmKillThrottle = 0x01;
  817. static const GotoPositionMask c_gpmFinished = 0x02;
  818. static const GotoPositionMask c_gpmPivot = 0x04;
  819. static const GotoPositionMask c_gpmEnter = 0x08;
  820. static const GotoPositionMask c_gpmNoDodge = 0x10;
  821. static const GotoPositionMask c_gpmFast = 0x40;
  822. static const GotoPositionMask c_gpmDodgeShips = 0x80;
  823. static const GotoPositionMask c_gpmRoll = 0x100;
  824. static const float c_fOffsetFudge = 10.0f;
  825. static const char c_stateSeek = 0;
  826. static const char c_stateCoast = 1;
  827. static const char c_statePivot = 2;
  828. static const char c_stateEnter = 3;
  829. GotoPositionMask Waypoint::DoApproach(IshipIGC* pship,
  830. const Vector& myPosition,
  831. const Vector& itsPosition,
  832. int nLand,
  833. const Vector* pCenters,
  834. const Vector* pDirections,
  835. float distanceRest,
  836. const Vector& positionRest,
  837. Vector* pvectorGoto,
  838. ImodelIGC** ppmodelSkip,
  839. Vector* pvectorFacing)
  840. {
  841. GotoPositionMask gpm;
  842. assert (nLand > 0);
  843. //The best bay is the one where the distance between our rest position and the approach strip is the smallest
  844. Vector dpNow = myPosition - itsPosition;
  845. Vector dpRest = positionRest - itsPosition;
  846. float myRadius = (pship->GetRadius() + m_pmodelTarget->GetRadius() + 10.0f); //my radius plus a fudge factor
  847. const Vector* pdirectionBest;
  848. const Vector* pcenterBest;
  849. Vector goalBest;
  850. float error2Best = FLT_MAX;
  851. float offset2Best;
  852. int bayBest;
  853. for (int i = 0; (i < nLand); i++)
  854. {
  855. const Vector* pcenter = pCenters + i;
  856. const Vector* pdirection = pDirections + i;
  857. //This is where we want to go
  858. Vector goal;
  859. {
  860. //Find t such that
  861. //|*pcenter + *pdirection * t| = R
  862. //(pc + pd * t)^2 = R^2
  863. // pc^2 + 2pd pc t + pd^2 t^2 - r^2 = 0
  864. assert (pdirection->LengthSquared() >= 0.98f);
  865. assert (pdirection->LengthSquared() <= 1.02f);
  866. float b = *pcenter * *pdirection;
  867. float c = *pcenter * *pcenter - myRadius * myRadius;
  868. assert (c < 0.0f);
  869. float t = sqrt(b*b - c) - b;
  870. assert (t >= 0.0f);
  871. goal = *pcenter + *pdirection * t;
  872. }
  873. //Look at where both our current position and reset positions are with respect to the line
  874. Vector deltaNow = (dpNow - *pcenter);
  875. float dotNow = deltaNow * *pdirection;
  876. Vector deltaRest = (dpRest - *pcenter);
  877. float dotRest = (deltaRest * *pdirection);
  878. float error2;
  879. float offset2;
  880. if ((dotNow >= 0.0f) || (dotRest >= myRadius))
  881. {
  882. //Ship is infront of the bay ... error is distance between
  883. //respostion and approach line
  884. error2 = deltaRest.LengthSquared() - dotRest * dotRest;
  885. offset2 = deltaNow.LengthSquared() - dotNow * dotNow;
  886. }
  887. else
  888. {
  889. //Ship is behind (or not far enough in front) ... error is
  890. //distance from rest position and approach point
  891. error2 = (dpRest - goal).LengthSquared();
  892. offset2 = FLT_MAX;
  893. }
  894. if (error2 < error2Best)
  895. {
  896. pdirectionBest = pdirection;
  897. pcenterBest = pcenter;
  898. goalBest = goal;
  899. error2Best = error2;
  900. offset2Best = offset2;
  901. bayBest = i;
  902. }
  903. }
  904. if (error2Best > 36.0f)
  905. {
  906. //Not able to drift into position .. continue normally
  907. *ppmodelSkip = NULL;
  908. *pvectorGoto = goalBest + itsPosition;
  909. float d2 = (dpNow - goalBest).LengthSquared();
  910. if ((d2 > distanceRest * distanceRest * 2.0f) ||
  911. ((dpRest - goalBest).LengthSquared() <= d2 + 1.0f))
  912. {
  913. //We are moving in more or less the correct direction
  914. gpm = ((dpNow - *pcenterBest) * *pdirectionBest >= 0.0f) ? c_gpmDodgeShips : 0;
  915. }
  916. else
  917. {
  918. //Our rest position is further from the goal than our current position
  919. *pvectorFacing = *pvectorGoto - myPosition;
  920. gpm = c_gpmPivot;
  921. }
  922. }
  923. else
  924. {
  925. //Our rest position is close to the line ... are we close to the line of approach?
  926. if (offset2Best > 40.0f)
  927. {
  928. //We are not close to the approach line ... coast in
  929. gpm = c_gpmKillThrottle | c_gpmDodgeShips;
  930. *ppmodelSkip = NULL;
  931. *pvectorGoto = goalBest + itsPosition;
  932. }
  933. else
  934. {
  935. //We are on the line and either not moving or moving along the line
  936. *ppmodelSkip = m_pmodelTarget;
  937. *pvectorGoto = *pcenterBest + itsPosition;
  938. *pvectorFacing = *pvectorGoto - myPosition;
  939. float rateYaw = pship->GetCurrentTurnRate(c_axisYaw);
  940. float ratePitch = pship->GetCurrentTurnRate(c_axisPitch);
  941. float rate2 = rateYaw * rateYaw + ratePitch * ratePitch;
  942. static const float rateMax = 0.01f;
  943. static const float cosMin = 0.999f * 0.999f;
  944. if ((rate2 < rateMax * rateMax) && (pship->GetOrientation().CosForward2(*pvectorFacing) > cosMin))
  945. {
  946. //On track, going the right way and not turning .... charge
  947. gpm = c_gpmEnter | c_gpmNoDodge;
  948. }
  949. else
  950. {
  951. //On track and almost stopped ... pivot to face the bay
  952. gpm = c_gpmPivot | c_gpmDodgeShips;
  953. }
  954. }
  955. }
  956. return gpm;
  957. }
  958. GotoPositionMask Waypoint::GetGotoPosition(IshipIGC* pship,
  959. float distanceRest,
  960. const Vector& positionRest,
  961. Vector* pvectorGoto,
  962. ImodelIGC** ppmodelSkip,
  963. Vector* pvectorFacing)
  964. {
  965. assert (pship);
  966. const Vector& myPosition = pship->GetPosition();
  967. GotoPositionMask gpm;
  968. assert (m_pmodelTarget);
  969. const Vector& itsPosition = m_pmodelTarget->GetPosition();
  970. if (m_objective == Waypoint::c_oGoto)
  971. {
  972. Vector vRest = itsPosition - positionRest;
  973. float dRest2 = vRest.LengthSquared();
  974. Vector vCenters = itsPosition - myPosition;
  975. float dCenters2 = vCenters.LengthSquared();
  976. float radius = m_pmodelTarget->GetRadius() + pship->GetRadius();
  977. //First ... is there a danger of colliding with the object?
  978. float radiusRest = radius + distanceRest;
  979. if (dCenters2 <= radiusRest * radiusRest)
  980. {
  981. //Yes there is ... pivot
  982. gpm = c_gpmPivot;
  983. *pvectorFacing = vCenters;
  984. *ppmodelSkip = NULL;
  985. }
  986. else
  987. {
  988. //No danger of a collision
  989. *ppmodelSkip = m_pmodelTarget;
  990. float offset = radius + c_fOffsetFudge;
  991. if (dRest2 < offset * offset)
  992. {
  993. if (distanceRest < 0.5f)
  994. {
  995. gpm = (c_gpmPivot | c_gpmFinished);
  996. *pvectorFacing = vCenters;
  997. }
  998. else
  999. {
  1000. gpm = c_gpmKillThrottle;
  1001. *pvectorGoto = itsPosition;
  1002. }
  1003. }
  1004. else
  1005. {
  1006. gpm = 0;
  1007. *pvectorGoto = itsPosition;
  1008. }
  1009. }
  1010. }
  1011. else
  1012. {
  1013. switch (m_pmodelTarget->GetObjectType())
  1014. {
  1015. case OT_asteroid:
  1016. {
  1017. Vector centers[2];
  1018. Vector directions[2];
  1019. centers[0].x = centers[0].y = centers[0].z = 0.0f;
  1020. centers[1].x = centers[1].y = centers[1].z = 0.0f;
  1021. const Rotation& r = m_pmodelTarget->GetRotation();
  1022. directions[0] = r.axis();
  1023. directions[1] = -r.axis();
  1024. gpm = DoApproach(pship, myPosition, itsPosition,
  1025. 2, centers, directions,
  1026. distanceRest,
  1027. positionRest,
  1028. pvectorGoto,
  1029. ppmodelSkip,
  1030. pvectorFacing);
  1031. if (gpm & c_gpmEnter)
  1032. {
  1033. gpm |= c_gpmFinished;
  1034. }
  1035. }
  1036. break;
  1037. case OT_ship:
  1038. {
  1039. //Trying to pick up a ship ... where will he be when we get there?
  1040. Vector direction;
  1041. Vector dp = itsPosition - myPosition;
  1042. Vector dv = m_pmodelTarget->GetVelocity();
  1043. float t = solveForImpact(dp, dv,
  1044. pship->GetHullType()->GetMaxSpeed(), 0.0f, &direction);
  1045. gpm = c_gpmFast;
  1046. *pvectorGoto = itsPosition + t * dv;
  1047. *ppmodelSkip = m_pmodelTarget;
  1048. }
  1049. break;
  1050. case OT_probe:
  1051. {
  1052. gpm = c_gpmFast;
  1053. *pvectorGoto = itsPosition;
  1054. *ppmodelSkip = m_pmodelTarget;
  1055. }
  1056. break;
  1057. case OT_station:
  1058. {
  1059. //Trying to dock ...
  1060. IhullTypeIGC* pht = pship->GetBaseHullType();
  1061. assert (pht);
  1062. const IstationTypeIGC* pst = ((IstationIGC*)m_pmodelTarget)->GetStationType();
  1063. {
  1064. int nLand = pht->HasCapability(c_habmFighter)
  1065. ? pst->GetLandSlots()
  1066. : pst->GetCapLandSlots();
  1067. if (nLand == 0)
  1068. {
  1069. //No landing bays ... power glide in
  1070. gpm = c_gpmFast;
  1071. *pvectorGoto = itsPosition;
  1072. *ppmodelSkip = m_pmodelTarget;
  1073. }
  1074. else
  1075. {
  1076. assert (nLand > 0);
  1077. assert (nLand <= c_maxLandSlots);
  1078. Vector centers[c_maxLandSlots];
  1079. Vector directions[c_maxLandSlots];
  1080. const Orientation& itsOrientation = m_pmodelTarget->GetOrientation();
  1081. for (int i = 0; (i < nLand); i++)
  1082. {
  1083. centers[i] = pst->GetLandPosition(i, 0) * itsOrientation;
  1084. directions[i] = pst->GetLandDirection(i, 0) * itsOrientation;
  1085. }
  1086. gpm = DoApproach(pship, myPosition, itsPosition,
  1087. nLand, centers, directions,
  1088. distanceRest,
  1089. positionRest,
  1090. pvectorGoto,
  1091. ppmodelSkip,
  1092. pvectorFacing);
  1093. if (gpm & c_gpmEnter)
  1094. {
  1095. gpm |= c_gpmRoll;
  1096. }
  1097. }
  1098. }
  1099. }
  1100. break;
  1101. default:
  1102. {
  1103. gpm = c_gpmFast;
  1104. *pvectorGoto = itsPosition;
  1105. *ppmodelSkip = m_pmodelTarget;
  1106. }
  1107. break;
  1108. }
  1109. }
  1110. return gpm;
  1111. }
  1112. /*-------------------------------------------------------------------------
  1113. * Function: getDirection
  1114. *-------------------------------------------------------------------------
  1115. * Purpose:
  1116. * Get the button control mask to thrust in a given direction
  1117. */
  1118. static int getDirection(const Vector& dP,
  1119. const Orientation& orientation)
  1120. {
  1121. float z = dP * orientation.GetBackward();
  1122. float y = dP * orientation.GetUp();
  1123. float x = dP * orientation.GetRight();
  1124. double absX = fabs(x);
  1125. double absY = fabs(y);
  1126. double absZ = fabs(z);
  1127. int stateX = (x >= 0.0f ? rightButtonIGC : leftButtonIGC);
  1128. int stateY = (y >= 0.0f ? upButtonIGC : downButtonIGC);
  1129. int stateZ = (z <= 0.0f ? forwardButtonIGC : backwardButtonIGC);
  1130. const float c_fT = 2.0f;
  1131. #define GetState(a,b,c) (abs##a > c_fT * abs##b) \
  1132. ? state##a \
  1133. : (abs##a > c_fT * abs##c) \
  1134. ? (state##a | state##b) \
  1135. : (stateX | stateY | stateZ)
  1136. int state;
  1137. if (absX > absY)
  1138. {
  1139. //x > y
  1140. if (absY > absZ)
  1141. {
  1142. //x > y > z
  1143. state = GetState(X, Y, Z);
  1144. }
  1145. else if (absX > absZ)
  1146. {
  1147. // x > z > y
  1148. state = GetState(X, Z, Y);
  1149. }
  1150. else
  1151. {
  1152. //z > x > y
  1153. state = GetState(Z, X, Y);
  1154. }
  1155. }
  1156. else
  1157. {
  1158. //y > x
  1159. if (absX > absZ)
  1160. {
  1161. //y > x > z
  1162. state = GetState(Y, X, Z);
  1163. }
  1164. else if (absY > absZ)
  1165. {
  1166. // y > z > x
  1167. state = GetState(Y, Z, X);
  1168. }
  1169. else
  1170. {
  1171. //z > y > x
  1172. state = GetState(Z, Y, X);
  1173. }
  1174. }
  1175. #undef GetState
  1176. return state;
  1177. }
  1178. bool Ignore(IshipIGC* pship, ImodelIGC* pmodel)
  1179. {
  1180. bool ignore = false;
  1181. IsideIGC* mySide = pship->GetSide();
  1182. IsideIGC* hisSide = pmodel->GetSide();
  1183. ObjectType type = pmodel->GetObjectType();
  1184. if (type == OT_ship)
  1185. {
  1186. IshipIGC* pshipHim = (IshipIGC*)pmodel;
  1187. IhullTypeIGC* phtHim = pshipHim->GetBaseHullType();
  1188. if (phtHim == NULL)
  1189. ignore = true;
  1190. else
  1191. {
  1192. if (mySide == hisSide)
  1193. {
  1194. if ((pshipHim->GetObjectID() < pship->GetObjectID()) && //he has a lower ship ID
  1195. (pship->GetPilotType() < c_ptPlayer) && //if we are a drone &
  1196. (pshipHim->GetPilotType() < c_ptPlayer) && //he a drone that
  1197. ((pshipHim->GetStateM() & miningMaskIGC) == 0) && //isn't mining
  1198. (pshipHim->GetRipcordModel() == NULL)) //isn't ripcording
  1199. {
  1200. ignore = true; //ignore him
  1201. }
  1202. else
  1203. {
  1204. HullAbilityBitMask habmHim = phtHim->GetCapabilities();
  1205. HullAbilityBitMask habmMe = pship->GetBaseHullType()->GetCapabilities();
  1206. ignore = ((habmMe & c_habmLifepod) && (habmHim & c_habmRescue)) ||
  1207. ((habmMe & c_habmRescue) && (habmHim & c_habmLifepod)) ||
  1208. ((habmMe & c_habmCarrier) && (habmHim & c_habmLandOnCarrier));
  1209. }
  1210. }
  1211. else if (pship->GetPilotType() < c_ptPlayer)
  1212. ignore = true; //trucker rules of road: we're heavier
  1213. }
  1214. }
  1215. else if ((type == OT_mine) && (mySide == hisSide)) //We can ignore friendly minefields
  1216. {
  1217. ignore = true;
  1218. }
  1219. return ignore;
  1220. }
  1221. bool Dodge(IshipIGC* pship,
  1222. ImodelIGC* pmodelIgnore,
  1223. int* pstate,
  1224. bool bShipsOnly,
  1225. float tMax)
  1226. {
  1227. IclusterIGC* pcluster = pship->GetCluster();
  1228. assert (pcluster);
  1229. const Vector& myPosition = pship->GetPosition();
  1230. const Vector& myVelocity = pship->GetVelocity();
  1231. const Orientation& myOrientation = pship->GetOrientation();
  1232. float speed = myVelocity.Length();
  1233. float myRadius = pship->GetRadius() + 1.0f;
  1234. float myAcceleration = pship->GetHullType()->GetThrust() / pship->GetMass();
  1235. HitTest* myHitTest = pship->GetHitTest();
  1236. ImodelIGC* pmodelCollide = NULL;
  1237. float tCollide = FLT_MAX;
  1238. for (ModelLinkIGC* pml = (bShipsOnly
  1239. ? (ModelLinkIGC*)(pcluster->GetShips()->first())
  1240. : pcluster->GetPickableModels()->first());
  1241. (pml != NULL); pml = pml->next())
  1242. {
  1243. ImodelIGC* pmodel = pml->data();
  1244. if ((pmodel != pship) &&
  1245. (pmodel != pmodelIgnore) &&
  1246. (pmodel->GetHitTest()->GetNoHit() != myHitTest))
  1247. {
  1248. //Ignore the appropriate things plus minefields if we are going slowly enough.
  1249. if (!Ignore(pship, pmodel) && ((speed > 50.0f) || (pmodel->GetObjectType() != OT_mine)))
  1250. {
  1251. //Get the distance of closest approach
  1252. const Vector& hisPosition = pmodel->GetPosition();
  1253. const Vector& hisVelocity = pmodel->GetVelocity();
  1254. float r = pmodel->GetRadius() + myRadius;
  1255. Vector dp = hisPosition - myPosition;
  1256. float d2 = dp.LengthSquared();
  1257. Vector direction;
  1258. float t = (d2 > r * r)
  1259. ? solveForImpact(dp,
  1260. hisVelocity - myVelocity,
  1261. 0.0f,
  1262. r,
  1263. &direction)
  1264. : 0.0f;
  1265. assert (t >= 0.0f);
  1266. if (t < tCollide)
  1267. {
  1268. if (tMax >= 0.0f
  1269. ? (t <= tMax)
  1270. : (t*t <= (4.0f * r / myAcceleration)))
  1271. {
  1272. pmodelCollide = pmodel;
  1273. tCollide = t;
  1274. }
  1275. }
  1276. }
  1277. }
  1278. }
  1279. if (pmodelCollide)
  1280. {
  1281. // We are going to collide: find our positions at closest approach
  1282. const Vector dp = myPosition - pmodelCollide->GetPosition();
  1283. Vector ca;
  1284. if (tCollide == 0.0f)
  1285. ca = dp; //Already inside the object ... dodge straight away from its center
  1286. else
  1287. {
  1288. const Vector dv = myVelocity - pmodelCollide->GetVelocity();
  1289. float ldv2 = dv.LengthSquared();
  1290. ca = ldv2 > 0.1f
  1291. ? (dp - dv * ((dp * dv) / ldv2))
  1292. : dp;
  1293. if (ca.LengthSquared() < 0.1f)
  1294. {
  1295. //Aimed straight at the center of the object ... dodge in a nice
  1296. //orthogonal direction to the original displacement
  1297. ca = dp.GetOrthogonalVector();
  1298. }
  1299. }
  1300. // Thrust along the away vector
  1301. *pstate = getDirection(ca, myOrientation);
  1302. return true;
  1303. }
  1304. return false;
  1305. }
  1306. bool GotoPlan::Execute(Time now, float dt, bool bDodge)
  1307. {
  1308. int stateM;
  1309. ControlData controls;
  1310. bool bDone = SetControls(dt, bDodge, &controls, &stateM);
  1311. const int c_maneuverButtons = backwardButtonIGC |
  1312. forwardButtonIGC |
  1313. leftButtonIGC |
  1314. rightButtonIGC |
  1315. upButtonIGC |
  1316. downButtonIGC |
  1317. afterburnerButtonIGC |
  1318. coastButtonIGC |
  1319. oneWeaponIGC | //Disable any weapon fire
  1320. allWeaponsIGC;
  1321. m_pship->SetStateBits(c_maneuverButtons, stateM);
  1322. m_pship->SetControls(controls);
  1323. return bDone;
  1324. }
  1325. bool GotoPlan::SetControls(float dt, bool bDodge, ControlData* pcontrols, int* pstate)
  1326. {
  1327. bool bDone;
  1328. *pstate = 0;
  1329. pcontrols->Reset();
  1330. if (m_maskWaypoints != 0)
  1331. {
  1332. assert (m_wpTarget.m_pmodelTarget);
  1333. IclusterIGC* pcluster = m_pship->GetCluster();
  1334. assert (pcluster);
  1335. if (pcluster != m_pvOldCluster)
  1336. {
  1337. //The ship's cluster changed ... recalculate goals from the original goal
  1338. m_wpWarp.Reset();
  1339. m_maskWaypoints = c_wpTarget;
  1340. //See if the goal was to enter a warp
  1341. if (m_wpTarget.m_pmodelTarget &&
  1342. (m_wpTarget.m_objective == Waypoint::c_oEnter) &&
  1343. (m_wpTarget.m_pmodelTarget->GetObjectType() == OT_warp) &&
  1344. (((IwarpIGC*)(m_wpTarget.m_pmodelTarget))->GetDestination()->GetCluster() == pcluster))
  1345. {
  1346. m_wpTarget.Reset();
  1347. m_maskWaypoints = 0;
  1348. return true;
  1349. }
  1350. m_pvOldCluster = pcluster;
  1351. }
  1352. const Vector& myPosition = m_pship->GetPosition();
  1353. const Vector& myVelocity = m_pship->GetVelocity();
  1354. const Orientation& myOrientation = m_pship->GetOrientation();
  1355. float speed = myVelocity.Length();
  1356. float myRadius = m_pship->GetRadius() + 2.0f;
  1357. bDone = false;
  1358. IclusterIGC* pclusterTarget = m_pship->GetMission()->GetIgcSite()->GetCluster(m_pship, m_wpTarget.m_pmodelTarget);
  1359. //No point in going towards a dead target or a target we can not see
  1360. if (pclusterTarget)
  1361. {
  1362. if (pcluster != pclusterTarget)
  1363. {
  1364. if (((m_maskWaypoints & c_wpWarp) == 0) ||
  1365. (pclusterTarget != m_pvOldClusterTarget))
  1366. {
  1367. //The target is in a different cluster from the ship and either we have no
  1368. //warp waypoint selected or the target changed clusters. Either way ... recalculate
  1369. //the warp waypoint
  1370. //Clear the old intermediate waypoints
  1371. m_wpWarp.Reset();
  1372. m_pvOldClusterTarget = pclusterTarget;
  1373. bool bCoward = (m_pship->GetPilotType() < c_ptCarrier);
  1374. IwarpIGC* pwarp = FindPath(m_pship, pclusterTarget, bCoward);
  1375. if (bCoward && (pwarp == NULL))
  1376. pwarp = FindPath(m_pship, pclusterTarget, false);
  1377. if (pwarp)
  1378. {
  1379. m_wpWarp.Set(Waypoint::c_oEnter, pwarp);
  1380. m_maskWaypoints = c_wpTarget | c_wpWarp;
  1381. }
  1382. else
  1383. {
  1384. //Do not know of a warp to the intended target ... keep the original target around & clear the warp waypoint
  1385. m_maskWaypoints = c_wpTarget;
  1386. if (bDodge)
  1387. Dodge(m_pship, NULL, pstate);
  1388. return false;
  1389. }
  1390. }
  1391. }
  1392. else if (m_wpTarget.m_pmodelTarget->GetCluster() == NULL)
  1393. {
  1394. //We are in the target cluster but the target is not here ... probably because it is docked.
  1395. //wait and do nothing (but clear the warp waypoint since we're in the right cluster.
  1396. m_maskWaypoints = c_wpTarget;
  1397. if (bDodge)
  1398. Dodge(m_pship, NULL, pstate);
  1399. return false;
  1400. }
  1401. const IhullTypeIGC* pht = m_pship->GetHullType();
  1402. double backMultiplier = pht->GetBackMultiplier();
  1403. double vMax = pht->GetMaxSpeed() * backMultiplier;
  1404. double thrustMax = pht->GetThrust() * backMultiplier;
  1405. double mass = m_pship->GetMass();
  1406. double k = thrustMax / (mass * vMax);
  1407. //Calculate the ship's rest position if it kills its throttle
  1408. float distanceRest = 0.0f;
  1409. Vector positionRest = myPosition;
  1410. if (speed > 0.1f)
  1411. {
  1412. //If we killed our throttle, where would we drift to?
  1413. //From the differential equations:
  1414. // dV/dT = -k V(t) + A
  1415. //
  1416. // A = acceleration = thrust / mass
  1417. // k = drag coefficient = thrustMax / (vMax * mass)
  1418. //
  1419. // Vterminal = A / k
  1420. //
  1421. // V(t) = Vterminal + (V(0) - Vterminal) * exp(-k t)
  1422. //In this particular case ... the ship is decelerating,
  1423. //so Vterminal = -vMax
  1424. //Solve V(t) = 0 = Vterminal + (V(0) - Vterminal) * exp(-k t)
  1425. //-Vterminal = (V(0) - Vterminal) exp (-k t) or, where Vmax = -Vterminal & speed = V(0)
  1426. // Vmax = (speed + Vmax) exp (-kt)
  1427. //t = ln(vMax / (v0 + vMax)) / -k
  1428. double expmkt = vMax / (speed + vMax);
  1429. double mkt = log(expmkt);
  1430. //S(t) = Integral (0 -> t) (V(t) dt)
  1431. // = s0 + Vterminal (t - 0) + (speed - Vterminal) * (1/-k) (exp(-kt) - exp(0))
  1432. // = 0 +
  1433. // Vmax * -kt / k +
  1434. // (speed + Vmax) * (1.0 - exp(-kt)) / k
  1435. //
  1436. // But ... -kt = ln (Vmax / (speed + Vmax)), so
  1437. // S(t) = Vmax * -kt / k + (speed * Vmax) * (1 - Vmax / (speed + Vmax)) / k
  1438. // = Vmax * -kt/k + speed/k
  1439. // = (Vmax * -kt + speed) / k
  1440. distanceRest = float((vMax * mkt + speed) / k);
  1441. positionRest += myVelocity * (distanceRest / speed);
  1442. }
  1443. GotoPositionMask gpm;
  1444. Vector positionGoto;
  1445. ImodelIGC* pmodelSkip;
  1446. Vector facing;
  1447. assert (m_maskWaypoints & c_wpTarget);
  1448. gpm = ((m_maskWaypoints & c_wpWarp) ? m_wpWarp : m_wpTarget).GetGotoPosition(m_pship,
  1449. distanceRest,
  1450. positionRest,
  1451. &positionGoto,
  1452. &pmodelSkip,
  1453. &facing);
  1454. //First priority ... dodge
  1455. if (((gpm & c_gpmNoDodge) == 0) && bDodge)
  1456. {
  1457. if (Dodge(m_pship, pmodelSkip, pstate, (gpm & c_gpmDodgeShips) != 0))
  1458. {
  1459. if ((gpm & (c_gpmPivot | c_gpmEnter)) != 0)
  1460. {
  1461. if (facing * facing >= 0.1f)
  1462. turnToFace(facing, dt, m_pship, pcontrols, m_fSkill);
  1463. }
  1464. else
  1465. {
  1466. Vector path = positionGoto - myPosition;
  1467. if (path * path >= 0.1f)
  1468. turnToFace(path, dt, m_pship, pcontrols, m_fSkill);
  1469. }
  1470. return false;
  1471. }
  1472. }
  1473. pcontrols->jsValues[c_axisThrottle] = -1.0f;
  1474. if (((gpm & c_gpmFinished) != 0) && (m_wpTarget.m_pmodelTarget->GetObjectType() != OT_ship))
  1475. {
  1476. m_wpWarp.Reset();
  1477. m_wpTarget.Reset();
  1478. m_maskWaypoints = 0;
  1479. bDone = true;
  1480. }
  1481. else if ((gpm & c_gpmPivot) != 0)
  1482. {
  1483. //Pivot implies kill throttle
  1484. if (facing * facing > 0.1f)
  1485. turnToFace(facing, dt, m_pship, pcontrols, m_fSkill);
  1486. }
  1487. else if ((gpm & c_gpmKillThrottle) != 0)
  1488. {
  1489. Vector path = positionGoto - myPosition;
  1490. if (path * path >= 0.1f)
  1491. turnToFace(path, dt, m_pship, pcontrols, m_fSkill);
  1492. }
  1493. else if ((gpm & c_gpmEnter) != 0)
  1494. {
  1495. if (facing * facing > 0.1f)
  1496. turnToFace(facing, dt, m_pship, pcontrols, m_fSkill);
  1497. if ((gpm & c_gpmRoll) != 0)
  1498. {
  1499. float rollMax2 = 1.0f -
  1500. pcontrols->jsValues[c_axisPitch] * pcontrols->jsValues[c_axisPitch] -
  1501. pcontrols->jsValues[c_axisYaw] * pcontrols->jsValues[c_axisYaw];
  1502. //What is the angle we'd like to roll? Is the angle between the ship's Y axis and the global z-axis
  1503. const Orientation& o = m_pship->GetOrientation();
  1504. double cosRoll = o.GetUp().z; //Dot product of the up vector with (0,0,1)
  1505. if ((cosRoll < 0.99) && (cosRoll > -0.99))
  1506. {
  1507. float roll = float((cosRoll >= 0.0) ? -acos(cosRoll) : acos(-cosRoll));
  1508. if (o.GetRight().z < 0.0f)
  1509. roll = -roll;
  1510. float tm = m_pship->GetTorqueMultiplier();
  1511. const IhullTypeIGC* pht = m_pship->GetHullType();
  1512. float mass = m_pship->GetMass();
  1513. assert (mass > 0.0f);
  1514. float rollRate = m_pship->GetCurrentTurnRate(c_axisRoll);
  1515. roll -= (float)(m_fSkill * fabs(rollRate) * (0.5f * rollRate * mass / (tm * pht->GetTurnTorque(c_axisRoll))));
  1516. float maxRoll= dt * pht->GetMaxTurnRate(c_axisRoll);
  1517. double r = roll / maxRoll;
  1518. double d2 = pcontrols->jsValues[c_axisPitch] * pcontrols->jsValues[c_axisPitch] +
  1519. pcontrols->jsValues[c_axisYaw] * pcontrols->jsValues[c_axisYaw];
  1520. if (d2 + r * r > 1.0)
  1521. {
  1522. float f = float(sqrt(1.0 - d2));
  1523. pcontrols->jsValues[c_axisRoll] = (r > 0.0) ? f : -f;
  1524. }
  1525. else
  1526. pcontrols->jsValues[c_axisRoll] = float(r);
  1527. }
  1528. }
  1529. pcontrols->jsValues[c_axisThrottle] = 1.0f;
  1530. }
  1531. else
  1532. {
  1533. //We want to go someplace and we're not coasting to a stop (which implies we got a long way to go)
  1534. //see if we need to plot a course around anything in the meantime.
  1535. Vector path = positionGoto - myPosition;
  1536. float distance2 = path.LengthSquared();
  1537. const float divertOffset = 10.0f;
  1538. float dMax2 = distance2;
  1539. ImodelIGC* pmodelDivert = NULL;
  1540. for (ModelLinkIGC* pml = pcluster->GetPickableModels()->first();
  1541. (pml != NULL);
  1542. pml = pml->next())
  1543. {
  1544. ImodelIGC* pmodel = pml->data();
  1545. if ((pmodel != m_pship) &&
  1546. (pmodel != pmodelSkip) &&
  1547. !Ignore(m_pship, pmodel))
  1548. {
  1549. ObjectType type = pmodel->GetObjectType();
  1550. if ((type != OT_mine) &&
  1551. ((type != OT_ship) || (pmodel->GetVelocity().LengthSquared() < 1.0f)))
  1552. {
  1553. //Vector to the center of the object
  1554. const Vector& itsPosition = pmodel->GetPosition();
  1555. Vector dp = itsPosition - myPosition;
  1556. float d2 = dp.LengthSquared();
  1557. if (d2 < dMax2) //Object is closer that our goal
  1558. {
  1559. float dot = dp * path;
  1560. if (dot >= 0.0f) //Object is not behind us
  1561. {
  1562. float r = myRadius + pmodel->GetRadius() + divertOffset;
  1563. float r2 = r*r;
  1564. Vector closest = myPosition + path * (dot / distance2);
  1565. Vector offset = closest - itsPosition;
  1566. float offsetLength2 = offset.LengthSquared();
  1567. if (offsetLength2 < r2)
  1568. {
  1569. pmodelDivert = pmodel;
  1570. dMax2 = d2;
  1571. }
  1572. }
  1573. }
  1574. }
  1575. }
  1576. }
  1577. if (pmodelDivert)
  1578. {
  1579. //We need to get around this object ...
  1580. const Vector& itsPosition = pmodelDivert->GetPosition();
  1581. Vector dp = itsPosition - myPosition;
  1582. float dot = dp * path;
  1583. assert (dot >= 0.0f);
  1584. Vector closest = myPosition + path * (dot / distance2);
  1585. Vector offset = closest - itsPosition;
  1586. float offsetLength2 = offset.LengthSquared();
  1587. //The object extends across our path ... recalculate a new path
  1588. Vector cross;
  1589. if (offsetLength2 >= 0.5f)
  1590. {
  1591. Vector n = CrossProduct(offset, dp);
  1592. cross = CrossProduct(dp, n);
  1593. }
  1594. else
  1595. cross = dp.GetOrthogonalVector();
  1596. if (cross.LengthSquared() != 0)
  1597. cross = cross.Normalize();
  1598. else
  1599. cross = Vector::RandomDirection();
  1600. double rpf = myRadius + pmodelDivert->GetRadius() + divertOffset;
  1601. double rpf2 = rpf*rpf;
  1602. double dpLength2 = dp.LengthSquared();
  1603. double sinTheta2 = rpf * rpf / dpLength2;
  1604. if (sinTheta2 < 0.99)
  1605. {
  1606. double cosTheta2 = (1.0 - sinTheta2);
  1607. //This is a vector tangent to the blocking object
  1608. Vector tangent = dp + cross * float(sqrt(rpf2 / cosTheta2));
  1609. float tangentLength2 = tangent.LengthSquared();
  1610. float dot = dp * tangent;
  1611. assert (dot > 0.0f);
  1612. Vector pca = tangent * (dot / tangentLength2);
  1613. Vector radial = (pca - dp).Normalize();
  1614. path = pca + radial * float(rpf * 0.02);
  1615. }
  1616. else
  1617. {
  1618. //We are very close to the object ... turn 90
  1619. path = cross * float(rpf);
  1620. }
  1621. }
  1622. //Always face where we are going
  1623. if (path * path >= 0.1f)
  1624. {
  1625. float da = turnToFace(path, dt, m_pship, pcontrols, m_fSkill);
  1626. if (da < pi / 8.0f)
  1627. {
  1628. float fThrottleMax = 1.0f;
  1629. if (((gpm & c_gpmFast) == 0) &&
  1630. ((positionGoto - myPosition).LengthSquared() < distanceRest * distanceRest))
  1631. {
  1632. //We are close enough to want to slow down
  1633. fThrottleMax = -1.0f;
  1634. }
  1635. else
  1636. {
  1637. //Are we headed through a minefield?
  1638. IsideIGC* mySide = m_pship->GetSide();
  1639. for (ModelLinkIGC* pml = pcluster->GetPickableModels()->first();
  1640. (pml != NULL);
  1641. pml = pml->next())
  1642. {
  1643. ImodelIGC* pmodel = pml->data();
  1644. if ((pmodel->GetObjectType() == OT_mine) && (pmodel->GetSide() != mySide))
  1645. {
  1646. //Vector to the center of the object
  1647. const Vector& itsPosition = pmodel->GetPosition();
  1648. Vector dp = itsPosition - myPosition;
  1649. float d2 = dp.LengthSquared();
  1650. if (d2 < distance2) //Object is closer that our goal
  1651. {
  1652. float dot = dp * path;
  1653. if (dot >= 0.0f) //Object is not behind us
  1654. {
  1655. float r = myRadius + pmodel->GetRadius();
  1656. float rSlow = r + distanceRest;
  1657. if (d2 <= rSlow * rSlow)
  1658. {
  1659. float r2 = r*r;
  1660. Vector closest = myPosition + path * (dot / distance2);
  1661. Vector offset = closest - itsPosition;
  1662. float offsetLength2 = offset.LengthSquared();
  1663. if (offsetLength2 < r2)
  1664. {
  1665. fThrottleMax = -0.5;
  1666. break;
  1667. }
  1668. }
  1669. }
  1670. }
  1671. }
  1672. }
  1673. }
  1674. pcontrols->jsValues[c_axisThrottle] = fThrottleMax;
  1675. }
  1676. else
  1677. *pstate = getDirection(path - myVelocity * float(1.0 / k), myOrientation);
  1678. }
  1679. else
  1680. {
  1681. pcontrols->Reset();
  1682. }
  1683. }
  1684. }
  1685. }
  1686. else
  1687. bDone = true;
  1688. return bDone;
  1689. }
  1690. bool LineOfSightExist(const IclusterIGC* pcluster,
  1691. const ImodelIGC* pmodel1,
  1692. const ImodelIGC* pmodel2)
  1693. {
  1694. assert (pcluster);
  1695. assert (pmodel1);
  1696. assert (pmodel1->GetObjectType() != OT_asteroid);
  1697. assert (pmodel2);
  1698. assert (pmodel1->GetCluster() == pcluster);
  1699. assert (pmodel2->GetCluster() == pcluster);
  1700. // P1 is the eye,and P2 is the center of the target that we want to know whether or not is visible
  1701. const Vector& P1 = pmodel1->GetPosition();
  1702. const Vector& P2 = pmodel2->GetPosition();
  1703. // compute the vector between the two points, get its squared length, the reciprocal of the length, and then normalize it
  1704. Vector V12 = P2 - P1;
  1705. float fLengthSquaredV12 = V12.LengthSquared (),
  1706. fOverLengthV12 = 1.0f / sqrtf (fLengthSquaredV12);
  1707. V12 *= fOverLengthV12;
  1708. // compute the angle subtended by the target from the eye
  1709. float fVisibleAngle = asinf (pmodel2->GetRadius() * fOverLengthV12);
  1710. for (ModelLinkIGC* pml = ((ModelListIGC*)(pcluster->GetAsteroids()))->first(); (pml != NULL); pml = pml->next())
  1711. {
  1712. ImodelIGC* pmodel = pml->data();
  1713. if (pmodel2 != pmodel)
  1714. {
  1715. // P3 is the center of the object that might obscure our view of P2
  1716. const Vector& P3 = pmodel->GetPosition();
  1717. // Compute the vector between the eye and the obstacle, and get its squared length.
  1718. Vector V13 = P3 - P1;
  1719. float fLengthSquaredV13 = V13.LengthSquared ();
  1720. // if the obscruing object is closer than the target, then it might really
  1721. // obscure the view of the target
  1722. if (fLengthSquaredV13 < fLengthSquaredV12)
  1723. {
  1724. float dot = (V12 * V13);
  1725. if (dot > 0.0f)
  1726. {
  1727. // If the dot product is negative, the obstacle is behind the eye and could not
  1728. // possibly obscure the view (unless the eye is inside of the obscuring object,
  1729. // which we hope won't happen).
  1730. // compute the reciprocal of the length of the vector, and then normalize it
  1731. float fOverLengthV13 = 1.0f / sqrtf (fLengthSquaredV13);
  1732. //V13 *= fOverLengthV13;
  1733. // Compute the dot product of the two vectors, this is the cosine of
  1734. // the angle between them.
  1735. float fCosineSeparationAngle = dot * fOverLengthV13;
  1736. //assert (fCosineSeparationAngle > 0.0f); //This should be true, but it only takes a little round-off to spoil a day
  1737. {
  1738. // Get the radius of the obscuring model, and scale it to simulate a dense core
  1739. // or variable geometry. We then compute the coverage angle of the obscuring
  1740. // object.
  1741. float fRadius3 = pmodel->GetRadius () * 0.5f;
  1742. float fCoveredAngle = asinf (fRadius3 * fOverLengthV13);
  1743. // Compute the separation angle of the line to the target and the line to the
  1744. // obscuring object. The farthest angle from the obscuring object at which the
  1745. // target can be oscured is the separation angle plus the visible angle.
  1746. float fSeparationAngle = acosf (fCosineSeparationAngle);
  1747. float fMaximumSeparationAngle = fSeparationAngle + fVisibleAngle;
  1748. // If the farthest angle at which the target can be viewed is covered by the obscuring object,
  1749. // then the target is not visible and we return false.
  1750. if (fMaximumSeparationAngle < fCoveredAngle)
  1751. return false;
  1752. }
  1753. }
  1754. }
  1755. }
  1756. }
  1757. return true;
  1758. }
  1759. IshipIGC* CreateDrone(ImissionIGC* pmission,
  1760. ShipID shipID,
  1761. PilotType pt,
  1762. const char* pszName,
  1763. HullID hullID,
  1764. IsideIGC* pside,
  1765. AbilityBitMask abmOrders,
  1766. float shootSkill,
  1767. float moveSkill,
  1768. float bravery)
  1769. {
  1770. // Do IGC initialization:
  1771. DataShipIGC ds;
  1772. ds.shipID = shipID;
  1773. ds.hullID = hullID;
  1774. ds.sideID = pside->GetObjectID();
  1775. //ds.wingID = 0;
  1776. ds.nKills = 0;
  1777. ds.nDeaths = 0;
  1778. ds.pilotType = pt;
  1779. ds.abmOrders = abmOrders;
  1780. ds.baseObjectID = NA;
  1781. if (pszName)
  1782. {
  1783. assert (strlen(pszName) < c_cbName - 4);
  1784. strcpy(ds.name, pszName);
  1785. //Is the name unique?
  1786. ShipLinkIGC* psl;
  1787. for (psl = pmission->GetShips()->first();
  1788. ((psl != NULL) && (_stricmp(pszName, psl->data()->GetName()) != 0));
  1789. psl = psl->next())
  1790. {
  1791. }
  1792. if (psl != NULL)
  1793. {
  1794. //Name is not unique ... make it unique
  1795. _itoa(shipID, ds.name + strlen(ds.name), 10);
  1796. }
  1797. }
  1798. else
  1799. ds.name[0] = '\0';
  1800. ds.nParts = 0;
  1801. IshipIGC* ship = (IshipIGC*)(pmission->CreateObject(pmission->GetLastUpdate(), // Make the IGC ship
  1802. OT_ship, &ds,
  1803. sizeof(DataShipIGC)));
  1804. assert (ship);
  1805. //Try to fill out the drone with the specified parts
  1806. {
  1807. IhullTypeIGC* pht = ship->GetBaseHullType();
  1808. for (PartTypeLinkIGC* ptl = pht->GetPreferredPartTypes()->first();
  1809. (ptl != NULL);
  1810. ptl = ptl->next())
  1811. {
  1812. IpartTypeIGC* ppt = ptl->data();
  1813. //Mount the part anyplace it can be mounted. Ignore price (included in the cost of the drone) & availability
  1814. EquipmentType et = ppt->GetEquipmentType();
  1815. Mount mountMax = (et == ET_Weapon)
  1816. ? pht->GetMaxWeapons()
  1817. : 1;
  1818. for (Mount i = 0; (i < mountMax); i++)
  1819. {
  1820. if ((ship->GetMountedPart(et, i) == NULL) && pht->CanMount(ppt, i))
  1821. ship->CreateAndAddPart(ppt, i, 0x7fff);
  1822. }
  1823. }
  1824. }
  1825. ship->SetAutopilot(true);
  1826. ship->Release();
  1827. return ship;
  1828. }
  1829. ClusterWarning GetClusterWarning(AssetMask am, bool bInvulnerableStations)
  1830. {
  1831. ClusterWarning cw;
  1832. if ((am & c_amEnemyAPC) && (am & c_amStation) && !bInvulnerableStations)
  1833. cw = c_cwStationCaptureThreat;
  1834. else if ((am & (c_amEnemyTeleport | c_amEnemyTeleportShip)) && (am & c_amStation) && !bInvulnerableStations)
  1835. cw = c_cwStationTeleportThreat;
  1836. else if ((am & c_amEnemyBomber) && (am & c_amStation) && !bInvulnerableStations)
  1837. cw = c_cwStationThreatened;
  1838. else if ((am & c_amEnemyAPC) && !bInvulnerableStations)
  1839. cw = c_cwTransportInCluster;
  1840. else if (am & c_amEnemyTeleportShip)
  1841. cw = c_cwTeleportInCluster;
  1842. else if (am & c_amEnemyCapital)
  1843. cw = c_cwCapitalInCluster;
  1844. else if (am & c_amEnemyCarrier)
  1845. cw = c_cwEnemyCarrierInCluster;
  1846. else if ((am & c_amEnemyBomber) && !bInvulnerableStations)
  1847. cw = c_cwBomberInCluster;
  1848. else if ((am & c_amEnemyFighter) && (am & c_amCarrier))
  1849. cw = c_cwCarrierThreatened;
  1850. else if ((am & c_amEnemyFighter) && (am & c_amBuilder))
  1851. cw = c_cwBuilderThreatened;
  1852. else if ((am & c_amEnemyFighter) && (am & c_amMiner))
  1853. cw = c_cwMinerThreatened;
  1854. else if ((am & c_amEnemyFighter) && (am & c_amFighter))
  1855. cw = c_cwCombatInCluster;
  1856. else if (am & c_amEnemyBuilder)
  1857. cw = c_cwEnemyBuilderInCluster;
  1858. else if (am & c_amEnemyMiner)
  1859. cw = c_cwEnemyMinerInCluster;
  1860. else if (am & c_amEnemyFighter)
  1861. cw = c_cwEnemyFighterInCluster;
  1862. else
  1863. cw = c_cwNoThreat;
  1864. return cw;
  1865. }
  1866. const char* GetClusterWarningText(ClusterWarning cw)
  1867. {
  1868. static const char* c_pszAlerts[c_cwMax] =
  1869. {
  1870. "No threat",
  1871. "Enemy fighter spotted",
  1872. "Enemy miner spotted",
  1873. "Enemy builder spotted",
  1874. "Conflict",
  1875. "Miner at risk",
  1876. "Builder at risk",
  1877. "Carrier at risk",
  1878. "Enemy bomber spotted",
  1879. "Enemy carrier spotted",
  1880. "Enemy capital ship spotted",
  1881. "Enemy assault ship spotted",
  1882. "Enemy transport spotted",
  1883. "Station at risk",
  1884. "Station at risk by teleport",
  1885. "Station at risk of capture"
  1886. };
  1887. assert(cw >= 0 && cw < c_cwMax);
  1888. return c_pszAlerts[cw];
  1889. }
  1890. DamageTrack::DamageTrack(DamageTrackSet* pdts)
  1891. :
  1892. m_pset(pdts)
  1893. {
  1894. assert (pdts);
  1895. pdts->AddTrack(this);
  1896. }
  1897. DamageTrack::~DamageTrack(void)
  1898. {
  1899. Reset();
  1900. assert (m_pset);
  1901. m_pset->DeleteTrack(this);
  1902. }
  1903. void DamageTrack::SwitchSlots(void)
  1904. {
  1905. DamageBucketLink* l = m_buckets.first();
  1906. while (l)
  1907. {
  1908. //Prefetch the next pointer
  1909. DamageBucketLink* lNext = l->next();
  1910. DamageBucket* db = l->data();
  1911. db->SwitchSlots(m_pset->m_idSlot);
  1912. if (db->totalDamage() < 0.5f) //Allow a generous amount for roundoff errors
  1913. delete db; //Also deletes the link
  1914. l = lNext;
  1915. }
  1916. //We could sort here ... but let's not. Do a lazy sort the next time we take damage
  1917. }
  1918. void DamageTrack::ApplyDamage(Time timeNow,
  1919. ImodelIGC* pmodel,
  1920. float damage)
  1921. {
  1922. DamageBucketLink* l;
  1923. for (l = m_buckets.first();
  1924. ((l != NULL) && (l->data()->model() != pmodel));
  1925. l = l->next())
  1926. {
  1927. }
  1928. DamageBucket* pBucket;
  1929. if (l == NULL)
  1930. {
  1931. //No bucket existed for the model ... create one
  1932. pBucket = new DamageBucket(this, pmodel);
  1933. }
  1934. else
  1935. pBucket = l->data();
  1936. pBucket->ApplyDamage(timeNow, m_pset->m_idSlot, damage);
  1937. DamageTrack::sort(&m_buckets);
  1938. }
  1939. void DamageTrack::Reset(void)
  1940. {
  1941. DamageBucketLink* pdbl;
  1942. while (pdbl = m_buckets.first()) //Intentional
  1943. delete pdbl->data();
  1944. }
  1945. void DamageTrack::sort(DamageBucketList* pListBuckets)
  1946. {
  1947. if (pListBuckets->n() > 1)
  1948. {
  1949. //Sort the elements of the bucket list which is almost sorted
  1950. DamageBucketLink* p = pListBuckets->first();
  1951. while (true)
  1952. {
  1953. DamageBucketLink* next = p->next();
  1954. if (!next)
  1955. break;
  1956. if (p->data()->totalDamage() < next->data()->totalDamage())
  1957. {
  1958. //*p < *next (& therefore either p or next is out of order)
  1959. //Move next forward as much as is needed to re-establish the sort of all
  1960. //of the elements between the start of the list and p
  1961. next->unlink();
  1962. DamageBucketLink* back = p->txen();
  1963. while ((back) && (back->data()->totalDamage() >= next->data()->totalDamage()))
  1964. {
  1965. back = back->txen();
  1966. }
  1967. if (back)
  1968. {
  1969. //back has done more damage than next ... therefore next should be after it.
  1970. back->next(next);
  1971. }
  1972. else
  1973. {
  1974. //couldn't find a bucket with a higher damage total than next ...
  1975. //put next at the front of the list.
  1976. pListBuckets->first(next);
  1977. }
  1978. }
  1979. else
  1980. p = next;
  1981. }
  1982. }
  1983. }
  1984. void PlayerScoreObject::CalculateScore(ImissionIGC* pmission)
  1985. {
  1986. if (m_dtPlayed == 0.0f)
  1987. return;
  1988. float kMax = m_dtPlayed / (15.0f * 60.0f); //1.0 / 15 minutes
  1989. m_fScore = float(m_cWarpsSpotted) * pmission->GetFloatConstant(c_fcidPointsWarp) +
  1990. float(m_cAsteroidsSpotted) * pmission->GetFloatConstant(c_fcidPointsAsteroid) +
  1991. m_cTechsRecovered * pmission->GetFloatConstant(c_fcidPointsTech) +
  1992. (m_cMinerKills * kMax) * pmission->GetFloatConstant(c_fcidPointsMiner) / (m_cMinerKills + kMax) +
  1993. (m_cBuilderKills * kMax) * pmission->GetFloatConstant(c_fcidPointsBuilder) / (m_cBuilderKills + kMax) +
  1994. (m_cLayerKills * kMax) * pmission->GetFloatConstant(c_fcidPointsLayer) / (m_cLayerKills + kMax) +
  1995. (m_cCarrierKills * kMax) * pmission->GetFloatConstant(c_fcidPointsCarrier) / (m_cCarrierKills + kMax) +
  1996. m_cPlayerKills * pmission->GetFloatConstant(c_fcidPointsPlayer) +
  1997. (m_cBaseKills * kMax) * pmission->GetFloatConstant(c_fcidPointsBaseKill) / (m_cBaseKills + kMax) +
  1998. (m_cBaseCaptures * kMax) * pmission->GetFloatConstant(c_fcidPointsBaseCapture) / (m_cBaseCaptures + kMax) +
  1999. float(m_cRescues) * pmission->GetFloatConstant(c_fcidPointsRescues) +
  2000. float(m_cArtifacts) * pmission->GetFloatConstant(c_fcidPointsArtifacts) +
  2001. float(m_cFlags) * pmission->GetFloatConstant(c_fcidPointsFlags);
  2002. if (m_bWin)
  2003. m_fScore *= 2.0f;
  2004. }
  2005. float PlayerScoreObject::GetScore(void) const
  2006. {
  2007. return m_fScore;
  2008. }
  2009. void PlayerScoreObject::SetScore(float fNewScore)
  2010. {
  2011. m_fScore = fNewScore;
  2012. }
  2013. void PlayerScoreObject::AdjustCombatRating(ImissionIGC* pmission,
  2014. PlayerScoreObject* ppsoKiller,
  2015. PlayerScoreObject* ppsoKillee)
  2016. {
  2017. float k = (pmission->GetFloatConstant(c_fcidRatingAdd) +
  2018. ppsoKillee->m_fCombatRating - ppsoKiller->m_fCombatRating) /
  2019. pmission->GetFloatConstant(c_fcidRatingDivide);
  2020. ppsoKiller->m_fCombatRating = ppsoKiller->m_fCombatRating * (1.0f - k) + k;
  2021. ppsoKillee->m_fCombatRating = ppsoKillee->m_fCombatRating * (1.0f - k);
  2022. }
  2023. int GetDistance(IshipIGC* pship,
  2024. IclusterIGC* pcluster,
  2025. IclusterIGC* pclusterStop,
  2026. int maxDistance)
  2027. {
  2028. assert (pcluster != pclusterStop);
  2029. int distance = 1;
  2030. //Search adjacent clusters for an appropriate target
  2031. WarpListIGC warpsOne;
  2032. WarpListIGC warpsTwo;
  2033. ClusterListIGC clustersVisited;
  2034. WarpListIGC* pwlOneAway = &warpsOne;
  2035. WarpListIGC* pwlTwoAway = &warpsTwo;
  2036. while (true)
  2037. {
  2038. assert (pcluster);
  2039. clustersVisited.first(pcluster); //We've already visited this cluster
  2040. //Push the destinations of the warps in pcluster onto the end the list of
  2041. //warps that are an extra jump away
  2042. {
  2043. for (WarpLinkIGC* l = pcluster->GetWarps()->first(); (l != NULL); l = l->next())
  2044. {
  2045. IwarpIGC* w = l->data();
  2046. if (pship->CanSee(w))
  2047. {
  2048. IwarpIGC* pwarpDestination = w->GetDestination();
  2049. if (pwarpDestination)
  2050. {
  2051. IclusterIGC* pclusterOther = pwarpDestination->GetCluster();
  2052. if (pclusterOther == pclusterStop)
  2053. return distance;
  2054. else if (clustersVisited.find(pclusterOther) == NULL)
  2055. pwlTwoAway->last(pwarpDestination);
  2056. }
  2057. }
  2058. }
  2059. }
  2060. //Find the next cluster to search
  2061. if (pwlOneAway->n() == 0)
  2062. {
  2063. if ((pwlTwoAway->n() == 0) || (distance++ >= maxDistance))
  2064. return 0x7fffffff;
  2065. //No clusters in the current distance bracket ... start on the clusters in the next distance bracket
  2066. WarpListIGC* pwl = pwlOneAway;
  2067. pwlOneAway = pwlTwoAway;
  2068. pwlTwoAway = pwl;
  2069. }
  2070. assert (pwlOneAway->n() > 0);
  2071. WarpLinkIGC* plink = pwlOneAway->first();
  2072. IwarpIGC* pwarp = plink->data();
  2073. delete plink;
  2074. pcluster = pwarp->GetCluster();
  2075. }
  2076. }
  2077. static void GetAsteroidName(const char* pszPrefix,
  2078. AsteroidID id,
  2079. char* bfr)
  2080. {
  2081. const int c_iMultiplier = 5237;
  2082. const int c_iModulo = 8713;
  2083. const int c_iOffset = 1093;
  2084. const int c_iPlus = 1024;
  2085. int l = strlen(pszPrefix);
  2086. if (l == 0)
  2087. l = 2; //Allow for asteroids with no default name
  2088. memcpy(bfr, pszPrefix, l);
  2089. _itoa(c_iPlus + ((id + c_iOffset) * c_iMultiplier) % c_iModulo,
  2090. &bfr[l], 10);
  2091. }
  2092. static Vector RandomPosition(const ModelListIGC* models, float rThing, float radius, float lens)
  2093. {
  2094. const float maxStationRadius = 550.0f;
  2095. if (rThing < maxStationRadius)
  2096. rThing = maxStationRadius;
  2097. Vector position;
  2098. ModelLinkIGC* pmlink;
  2099. do
  2100. {
  2101. //Pick a random position for the asteroid
  2102. //in a squashed disk
  2103. position = Vector::RandomDirection();
  2104. position *= radius * pow(random(0.0f, 1.0f), 1.0f/3.0f);
  2105. position.z *= lens;
  2106. //Verify that it is not close to any other model in the cluster
  2107. for (pmlink = models->first(); (pmlink != NULL); pmlink = pmlink->next())
  2108. {
  2109. ImodelIGC* pm = pmlink->data();
  2110. float r = pm->GetRadius();
  2111. if (r < maxStationRadius)
  2112. r = maxStationRadius;
  2113. r += rThing;
  2114. if ((pm->GetPosition() - position).LengthSquared() < 2.0f * r * r)
  2115. {
  2116. //Too close ... try again
  2117. radius *= 1.02f;
  2118. break;
  2119. }
  2120. }
  2121. }
  2122. while (pmlink);
  2123. return position;
  2124. }
  2125. void CreateAsteroid(ImissionIGC* pmission,
  2126. IclusterIGC* pcluster,
  2127. int type,
  2128. float amountHe3)
  2129. {
  2130. DataAsteroidIGC da;
  2131. da.asteroidDef = IasteroidIGC::GetTypeDefaults(type);
  2132. static const Vector xAxis(1.0, 0.0, 0.0);
  2133. static const Vector zAxis(0.0, 0.0, 1.0);
  2134. da.clusterID = pcluster->GetObjectID();
  2135. da.forward = Vector::RandomDirection();
  2136. da.up = CrossProduct(da.forward, xAxis);
  2137. if (da.up.LengthSquared() <= 0.1f)
  2138. da.up = CrossProduct(da.forward, zAxis);
  2139. assert(da.up.LengthSquared() > 0.1f);
  2140. da.up.SetNormalize();
  2141. da.asteroidDef.radius = (short) randomInt(da.asteroidDef.radius, 2*da.asteroidDef.radius);
  2142. da.signature = ((float) da.asteroidDef.radius) / 100.0f;
  2143. da.fraction = 1.0f;
  2144. da.rotation.axis(da.forward);
  2145. da.rotation.angle(0.2f / da.signature);
  2146. if (da.asteroidDef.ore != 0.0f)
  2147. da.asteroidDef.ore *= amountHe3;
  2148. da.asteroidDef.oreMax = da.asteroidDef.ore;
  2149. da.asteroidDef.asteroidID = pmission->GenerateNewAsteroidID();
  2150. GetAsteroidName(IasteroidIGC::GetTypePrefix(type),
  2151. da.asteroidDef.asteroidID, da.name);
  2152. da.position = RandomPosition(pcluster->GetModels(), da.asteroidDef.radius,
  2153. pmission->GetFloatConstant(c_fcidRadiusUniverse) * 0.75f,
  2154. pmission->GetFloatConstant(c_fcidLensMultiplier));
  2155. IObject * o = pmission->CreateObject(pmission->GetLastUpdate(),
  2156. OT_asteroid,
  2157. &da,
  2158. sizeof(da));
  2159. assert (o);
  2160. o->Release();
  2161. }
  2162. static IstationIGC* CreatePedestalAndFlag(ImissionIGC* pmission,
  2163. SectorID clusterID,
  2164. SideID sideID,
  2165. const Vector& position,
  2166. float dz)
  2167. {
  2168. Time now = pmission->GetLastUpdate();
  2169. DataStationIGC ds;
  2170. ds.clusterID = clusterID;
  2171. ds.position = position;
  2172. ds.position.z += dz * c_fPedestalOffset;
  2173. ds.forward.x = ds.forward.y = 0.0f;
  2174. ds.forward.z = dz;
  2175. ds.up.x = 1.0f;
  2176. ds.up.y = ds.up.z = 0.0f;
  2177. ds.rotation.axis(ds.forward);
  2178. ds.rotation.angle(0.0f);
  2179. ds.bpHull = 1.0f;
  2180. ds.bpShield = 0.0f;
  2181. ds.sideID = sideID;
  2182. ds.stationID = pmission->GenerateNewStationID();
  2183. {
  2184. IstationTypeIGC* ppedestal = pmission->GetStationTypes()->last()->data();
  2185. assert (ppedestal->HasCapability(c_sabmPedestal));
  2186. ds.stationTypeID = ppedestal->GetObjectID();
  2187. strcpy(ds.name, ppedestal->GetName());
  2188. }
  2189. IstationIGC* pstation = (IstationIGC*)(pmission->CreateObject(now,
  2190. OT_station,
  2191. &ds,
  2192. sizeof(ds)));
  2193. assert(pstation);
  2194. DataTreasureIGC dt;
  2195. dt.treasureCode = c_tcFlag;
  2196. dt.treasureID = sideID;
  2197. dt.amount = 0;
  2198. dt.clusterID = clusterID;
  2199. dt.lifespan = 10.0f * 24.0f * 3600.0f;
  2200. dt.objectID = pmission->GenerateNewTreasureID();
  2201. dt.p0 = ds.position;
  2202. dt.p0.z += (c_fFlagOffset + pstation->GetRadius()) * dz;
  2203. dt.v0 = Vector::GetZero();
  2204. dt.time0 = now;
  2205. ItreasureIGC* t = (ItreasureIGC *)(pmission->CreateObject(now, OT_treasure,
  2206. &dt, sizeof(dt)));
  2207. assert (t);
  2208. t->Release();
  2209. //Bad form here ... but we know that the station's release count will not go to zero
  2210. pstation->Release();
  2211. return pstation;
  2212. }
  2213. void PopulateCluster(ImissionIGC* pmission,
  2214. const MissionParams* pmp,
  2215. IclusterIGC* pcluster,
  2216. float amountHe3,
  2217. bool bAsteroids,
  2218. bool bTreasures,
  2219. short nMineableAsteroidsMultiplier,
  2220. short nSpecialAsteroidsMultiplier,
  2221. short nAsteroidsMultiplier)
  2222. {
  2223. Time now = pmission->GetLastUpdate();
  2224. SectorID clusterID = pcluster->GetObjectID();
  2225. const StationListIGC* pstations = pcluster->GetStations();
  2226. bool bHomeSector = pcluster->GetHomeSector();
  2227. if (pmp->IsFlagsGame())
  2228. {
  2229. for (StationLinkIGC* psl = pstations->last(); (psl != NULL); psl = psl->txen())
  2230. {
  2231. IstationIGC* pstation = psl->data();
  2232. IsideIGC* pside = pstation->GetSide();
  2233. if (pstation->GetBaseStationType()->HasCapability(c_sabmStart))
  2234. {
  2235. SideID sideID = pside->GetObjectID();
  2236. const Vector& position = pstation->GetPosition();
  2237. CreatePedestalAndFlag(pmission, clusterID,
  2238. sideID,
  2239. position, 1.0f);
  2240. CreatePedestalAndFlag(pmission, clusterID,
  2241. sideID,
  2242. position, -1.0f);
  2243. }
  2244. }
  2245. }
  2246. {
  2247. //Make all stations invisible to all sides but their own
  2248. const SideListIGC* psides = pmission->GetSides();
  2249. for (StationLinkIGC* psl = pstations->first(); (psl != NULL); psl = psl->next())
  2250. {
  2251. IstationIGC* pstation = psl->data();
  2252. IsideIGC* pside = pstation->GetSide();
  2253. for (SideLinkIGC* p = psides->first(); (p != NULL); p = p->next())
  2254. {
  2255. if (p->data() != pside)
  2256. pstation->SetSideVisibility(p->data(),
  2257. false);
  2258. }
  2259. }
  2260. }
  2261. if (bAsteroids)
  2262. {
  2263. {
  2264. short nMineableAsteroids;
  2265. if (nMineableAsteroidsMultiplier >= 0)
  2266. {
  2267. nMineableAsteroids = bHomeSector
  2268. ? pmp->nPlayerSectorMineableAsteroids
  2269. : pmp->nNeutralSectorMineableAsteroids;
  2270. nMineableAsteroids *= nMineableAsteroidsMultiplier;
  2271. }
  2272. else
  2273. nMineableAsteroids = -nMineableAsteroidsMultiplier;
  2274. for (short i = 0; (i < nMineableAsteroids); i++)
  2275. {
  2276. CreateAsteroid(pmission, pcluster, IasteroidIGC::GetRandomType(c_aabmMineHe3), amountHe3);
  2277. }
  2278. }
  2279. {
  2280. short nSpecialAsteroids;
  2281. if (nSpecialAsteroidsMultiplier >= 0)
  2282. {
  2283. nSpecialAsteroids = bHomeSector
  2284. ? pmp->nPlayerSectorSpecialAsteroids
  2285. : pmp->nNeutralSectorSpecialAsteroids;
  2286. nSpecialAsteroids *= nSpecialAsteroidsMultiplier;
  2287. }
  2288. else
  2289. nSpecialAsteroids = -nSpecialAsteroidsMultiplier;
  2290. int offset = IasteroidIGC::NumberSpecialAsteroids(pmp);
  2291. if (offset != 1)
  2292. offset = randomInt(1, offset);
  2293. for (short i = 0; (i < nSpecialAsteroids); i++)
  2294. {
  2295. CreateAsteroid(pmission, pcluster, IasteroidIGC::GetSpecialAsterioid(pmp, offset++), 0.0f);
  2296. }
  2297. }
  2298. {
  2299. short nAsteroids;
  2300. if (nAsteroidsMultiplier >= 0)
  2301. {
  2302. nAsteroids = bHomeSector
  2303. ? pmp->nPlayerSectorAsteroids
  2304. : pmp->nNeutralSectorAsteroids;
  2305. nAsteroids *= nAsteroidsMultiplier;
  2306. }
  2307. else
  2308. nAsteroids = -nAsteroidsMultiplier;
  2309. for (short i = 0; (i < nAsteroids); i++)
  2310. {
  2311. CreateAsteroid(pmission, pcluster, IasteroidIGC::GetRandomType(c_aabmBuildable), 0.0f);
  2312. }
  2313. }
  2314. }
  2315. if (bTreasures)
  2316. {
  2317. short nTreasures;
  2318. short tsi;
  2319. if (bHomeSector)
  2320. {
  2321. nTreasures = pmp->nPlayerSectorTreasures;
  2322. tsi = pmp->tsiPlayerStart;
  2323. }
  2324. else
  2325. {
  2326. if (pmp->IsArtifactsGame())
  2327. {
  2328. pmission->GenerateTreasure(now,
  2329. pcluster,
  2330. -2); //Special hack for flags
  2331. }
  2332. nTreasures = pmp->nNeutralSectorTreasures;
  2333. tsi = pmp->tsiNeutralStart;
  2334. }
  2335. for (short i = 0; (i < nTreasures); i++)
  2336. {
  2337. pmission->GenerateTreasure(now,
  2338. pcluster,
  2339. tsi);
  2340. }
  2341. }
  2342. }