#include "pch.h"
#include <math.h>
const char* c_pszWingName[c_widMax] =
{ "command",
"attack",
"defend",
"escort",
"search",
"alpha",
"bravo",
"charlie",
"delta",
"echo",
};
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
{
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))
{
t = (FLT_MAX / 500000000.0f); }
else
{
if (b24ac < 0.0f) {
debugf("common.cpp b24ac is less than zero about to sqrt it, it to 0.1f\n");
b24ac = 0.1f;
}
t = (b + (float)sqrt(b24ac)) / (-2.0f * a); if (t > 0.0f) {
*direction = (deltaP + t * deltaV) / (t * speed + radius); }
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; const Orientation& myOrientation = pship->GetOrientation();
double cosTurn = myOrientation.CosForward(deltaTarget);
if (cosTurn <= -0.999)
{
controls->jsValues[c_axisYaw] = 1.0f;
controls->jsValues[c_axisPitch] = 0.0f;
deltaAngle = pi;
}
else
{
float yaw;
float pitch;
if (cosTurn < 0.98)
{
deltaAngle = (float)acos(cosTurn);
Vector twist = CrossProduct(myOrientation.GetBackward(), deltaTarget).Normalize();
yaw = -(twist * myOrientation.GetUp()) * deltaAngle;
pitch = (twist * myOrientation.GetRight()) * deltaAngle;
}
else
{
yaw = acos(myOrientation.CosRight(deltaTarget)) - 0.5f * pi;
pitch = acos(myOrientation.CosUp(deltaTarget)) - 0.5f * pi;
deltaAngle = (float)sqrt(yaw * yaw + pitch * pitch);
}
{
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))));
}
}
{
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;
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; bool rc = false;
do
{
IstationIGC* ps = psl->data();
if ((!ps->GetStationType()->HasCapability(c_sabmPedestal)) &&
ps->SeenBySide(pside))
{
if (pside != ps->GetSide())
return false; rc = true;
}
psl = psl->next();
}
while (psl != NULL);
ImissionIGC* pmission = pside->GetMission(); const MissionParams* pmp = pmission->GetMissionParams(); if (!(pmp->bExperimental)) {
return rc; }
if(rc == false)
return rc;
if(pcluster->GetShips() != NULL)
{
int friendlyShipCount = 0;
for (ShipLinkIGC* psl = pcluster->GetShips()->first(); (psl != NULL); psl = psl->next()) {
IshipIGC* pship = psl->data();
if (pship->SeenBySide(pside) || pship->GetSide() == pside)
{
if (pside != pship->GetSide()) { friendlyShipCount--;
}
else { friendlyShipCount++;
}
}
}
if(friendlyShipCount>=0) {
rc = true; }
else
{
rc = false;
}
}
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;
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)
{
while ((mLink != NULL) && (mLink->data() != pmodelCurrent))
mLink = mLink->next();
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);
int nTargeting = 0x7fff; float distance = FLT_MAX;
ModelLinkIGC* l = mLink;
do
{
ImodelIGC* m = l->data();
if ((m != pship) && ((!pship) || pship->CanSee(m)) && FindableModel(m, pside, ttMask, abmAbilities))
{
if (ttBest)
{
int n = 0;
float d;
if (ttMask & (c_ttFront | c_ttNearest))
{
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;
{
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;
if ((m != pmodelCurrent) || (pmodelTarget == NULL))
pmodelTarget = m;
}
}
else
{
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;
{
WarpListIGC warpsOne;
WarpListIGC warpsTwo;
ClusterListIGC clustersVisited;
WarpListIGC* pwlOneAway = &warpsOne;
WarpListIGC* pwlTwoAway = &warpsTwo;
clustersVisited.first(pcluster); ttMask &= ~(c_ttAnyCluster | c_ttFront | c_ttShip | c_ttBuoy | c_ttMissile);
bool firstMatchF = false;
do
{
assert (pcluster);
{
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();
if (clustersVisited.find(pclusterOther) == NULL)
{
if (((ttMask & c_ttCowardly) == 0) || IsFriendlyCluster(pclusterOther, pside))
pwlTwoAway->last(pwarpDestination);
}
}
}
}
}
if (pwlOneAway->n() == 0)
{
if ((pwlTwoAway->n() == 0) || (distance++ >= maxDistance))
{
break;
}
else
{
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);
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();
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) {
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;
}
}
}
}
}
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);
{
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;
PathLink* p = unexplored.first();
while (true)
{
if (p == NULL)
{
unexplored.last(pl);
break;
}
else if (path.distance < p->data().distance)
{
p->txen(pl);
break;
}
p = p->next();
}
}
}
}
if (unexplored.n() == 0)
return NULL;
}
while (true)
{
PathLink* plinkClosest = unexplored.first();
if (!plinkClosest)
return NULL; const Path& path = plinkClosest->data();
IwarpIGC* pwarp = path.pwarp;
IclusterIGC* pclusterNext = pwarp->GetDestination()->GetCluster();
if (pclusterNext == pclusterTarget)
{
return path.pwarpStart;
}
explored.last(pclusterNext);
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 = false; IclusterIGC* pcluster = pmodel->GetCluster();
if (pcluster)
{
if (pfnCluster(pcluster, pdata))
{
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; 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);
short ammo = pship->GetAmmo();
if (ammo >= 500)
return c_asFull;
float consumption = 0.0f;
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);
Vector dpNow = myPosition - itsPosition;
Vector dpRest = positionRest - itsPosition;
float myRadius = (pship->GetRadius() + m_pmodelTarget->GetRadius() + 10.0f); const Vector* pdirectionBest;
const Vector* pcenterBest;
Vector goalBest;
float error2Best = FLT_MAX;
float offset2Best;
int bayBest;
ImissionIGC* pmission = pship->GetMission(); const MissionParams* pmp = pmission->GetMissionParams(); for (int i = 0; (i < nLand); i++)
{
const Vector* pcenter = pCenters + i;
const Vector* pdirection = pDirections + i;
Vector goal;
{
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=0.1f;
if ((b*b - c) < 0.0f) {
debugf("common.cpp b*b-c is less than zero about to sqrt it, set t to 0.1f\n");
} else { t = sqrt(b*b - c) - b; }
assert (t >= 0.0f);
goal = *pcenter + *pdirection * t;
}
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))
{
error2 = deltaRest.LengthSquared() - dotRest * dotRest;
offset2 = deltaNow.LengthSquared() - dotNow * dotNow;
}
else
{
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)
{
*ppmodelSkip = NULL;
*pvectorGoto = goalBest + itsPosition;
float d2 = (dpNow - goalBest).LengthSquared();
if ((d2 > distanceRest * distanceRest * 2.0f) ||
((dpRest - goalBest).LengthSquared() <= d2 + 1.0f))
{
gpm = ((dpNow - *pcenterBest) * *pdirectionBest >= 0.0f) ? c_gpmDodgeShips : 0;
}
else
{
*pvectorFacing = *pvectorGoto - myPosition;
gpm = c_gpmPivot;
}
}
else
{
if (offset2Best > 40.0f)
{
gpm = c_gpmKillThrottle | c_gpmDodgeShips;
*ppmodelSkip = NULL;
*pvectorGoto = goalBest + itsPosition;
}
else
{
*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;
float rateMax = 0.01f; if (pmp->bExperimental)
rateMax = 0.10f; static const float cosMin = 0.999f * 0.999f;
if ((rate2 < rateMax * rateMax) && (pship->GetOrientation().CosForward2(*pvectorFacing) > cosMin))
{
gpm = c_gpmEnter | c_gpmNoDodge;
}
else
{
gpm = c_gpmPivot | c_gpmNoDodge; }
}
}
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();
float radiusRest = radius + distanceRest;
if (dCenters2 <= radiusRest * radiusRest)
{
gpm = c_gpmPivot;
*pvectorFacing = vCenters;
*ppmodelSkip = NULL;
}
else
{
*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];
float uniquePosX = ((int)pship->GetObjectID() %10)/10;
float uniquePosY = ((int)pship->GetObjectID() %9)/9;
float uniquePosZ = ((int)pship->GetObjectID() %8)/8;
centers[0].x = uniquePosX;
centers[0].y = uniquePosY;
centers[0].z = uniquePosZ;
centers[1].x = uniquePosX;
centers[1].y = uniquePosY;
centers[1].z = uniquePosZ;
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:
{
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:
{
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)
{
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;
}
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)
{
if (absY > absZ)
{
state = GetState(X, Y, Z);
}
else if (absX > absZ)
{
state = GetState(X, Z, Y);
}
else
{
state = GetState(Z, X, Y);
}
}
else
{
if (absX > absZ)
{
state = GetState(Y, X, Z);
}
else if (absY > absZ)
{
state = GetState(Y, Z, X);
}
else
{
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()) && (pship->GetPilotType() < c_ptPlayer) && (pshipHim->GetPilotType() < c_ptPlayer) && ((pshipHim->GetStateM() & miningMaskIGC) == 0) && (pshipHim->GetRipcordModel() == NULL)) {
ignore = true; }
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; }
}
else if ((type == OT_mine) && (mySide == hisSide)) {
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))
{
if (!Ignore(pship, pmodel) && ((speed > 50.0f) || (pmodel->GetObjectType() != OT_mine)))
{
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)
{
const Vector dp = myPosition - pmodelCollide->GetPosition();
Vector ca;
if (tCollide == 0.0f)
ca = dp; 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)
{
ca = dp.GetOrthogonalVector();
}
}
*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 | 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)
{
m_wpWarp.Reset();
m_maskWaypoints = c_wpTarget;
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);
if (pclusterTarget)
{
if (pcluster != pclusterTarget)
{
if (((m_maskWaypoints & c_wpWarp) == 0) ||
(pclusterTarget != m_pvOldClusterTarget))
{
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
{
m_maskWaypoints = c_wpTarget;
if (bDodge)
Dodge(m_pship, NULL, pstate);
return false;
}
}
}
else if (m_wpTarget.m_pmodelTarget->GetCluster() == NULL)
{
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);
float distanceRest = 0.0f;
Vector positionRest = myPosition;
if (speed > 0.1f)
{
double expmkt = vMax / (speed + vMax);
double mkt = log(expmkt);
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);
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)
{
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)
{
const Orientation& o = m_pship->GetOrientation();
double cosRoll = o.GetUp().z; 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)
{
if (d2 > 1.0) {
d2=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
{
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)))
{
const Vector& itsPosition = pmodel->GetPosition();
Vector dp = itsPosition - myPosition;
float d2 = dp.LengthSquared();
if (d2 < dMax2) {
float dot = dp * path;
if (dot >= 0.0f) {
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)
{
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();
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);
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
{
path = cross * float(rpf);
}
}
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))
{
fThrottleMax = -1.0f;
}
else
{
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))
{
const Vector& itsPosition = pmodel->GetPosition();
Vector dp = itsPosition - myPosition;
float d2 = dp.LengthSquared();
if (d2 < distance2) {
float dot = dp * path;
if (dot >= 0.0f) {
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);
const Vector& P1 = pmodel1->GetPosition();
const Vector& P2 = pmodel2->GetPosition();
Vector V12 = P2 - P1;
float fLengthSquaredV12 = V12.LengthSquared (),
fOverLengthV12 = 1.0f / sqrtf (fLengthSquaredV12);
V12 *= fOverLengthV12;
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)
{
const Vector& P3 = pmodel->GetPosition();
Vector V13 = P3 - P1;
float fLengthSquaredV13 = V13.LengthSquared ();
if (fLengthSquaredV13 < fLengthSquaredV12)
{
float dot = (V12 * V13);
if (dot > 0.0f)
{
float fOverLengthV13 = 1.0f / sqrtf (fLengthSquaredV13);
float fCosineSeparationAngle = dot * fOverLengthV13;
{
float fRadius3 = pmodel->GetRadius () * 0.5f;
float fCoveredAngle = asinf (fRadius3 * fOverLengthV13);
float fSeparationAngle = acosf (fCosineSeparationAngle);
float fMaximumSeparationAngle = fSeparationAngle + fVisibleAngle;
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)
{
DataShipIGC ds;
ds.shipID = shipID;
ds.hullID = hullID;
ds.sideID = pside->GetObjectID();
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);
ShipLinkIGC* psl;
for (psl = pmission->GetShips()->first();
((psl != NULL) && (_stricmp(pszName, psl->data()->GetName()) != 0));
psl = psl->next())
{
}
if (psl != NULL)
{
_itoa(shipID, ds.name + strlen(ds.name), 10);
}
}
else
ds.name[0] = '\0';
ds.nParts = 0;
IshipIGC* ship = (IshipIGC*)(pmission->CreateObject(pmission->GetLastUpdate(), OT_ship, &ds,
sizeof(DataShipIGC)));
assert (ship);
{
IhullTypeIGC* pht = ship->GetBaseHullType();
for (PartTypeLinkIGC* ptl = pht->GetPreferredPartTypes()->first();
(ptl != NULL);
ptl = ptl->next())
{
IpartTypeIGC* ppt = ptl->data();
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)
{
DamageBucketLink* lNext = l->next();
DamageBucket* db = l->data();
db->SwitchSlots(m_pset->m_idSlot);
if (db->totalDamage() < 0.5f) delete db; l = lNext;
}
}
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)
{
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()) delete pdbl->data();
}
void DamageTrack::sort(DamageBucketList* pListBuckets)
{
if (pListBuckets->n() > 1)
{
DamageBucketLink* p = pListBuckets->first();
while (true)
{
DamageBucketLink* next = p->next();
if (!next)
break;
if (p->data()->totalDamage() < next->data()->totalDamage())
{
next->unlink();
DamageBucketLink* back = p->txen();
while ((back) && (back->data()->totalDamage() >= next->data()->totalDamage()))
{
back = back->txen();
}
if (back)
{
back->next(next);
}
else
{
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);
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;
WarpListIGC warpsOne;
WarpListIGC warpsTwo;
ClusterListIGC clustersVisited;
WarpListIGC* pwlOneAway = &warpsOne;
WarpListIGC* pwlTwoAway = &warpsTwo;
while (true)
{
assert (pcluster);
clustersVisited.first(pcluster); {
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);
}
}
}
}
if (pwlOneAway->n() == 0)
{
if ((pwlTwoAway->n() == 0) || (distance++ >= maxDistance))
return 0x7fffffff;
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; 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
{
position = Vector::RandomDirection();
position *= radius * pow(random(0.0f, 1.0f), 1.0f/3.0f);
position.z *= lens;
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)
{
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();
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);
}
}
}
{
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); }
nTreasures = pmp->nNeutralSectorTreasures;
tsi = pmp->tsiNeutralStart;
}
for (short i = 0; (i < nTreasures); i++)
{
pmission->GenerateTreasure(now,
pcluster,
tsi);
}
}
}