1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764 |
- #include "pch.h"
- #include <math.h>
- const char* c_pszWingName[c_widMax] =
- { "command",
- "attack",
- "defend",
- "escort",
- "search",
- "alpha",
- "bravo",
- "charlie",
- "delta",
- "echo",
- //"foxtrot",
- //"golf",
- //"hotel",
- //"india"//,
- //"juliet"
- };
- const CommandData c_cdAllCommands[c_cidMax] =
- {
- { "goto", "acdefaultbmp", "qudefaultbmp" },
- { "attack", "ackillbmp", "qukillbmp" },
- { "capture", "accptbmp", "qucptbmp" },
- { "defend", "acdefendbmp", "qudefendbmp" },
- { "pickup", "acpickupbmp", "qupickupbmp" },
- { "goto", "acgotobmp", "qugotobmp" },
- { "repair", "acrepairbmp", "qurepairbmp" },
- { "join", "acjoinbmp", "qujoinbmp" },
- { "mine", "acminebmp", "quminebmp" },
- { "build", "acbuildbmp", "qubuildbmp" }
- };
- const int c_ttTypebits[OT_modelEnd+1] =
- { c_ttShip, c_ttStation, c_ttMissile,
- c_ttMine, c_ttProbe, c_ttAsteroid, 0, c_ttWarp, c_ttTreasure, c_ttBuoy, 0, 0};
- float solveForImpact(const Vector& deltaP,
- const Vector& deltaV,
- float speed,
- float radius,
- Vector* direction)
- {
- assert (speed >= 0.0f);
- assert (direction);
- float t;
- float c = deltaP * deltaP - radius * radius;
- if (c <= 0.0f)
- {
- t = 0.0f;
- *direction = deltaP.Normalize();
- }
- else
- {
- //Solve for the time when the projectile is t * speed + radius units away from the target.
- // distance(t) = radius + speed * t = |deltaP + t * deltaV|
- // distance(t)^2 = radius ^2 + 2 * radius * speed * t + speed ^2 * t^2 = deltaP * deltaP + 2 * t * deltaV * deltaP+ t^2 * deltaV * deltaV
- //
- // 0 = deltaP^2 - radius^2 + 2 * t * (deltaV * deltaP - radius * speed) + t^2 * (deltaV^2 - speed^2)
- //
- float a = deltaV * deltaV - (speed * speed);
- float b = 2.0f * (deltaV * deltaP - radius * speed);
- float b24ac = b * b - 4.0f * a * c;
- if ((a == 0.0f) || ((a > 0.0f) && (b > 0.0f)) || (b24ac < 0.0f))
- {
- //No valid solution
- t = FLT_MAX;
- }
- else
- {
- //quadratic formula ... we only care about the smallest root > 0
- t = (b + (float)sqrt(b24ac)) / (-2.0f * a); //(-b-sqrt(b24ac))/(2a) Only or smallest possible positive root
- if (t > 0.0f) //should always be the case, but round-off error can cause problems
- {
- //Found a solution: now find the direction in which to shoot:
- *direction = (deltaP + t * deltaV) / (t * speed + radius); //Should be a unit vector
- }
- else
- {
- t = 0.0f;
- *direction = deltaP.Normalize();
- }
- }
- }
- return t;
- }
- float solveForLead(ImodelIGC* shooter,
- ImodelIGC* target,
- IweaponIGC* weapon,
- Vector* direction,
- float skill)
- {
- assert (shooter);
- assert (target);
- assert (weapon);
- assert (direction);
- const Vector& myPosition = shooter->GetPosition();
- const Vector& myVelocity = shooter->GetVelocity();
- const Orientation& myOrientation = shooter->GetOrientation();
- const Vector& hisPosition = target->GetPosition();
- const Vector& hisVelocity = target->GetVelocity() * skill;
- IprojectileTypeIGC* pt = weapon->GetProjectileType();
- assert (pt);
- return solveForImpact(hisPosition - (myPosition + weapon->GetPosition() * myOrientation),
- pt->GetAbsoluteF()
- ? hisVelocity
- : (hisVelocity - myVelocity),
- pt->GetSpeed(), target->GetRadius(),
- direction);
- }
- float turnToFace(const Vector& deltaTarget,
- float dt,
- IshipIGC* pship,
- ControlData* controls,
- float skill)
- {
- float deltaAngle;
- const IhullTypeIGC* pht = pship->GetHullType();
- assert (controls);
- controls->jsValues[c_axisRoll] = 0.0f; //Ships never try to roll
- const Orientation& myOrientation = pship->GetOrientation();
- double cosTurn = myOrientation.CosForward(deltaTarget);
- if (cosTurn <= -0.999)
- {
- //Target is almost exactly behind, just yaw
- //(and assume the time increment is small enough
- //that yawing at max is appropriate).
- controls->jsValues[c_axisYaw] = 1.0f;
- controls->jsValues[c_axisPitch] = 0.0f;
- deltaAngle = pi;
- }
- else
- {
- float yaw;
- float pitch;
- if (cosTurn < 0.98)
- {
- //Target is somewhere other than in directly in front
- deltaAngle = (float)acos(cosTurn);
- //Find the vector we'd like to rotate about
- Vector twist = CrossProduct(myOrientation.GetBackward(), deltaTarget).Normalize();
- yaw = -(twist * myOrientation.GetUp()) * deltaAngle;
- pitch = (twist * myOrientation.GetRight()) * deltaAngle;
- }
- else
- {
- //The target is almost directly in front of us (within 11 degrees or so)
- //which would make getting the twist axis above a little dicey
- //So ... instead ... get the yaw and pitch off of the angles with the right * up
- yaw = acos(myOrientation.CosRight(deltaTarget)) - 0.5f * pi;
- pitch = acos(myOrientation.CosUp(deltaTarget)) - 0.5f * pi;
- deltaAngle = (float)sqrt(yaw * yaw + pitch * pitch);
- }
- //Adjust the yaw and pitch by the amount we are going to drift (due to our current
- //turning rates).
- {
- float tm = pship->GetTorqueMultiplier();
- float mass = pship->GetMass();
- assert (mass > 0.0f);
- {
- float yawRate = pship->GetCurrentTurnRate(c_axisYaw);
- yaw -= (float)(skill * fabs(yawRate) * (0.5f * yawRate * mass / (tm * pht->GetTurnTorque(c_axisYaw))));
- }
- {
- float pitchRate = pship->GetCurrentTurnRate(c_axisPitch);
- pitch -= (float)(skill * fabs(pitchRate) * (0.5f * pitchRate * mass / (tm * pht->GetTurnTorque(c_axisPitch))));
- }
- }
- //How do we want to set the controls so that we will turn to face the desired goal
- {
- float maxYaw = dt * pht->GetMaxTurnRate(c_axisYaw);
- float maxPitch = dt * pht->GetMaxTurnRate(c_axisPitch);
- float y = yaw / maxYaw;
- float p = pitch / maxPitch;
- float d2 = (y * y + p * p);
- if (d2 > 1.0f)
- {
- float f = (float)(1.0 / sqrt(d2));
- controls->jsValues[c_axisYaw] = y * f;
- controls->jsValues[c_axisPitch] = p * f;
- }
- else
- {
- controls->jsValues[c_axisYaw] = y;
- controls->jsValues[c_axisPitch] = p;
- }
- }
- }
- return deltaAngle;
- }
- bool FindableModel(ImodelIGC* m,
- IsideIGC* pside,
- int ttMask,
- AbilityBitMask abmAbilities)
- {
- bool okF = false;
- //You never target yourself or something marked as hidden
- ObjectType type = m->GetObjectType();
- if (GetTypebits(type) & ttMask)
- {
- IsideIGC* pHisSide = m->GetSide();
- int sidebits = (pHisSide == NULL)
- ? c_ttNeutral
- : ((pside == pHisSide)
- ? c_ttFriendly
- : c_ttEnemy);
- if ((sidebits & ttMask) ||
- ((type == OT_probe) && ((abmAbilities & c_eabmRescueAny) != 0) &&
- ((IprobeIGC*)m)->GetProbeType()->HasCapability(c_eabmRescueAny)))
- {
- if (abmAbilities != 0)
- {
- switch (type)
- {
- case OT_ship:
- {
- if (((IshipIGC*)m)->GetParentShip() == NULL)
- {
- IhullTypeIGC* pht = ((IshipIGC*)m)->GetBaseHullType();
- okF = pht && pht->HasCapability(abmAbilities);
- }
- else
- okF = false;
- }
- break;
- case OT_station:
- {
- okF = ((IstationIGC*)m)->GetStationType()->HasCapability(abmAbilities);
- }
- break;
- case OT_asteroid:
- {
- okF = ((IasteroidIGC*)m)->HasCapability(abmAbilities);
- }
- break;
- case OT_treasure:
- {
- okF = ((ItreasureIGC*)m)->GetTreasureCode() == c_tcFlag;
- }
- break;
- case OT_probe:
- {
- okF = ((IprobeIGC*)m)->GetProbeType()->HasCapability(abmAbilities);
- }
- break;
- default:
- assert (false);
- }
- }
- else
- okF = ((type != OT_ship) || (((IshipIGC*)m)->GetParentShip() == NULL));
- }
- }
- return okF;
- }
- static bool IsFriendlyCluster(IclusterIGC* pcluster, IsideIGC* pside)
- {
- StationLinkIGC* psl = pcluster->GetStations()->first();
- if (psl == NULL)
- return false; //No stations == unfriendly
- bool rc = false;
- do
- {
- IstationIGC* ps = psl->data();
- if ((!ps->GetStationType()->HasCapability(c_sabmPedestal)) &&
- ps->SeenBySide(pside))
- {
- if (pside != ps->GetSide())
- return false; //enemy has a station == unfriendly
- rc = true;
- }
- psl = psl->next();
- }
- while (psl != NULL);
- //It has stations but no enemy stations ... therefore at least one friendly station
- return rc;
- }
- struct ClusterPosition
- {
- IclusterIGC* pcluster;
- const Vector* pposition;
- };
- typedef Slist_utl<ClusterPosition> CPList;
- typedef Slink_utl<ClusterPosition> CPLink;
- static bool UniqueCP(CPList* cpl, IclusterIGC* pc)
- {
- for (CPLink* l = cpl->first(); (l != NULL); l = l->next())
- {
- if (l->data().pcluster == pc)
- return false;
- }
- return true;
- }
- static void NewCP(CPList* cpl, IclusterIGC* pc, const Vector* pposition)
- {
- CPLink* l = new CPLink;
- l->data().pcluster = pc;
- l->data().pposition = pposition;
- cpl->last(l);
- }
- ImodelIGC* FindTarget(IshipIGC* pship,
- int ttMask,
- ImodelIGC* pmodelCurrent,
- IclusterIGC* pcluster,
- const Vector* pposition,
- const Orientation* porientation,
- AbilityBitMask abmAbilities,
- int maxDistance)
- {
- if (!pcluster)
- pcluster = pship->GetCluster();
- if (!pcluster)
- return NULL;
- IsideIGC* pside = pship ? pship->GetSide() : NULL;
- if (pmodelCurrent && ((pmodelCurrent == pship) ||
- (!FindableModel(pmodelCurrent, pside, ttMask, abmAbilities)) ||
- (!pship->CanSee(pmodelCurrent))))
- pmodelCurrent = NULL;
- if (((ttMask & (c_ttFront | c_ttNearest)) == 0) && !pmodelCurrent && (pship || pposition))
- ttMask |= c_ttNearest;
- //This is a hack (which works only because the lists are lists of interface pointers)
- //so that, if we are only looking for a single type of target, we only search the list
- //of those types of targets.
- const ModelListIGC* models = ((ttMask & c_ttAllTypes) == c_ttShip)
- ? (const ModelListIGC*)(pcluster->GetShips())
- : (((ttMask & c_ttAllTypes) == c_ttStation)
- ? (const ModelListIGC*)(pcluster->GetStations())
- : (((ttMask & c_ttAllTypes) == c_ttWarp)
- ? (const ModelListIGC*)(pcluster->GetWarps())
- : ((ttMask & c_ttAllTypes) == c_ttTreasure)
- ? (const ModelListIGC*)(pcluster->GetTreasures())
- : pcluster->GetPickableModels()));
- assert (models);
- ImodelIGC* pmodelTarget = NULL;
- if (models->n() != 0)
- {
- int ttBest = (ttMask & (c_ttFront | c_ttNearest | c_ttLeastTargeted));
- if (ttMask & (c_ttFront | c_ttNearest))
- {
- if (!pposition)
- pposition = &(pship->GetPosition());
- if ((ttMask & c_ttFront) && !porientation)
- porientation = &(pship->GetOrientation());
- }
-
- ModelLinkIGC* mLink = models->first();
- if (pmodelCurrent)
- {
- //Find the link (in the model list) that corresponds to the current target
- while ((mLink != NULL) && (mLink->data() != pmodelCurrent))
- mLink = mLink->next();
- // mLink = mLink && (mLink != models->last()) ? mLink->next() : models->first();
- if (ttMask&c_ttPrevious)
- mLink = (mLink && (mLink != models->first())) ? mLink->txen() : models->last();
- else
- mLink = (mLink && (mLink != models->last())) ? mLink->next() : models->first();
- }
- assert (mLink);
- float capacity;
- if ((abmAbilities & c_aabmMineHe3) != 0)
- capacity = pside->GetMission()->GetFloatConstant(c_fcidCapacityHe3);
- //Search through the entire container list, starting at the link beyond mLink, wrapping around
- //until back at mLink for a ship or station hub in the same sector as the ship.
- int nTargeting = 0x7fff; //surely we can do better than this
- float distance = FLT_MAX;
- ModelLinkIGC* l = mLink;
- do
- {
- ImodelIGC* m = l->data();
- //You never target yourself or something marked as hidden
- if ((m != pship) && ((!pship) || pship->CanSee(m)) && FindableModel(m, pside, ttMask, abmAbilities))
- {
- if (ttBest)
- {
- int n;
- float d;
- if (ttMask & (c_ttFront | c_ttNearest))
- {
- //intentionally backwards so low cosforwards == in front
- Vector dp = (*pposition) - m->GetPosition();
- d = (ttMask & c_ttFront)
- ? porientation->CosForward2(dp)
- : dp.LengthSquared();
- }
- bool bReplace;
- if (ttMask & c_ttLeastTargeted)
- {
- assert ((ttMask & c_ttAllTypes) == c_ttAsteroid);
- assert (m->GetObjectType() == OT_asteroid);
- n = 0;
- {
- //Count the number of drones on this side building or mining this asteroid
- for (ShipLinkIGC* psl = pside->GetShips()->first(); (psl != NULL); psl = psl->next())
- {
- IshipIGC* ps = psl->data();
- if (ps->GetPilotType() < c_ptPlayer)
- {
- if ((ps->GetCommandTarget(c_cmdAccepted) == m) &&
- (ps->GetCommandID(c_cmdAccepted) >= c_cidMine))
- {
- n++;
- }
- }
- }
- }
- bReplace = ((n < nTargeting) || ((n == nTargeting) && (d < distance)));
- if (bReplace && ((abmAbilities & c_aabmMineHe3) != 0))
- {
- float ore = ((IasteroidIGC*)m)->GetOre() - float(n) * capacity;
- if (ore <= capacity * 0.25f)
- bReplace = false;
- }
- }
- else
- bReplace = (d < distance);
- if (bReplace)
- {
- nTargeting = n;
- distance = d;
- //Don't replace a valid (if less optimal) target with the
- //current one unless there is no target (pmodelCurrent is
- //always checked last).
- if ((m != pmodelCurrent) || (pmodelTarget == NULL))
- pmodelTarget = m;
- }
- }
- else
- {
- //and that is all we need ...
- pmodelTarget = m;
- break;
- }
- }
- if (ttMask & c_ttPrevious)
- l = (l == models->first()) ? models->last() : l->txen();
- else
- l = (l == models->last()) ? models->first() : l->next();
- }
- while (l != mLink);
- }
- if ((pmodelTarget) ||
- ((ttMask & c_ttAnyCluster) == 0) ||
- ((ttMask & (c_ttStation | c_ttAsteroid | c_ttTreasure | c_ttWarp)) == 0) ||
- (maxDistance == 0))
- return pmodelTarget;
- IclusterIGC* pclusterStart = pcluster;
- int distance = 0;
- {
- //Search adjacent clusters for an appropriate target
- WarpListIGC warpsOne;
- WarpListIGC warpsTwo;
- ClusterListIGC clustersVisited;
- WarpListIGC* pwlOneAway = &warpsOne;
- WarpListIGC* pwlTwoAway = &warpsTwo;
- clustersVisited.first(pcluster); //We've already visited this cluster
- //None of these bits make any sense when searching a remote cluster
- ttMask &= ~(c_ttAnyCluster | c_ttFront | c_ttShip | c_ttBuoy | c_ttMissile);
- bool firstMatchF = false;
- do
- {
- assert (pcluster);
- //Push the destinations of the warps in pcluster onto the end the list of
- //warps that are an extra jump away
- {
- for (WarpLinkIGC* l = pcluster->GetWarps()->first(); (l != NULL); l = l->next())
- {
- IwarpIGC* w = l->data();
- if ((!pship) || pship->CanSee(w))
- {
- IwarpIGC* pwarpDestination = w->GetDestination();
- if (pwarpDestination)
- {
- IclusterIGC* pclusterOther = pwarpDestination->GetCluster();
- //Have we visited pclusterOther?
- if (clustersVisited.find(pclusterOther) == NULL)
- {
- //No
- if (((ttMask & c_ttCowardly) == 0) || IsFriendlyCluster(pclusterOther, pside))
- pwlTwoAway->last(pwarpDestination);
- }
- }
- }
- }
- }
- //Find the next cluster to search
- if (pwlOneAway->n() == 0)
- {
- if ((pwlTwoAway->n() == 0) || (distance++ >= maxDistance))
- {
- //No place left to search
- break;
- }
- else
- {
- //No clusters in the current distance bracket ... start on the clusters in the next distance bracket
- WarpListIGC* pwl = pwlOneAway;
- pwlOneAway = pwlTwoAway;
- pwlTwoAway = pwl;
- }
- }
- assert (pwlOneAway->n() > 0);
- WarpLinkIGC* plink = (ttMask & c_ttPrevious) ? pwlOneAway->last() : pwlOneAway->first();
- IwarpIGC* pwarp = plink->data();
- delete plink;
- pposition = &(pwarp->GetPosition());
- pcluster = pwarp->GetCluster();
- clustersVisited.first(pcluster);
- pmodelTarget = FindTarget(pship, ttMask, NULL, pcluster, pposition, NULL, abmAbilities);
- //Skip over the existing target once
- if ((pmodelTarget != NULL) &&
- (pmodelTarget == pmodelCurrent) &&
- firstMatchF)
- {
- pmodelTarget = NULL;
- firstMatchF = false;
- }
- }
- while (pmodelTarget == NULL);
- }
- if (!pship || (ttMask & c_ttNoRipcord) ||
- (pship->GetBaseHullType() == NULL) ||
- (pship->GetFlag() != NA))
- {
- return pmodelTarget;
- }
- const IhullTypeIGC* pht = pship->GetHullType();
- HullAbilityBitMask habmShip = pht->GetCapabilities();
- if (habmShip & c_habmNoRipcord)
- return pmodelTarget;
- float ripcordSpeed = pht->GetRipcordSpeed();
- //OK ... the ship can rip. See if there is something "closer" via a ripcord.
- //First, make a list of all clusters that contain a ripcord
- CPList clustersRipcord;
- {
- for (StationLinkIGC* psl = pside->GetStations()->first(); (psl != NULL); psl = psl->next())
- {
- IstationIGC* ps = psl->data();
- if (ps->GetStationType()->HasCapability(c_sabmRipcord))
- {
- IclusterIGC* pc = ps->GetCluster();
- if ((pc != pclusterStart) && UniqueCP(&clustersRipcord, pc) &&
- (((ttMask & c_ttCowardly) == 0) || IsFriendlyCluster(pc, pside)))
- NewCP(&clustersRipcord, ps->GetCluster(), &(ps->GetPosition()));
- }
- }
- }
- if (pship->GetPilotType() >= c_ptPlayer) //non-players don't tp to probes or ships
- {
- HullAbilityBitMask habm = (habmShip & c_habmCanLtRipcord)
- ? (c_habmIsRipcordTarget | c_habmIsLtRipcordTarget)
- : c_habmIsRipcordTarget;
- ImissionIGC* pmission = pship->GetMission();
- IIgcSite* pigc = pmission->GetIgcSite();
- for (ShipLinkIGC* psl = pside->GetShips()->first(); (psl != NULL); psl = psl->next())
- {
- IshipIGC* ps = psl->data();
- if (ps != pship)
- {
- IclusterIGC* pc = pigc->GetRipcordCluster(ps, habm);
- if (pc && (pc != pclusterStart) && UniqueCP(&clustersRipcord, pc) &&
- (((ttMask & c_ttCowardly) == 0) || IsFriendlyCluster(pc, pside)))
- NewCP(&clustersRipcord, pc, NULL);
- }
- }
- for (ClusterLinkIGC* pcl = pmission->GetClusters()->first(); (pcl != NULL); pcl = pcl->next())
- {
- IclusterIGC* pc = pcl->data();
- if ((pc != pclusterStart) && UniqueCP(&clustersRipcord, pc) &&
- (((ttMask & c_ttCowardly) == 0) || IsFriendlyCluster(pc, pside)))
- {
- for (ProbeLinkIGC* ppl = pc->GetProbes()->first(); (ppl != NULL); ppl = ppl->next())
- {
- IprobeIGC* pprobe = ppl->data();
- if ((pprobe->GetSide() == pside) && pprobe->GetCanRipcord(ripcordSpeed))
- {
- NewCP(&clustersRipcord, pc, &(pprobe->GetPosition()));
- break;
- }
- }
- }
- }
- }
- //Now ... check each cluster to see if there is something closer
- ttMask |= c_ttNoRipcord | c_ttAnyCluster;
- distance--;
- int distanceBest = 0x7fffffff;
- for (CPLink* pcl = clustersRipcord.first(); (pcl != NULL); pcl = pcl->next())
- {
- ImodelIGC* pmodel = FindTarget(pship, ttMask, NULL, pcl->data().pcluster, pcl->data().pposition, NULL, abmAbilities, distance);
- if (pmodel)
- {
- IclusterIGC* pc = pmodel->GetCluster();
- assert (pc);
- int d = GetDistance(pship, pclusterStart, pc, distanceBest);
- if (d < distanceBest)
- {
- distanceBest = d;
- pmodelTarget = pmodel;
- }
- }
- }
- return pmodelTarget;
- }
- struct Path
- {
- IwarpIGC* pwarpStart;
- IwarpIGC* pwarp;
- float distance;
- };
- typedef Slist_utl<Path> PathList;
- typedef Slink_utl<Path> PathLink;
- IwarpIGC* FindPath(IshipIGC* pship,
- IclusterIGC* pclusterTarget,
- bool bCowardly)
- {
- assert (pship);
- IsideIGC* pside = pship->GetSide();
- IclusterIGC* pclusterCurrent = pship->GetCluster();
- assert (pclusterCurrent);
- if (pclusterCurrent == pclusterTarget)
- return NULL;
- const Vector& positionShip = pship->GetPosition();
- ClusterListIGC explored;
- PathList unexplored;
- explored.last(pclusterCurrent);
- {
- //Add the initial warps ... distance from the player to the aleph
- const WarpListIGC* pwarps = pclusterCurrent->GetWarps();
- for (WarpLinkIGC* wLink = pwarps->first(); (wLink != NULL); wLink = wLink->next())
- {
- IwarpIGC* pwarp = wLink->data();
- if (pship->CanSee(pwarp))
- {
- assert (pwarp->GetDestination());
- IclusterIGC* pclusterDestination = pwarp->GetDestination()->GetCluster();
- if ((!bCowardly) ||
- (pclusterTarget == pclusterDestination) ||
- IsFriendlyCluster(pclusterDestination, pside))
- {
- PathLink* pl = new PathLink;
- assert (pl);
- Path& path = pl->data();
- path.distance = (pwarp->GetPosition() - positionShip).LengthSquared();
- path.pwarpStart = path.pwarp = pwarp;
- //Keep the list sorted
- PathLink* p = unexplored.first();
- while (true)
- {
- if (p == NULL)
- {
- //Nothing left ... to the end of the list
- unexplored.last(pl);
- break;
- }
- else if (path.distance < p->data().distance)
- {
- //Insert in front of the existing link (which has a greater distance)
- p->txen(pl);
- break;
- }
- p = p->next();
- }
- }
- }
- }
- if (unexplored.n() == 0)
- return NULL;
- }
- while (true)
- {
- PathLink* plinkClosest = unexplored.first();
- if (!plinkClosest)
- return NULL; //Never found a path
- const Path& path = plinkClosest->data();
- IwarpIGC* pwarp = path.pwarp;
- IclusterIGC* pclusterNext = pwarp->GetDestination()->GetCluster();
- if (pclusterNext == pclusterTarget)
- {
- //Found a path to the target
- return path.pwarpStart;
- }
- explored.last(pclusterNext);
- //Add warps for the warp in the new cluster (that do not lead to a previously explored cluster)
- //Add all of the warps in this cluster to the unexplored list
- for (WarpLinkIGC* pwl = pclusterNext->GetWarps()->first(); (pwl != NULL); pwl = pwl->next())
- {
- IwarpIGC* pwarp = pwl->data();
- if (pship->CanSee(pwarp))
- {
- IclusterIGC* pclusterDestination = pwarp->GetDestination()->GetCluster();
- if ((!bCowardly) ||
- (pclusterDestination == pclusterTarget) ||
- IsFriendlyCluster(pclusterDestination, pside))
- {
- if (explored.find(pclusterDestination) == NULL)
- {
- PathLink* ppl = new PathLink;
- assert (ppl);
- Path& ppathNew = ppl->data();
- ppathNew.pwarpStart = path.pwarpStart;
- ppathNew.pwarp = pwarp;
- unexplored.last(ppl);
- }
- }
- }
- }
- delete plinkClosest;
- }
- }
- IwarpIGC* FindPath(IshipIGC* pShip,
- ImodelIGC* pTarget,
- bool bCowardly)
- {
- assert (pShip);
- assert (pTarget);
- IclusterIGC* pclusterTarget = pTarget->GetMission()->GetIgcSite()->GetCluster(pShip, pTarget);
- return pclusterTarget ? FindPath(pShip, pclusterTarget, bCowardly) : NULL;
- }
- bool SearchClusters(ImodelIGC* pmodel,
- void* pdata,
- bool (*pfnCluster)(IclusterIGC* pcluster,
- void* pdata))
- {
- assert (pmodel);
- bool rc;
- IclusterIGC* pcluster = pmodel->GetCluster();
- if (pcluster)
- {
- if (pfnCluster(pcluster, pdata))
- {
- //That was easy: success on the first cluster checked
- rc = true;
- }
- else
- {
- }
- }
- else
- rc = false;
- return rc;
- }
- const char* GetModelType(ImodelIGC* pmodel)
- {
- switch (pmodel->GetObjectType())
- {
- case OT_ship:
- return ((IshipIGC*)pmodel)->GetHullType()->GetName();
- case OT_projectile:
- return "";
- return "minefield";
- case OT_station:
- return ((IstationIGC*)pmodel)->GetStationType()->GetName();
- case OT_buoy:
- return "";
- case OT_asteroid:
- return IasteroidIGC::GetTypeName(((IasteroidIGC*)pmodel)->GetCapabilities());
- case OT_warp:
- return "aleph";
- case OT_treasure:
- {
- IbuyableIGC* pb = ((ItreasureIGC*)pmodel)->GetBuyable();
- if (pb)
- return pb->GetName();
- else
- {
- static const char* szNames[] = {"", "", "Powerup", "", "Cash", ""};
- TreasureCode tc = ((ItreasureIGC*)pmodel)->GetTreasureCode();
- assert ((tc == c_tcPowerup) || (tc == c_tcCash) || (tc == c_tcFlag));
- return szNames[tc];
- }
- }
- case OT_mine:
- case OT_probe:
- return pmodel->GetName() + 1; //Special hack where probes & mines always have a secondary name
- default:
- {
- assert (pmodel->GetObjectType() == OT_missile);
- return ((ImissileIGC*)pmodel)->GetMissileType()->GetName();
- }
- }
- }
- const char* GetModelName(ImodelIGC* pmodel)
- {
- assert (pmodel);
- const char* n = pmodel->GetName();
- if (n[0] != '\0')
- return n;
- else
- {
- ObjectType type = pmodel->GetObjectType();
- return ((type == OT_asteroid) || (type == OT_buoy)) ? (n + 1) : GetModelType(pmodel);
- }
- }
- AmmoState GetAmmoState(IshipIGC* pship)
- {
- assert (pship);
- //speed hack ... assume that 500 rounds is enough for anything without checking guns
- short ammo = pship->GetAmmo();
- if (ammo >= 500)
- return c_asFull;
- float consumption = 0.0f;
- //Go through the ship's mounted weapons and count up the number of bullets it needs/second
- for (Mount i = pship->GetHullType()->GetMaxWeapons() - 1; (i >= 0); i--)
- {
- IweaponIGC* w = (IweaponIGC*)(pship->GetMountedPart(ET_Weapon, i));
- if (w)
- {
- short aps = w->GetAmmoPerShot();
- ammo -= aps;
- consumption += ((float)aps) / w->GetDtBurst();
- }
- }
- return (consumption == 0.0f)
- ? c_asFull
- : ((ammo <= 0)
- ? c_asEmpty
- : (((float)ammo) / consumption) < 10.0f ? c_asLow : c_asFull);
- }
- static const GotoPositionMask c_gpmKillThrottle = 0x01;
- static const GotoPositionMask c_gpmFinished = 0x02;
- static const GotoPositionMask c_gpmPivot = 0x04;
- static const GotoPositionMask c_gpmEnter = 0x08;
- static const GotoPositionMask c_gpmNoDodge = 0x10;
- static const GotoPositionMask c_gpmFast = 0x40;
- static const GotoPositionMask c_gpmDodgeShips = 0x80;
- static const GotoPositionMask c_gpmRoll = 0x100;
- static const float c_fOffsetFudge = 10.0f;
- static const char c_stateSeek = 0;
- static const char c_stateCoast = 1;
- static const char c_statePivot = 2;
- static const char c_stateEnter = 3;
- GotoPositionMask Waypoint::DoApproach(IshipIGC* pship,
- const Vector& myPosition,
- const Vector& itsPosition,
- int nLand,
- const Vector* pCenters,
- const Vector* pDirections,
- float distanceRest,
- const Vector& positionRest,
- Vector* pvectorGoto,
- ImodelIGC** ppmodelSkip,
- Vector* pvectorFacing)
- {
- GotoPositionMask gpm;
- assert (nLand > 0);
- //The best bay is the one where the distance between our rest position and the approach strip is the smallest
- Vector dpNow = myPosition - itsPosition;
- Vector dpRest = positionRest - itsPosition;
- float myRadius = (pship->GetRadius() + m_pmodelTarget->GetRadius() + 10.0f); //my radius plus a fudge factor
- const Vector* pdirectionBest;
- const Vector* pcenterBest;
- Vector goalBest;
- float error2Best = FLT_MAX;
- float offset2Best;
- int bayBest;
- for (int i = 0; (i < nLand); i++)
- {
- const Vector* pcenter = pCenters + i;
- const Vector* pdirection = pDirections + i;
- //This is where we want to go
- Vector goal;
- {
- //Find t such that
- //|*pcenter + *pdirection * t| = R
- //(pc + pd * t)^2 = R^2
- // pc^2 + 2pd pc t + pd^2 t^2 - r^2 = 0
- assert (pdirection->LengthSquared() >= 0.98f);
- assert (pdirection->LengthSquared() <= 1.02f);
- float b = *pcenter * *pdirection;
- float c = *pcenter * *pcenter - myRadius * myRadius;
- assert (c < 0.0f);
- float t = sqrt(b*b - c) - b;
- assert (t >= 0.0f);
- goal = *pcenter + *pdirection * t;
- }
- //Look at where both our current position and reset positions are with respect to the line
- Vector deltaNow = (dpNow - *pcenter);
- float dotNow = deltaNow * *pdirection;
- Vector deltaRest = (dpRest - *pcenter);
- float dotRest = (deltaRest * *pdirection);
- float error2;
- float offset2;
- if ((dotNow >= 0.0f) || (dotRest >= myRadius))
- {
- //Ship is infront of the bay ... error is distance between
- //respostion and approach line
- error2 = deltaRest.LengthSquared() - dotRest * dotRest;
- offset2 = deltaNow.LengthSquared() - dotNow * dotNow;
- }
- else
- {
- //Ship is behind (or not far enough in front) ... error is
- //distance from rest position and approach point
- error2 = (dpRest - goal).LengthSquared();
- offset2 = FLT_MAX;
- }
- if (error2 < error2Best)
- {
- pdirectionBest = pdirection;
- pcenterBest = pcenter;
- goalBest = goal;
- error2Best = error2;
- offset2Best = offset2;
- bayBest = i;
- }
- }
- if (error2Best > 36.0f)
- {
- //Not able to drift into position .. continue normally
- *ppmodelSkip = NULL;
- *pvectorGoto = goalBest + itsPosition;
- float d2 = (dpNow - goalBest).LengthSquared();
- if ((d2 > distanceRest * distanceRest * 2.0f) ||
- ((dpRest - goalBest).LengthSquared() <= d2 + 1.0f))
- {
- //We are moving in more or less the correct direction
- gpm = ((dpNow - *pcenterBest) * *pdirectionBest >= 0.0f) ? c_gpmDodgeShips : 0;
- }
- else
- {
- //Our rest position is further from the goal than our current position
- *pvectorFacing = *pvectorGoto - myPosition;
- gpm = c_gpmPivot;
- }
- }
- else
- {
- //Our rest position is close to the line ... are we close to the line of approach?
- if (offset2Best > 40.0f)
- {
- //We are not close to the approach line ... coast in
- gpm = c_gpmKillThrottle | c_gpmDodgeShips;
- *ppmodelSkip = NULL;
- *pvectorGoto = goalBest + itsPosition;
- }
- else
- {
- //We are on the line and either not moving or moving along the line
- *ppmodelSkip = m_pmodelTarget;
- *pvectorGoto = *pcenterBest + itsPosition;
- *pvectorFacing = *pvectorGoto - myPosition;
- float rateYaw = pship->GetCurrentTurnRate(c_axisYaw);
- float ratePitch = pship->GetCurrentTurnRate(c_axisPitch);
- float rate2 = rateYaw * rateYaw + ratePitch * ratePitch;
- static const float rateMax = 0.01f;
- static const float cosMin = 0.999f * 0.999f;
- if ((rate2 < rateMax * rateMax) && (pship->GetOrientation().CosForward2(*pvectorFacing) > cosMin))
- {
- //On track, going the right way and not turning .... charge
- gpm = c_gpmEnter | c_gpmNoDodge;
- }
- else
- {
- //On track and almost stopped ... pivot to face the bay
- gpm = c_gpmPivot | c_gpmDodgeShips;
- }
- }
- }
- return gpm;
- }
- GotoPositionMask Waypoint::GetGotoPosition(IshipIGC* pship,
- float distanceRest,
- const Vector& positionRest,
- Vector* pvectorGoto,
- ImodelIGC** ppmodelSkip,
- Vector* pvectorFacing)
- {
- assert (pship);
- const Vector& myPosition = pship->GetPosition();
- GotoPositionMask gpm;
- assert (m_pmodelTarget);
- const Vector& itsPosition = m_pmodelTarget->GetPosition();
- if (m_objective == Waypoint::c_oGoto)
- {
- Vector vRest = itsPosition - positionRest;
- float dRest2 = vRest.LengthSquared();
- Vector vCenters = itsPosition - myPosition;
- float dCenters2 = vCenters.LengthSquared();
- float radius = m_pmodelTarget->GetRadius() + pship->GetRadius();
-
- //First ... is there a danger of colliding with the object?
- float radiusRest = radius + distanceRest;
- if (dCenters2 <= radiusRest * radiusRest)
- {
- //Yes there is ... pivot
- gpm = c_gpmPivot;
- *pvectorFacing = vCenters;
- *ppmodelSkip = NULL;
- }
- else
- {
- //No danger of a collision
- *ppmodelSkip = m_pmodelTarget;
- float offset = radius + c_fOffsetFudge;
- if (dRest2 < offset * offset)
- {
- if (distanceRest < 0.5f)
- {
- gpm = (c_gpmPivot | c_gpmFinished);
- *pvectorFacing = vCenters;
- }
- else
- {
- gpm = c_gpmKillThrottle;
- *pvectorGoto = itsPosition;
- }
- }
- else
- {
- gpm = 0;
- *pvectorGoto = itsPosition;
- }
- }
- }
- else
- {
- switch (m_pmodelTarget->GetObjectType())
- {
- case OT_asteroid:
- {
- Vector centers[2];
- Vector directions[2];
- centers[0].x = centers[0].y = centers[0].z = 0.0f;
- centers[1].x = centers[1].y = centers[1].z = 0.0f;
- const Rotation& r = m_pmodelTarget->GetRotation();
- directions[0] = r.axis();
- directions[1] = -r.axis();
- gpm = DoApproach(pship, myPosition, itsPosition,
- 2, centers, directions,
- distanceRest,
- positionRest,
- pvectorGoto,
- ppmodelSkip,
- pvectorFacing);
- if (gpm & c_gpmEnter)
- {
- gpm |= c_gpmFinished;
- }
- }
- break;
- case OT_ship:
- {
- //Trying to pick up a ship ... where will he be when we get there?
- Vector direction;
- Vector dp = itsPosition - myPosition;
- Vector dv = m_pmodelTarget->GetVelocity();
- float t = solveForImpact(dp, dv,
- pship->GetHullType()->GetMaxSpeed(), 0.0f, &direction);
-
- gpm = c_gpmFast;
- *pvectorGoto = itsPosition + t * dv;
- *ppmodelSkip = m_pmodelTarget;
- }
- break;
- case OT_probe:
- {
- gpm = c_gpmFast;
- *pvectorGoto = itsPosition;
- *ppmodelSkip = m_pmodelTarget;
- }
- break;
- case OT_station:
- {
- //Trying to dock ...
- IhullTypeIGC* pht = pship->GetBaseHullType();
- assert (pht);
- const IstationTypeIGC* pst = ((IstationIGC*)m_pmodelTarget)->GetStationType();
- {
- int nLand = pht->HasCapability(c_habmFighter)
- ? pst->GetLandSlots()
- : pst->GetCapLandSlots();
- if (nLand == 0)
- {
- //No landing bays ... power glide in
- gpm = c_gpmFast;
- *pvectorGoto = itsPosition;
- *ppmodelSkip = m_pmodelTarget;
- }
- else
- {
- assert (nLand > 0);
- assert (nLand <= c_maxLandSlots);
- Vector centers[c_maxLandSlots];
- Vector directions[c_maxLandSlots];
- const Orientation& itsOrientation = m_pmodelTarget->GetOrientation();
- for (int i = 0; (i < nLand); i++)
- {
- centers[i] = pst->GetLandPosition(i, 0) * itsOrientation;
- directions[i] = pst->GetLandDirection(i, 0) * itsOrientation;
- }
- gpm = DoApproach(pship, myPosition, itsPosition,
- nLand, centers, directions,
- distanceRest,
- positionRest,
- pvectorGoto,
- ppmodelSkip,
- pvectorFacing);
- if (gpm & c_gpmEnter)
- {
- gpm |= c_gpmRoll;
- }
- }
- }
- }
- break;
- default:
- {
- gpm = c_gpmFast;
- *pvectorGoto = itsPosition;
- *ppmodelSkip = m_pmodelTarget;
- }
- break;
- }
- }
- return gpm;
- }
- /*-------------------------------------------------------------------------
- * Function: getDirection
- *-------------------------------------------------------------------------
- * Purpose:
- * Get the button control mask to thrust in a given direction
- */
- static int getDirection(const Vector& dP,
- const Orientation& orientation)
- {
- float z = dP * orientation.GetBackward();
- float y = dP * orientation.GetUp();
- float x = dP * orientation.GetRight();
- double absX = fabs(x);
- double absY = fabs(y);
- double absZ = fabs(z);
- int stateX = (x >= 0.0f ? rightButtonIGC : leftButtonIGC);
- int stateY = (y >= 0.0f ? upButtonIGC : downButtonIGC);
- int stateZ = (z <= 0.0f ? forwardButtonIGC : backwardButtonIGC);
- const float c_fT = 2.0f;
- #define GetState(a,b,c) (abs##a > c_fT * abs##b) \
- ? state##a \
- : (abs##a > c_fT * abs##c) \
- ? (state##a | state##b) \
- : (stateX | stateY | stateZ)
- int state;
- if (absX > absY)
- {
- //x > y
- if (absY > absZ)
- {
- //x > y > z
- state = GetState(X, Y, Z);
- }
- else if (absX > absZ)
- {
- // x > z > y
- state = GetState(X, Z, Y);
- }
- else
- {
- //z > x > y
- state = GetState(Z, X, Y);
- }
- }
- else
- {
- //y > x
- if (absX > absZ)
- {
- //y > x > z
- state = GetState(Y, X, Z);
- }
- else if (absY > absZ)
- {
- // y > z > x
- state = GetState(Y, Z, X);
- }
- else
- {
- //z > y > x
- state = GetState(Z, Y, X);
- }
- }
- #undef GetState
- return state;
- }
- bool Ignore(IshipIGC* pship, ImodelIGC* pmodel)
- {
- bool ignore = false;
- IsideIGC* mySide = pship->GetSide();
- IsideIGC* hisSide = pmodel->GetSide();
- ObjectType type = pmodel->GetObjectType();
- if (type == OT_ship)
- {
- IshipIGC* pshipHim = (IshipIGC*)pmodel;
- IhullTypeIGC* phtHim = pshipHim->GetBaseHullType();
- if (phtHim == NULL)
- ignore = true;
- else
- {
- if (mySide == hisSide)
- {
- if ((pshipHim->GetObjectID() < pship->GetObjectID()) && //he has a lower ship ID
- (pship->GetPilotType() < c_ptPlayer) && //if we are a drone &
- (pshipHim->GetPilotType() < c_ptPlayer) && //he a drone that
- ((pshipHim->GetStateM() & miningMaskIGC) == 0) && //isn't mining
- (pshipHim->GetRipcordModel() == NULL)) //isn't ripcording
- {
- ignore = true; //ignore him
- }
- else
- {
- HullAbilityBitMask habmHim = phtHim->GetCapabilities();
- HullAbilityBitMask habmMe = pship->GetBaseHullType()->GetCapabilities();
- ignore = ((habmMe & c_habmLifepod) && (habmHim & c_habmRescue)) ||
- ((habmMe & c_habmRescue) && (habmHim & c_habmLifepod)) ||
- ((habmMe & c_habmCarrier) && (habmHim & c_habmLandOnCarrier));
- }
- }
- else if (pship->GetPilotType() < c_ptPlayer)
- ignore = true; //trucker rules of road: we're heavier
- }
- }
- else if ((type == OT_mine) && (mySide == hisSide)) //We can ignore friendly minefields
- {
- ignore = true;
- }
- return ignore;
- }
- bool Dodge(IshipIGC* pship,
- ImodelIGC* pmodelIgnore,
- int* pstate,
- bool bShipsOnly,
- float tMax)
- {
- IclusterIGC* pcluster = pship->GetCluster();
- assert (pcluster);
- const Vector& myPosition = pship->GetPosition();
- const Vector& myVelocity = pship->GetVelocity();
- const Orientation& myOrientation = pship->GetOrientation();
- float speed = myVelocity.Length();
- float myRadius = pship->GetRadius() + 1.0f;
- float myAcceleration = pship->GetHullType()->GetThrust() / pship->GetMass();
- HitTest* myHitTest = pship->GetHitTest();
- ImodelIGC* pmodelCollide = NULL;
- float tCollide = FLT_MAX;
- for (ModelLinkIGC* pml = (bShipsOnly
- ? (ModelLinkIGC*)(pcluster->GetShips()->first())
- : pcluster->GetPickableModels()->first());
- (pml != NULL); pml = pml->next())
- {
- ImodelIGC* pmodel = pml->data();
- if ((pmodel != pship) &&
- (pmodel != pmodelIgnore) &&
- (pmodel->GetHitTest()->GetNoHit() != myHitTest))
-
- {
- //Ignore the appropriate things plus minefields if we are going slowly enough.
- if (!Ignore(pship, pmodel) && ((speed > 50.0f) || (pmodel->GetObjectType() != OT_mine)))
- {
- //Get the distance of closest approach
- const Vector& hisPosition = pmodel->GetPosition();
- const Vector& hisVelocity = pmodel->GetVelocity();
- float r = pmodel->GetRadius() + myRadius;
- Vector dp = hisPosition - myPosition;
- float d2 = dp.LengthSquared();
- Vector direction;
- float t = (d2 > r * r)
- ? solveForImpact(dp,
- hisVelocity - myVelocity,
- 0.0f,
- r,
- &direction)
- : 0.0f;
- assert (t >= 0.0f);
- if (t < tCollide)
- {
- if (tMax >= 0.0f
- ? (t <= tMax)
- : (t*t <= (4.0f * r / myAcceleration)))
- {
- pmodelCollide = pmodel;
- tCollide = t;
- }
- }
- }
- }
- }
- if (pmodelCollide)
- {
- // We are going to collide: find our positions at closest approach
- const Vector dp = myPosition - pmodelCollide->GetPosition();
- Vector ca;
- if (tCollide == 0.0f)
- ca = dp; //Already inside the object ... dodge straight away from its center
- else
- {
- const Vector dv = myVelocity - pmodelCollide->GetVelocity();
- float ldv2 = dv.LengthSquared();
- ca = ldv2 > 0.1f
- ? (dp - dv * ((dp * dv) / ldv2))
- : dp;
- if (ca.LengthSquared() < 0.1f)
- {
- //Aimed straight at the center of the object ... dodge in a nice
- //orthogonal direction to the original displacement
- ca = dp.GetOrthogonalVector();
- }
- }
- // Thrust along the away vector
- *pstate = getDirection(ca, myOrientation);
- return true;
- }
- return false;
- }
- bool GotoPlan::Execute(Time now, float dt, bool bDodge)
- {
- int stateM;
- ControlData controls;
- bool bDone = SetControls(dt, bDodge, &controls, &stateM);
- const int c_maneuverButtons = backwardButtonIGC |
- forwardButtonIGC |
- leftButtonIGC |
- rightButtonIGC |
- upButtonIGC |
- downButtonIGC |
- afterburnerButtonIGC |
- coastButtonIGC |
- oneWeaponIGC | //Disable any weapon fire
- allWeaponsIGC;
- m_pship->SetStateBits(c_maneuverButtons, stateM);
- m_pship->SetControls(controls);
- return bDone;
- }
- bool GotoPlan::SetControls(float dt, bool bDodge, ControlData* pcontrols, int* pstate)
- {
- bool bDone;
- *pstate = 0;
- pcontrols->Reset();
- if (m_maskWaypoints != 0)
- {
- assert (m_wpTarget.m_pmodelTarget);
- IclusterIGC* pcluster = m_pship->GetCluster();
- assert (pcluster);
- if (pcluster != m_pvOldCluster)
- {
- //The ship's cluster changed ... recalculate goals from the original goal
- m_wpWarp.Reset();
- m_maskWaypoints = c_wpTarget;
- //See if the goal was to enter a warp
- if (m_wpTarget.m_pmodelTarget &&
- (m_wpTarget.m_objective == Waypoint::c_oEnter) &&
- (m_wpTarget.m_pmodelTarget->GetObjectType() == OT_warp) &&
- (((IwarpIGC*)(m_wpTarget.m_pmodelTarget))->GetDestination()->GetCluster() == pcluster))
- {
- m_wpTarget.Reset();
- m_maskWaypoints = 0;
- return true;
- }
- m_pvOldCluster = pcluster;
- }
- const Vector& myPosition = m_pship->GetPosition();
- const Vector& myVelocity = m_pship->GetVelocity();
- const Orientation& myOrientation = m_pship->GetOrientation();
- float speed = myVelocity.Length();
- float myRadius = m_pship->GetRadius() + 2.0f;
- bDone = false;
- IclusterIGC* pclusterTarget = m_pship->GetMission()->GetIgcSite()->GetCluster(m_pship, m_wpTarget.m_pmodelTarget);
- //No point in going towards a dead target or a target we can not see
- if (pclusterTarget)
- {
- if (pcluster != pclusterTarget)
- {
- if (((m_maskWaypoints & c_wpWarp) == 0) ||
- (pclusterTarget != m_pvOldClusterTarget))
- {
- //The target is in a different cluster from the ship and either we have no
- //warp waypoint selected or the target changed clusters. Either way ... recalculate
- //the warp waypoint
- //Clear the old intermediate waypoints
- m_wpWarp.Reset();
- m_pvOldClusterTarget = pclusterTarget;
- bool bCoward = (m_pship->GetPilotType() < c_ptCarrier);
- IwarpIGC* pwarp = FindPath(m_pship, pclusterTarget, bCoward);
- if (bCoward && (pwarp == NULL))
- pwarp = FindPath(m_pship, pclusterTarget, false);
- if (pwarp)
- {
- m_wpWarp.Set(Waypoint::c_oEnter, pwarp);
- m_maskWaypoints = c_wpTarget | c_wpWarp;
- }
- else
- {
- //Do not know of a warp to the intended target ... keep the original target around & clear the warp waypoint
- m_maskWaypoints = c_wpTarget;
- if (bDodge)
- Dodge(m_pship, NULL, pstate);
- return false;
- }
- }
- }
- else if (m_wpTarget.m_pmodelTarget->GetCluster() == NULL)
- {
- //We are in the target cluster but the target is not here ... probably because it is docked.
- //wait and do nothing (but clear the warp waypoint since we're in the right cluster.
- m_maskWaypoints = c_wpTarget;
- if (bDodge)
- Dodge(m_pship, NULL, pstate);
- return false;
- }
- const IhullTypeIGC* pht = m_pship->GetHullType();
- double backMultiplier = pht->GetBackMultiplier();
- double vMax = pht->GetMaxSpeed() * backMultiplier;
- double thrustMax = pht->GetThrust() * backMultiplier;
- double mass = m_pship->GetMass();
- double k = thrustMax / (mass * vMax);
- //Calculate the ship's rest position if it kills its throttle
- float distanceRest = 0.0f;
- Vector positionRest = myPosition;
- if (speed > 0.1f)
- {
- //If we killed our throttle, where would we drift to?
- //From the differential equations:
- // dV/dT = -k V(t) + A
- //
- // A = acceleration = thrust / mass
- // k = drag coefficient = thrustMax / (vMax * mass)
- //
- // Vterminal = A / k
- //
- // V(t) = Vterminal + (V(0) - Vterminal) * exp(-k t)
- //In this particular case ... the ship is decelerating,
- //so Vterminal = -vMax
- //Solve V(t) = 0 = Vterminal + (V(0) - Vterminal) * exp(-k t)
- //-Vterminal = (V(0) - Vterminal) exp (-k t) or, where Vmax = -Vterminal & speed = V(0)
- // Vmax = (speed + Vmax) exp (-kt)
- //t = ln(vMax / (v0 + vMax)) / -k
- double expmkt = vMax / (speed + vMax);
- double mkt = log(expmkt);
- //S(t) = Integral (0 -> t) (V(t) dt)
- // = s0 + Vterminal (t - 0) + (speed - Vterminal) * (1/-k) (exp(-kt) - exp(0))
- // = 0 +
- // Vmax * -kt / k +
- // (speed + Vmax) * (1.0 - exp(-kt)) / k
- //
- // But ... -kt = ln (Vmax / (speed + Vmax)), so
- // S(t) = Vmax * -kt / k + (speed * Vmax) * (1 - Vmax / (speed + Vmax)) / k
- // = Vmax * -kt/k + speed/k
- // = (Vmax * -kt + speed) / k
- distanceRest = float((vMax * mkt + speed) / k);
- positionRest += myVelocity * (distanceRest / speed);
- }
- GotoPositionMask gpm;
- Vector positionGoto;
- ImodelIGC* pmodelSkip;
- Vector facing;
- assert (m_maskWaypoints & c_wpTarget);
- gpm = ((m_maskWaypoints & c_wpWarp) ? m_wpWarp : m_wpTarget).GetGotoPosition(m_pship,
- distanceRest,
- positionRest,
- &positionGoto,
- &pmodelSkip,
- &facing);
- //First priority ... dodge
- if (((gpm & c_gpmNoDodge) == 0) && bDodge)
- {
- if (Dodge(m_pship, pmodelSkip, pstate, (gpm & c_gpmDodgeShips) != 0))
- {
- if ((gpm & (c_gpmPivot | c_gpmEnter)) != 0)
- {
- if (facing * facing >= 0.1f)
- turnToFace(facing, dt, m_pship, pcontrols, m_fSkill);
- }
- else
- {
- Vector path = positionGoto - myPosition;
- if (path * path >= 0.1f)
- turnToFace(path, dt, m_pship, pcontrols, m_fSkill);
- }
- return false;
- }
- }
- pcontrols->jsValues[c_axisThrottle] = -1.0f;
- if (((gpm & c_gpmFinished) != 0) && (m_wpTarget.m_pmodelTarget->GetObjectType() != OT_ship))
- {
- m_wpWarp.Reset();
- m_wpTarget.Reset();
- m_maskWaypoints = 0;
- bDone = true;
- }
- else if ((gpm & c_gpmPivot) != 0)
- {
- //Pivot implies kill throttle
- if (facing * facing > 0.1f)
- turnToFace(facing, dt, m_pship, pcontrols, m_fSkill);
- }
- else if ((gpm & c_gpmKillThrottle) != 0)
- {
- Vector path = positionGoto - myPosition;
- if (path * path >= 0.1f)
- turnToFace(path, dt, m_pship, pcontrols, m_fSkill);
- }
- else if ((gpm & c_gpmEnter) != 0)
- {
- if (facing * facing > 0.1f)
- turnToFace(facing, dt, m_pship, pcontrols, m_fSkill);
- if ((gpm & c_gpmRoll) != 0)
- {
- float rollMax2 = 1.0f -
- pcontrols->jsValues[c_axisPitch] * pcontrols->jsValues[c_axisPitch] -
- pcontrols->jsValues[c_axisYaw] * pcontrols->jsValues[c_axisYaw];
- //What is the angle we'd like to roll? Is the angle between the ship's Y axis and the global z-axis
- const Orientation& o = m_pship->GetOrientation();
- double cosRoll = o.GetUp().z; //Dot product of the up vector with (0,0,1)
- if ((cosRoll < 0.99) && (cosRoll > -0.99))
- {
- float roll = float((cosRoll >= 0.0) ? -acos(cosRoll) : acos(-cosRoll));
- if (o.GetRight().z < 0.0f)
- roll = -roll;
- float tm = m_pship->GetTorqueMultiplier();
- const IhullTypeIGC* pht = m_pship->GetHullType();
- float mass = m_pship->GetMass();
- assert (mass > 0.0f);
- float rollRate = m_pship->GetCurrentTurnRate(c_axisRoll);
- roll -= (float)(m_fSkill * fabs(rollRate) * (0.5f * rollRate * mass / (tm * pht->GetTurnTorque(c_axisRoll))));
- float maxRoll= dt * pht->GetMaxTurnRate(c_axisRoll);
- double r = roll / maxRoll;
- double d2 = pcontrols->jsValues[c_axisPitch] * pcontrols->jsValues[c_axisPitch] +
- pcontrols->jsValues[c_axisYaw] * pcontrols->jsValues[c_axisYaw];
- if (d2 + r * r > 1.0)
- {
- float f = float(sqrt(1.0 - d2));
- pcontrols->jsValues[c_axisRoll] = (r > 0.0) ? f : -f;
- }
- else
- pcontrols->jsValues[c_axisRoll] = float(r);
- }
- }
- pcontrols->jsValues[c_axisThrottle] = 1.0f;
- }
- else
- {
- //We want to go someplace and we're not coasting to a stop (which implies we got a long way to go)
- //see if we need to plot a course around anything in the meantime.
- Vector path = positionGoto - myPosition;
- float distance2 = path.LengthSquared();
- const float divertOffset = 10.0f;
- float dMax2 = distance2;
- ImodelIGC* pmodelDivert = NULL;
- for (ModelLinkIGC* pml = pcluster->GetPickableModels()->first();
- (pml != NULL);
- pml = pml->next())
- {
- ImodelIGC* pmodel = pml->data();
- if ((pmodel != m_pship) &&
- (pmodel != pmodelSkip) &&
- !Ignore(m_pship, pmodel))
- {
- ObjectType type = pmodel->GetObjectType();
- if ((type != OT_mine) &&
- ((type != OT_ship) || (pmodel->GetVelocity().LengthSquared() < 1.0f)))
- {
- //Vector to the center of the object
- const Vector& itsPosition = pmodel->GetPosition();
- Vector dp = itsPosition - myPosition;
- float d2 = dp.LengthSquared();
- if (d2 < dMax2) //Object is closer that our goal
- {
- float dot = dp * path;
- if (dot >= 0.0f) //Object is not behind us
- {
- float r = myRadius + pmodel->GetRadius() + divertOffset;
- float r2 = r*r;
- Vector closest = myPosition + path * (dot / distance2);
- Vector offset = closest - itsPosition;
- float offsetLength2 = offset.LengthSquared();
- if (offsetLength2 < r2)
- {
- pmodelDivert = pmodel;
- dMax2 = d2;
- }
- }
- }
- }
- }
- }
- if (pmodelDivert)
- {
- //We need to get around this object ...
- const Vector& itsPosition = pmodelDivert->GetPosition();
- Vector dp = itsPosition - myPosition;
- float dot = dp * path;
- assert (dot >= 0.0f);
- Vector closest = myPosition + path * (dot / distance2);
- Vector offset = closest - itsPosition;
- float offsetLength2 = offset.LengthSquared();
- //The object extends across our path ... recalculate a new path
- Vector cross;
- if (offsetLength2 >= 0.5f)
- {
- Vector n = CrossProduct(offset, dp);
- cross = CrossProduct(dp, n);
- }
- else
- cross = dp.GetOrthogonalVector();
- if (cross.LengthSquared() != 0)
- cross = cross.Normalize();
- else
- cross = Vector::RandomDirection();
-
- double rpf = myRadius + pmodelDivert->GetRadius() + divertOffset;
- double rpf2 = rpf*rpf;
-
- double dpLength2 = dp.LengthSquared();
- double sinTheta2 = rpf * rpf / dpLength2;
- if (sinTheta2 < 0.99)
- {
- double cosTheta2 = (1.0 - sinTheta2);
- //This is a vector tangent to the blocking object
- Vector tangent = dp + cross * float(sqrt(rpf2 / cosTheta2));
- float tangentLength2 = tangent.LengthSquared();
- float dot = dp * tangent;
- assert (dot > 0.0f);
- Vector pca = tangent * (dot / tangentLength2);
- Vector radial = (pca - dp).Normalize();
- path = pca + radial * float(rpf * 0.02);
- }
- else
- {
- //We are very close to the object ... turn 90
- path = cross * float(rpf);
- }
- }
- //Always face where we are going
- if (path * path >= 0.1f)
- {
- float da = turnToFace(path, dt, m_pship, pcontrols, m_fSkill);
- if (da < pi / 8.0f)
- {
- float fThrottleMax = 1.0f;
- if (((gpm & c_gpmFast) == 0) &&
- ((positionGoto - myPosition).LengthSquared() < distanceRest * distanceRest))
- {
- //We are close enough to want to slow down
- fThrottleMax = -1.0f;
- }
- else
- {
- //Are we headed through a minefield?
- IsideIGC* mySide = m_pship->GetSide();
- for (ModelLinkIGC* pml = pcluster->GetPickableModels()->first();
- (pml != NULL);
- pml = pml->next())
- {
- ImodelIGC* pmodel = pml->data();
- if ((pmodel->GetObjectType() == OT_mine) && (pmodel->GetSide() != mySide))
- {
- //Vector to the center of the object
- const Vector& itsPosition = pmodel->GetPosition();
- Vector dp = itsPosition - myPosition;
- float d2 = dp.LengthSquared();
- if (d2 < distance2) //Object is closer that our goal
- {
- float dot = dp * path;
- if (dot >= 0.0f) //Object is not behind us
- {
- float r = myRadius + pmodel->GetRadius();
- float rSlow = r + distanceRest;
- if (d2 <= rSlow * rSlow)
- {
- float r2 = r*r;
- Vector closest = myPosition + path * (dot / distance2);
- Vector offset = closest - itsPosition;
- float offsetLength2 = offset.LengthSquared();
- if (offsetLength2 < r2)
- {
- fThrottleMax = -0.5;
- break;
- }
- }
- }
- }
- }
- }
- }
- pcontrols->jsValues[c_axisThrottle] = fThrottleMax;
- }
- else
- *pstate = getDirection(path - myVelocity * float(1.0 / k), myOrientation);
- }
- else
- {
- pcontrols->Reset();
- }
- }
- }
- }
- else
- bDone = true;
- return bDone;
- }
- bool LineOfSightExist(const IclusterIGC* pcluster,
- const ImodelIGC* pmodel1,
- const ImodelIGC* pmodel2)
- {
- assert (pcluster);
- assert (pmodel1);
- assert (pmodel1->GetObjectType() != OT_asteroid);
- assert (pmodel2);
- assert (pmodel1->GetCluster() == pcluster);
- assert (pmodel2->GetCluster() == pcluster);
- // P1 is the eye,and P2 is the center of the target that we want to know whether or not is visible
- const Vector& P1 = pmodel1->GetPosition();
- const Vector& P2 = pmodel2->GetPosition();
- // compute the vector between the two points, get its squared length, the reciprocal of the length, and then normalize it
- Vector V12 = P2 - P1;
- float fLengthSquaredV12 = V12.LengthSquared (),
- fOverLengthV12 = 1.0f / sqrtf (fLengthSquaredV12);
- V12 *= fOverLengthV12;
- // compute the angle subtended by the target from the eye
- float fVisibleAngle = asinf (pmodel2->GetRadius() * fOverLengthV12);
- for (ModelLinkIGC* pml = ((ModelListIGC*)(pcluster->GetAsteroids()))->first(); (pml != NULL); pml = pml->next())
- {
- ImodelIGC* pmodel = pml->data();
- if (pmodel2 != pmodel)
- {
- // P3 is the center of the object that might obscure our view of P2
- const Vector& P3 = pmodel->GetPosition();
- // Compute the vector between the eye and the obstacle, and get its squared length.
- Vector V13 = P3 - P1;
- float fLengthSquaredV13 = V13.LengthSquared ();
- // if the obscruing object is closer than the target, then it might really
- // obscure the view of the target
- if (fLengthSquaredV13 < fLengthSquaredV12)
- {
- float dot = (V12 * V13);
- if (dot > 0.0f)
- {
- // If the dot product is negative, the obstacle is behind the eye and could not
- // possibly obscure the view (unless the eye is inside of the obscuring object,
- // which we hope won't happen).
- // compute the reciprocal of the length of the vector, and then normalize it
- float fOverLengthV13 = 1.0f / sqrtf (fLengthSquaredV13);
- //V13 *= fOverLengthV13;
- // Compute the dot product of the two vectors, this is the cosine of
- // the angle between them.
- float fCosineSeparationAngle = dot * fOverLengthV13;
- //assert (fCosineSeparationAngle > 0.0f); //This should be true, but it only takes a little round-off to spoil a day
- {
- // Get the radius of the obscuring model, and scale it to simulate a dense core
- // or variable geometry. We then compute the coverage angle of the obscuring
- // object.
- float fRadius3 = pmodel->GetRadius () * 0.5f;
- float fCoveredAngle = asinf (fRadius3 * fOverLengthV13);
- // Compute the separation angle of the line to the target and the line to the
- // obscuring object. The farthest angle from the obscuring object at which the
- // target can be oscured is the separation angle plus the visible angle.
- float fSeparationAngle = acosf (fCosineSeparationAngle);
- float fMaximumSeparationAngle = fSeparationAngle + fVisibleAngle;
- // If the farthest angle at which the target can be viewed is covered by the obscuring object,
- // then the target is not visible and we return false.
- if (fMaximumSeparationAngle < fCoveredAngle)
- return false;
- }
- }
- }
- }
- }
- return true;
- }
- IshipIGC* CreateDrone(ImissionIGC* pmission,
- ShipID shipID,
- PilotType pt,
- const char* pszName,
- HullID hullID,
- IsideIGC* pside,
- AbilityBitMask abmOrders,
- float shootSkill,
- float moveSkill,
- float bravery)
- {
- // Do IGC initialization:
- DataShipIGC ds;
- ds.shipID = shipID;
- ds.hullID = hullID;
- ds.sideID = pside->GetObjectID();
- //ds.wingID = 0;
- ds.nKills = 0;
- ds.nDeaths = 0;
- ds.pilotType = pt;
- ds.abmOrders = abmOrders;
- ds.baseObjectID = NA;
- if (pszName)
- {
- assert (strlen(pszName) < c_cbName - 4);
- strcpy(ds.name, pszName);
- //Is the name unique?
- ShipLinkIGC* psl;
- for (psl = pmission->GetShips()->first();
- ((psl != NULL) && (_stricmp(pszName, psl->data()->GetName()) != 0));
- psl = psl->next())
- {
- }
-
- if (psl != NULL)
- {
- //Name is not unique ... make it unique
- _itoa(shipID, ds.name + strlen(ds.name), 10);
- }
- }
- else
- ds.name[0] = '\0';
- ds.nParts = 0;
- IshipIGC* ship = (IshipIGC*)(pmission->CreateObject(pmission->GetLastUpdate(), // Make the IGC ship
- OT_ship, &ds,
- sizeof(DataShipIGC)));
- assert (ship);
- //Try to fill out the drone with the specified parts
- {
- IhullTypeIGC* pht = ship->GetBaseHullType();
- for (PartTypeLinkIGC* ptl = pht->GetPreferredPartTypes()->first();
- (ptl != NULL);
- ptl = ptl->next())
- {
- IpartTypeIGC* ppt = ptl->data();
- //Mount the part anyplace it can be mounted. Ignore price (included in the cost of the drone) & availability
- EquipmentType et = ppt->GetEquipmentType();
- Mount mountMax = (et == ET_Weapon)
- ? pht->GetMaxWeapons()
- : 1;
- for (Mount i = 0; (i < mountMax); i++)
- {
- if ((ship->GetMountedPart(et, i) == NULL) && pht->CanMount(ppt, i))
- ship->CreateAndAddPart(ppt, i, 0x7fff);
- }
- }
- }
- ship->SetAutopilot(true);
- ship->Release();
- return ship;
- }
- ClusterWarning GetClusterWarning(AssetMask am, bool bInvulnerableStations)
- {
- ClusterWarning cw;
- if ((am & c_amEnemyAPC) && (am & c_amStation) && !bInvulnerableStations)
- cw = c_cwStationCaptureThreat;
- else if ((am & (c_amEnemyTeleport | c_amEnemyTeleportShip)) && (am & c_amStation) && !bInvulnerableStations)
- cw = c_cwStationTeleportThreat;
- else if ((am & c_amEnemyBomber) && (am & c_amStation) && !bInvulnerableStations)
- cw = c_cwStationThreatened;
- else if ((am & c_amEnemyAPC) && !bInvulnerableStations)
- cw = c_cwTransportInCluster;
- else if (am & c_amEnemyTeleportShip)
- cw = c_cwTeleportInCluster;
- else if (am & c_amEnemyCapital)
- cw = c_cwCapitalInCluster;
- else if (am & c_amEnemyCarrier)
- cw = c_cwEnemyCarrierInCluster;
- else if ((am & c_amEnemyBomber) && !bInvulnerableStations)
- cw = c_cwBomberInCluster;
- else if ((am & c_amEnemyFighter) && (am & c_amCarrier))
- cw = c_cwCarrierThreatened;
- else if ((am & c_amEnemyFighter) && (am & c_amBuilder))
- cw = c_cwBuilderThreatened;
- else if ((am & c_amEnemyFighter) && (am & c_amMiner))
- cw = c_cwMinerThreatened;
- else if ((am & c_amEnemyFighter) && (am & c_amFighter))
- cw = c_cwCombatInCluster;
- else if (am & c_amEnemyBuilder)
- cw = c_cwEnemyBuilderInCluster;
- else if (am & c_amEnemyMiner)
- cw = c_cwEnemyMinerInCluster;
- else if (am & c_amEnemyFighter)
- cw = c_cwEnemyFighterInCluster;
- else
- cw = c_cwNoThreat;
- return cw;
- }
- const char* GetClusterWarningText(ClusterWarning cw)
- {
- static const char* c_pszAlerts[c_cwMax] =
- {
- "No threat",
- "Enemy fighter spotted",
- "Enemy miner spotted",
- "Enemy builder spotted",
- "Conflict",
- "Miner at risk",
- "Builder at risk",
- "Carrier at risk",
- "Enemy bomber spotted",
- "Enemy carrier spotted",
- "Enemy capital ship spotted",
- "Enemy assault ship spotted",
- "Enemy transport spotted",
- "Station at risk",
- "Station at risk by teleport",
- "Station at risk of capture"
- };
- assert(cw >= 0 && cw < c_cwMax);
- return c_pszAlerts[cw];
- }
- DamageTrack::DamageTrack(DamageTrackSet* pdts)
- :
- m_pset(pdts)
- {
- assert (pdts);
- pdts->AddTrack(this);
- }
- DamageTrack::~DamageTrack(void)
- {
- Reset();
- assert (m_pset);
- m_pset->DeleteTrack(this);
- }
- void DamageTrack::SwitchSlots(void)
- {
- DamageBucketLink* l = m_buckets.first();
- while (l)
- {
- //Prefetch the next pointer
- DamageBucketLink* lNext = l->next();
- DamageBucket* db = l->data();
- db->SwitchSlots(m_pset->m_idSlot);
- if (db->totalDamage() < 0.5f) //Allow a generous amount for roundoff errors
- delete db; //Also deletes the link
- l = lNext;
- }
- //We could sort here ... but let's not. Do a lazy sort the next time we take damage
- }
- void DamageTrack::ApplyDamage(Time timeNow,
- ImodelIGC* pmodel,
- float damage)
- {
- DamageBucketLink* l;
- for (l = m_buckets.first();
- ((l != NULL) && (l->data()->model() != pmodel));
- l = l->next())
- {
- }
- DamageBucket* pBucket;
- if (l == NULL)
- {
- //No bucket existed for the model ... create one
- pBucket = new DamageBucket(this, pmodel);
- }
- else
- pBucket = l->data();
- pBucket->ApplyDamage(timeNow, m_pset->m_idSlot, damage);
- DamageTrack::sort(&m_buckets);
- }
- void DamageTrack::Reset(void)
- {
- DamageBucketLink* pdbl;
- while (pdbl = m_buckets.first()) //Intentional
- delete pdbl->data();
- }
- void DamageTrack::sort(DamageBucketList* pListBuckets)
- {
- if (pListBuckets->n() > 1)
- {
- //Sort the elements of the bucket list which is almost sorted
- DamageBucketLink* p = pListBuckets->first();
- while (true)
- {
- DamageBucketLink* next = p->next();
- if (!next)
- break;
-
- if (p->data()->totalDamage() < next->data()->totalDamage())
- {
- //*p < *next (& therefore either p or next is out of order)
- //Move next forward as much as is needed to re-establish the sort of all
- //of the elements between the start of the list and p
- next->unlink();
- DamageBucketLink* back = p->txen();
- while ((back) && (back->data()->totalDamage() >= next->data()->totalDamage()))
- {
- back = back->txen();
- }
- if (back)
- {
- //back has done more damage than next ... therefore next should be after it.
- back->next(next);
- }
- else
- {
- //couldn't find a bucket with a higher damage total than next ...
- //put next at the front of the list.
- pListBuckets->first(next);
- }
- }
- else
- p = next;
- }
- }
- }
- void PlayerScoreObject::CalculateScore(ImissionIGC* pmission)
- {
- if (m_dtPlayed == 0.0f)
- return;
- float kMax = m_dtPlayed / (15.0f * 60.0f); //1.0 / 15 minutes
-
- m_fScore = float(m_cWarpsSpotted) * pmission->GetFloatConstant(c_fcidPointsWarp) +
- float(m_cAsteroidsSpotted) * pmission->GetFloatConstant(c_fcidPointsAsteroid) +
- m_cTechsRecovered * pmission->GetFloatConstant(c_fcidPointsTech) +
- (m_cMinerKills * kMax) * pmission->GetFloatConstant(c_fcidPointsMiner) / (m_cMinerKills + kMax) +
- (m_cBuilderKills * kMax) * pmission->GetFloatConstant(c_fcidPointsBuilder) / (m_cBuilderKills + kMax) +
- (m_cLayerKills * kMax) * pmission->GetFloatConstant(c_fcidPointsLayer) / (m_cLayerKills + kMax) +
- (m_cCarrierKills * kMax) * pmission->GetFloatConstant(c_fcidPointsCarrier) / (m_cCarrierKills + kMax) +
- m_cPlayerKills * pmission->GetFloatConstant(c_fcidPointsPlayer) +
- (m_cBaseKills * kMax) * pmission->GetFloatConstant(c_fcidPointsBaseKill) / (m_cBaseKills + kMax) +
- (m_cBaseCaptures * kMax) * pmission->GetFloatConstant(c_fcidPointsBaseCapture) / (m_cBaseCaptures + kMax) +
- float(m_cRescues) * pmission->GetFloatConstant(c_fcidPointsRescues) +
- float(m_cArtifacts) * pmission->GetFloatConstant(c_fcidPointsArtifacts) +
- float(m_cFlags) * pmission->GetFloatConstant(c_fcidPointsFlags);
- if (m_bWin)
- m_fScore *= 2.0f;
- }
- float PlayerScoreObject::GetScore(void) const
- {
- return m_fScore;
- }
- void PlayerScoreObject::SetScore(float fNewScore)
- {
- m_fScore = fNewScore;
- }
- void PlayerScoreObject::AdjustCombatRating(ImissionIGC* pmission,
- PlayerScoreObject* ppsoKiller,
- PlayerScoreObject* ppsoKillee)
- {
- float k = (pmission->GetFloatConstant(c_fcidRatingAdd) +
- ppsoKillee->m_fCombatRating - ppsoKiller->m_fCombatRating) /
- pmission->GetFloatConstant(c_fcidRatingDivide);
- ppsoKiller->m_fCombatRating = ppsoKiller->m_fCombatRating * (1.0f - k) + k;
- ppsoKillee->m_fCombatRating = ppsoKillee->m_fCombatRating * (1.0f - k);
- }
- int GetDistance(IshipIGC* pship,
- IclusterIGC* pcluster,
- IclusterIGC* pclusterStop,
- int maxDistance)
- {
- assert (pcluster != pclusterStop);
- int distance = 1;
- //Search adjacent clusters for an appropriate target
- WarpListIGC warpsOne;
- WarpListIGC warpsTwo;
- ClusterListIGC clustersVisited;
- WarpListIGC* pwlOneAway = &warpsOne;
- WarpListIGC* pwlTwoAway = &warpsTwo;
- while (true)
- {
- assert (pcluster);
- clustersVisited.first(pcluster); //We've already visited this cluster
- //Push the destinations of the warps in pcluster onto the end the list of
- //warps that are an extra jump away
- {
- for (WarpLinkIGC* l = pcluster->GetWarps()->first(); (l != NULL); l = l->next())
- {
- IwarpIGC* w = l->data();
- if (pship->CanSee(w))
- {
- IwarpIGC* pwarpDestination = w->GetDestination();
- if (pwarpDestination)
- {
- IclusterIGC* pclusterOther = pwarpDestination->GetCluster();
- if (pclusterOther == pclusterStop)
- return distance;
- else if (clustersVisited.find(pclusterOther) == NULL)
- pwlTwoAway->last(pwarpDestination);
- }
- }
- }
- }
- //Find the next cluster to search
- if (pwlOneAway->n() == 0)
- {
- if ((pwlTwoAway->n() == 0) || (distance++ >= maxDistance))
- return 0x7fffffff;
- //No clusters in the current distance bracket ... start on the clusters in the next distance bracket
- WarpListIGC* pwl = pwlOneAway;
- pwlOneAway = pwlTwoAway;
- pwlTwoAway = pwl;
- }
- assert (pwlOneAway->n() > 0);
- WarpLinkIGC* plink = pwlOneAway->first();
- IwarpIGC* pwarp = plink->data();
- delete plink;
- pcluster = pwarp->GetCluster();
- }
- }
- static void GetAsteroidName(const char* pszPrefix,
- AsteroidID id,
- char* bfr)
- {
- const int c_iMultiplier = 5237;
- const int c_iModulo = 8713;
- const int c_iOffset = 1093;
- const int c_iPlus = 1024;
- int l = strlen(pszPrefix);
- if (l == 0)
- l = 2; //Allow for asteroids with no default name
- memcpy(bfr, pszPrefix, l);
- _itoa(c_iPlus + ((id + c_iOffset) * c_iMultiplier) % c_iModulo,
- &bfr[l], 10);
- }
- static Vector RandomPosition(const ModelListIGC* models, float rThing, float radius, float lens)
- {
- const float maxStationRadius = 550.0f;
- if (rThing < maxStationRadius)
- rThing = maxStationRadius;
- Vector position;
- ModelLinkIGC* pmlink;
- do
- {
- //Pick a random position for the asteroid
- //in a squashed disk
- position = Vector::RandomDirection();
- position *= radius * pow(random(0.0f, 1.0f), 1.0f/3.0f);
- position.z *= lens;
- //Verify that it is not close to any other model in the cluster
- for (pmlink = models->first(); (pmlink != NULL); pmlink = pmlink->next())
- {
- ImodelIGC* pm = pmlink->data();
- float r = pm->GetRadius();
- if (r < maxStationRadius)
- r = maxStationRadius;
- r += rThing;
- if ((pm->GetPosition() - position).LengthSquared() < 2.0f * r * r)
- {
- //Too close ... try again
- radius *= 1.02f;
- break;
- }
- }
- }
- while (pmlink);
- return position;
- }
- void CreateAsteroid(ImissionIGC* pmission,
- IclusterIGC* pcluster,
- int type,
- float amountHe3)
- {
- DataAsteroidIGC da;
- da.asteroidDef = IasteroidIGC::GetTypeDefaults(type);
- static const Vector xAxis(1.0, 0.0, 0.0);
- static const Vector zAxis(0.0, 0.0, 1.0);
- da.clusterID = pcluster->GetObjectID();
- da.forward = Vector::RandomDirection();
- da.up = CrossProduct(da.forward, xAxis);
- if (da.up.LengthSquared() <= 0.1f)
- da.up = CrossProduct(da.forward, zAxis);
- assert(da.up.LengthSquared() > 0.1f);
- da.up.SetNormalize();
- da.asteroidDef.radius = (short) randomInt(da.asteroidDef.radius, 2*da.asteroidDef.radius);
- da.signature = ((float) da.asteroidDef.radius) / 100.0f;
- da.fraction = 1.0f;
- da.rotation.axis(da.forward);
- da.rotation.angle(0.2f / da.signature);
- if (da.asteroidDef.ore != 0.0f)
- da.asteroidDef.ore *= amountHe3;
- da.asteroidDef.oreMax = da.asteroidDef.ore;
- da.asteroidDef.asteroidID = pmission->GenerateNewAsteroidID();
- GetAsteroidName(IasteroidIGC::GetTypePrefix(type),
- da.asteroidDef.asteroidID, da.name);
- da.position = RandomPosition(pcluster->GetModels(), da.asteroidDef.radius,
- pmission->GetFloatConstant(c_fcidRadiusUniverse) * 0.75f,
- pmission->GetFloatConstant(c_fcidLensMultiplier));
- IObject * o = pmission->CreateObject(pmission->GetLastUpdate(),
- OT_asteroid,
- &da,
- sizeof(da));
- assert (o);
- o->Release();
- }
- static IstationIGC* CreatePedestalAndFlag(ImissionIGC* pmission,
- SectorID clusterID,
- SideID sideID,
- const Vector& position,
- float dz)
- {
- Time now = pmission->GetLastUpdate();
- DataStationIGC ds;
- ds.clusterID = clusterID;
- ds.position = position;
- ds.position.z += dz * c_fPedestalOffset;
- ds.forward.x = ds.forward.y = 0.0f;
- ds.forward.z = dz;
- ds.up.x = 1.0f;
- ds.up.y = ds.up.z = 0.0f;
- ds.rotation.axis(ds.forward);
- ds.rotation.angle(0.0f);
- ds.bpHull = 1.0f;
- ds.bpShield = 0.0f;
- ds.sideID = sideID;
- ds.stationID = pmission->GenerateNewStationID();
- {
- IstationTypeIGC* ppedestal = pmission->GetStationTypes()->last()->data();
- assert (ppedestal->HasCapability(c_sabmPedestal));
- ds.stationTypeID = ppedestal->GetObjectID();
- strcpy(ds.name, ppedestal->GetName());
- }
- IstationIGC* pstation = (IstationIGC*)(pmission->CreateObject(now,
- OT_station,
- &ds,
- sizeof(ds)));
- assert(pstation);
- DataTreasureIGC dt;
- dt.treasureCode = c_tcFlag;
- dt.treasureID = sideID;
- dt.amount = 0;
- dt.clusterID = clusterID;
- dt.lifespan = 10.0f * 24.0f * 3600.0f;
- dt.objectID = pmission->GenerateNewTreasureID();
- dt.p0 = ds.position;
- dt.p0.z += (c_fFlagOffset + pstation->GetRadius()) * dz;
- dt.v0 = Vector::GetZero();
- dt.time0 = now;
- ItreasureIGC* t = (ItreasureIGC *)(pmission->CreateObject(now, OT_treasure,
- &dt, sizeof(dt)));
- assert (t);
- t->Release();
- //Bad form here ... but we know that the station's release count will not go to zero
- pstation->Release();
- return pstation;
- }
- void PopulateCluster(ImissionIGC* pmission,
- const MissionParams* pmp,
- IclusterIGC* pcluster,
- float amountHe3,
- bool bAsteroids,
- bool bTreasures,
- short nMineableAsteroidsMultiplier,
- short nSpecialAsteroidsMultiplier,
- short nAsteroidsMultiplier)
- {
- Time now = pmission->GetLastUpdate();
- SectorID clusterID = pcluster->GetObjectID();
- const StationListIGC* pstations = pcluster->GetStations();
- bool bHomeSector = pcluster->GetHomeSector();
- if (pmp->IsFlagsGame())
- {
- for (StationLinkIGC* psl = pstations->last(); (psl != NULL); psl = psl->txen())
- {
- IstationIGC* pstation = psl->data();
- IsideIGC* pside = pstation->GetSide();
- if (pstation->GetBaseStationType()->HasCapability(c_sabmStart))
- {
- SideID sideID = pside->GetObjectID();
- const Vector& position = pstation->GetPosition();
- CreatePedestalAndFlag(pmission, clusterID,
- sideID,
- position, 1.0f);
- CreatePedestalAndFlag(pmission, clusterID,
- sideID,
- position, -1.0f);
- }
- }
- }
- {
- //Make all stations invisible to all sides but their own
- const SideListIGC* psides = pmission->GetSides();
- for (StationLinkIGC* psl = pstations->first(); (psl != NULL); psl = psl->next())
- {
- IstationIGC* pstation = psl->data();
- IsideIGC* pside = pstation->GetSide();
- for (SideLinkIGC* p = psides->first(); (p != NULL); p = p->next())
- {
- if (p->data() != pside)
- pstation->SetSideVisibility(p->data(),
- false);
- }
- }
- }
- if (bAsteroids)
- {
- {
- short nMineableAsteroids;
- if (nMineableAsteroidsMultiplier >= 0)
- {
- nMineableAsteroids = bHomeSector
- ? pmp->nPlayerSectorMineableAsteroids
- : pmp->nNeutralSectorMineableAsteroids;
- nMineableAsteroids *= nMineableAsteroidsMultiplier;
- }
- else
- nMineableAsteroids = -nMineableAsteroidsMultiplier;
- for (short i = 0; (i < nMineableAsteroids); i++)
- {
- CreateAsteroid(pmission, pcluster, IasteroidIGC::GetRandomType(c_aabmMineHe3), amountHe3);
- }
- }
- {
- short nSpecialAsteroids;
- if (nSpecialAsteroidsMultiplier >= 0)
- {
- nSpecialAsteroids = bHomeSector
- ? pmp->nPlayerSectorSpecialAsteroids
- : pmp->nNeutralSectorSpecialAsteroids;
- nSpecialAsteroids *= nSpecialAsteroidsMultiplier;
- }
- else
- nSpecialAsteroids = -nSpecialAsteroidsMultiplier;
- int offset = IasteroidIGC::NumberSpecialAsteroids(pmp);
- if (offset != 1)
- offset = randomInt(1, offset);
- for (short i = 0; (i < nSpecialAsteroids); i++)
- {
- CreateAsteroid(pmission, pcluster, IasteroidIGC::GetSpecialAsterioid(pmp, offset++), 0.0f);
- }
- }
- {
- short nAsteroids;
- if (nAsteroidsMultiplier >= 0)
- {
- nAsteroids = bHomeSector
- ? pmp->nPlayerSectorAsteroids
- : pmp->nNeutralSectorAsteroids;
- nAsteroids *= nAsteroidsMultiplier;
- }
- else
- nAsteroids = -nAsteroidsMultiplier;
- for (short i = 0; (i < nAsteroids); i++)
- {
- CreateAsteroid(pmission, pcluster, IasteroidIGC::GetRandomType(c_aabmBuildable), 0.0f);
- }
- }
- }
- if (bTreasures)
- {
- short nTreasures;
- short tsi;
- if (bHomeSector)
- {
- nTreasures = pmp->nPlayerSectorTreasures;
- tsi = pmp->tsiPlayerStart;
- }
- else
- {
- if (pmp->IsArtifactsGame())
- {
- pmission->GenerateTreasure(now,
- pcluster,
- -2); //Special hack for flags
- }
- nTreasures = pmp->nNeutralSectorTreasures;
- tsi = pmp->tsiNeutralStart;
- }
- for (short i = 0; (i < nTreasures); i++)
- {
- pmission->GenerateTreasure(now,
- pcluster,
- tsi);
- }
- }
- }
|