#include "pch.h"
float height = 30;
float base = 0.25f * height;
float c = sqrt(3.0f) / 2.0f;
float s = 0.5f;
float x = c * base;
float y = s * base;
float zn = 0.5f * height;
float zf = -0.5f * height;
Vector vector1( 0, base, zn);
Vector vector2(-x, -y, zn);
Vector vector3( x, -y, zn);
Vector vector4( 0, 0, zf);
float normalLength = sqrt(0.125f * 0.125f + 1.0f);
float normalPlane = 1.0f / normalLength;
float normalZ = 0.125f / normalLength;
Vector normal1( 0, 0, 1);
Vector normal2(-c * normalPlane, s * normalPlane, -normalZ);
Vector normal3( 0, -normalPlane, -normalZ);
Vector normal4( c * normalPlane, s * normalPlane, -normalZ);
Vertex g_pvertex[12] = {
Vertex(vector1, normal1),
Vertex(vector3, normal1),
Vertex(vector2, normal1),
Vertex(vector4, normal2),
Vertex(vector1, normal2),
Vertex(vector2, normal2),
Vertex(vector4, normal3),
Vertex(vector2, normal3),
Vertex(vector3, normal3),
Vertex(vector4, normal4),
Vertex(vector3, normal4),
Vertex(vector1, normal4),
};
WORD g_pindex[12] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
class IndicatorImage : public Image {
TRef<Surface> m_psurfaceDirection;
TRef<Surface> m_psurfaceDirectionIn;
TRef<Surface> m_psurfaceCenterInRange;
TRef<Surface> m_psurfaceCenterOutRange;
TRef<Surface> m_psurfaceLeadInRange;
TRef<Surface> m_psurfaceLeadOutRange;
TRef<Camera> m_pcamera;
TRef<Geo> m_pgeoPointer;
TRef<Material> m_pmaterial;
Viewport* GetViewport() { return Viewport::Cast(GetChild(0)); }
Camera* GetCamera() { return GetViewport()->GetCamera(); }
RectValue* GetViewRect() { return GetViewport()->GetViewRect(); }
Number* GetTime() { return Number::Cast(GetChild(1)); }
TRef<Surface> m_psurfaceTrainingOverlay;
public:
static TrainingOverlay s_trainingOverlay;
public:
IndicatorImage(Modeler* pmodeler, Viewport* pviewport, Number* ptime) :
Image(pviewport, ptime)
{
m_pcamera = new Camera();
m_pcamera->SetZClip(-10000, 10000);
m_psurfaceDirection = pmodeler->LoadSurface(AWF_FLIGHT_CURRENT_DIRECTION_ICON, true);
m_psurfaceDirectionIn = pmodeler->LoadSurface("directioninbmp", true);
m_psurfaceCenterInRange = pmodeler->LoadSurface(AWF_FLIGHT_CROSSHAIR_IN_ICON, true);
m_psurfaceCenterOutRange = pmodeler->LoadSurface(AWF_FLIGHT_CROSSHAIR_OUT_ICON, true);
m_psurfaceLeadInRange = pmodeler->LoadSurface(AWF_FLIGHT_LEAD_IN_ICON, true);
m_psurfaceLeadOutRange = pmodeler->LoadSurface(AWF_FLIGHT_LEAD_OUT_ICON, true);
m_psurfaceTrainingOverlay = pmodeler->LoadSurface (AWF_HUD_TRAINING_OVERLAY, true);
m_pgeoPointer = pmodeler->LoadGeo("pointer", true);
m_psurfaceDirection ->SetColorKey(Color(0, 0, 0));
m_psurfaceDirectionIn ->SetColorKey(Color(0, 0, 0));
m_psurfaceCenterInRange ->SetColorKey(Color(0, 0, 0));
m_psurfaceCenterOutRange->SetColorKey(Color(0, 0, 0));
m_psurfaceLeadInRange ->SetColorKey(Color(0, 0, 0));
m_psurfaceLeadOutRange ->SetColorKey(Color(0, 0, 0));
m_pmaterial = CreateMaterial(Color::Black());
}
void RenderDirectionIndicator(Context* pcontext)
{
const Vector& myVelocity = trekClient.GetShip()->GetSourceShip()->GetVelocity();
int speed = (int)(myVelocity.Length() + 0.5f);
if (GetWindow()->GetCameraMode() == TrekWindow::cmCockpit)
{
const Rect& rect = GetViewRect()->GetValue();
if (speed != 0)
{
Point pointImage;
if (GetCamera()->TransformDirectionToImage(myVelocity, pointImage))
{
pointImage = rect.TransformNDCToImage(pointImage);
if (rect.Inside(pointImage))
pcontext->DrawImage(m_psurfaceDirection, true, pointImage);
}
}
pcontext->SetBlendMode(BlendModeSourceAlpha);
pcontext->DrawImage3D(m_psurfaceDirectionIn, Color(0.4f, 0.4f, 0.4f, 0.4f), true, rect.Center());
}
}
void RenderLeadIndicator(Context* pcontext)
{
IshipIGC* pshipSource = trekClient.GetShip()->GetSourceShip();
IclusterIGC* pcluster = pshipSource->GetCluster();
if (pcluster)
{
bool bIisInRange = false;
ImodelIGC* targetModel = trekClient.GetShip()->GetCommandTarget(c_cmdCurrent);
if (targetModel &&
(targetModel->GetCluster() == pcluster) &&
pshipSource->CanSee(targetModel))
{
IshipIGC* pshipParent = trekClient.GetShip()->GetParentShip();
IshipIGC* pship;
IweaponIGC* w;
if (pshipParent)
{
pship = pshipParent;
Mount turretID = trekClient.GetShip()->GetTurretID();
w = (turretID != NA) ? ((IweaponIGC*)(pship->GetMountedPart(ET_Weapon, turretID))) : NULL;
}
else
{
pship = trekClient.GetShip();
w = trekClient.GetWeapon();
}
IsideIGC* pside = trekClient.GetSide();
if (w && ((GetWindow ()->GetCameraMode () == TrekWindow::cmExternalChase) || (GetWindow ()->GetCameraMode () == TrekWindow::cmExternalMissile) || ((targetModel->GetSide() == pside) == (w->GetProjectileType()->GetPower() < 0.0f))))
{
Vector vecLeadPosition;
float t = solveForLead(pship, targetModel, w, &vecLeadPosition);
float l = w->GetLifespan();
Rect rect = GetViewRect()->GetValue();
Point pointCenter = rect.Center();
Point pointLead;
if (t <= l)
bIisInRange = true;
if ((GetWindow ()->GetCameraMode () == TrekWindow::cmExternalChase) || (GetWindow ()->GetCameraMode () == TrekWindow::cmExternalMissile))
{
Vector shipPosition = trekClient.GetShip ()->GetPosition ();
Vector shipAimDirection = trekClient.GetShip ()->GetOrientation ().GetForward ();
IprojectileTypeIGC* pt = w->GetProjectileType();
float fProjectileSpeed = pt->GetSpeed();
Vector targetPosition = targetModel->GetPosition ();
float fTargetRadius = targetModel->GetRadius();
float fDistanceBetweenShips = (targetPosition - shipPosition).Length ();
float fLengthAlongTargetVector = t * fProjectileSpeed;
Vector cameraPosition = GetWindow ()->GetCamera ()->GetPosition ();
float fCosAngle = vecLeadPosition * shipAimDirection;
float fA = fLengthAlongTargetVector;
float fH = fA / fCosAngle;
float fHASquared = (fH * fH) - (fA * fA);
float fRadiusSquared = fTargetRadius * fTargetRadius;
bool bWillHit = (fHASquared <= fRadiusSquared) ? true : false;
Vector trackingRelativeToShip = shipAimDirection * fDistanceBetweenShips;
Vector trackingPosition = shipPosition + trackingRelativeToShip;
Vector realAimDirection = (trackingPosition - cameraPosition).Normalize ();
if (GetCamera()->TransformDirectionToImage (realAimDirection, pointLead))
{
pointLead = rect.TransformNDCToImage (pointLead);
pcontext->SetBlendMode (BlendModeSourceAlpha);
if (bIisInRange)
{
float green = bWillHit ? 1.0f - (fHASquared / fRadiusSquared) : 0.0f;
pcontext->DrawImage3D (m_psurfaceCenterOutRange, Color (1.0f - green, green, 0.0f), true, pointLead);
}
else
{
pcontext->DrawImage3D (m_psurfaceCenterOutRange, Color (0.33f, 0.33f, 0.33f, 0.5f), true, pointLead);
}
}
}
else
{
if (GetCamera()->TransformDirectionToImage(vecLeadPosition, pointLead))
{
pointLead = rect.TransformNDCToImage(pointLead);
bool bLeadIndicator;
if (pshipParent || pship->GetBaseHullType()->HasCapability(c_habmLeadIndicator) )
{
bLeadIndicator = true;
}
else
{
bLeadIndicator = false;
if (!bLeadIndicator)
{
for (ShipLinkIGC* psl = pcluster->GetShips()->first(); (psl != NULL); psl = psl->next())
{
IshipIGC* ps = psl->data();
if ((ps->GetSide() == pside) &&
(ps->GetParentShip() == NULL) &&
ps->GetBaseHullType()->HasCapability(c_habmRemoteLeadIndicator) &&
ps->InScannerRange(targetModel))
{
bLeadIndicator = true;
break;
}
}
}
}
if (bLeadIndicator)
{
pcontext->DrawImage((t <= l) ? m_psurfaceLeadInRange : m_psurfaceLeadOutRange,
true,
pointLead);
}
if (bIisInRange)
{
float dx = (pointCenter.X() - pointLead.X());
float dy = (pointCenter.Y() - pointLead.Y());
float l2 = dx * dx + dy * dy;
float r = targetModel->GetThingSite()->GetScreenRadius();
float g;
float f = l2 / (r * r);
if (f < 2.0f)
{
if (f < 1.0f)
g = 1.0f - f * 0.75f;
else
g = 0.5f - f / 4.0f;
}
else
g = 0.0f;
pcontext->SetBlendMode(BlendModeSourceAlpha);
pcontext->DrawImage3D(m_psurfaceCenterOutRange, Color(1.0f - g, g, 0.0f), true, pointCenter);
}
}
}
}
}
else if ((GetWindow ()->GetCameraMode () == TrekWindow::cmExternalChase) || (GetWindow ()->GetCameraMode () == TrekWindow::cmExternalMissile))
{
IshipIGC* pship = trekClient.GetShip();
if (! pship->GetParentShip())
{
IweaponIGC* pWeapon = trekClient.GetWeapon ();
if (pWeapon)
{
Rect rect = GetViewRect()->GetValue();
Point pointCenter = rect.Center();
Point pointRender;
Vector shipPosition = pship->GetPosition ();
Vector shipAimDirection = pship->GetOrientation ().GetForward ();
IprojectileTypeIGC* pt = pWeapon->GetProjectileType();
float fProjectileSpeed = pt->GetSpeed();
float fProjectileDistance = fProjectileSpeed * pWeapon->GetLifespan ();
Vector cameraPosition = GetWindow ()->GetCamera ()->GetPosition ();
Vector trackingRelativeToShip = shipAimDirection * fProjectileDistance;
Vector trackingPosition = shipPosition + trackingRelativeToShip;
Vector realAimDirection = (trackingPosition - cameraPosition).Normalize ();
if (GetCamera()->TransformDirectionToImage (realAimDirection, pointRender))
{
pointRender = rect.TransformNDCToImage (pointRender);
pcontext->SetBlendMode (BlendModeSourceAlpha);
pcontext->DrawImage3D (m_psurfaceCenterOutRange, Color (0.33f, 0.33f, 0.33f, 0.5f), true, pointRender);
}
}
}
}
GetWindow()->GetConsoleImage()->SetInRange(bIisInRange);
}
}
void RenderTurnIndicator(Context* pcontext, ImodelIGC* pmodelTarget, const Color& color)
{
IshipIGC* pshipSource = trekClient.GetShip()->GetSourceShip();
if (pmodelTarget &&
((pmodelTarget->GetObjectType() != OT_ship) ||
(((IshipIGC*)pmodelTarget)->GetSourceShip() != pshipSource)))
{
IclusterIGC* pcluster = pmodelTarget->GetCluster();
if (pcluster && (pcluster == pshipSource->GetCluster()) && pshipSource->CanSee(pmodelTarget))
{
const Point& dxy = pmodelTarget->GetThingSite()->GetScreenPosition();
float l = dxy.X() * dxy.X() + dxy.Y() * dxy.Y();
const float radiusRing = 108.0f;
if (l > radiusRing * radiusRing)
{
Point ndx(dxy / float(sqrt(l)));
Orientation orientShip( GetWindow()->GetCameraOrientation());
Vector vecForward(pmodelTarget->GetPosition() - pshipSource->GetPosition());
vecForward = orientShip.TimesInverse(vecForward).Normalize();
Vector vecLeft = vecForward.GetOrthogonalVector(); Vector vecUp = CrossProduct(vecForward, vecLeft );
Orientation orient(vecForward, vecUp);
pcontext->PushState();
pcontext->Translate(GetViewRect()->GetValue().Center() + radiusRing * ndx);
pcontext->Begin3DLayer(m_pcamera, false);
pcontext->DirectionalLight(Vector(0, 0, 1), color);
pcontext->SetAmbientLevel(0.25f);
pcontext->Multiply(Matrix(orient, Vector(0, 0, 0), 1.0f));
pcontext->Rotate(Vector(0, 0, 1), GetTime()->GetValue());
m_pmaterial->SetDiffuse(color);
pcontext->SetMaterial(m_pmaterial);
m_pgeoPointer->Render(pcontext);
pcontext->End3DLayer();
pcontext->PopState();
}
}
}
}
void RenderTrainingOverlay (Context* pContext)
{
Rect rect = GetViewRect()->GetValue();
Point pointCenter = rect.Center();
Point pointA;
Point pointB;
Point pointC;
Point pointDelta;
Point pointText;
Point pointTextSize;
TVector<VertexL> edgeVertices;
TVector<MeshIndex> edgeIndices;
ZString description;
Color colorLine (1.0f, 0.0f, 0.0f, 0.9f);
switch (s_trainingOverlay)
{
case NoTrainingOverlay:
return;
case SpeedTrainingOverlay:
{
pointDelta = Point (-1.0f, -1.0f);
pointA = pointCenter + Point (-25.0f, -105.5f);
pointB = pointA + (pointDelta * 100.0f);
description = "SPEED (meters per second)";
break;
}
case MotionIndicatorTrainingOverlay:
{
pointDelta = Point (-1.0f, 1.0f);
pointA = pointCenter + Point(-10.5f, 10.5f);
pointB = pointA + (pointDelta * 150.0f);
description = "MOTION INDICATOR";
break;
}
case ReticuleTrainingOverlay:
{
pointDelta = Point (-1.0f, 1.0f);
pointA = pointCenter + Point(-10.5f, 10.5f);
pointB = pointA + (pointDelta * 150.0f);
description = "RETICULE";
break;
}
case ThrottleTrainingOverlay:
{
pointDelta = Point (-1.0f, 1.0f);
pointA = pointCenter + Point(-126.5f, 93.5f);
pointB = pointA + (pointDelta * 50.0f);
description = "THROTTLE GAUGE";
break;
}
case FuelTrainingOverlay:
{
pointDelta = Point (1.0f, 1.0f);
pointA = pointCenter + Point (126.5f, 93.5f);
pointB = pointA + (pointDelta * 100.0f);
description = "FUEL GAUGE";
break;
}
case EnergyTrainingOverlay:
{
pointDelta = Point (1.0f, -1.0f);
pointA = pointCenter + Point (126.5f, -112.5f);
pointB = pointA + (pointDelta * 50.0f);
description = "ENERGY GAUGE";
break;
}
case AmmoTrainingOverlay:
{
pointDelta = Point (-1.0f, -1.0f);
pointA = pointCenter + Point (-126.5f, -112.5f);
pointB = pointA + (pointDelta * 50.0f);
description = "AMMUNITION GAUGE";
break;
}
case HullTrainingOverlay:
{
pointDelta = Point (-1.0f, 1.0f);
pointA = pointCenter + Point (-65.5f, 35.5f);
pointB = pointA + (pointDelta * 100.0f);
description = "ARMOR STRENGTH GAUGE";
break;
}
case ShieldTrainingOverlay:
{
pointDelta = Point (1.0f, 1.0f);
pointA = pointCenter + Point (65.5f, 35.5f);
pointB = pointA + (pointDelta * 100.0f);
description = "SHIELD STRENGTH GAUGE";
break;
}
case ChatTrainingOverlay:
{
pointDelta = Point (-1.0f, -1.0f);
pointA = pointCenter + Point (-140.0f, -50.0f + (rect.YMax () - pointCenter.Y ()));
pointB = pointA + (pointDelta * 100.0f);
description = "CHAT PANE";
break;
}
case SectorTrainingOverlay:
{
pointDelta = Point (1.0f, 1.0f);
pointA = Point (99.0f, 190.0f);
pointB = pointA + (pointDelta * 20.0f);
description = "SECTOR MAP";
break;
}
case InventoryTrainingOverlay:
{
pointDelta = Point (-1.0f, 1.0f);
pointA = Point (rect.XMax () - 140.0f, 225.0f);
pointB = pointA + (pointDelta * 20.0f);
description = "INVENTORY DISPLAY";
break;
}
case TargetTrainingOverlay:
{
pointDelta = Point (-1.0f, 1.0f);
pointA = pointCenter + Point (-70.0f, 120.0f - pointCenter.Y ());
pointB = pointA + (pointDelta * 100.0f);
description = "TARGET HUD";
break;
}
case CommandTrainingOverlay:
{
pointDelta = Point (1.0f, -1.0f);
pointA = pointCenter + Point (-(pointCenter.X () + -100.0f), -85.0f + (rect.YMax () - pointCenter.Y ()));
pointB = pointA + (pointDelta * 50.0f);
description = "CURRENT COMMAND";
break;
}
case EyeballTrainingOverlay:
{
pointDelta = Point (1.0f, -1.0f);
pointA = pointCenter + Point (140.5f, -140.5f);
pointB = pointA + (pointDelta * 50.0f);
description = "DETECTION INDICATOR";
break;
}
case KillBonusTrainingOverlay:
{
pointDelta = Point (-1.0f, -0.375f);
pointA = pointCenter + Point (-15.0f, -160.5f);
pointB = pointA + (pointDelta * 70.0f);
description = "KILL BONUS";
break;
}
}
pContext->PushState();
pContext->SetLineWidth (2.0f);
TRef<IEngineFont> pfont = TrekResources::SmallFont();
pointTextSize = Point::Cast(pfont->GetTextExtent(description));
pointC = pointB + Point (pointDelta.X () * pointTextSize.X (), 0.0f);
float fLeft;
float fBottom;
if (pointDelta.X () < 0.0f)
fLeft = pointC.X ();
else
fLeft = pointB.X ();
if (pointDelta.Y () < 0.0f)
fBottom = pointB.Y () + 2.0f;
else
fBottom = pointB.Y () - (float(pfont->GetHeight()) + 2.0f);
pointText = Point (fLeft, fBottom);
edgeVertices.SetCount (3);
edgeVertices.Set (0, VertexL (Vector (pointA.X (), pointA.Y (), 0.0f), colorLine));
edgeVertices.Set (1, VertexL (Vector (pointB.X (), pointB.Y (), 0.0f), colorLine));
edgeVertices.Set (2, VertexL (Vector (pointC.X (), pointC.Y (), 0.0f), colorLine));
edgeIndices.SetCount (4);
edgeIndices.Set (0, 0);
edgeIndices.Set (1, 1);
edgeIndices.Set (2, 1);
edgeIndices.Set (3, 2);
pContext->DrawLines(edgeVertices, edgeIndices);
pContext->DrawString(pfont, colorLine, pointText, description);
pContext->DrawImage(m_psurfaceTrainingOverlay, true, pointA);
pContext->PopState ();
}
void Render(Context* pcontext)
{
if (TrekWindow::InternalCamera(GetWindow()->GetCameraMode()) || (GetWindow()->GetCameraMode () == TrekWindow::cmExternalChase) || (GetWindow()->GetCameraMode () == TrekWindow::cmExternalMissile))
{
pcontext->SetShadeMode(ShadeModeFlat);
pcontext->SetLinearFilter(false, true);
pcontext->Clip(GetViewRect()->GetValue());
RenderLeadIndicator(pcontext);
ImodelIGC* pmodelAccepted = trekClient.GetShip()->GetCommandTarget(c_cmdAccepted);
ImodelIGC* pmodelCurrent = trekClient.GetShip()->GetCommandTarget(c_cmdCurrent );
if (pmodelAccepted == pmodelCurrent)
RenderTurnIndicator(pcontext, pmodelAccepted, Color(1, 1, 0));
else
{
RenderTurnIndicator(pcontext, pmodelAccepted, Color(0, 1, 0));
RenderTurnIndicator(pcontext, pmodelCurrent, Color(1, 0, 0));
}
RenderTrainingOverlay (pcontext);
RenderDirectionIndicator(pcontext);
}
}
};
TRef<Image> CreateIndicatorImage(Modeler* pmodeler, Viewport* pviewport, Number* ptime)
{
return new IndicatorImage(pmodeler, pviewport, ptime);
}
TrainingOverlay IndicatorImage::s_trainingOverlay = NoTrainingOverlay;
void SetTrainingOverlay (TrainingOverlay overlay)
{
IndicatorImage::s_trainingOverlay = overlay;
}