#include "Main.h"
void FrameHUDManager::StartFrame()
{
_Minerals = -1;
_Gas = -1;
_SupplyCurrent = -1;
_SupplyCap = -1;
_CurHP = -1;
_MaxHP = -1;
_CurShields = -1;
_MaxShields = -1;
_CurEnergy = -1;
_MaxEnergy = -1;
_ResourcesRemaining = -1;
_ControlGroupLevel = 0;
_SoloPortrait = NULL;
_WarpGatePresent = false;
_WarpGateCount = 0;
_IdleWorker = false;
_BuildQueuePresent = false;
for(UINT PortraitIndex = 0; PortraitIndex < BubblePortraitCount; PortraitIndex++)
{
_BubblePortraits[PortraitIndex].Tex = NULL;
}
for(UINT ActionButtonIndex = 0; ActionButtonIndex < ActionButtonCount; ActionButtonIndex++)
{
_ActionButtons[ActionButtonIndex].Tex = NULL;
_ActionButtons[ActionButtonIndex].State = ActionButtonStateInvalid;
}
for(UINT BuildQueueIndex = 0; BuildQueueIndex < BuildQueueCount; BuildQueueIndex++)
{
_BuildQueue[BuildQueueIndex].Tex = NULL;
}
_MinimapViewportObserved = false;
_MinimapViewportCenter = Vec2f(0.5f, 0.5f);
}
void FrameHUDManager::EndFrame()
{
if(!g_Context->Controller.ConsoleEnabled())
{
return;
}
if(DisplayHUDDetails)
{
g_Context->WriteConsole(String("ControlGroupLevel: ") + String(_ControlGroupLevel), RGBColor::White, OverlayPanelStatus);
g_Context->WriteConsole(String("WarpGateCount: ") + String(_WarpGateCount), RGBColor::White, OverlayPanelStatus);
}
if(DisplayViewport)
{
g_Context->WriteConsole(String("Viewport: ") + _MinimapViewportCenter.CommaSeparatedString(), RGBColor::White, OverlayPanelStatus);
}
if(DisplayUnitInfo)
{
g_Context->WriteConsole(String("Shields: ") + String(_CurShields) + String("/") + String(_MaxShields), RGBColor::Blue, OverlayPanelStatus);
g_Context->WriteConsole(String("HP: ") + String(_CurHP) + String("/") + String(_MaxHP), RGBColor::Green, OverlayPanelStatus);
g_Context->WriteConsole(String("Energy: ") + String(_CurEnergy) + String("/") + String(_MaxEnergy), RGBColor::Purple, OverlayPanelStatus);
g_Context->WriteConsole(String("Resources remaining: ") + String(_ResourcesRemaining), RGBColor::Green, OverlayPanelStatus);
if(_ResourcesRemaining > 0)
{
g_Context->Files.Events << GameTime() << '\t' << _ResourcesRemaining << endl;
}
}
if(DisplayResources)
{
g_Context->WriteConsole(String("Minerals: ") + String(_Minerals), RGBColor::Blue, OverlayPanelStatus);
g_Context->WriteConsole(String("Gas: ") + String(_Gas), RGBColor::Green, OverlayPanelStatus);
g_Context->WriteConsole(String("Supply: ") + String(_SupplyCurrent) + String("/") + String(_SupplyCap), RGBColor::White, OverlayPanelStatus);
}
if(DisplayPortraits)
{
if(_SoloPortrait != NULL)
{
g_Context->WriteConsole(String("Solo: ") + String(_SoloPortrait->ID()), RGBColor::Orange, OverlayPanelStatus);
}
for(UINT Y = 0; Y < 3; Y++)
{
String S = String("Row ") + String(Y) + String(": ");
for(UINT X = 0; X < 8; X++)
{
Texture *CurTexture = _BubblePortraits[Y * 8 + X].Tex;
if(CurTexture == NULL)
{
S += "E,";
}
else
{
S += CurTexture->ID() + String(",");
}
}
g_Context->WriteConsole(S, RGBColor::Orange, OverlayPanelStatus);
}
}
if(DisplayActionButtons)
{
for(UINT Y = 0; Y < 3; Y++)
{
String S = String("Row ") + String(Y) + String(": ");
for(UINT X = 0; X < 5; X++)
{
const FrameActionButton &CurButton = _ActionButtons[Y * 5 + X];
String Modifier;
if(CurButton.State == ActionButtonStateInvalid)
{
Modifier = "I";
}
if(CurButton.State == ActionButtonStateNormal)
{
Modifier = "N";
}
if(CurButton.State == ActionButtonStateDisabled)
{
Modifier = "D";
}
if(CurButton.State == ActionButtonStateSelected)
{
Modifier = "S";
}
if(CurButton.State == ActionButtonStateNotEnoughEnergy)
{
Modifier = "O";
}
if(CurButton.State == ActionButtonCoolingDown)
{
Modifier = "C";
}
if(CurButton.Tex == NULL)
{
S += "E,";
}
else
{
S += Modifier + CurButton.Tex->ID() + String(",");
}
}
if(S.Length() > 0)
{
S.PopEnd();
}
g_Context->WriteConsole(S, RGBColor::Orange, OverlayPanelStatus);
}
}
}
UINT FrameHUDManager::CountUnitsWithName(const String &Name) const
{
if(_SoloPortrait != NULL)
{
if(_SoloPortrait->ID() == Name)
{
return 1;
}
else
{
return 0;
}
}
else
{
UINT Result = 0;
for(UINT PortraitIndex = 0; PortraitIndex < BubblePortraitCount; PortraitIndex++)
{
Texture *CurTexture = _BubblePortraits[PortraitIndex].Tex;
if(CurTexture != NULL && CurTexture->ID() == Name)
{
Result++;
}
}
return Result;
}
}
void FrameHUDManager::ReportMinimapViewport(const RenderInfo &Info, const Vector<ProcessedVertex> &ProcessedVertices)
{
_MinimapViewportObserved = true;
Vec2f AverageScreenPos = Vec2f::Origin;
for(UINT Index = 0; Index < 8; Index++)
{
const Vec4f &Vertex = ProcessedVertices[Index].TransformedProjectionPos;
AverageScreenPos += Vec2f(Vertex.x, Vertex.y);
}
AverageScreenPos *= 0.125f;
_MinimapViewportCenter = g_Context->Constants.ScreenCoordToMinimapCoord(AverageScreenPos);
}
void FrameHUDManager::ReportWarpGatePresent()
{
_WarpGatePresent = true;
}
void FrameHUDManager::ReportIdleWorker()
{
_IdleWorker = true;
}
void FrameHUDManager::ReportBuildQueue()
{
_BuildQueuePresent = true;
}
bool FrameHUDManager::CurrentUnitUnderConstruction() const
{
for(UINT ActionIndex = 0; ActionIndex < ActionButtonCount - 1; ActionIndex++)
{
const FrameActionButton &CurAction = _ActionButtons[ActionIndex];
if(CurAction.Tex != NULL)
{
return false;
}
}
return (_ActionButtons[ActionButtonCount - 1].Tex != NULL && _ActionButtons[ActionButtonCount - 1].Tex->ID() == "Cancel");
}
void FrameHUDManager::ReportFont(const RenderInfo &Info)
{
const Rectangle2i &UnitInfoRegion = g_Context->Constants.GetRectangle2iConstant(ScreenConstantRectangle2iUnitInfoRegion);
const Rectangle2i &ResourceRegion = g_Context->Constants.GetRectangle2iConstant(ScreenConstantRectangle2iResourceRegion);
const Rectangle2i &WarpGateRegion = g_Context->Constants.GetRectangle2iConstant(ScreenConstantRectangle2iWarpGateRegion);
if(ScreenBoundInsideRegion(Info.ScreenBound, ResourceRegion))
{
ProcessResourceString(Info.Text);
}
if(ScreenBoundInsideRegion(Info.ScreenBound, UnitInfoRegion))
{
ProcessUnitInfoString(Info.Text);
}
if(ScreenBoundInsideRegion(Info.ScreenBound, WarpGateRegion))
{
int Value = Info.Text.ConvertToInteger();
if(Value >= 0 && Value < 20)
{
_WarpGateCount = Value;
}
}
}
void FrameHUDManager::ReportControlGroupLevel(const RenderInfo &Info)
{
UINT NewValue = 0;
if(Info.PrimitiveCount == 18 * 1)
{
NewValue = 1;
}
else if(Info.PrimitiveCount == 18 * 2)
{
NewValue = 2;
}
else if(Info.PrimitiveCount == 18 * 3)
{
NewValue = 3;
}
else if(Info.PrimitiveCount == 18 * 4)
{
NewValue = 4;
}
else if(Info.PrimitiveCount == 18 * 5)
{
NewValue = 5;
}
else
{
g_Context->Files.Assert << "Unexpected ControlGroupLevel\n";
return;
}
if(_ControlGroupLevel != 0 && _ControlGroupLevel != NewValue)
{
g_Context->Files.Assert << "Conflicting ControlGroupLevel: " << _ControlGroupLevel << ' ' << NewValue << endl;
return;
}
_ControlGroupLevel = NewValue;
}
void FrameHUDManager::ReportBubbleIcon(const RenderInfo &Info)
{
if(Info.PrimitiveCount != 2 || Info.PrimitiveType != D3DPT_TRIANGLELIST)
{
g_Context->Files.Assert << "Invalid icon parameters\n";
}
for(UINT Index = 0; Index < BuildQueueCount; Index++)
{
FrameBuildQueue &CurBuildQueue = _BuildQueue[Index];
if(CurBuildQueue.Tex == NULL)
{
CurBuildQueue.Tex = Info.Texture1;
return;
}
}
}
void FrameHUDManager::ReportPortrait(const RenderInfo &Info, const Vector<ProcessedVertex> &ProcessedVertices)
{
if(Info.PrimitiveCount > 48 || (Info.PrimitiveCount & 1) == 1 || Info.PrimitiveType != D3DPT_TRIANGLELIST || ProcessedVertices.Length() != Info.PrimitiveCount * 2)
{
g_Context->Files.Assert << "Invalid bubble portrait parameters\n";
return;
}
for(UINT BubbleIndex = 0; BubbleIndex < Info.PrimitiveCount / 2; BubbleIndex++)
{
Rectangle2f ScreenPosBounds;
for(UINT VertexIndex = 0; VertexIndex < 3; VertexIndex++)
{
const ProcessedVertex &CurVertex = ProcessedVertices[BubbleIndex * 4 + VertexIndex];
const Vec2f CurScreenPos = Vec2f(CurVertex.TransformedProjectionPos.x, CurVertex.TransformedProjectionPos.y);
if(VertexIndex == 0)
{
ScreenPosBounds.Min = CurScreenPos;
ScreenPosBounds.Max = CurScreenPos;
}
else
{
ScreenPosBounds.ExpandBoundingBox(CurScreenPos);
}
}
if(Info.PrimitiveCount == 2 && Math::Round(ScreenPosBounds.Width()) == 89 && Math::Round(ScreenPosBounds.Height()) == 89)
{
if(_SoloPortrait == NULL)
{
if(Info.Texture0 == NULL || Info.Texture0->Unit() == NULL)
{
g_Context->Files.Assert << "No unit info found for portrait\n";
}
else
{
_SoloPortrait = Info.Texture0;
}
}
else
{
g_Context->Files.Assert << "Multiple solo portraits\n";
}
}
else
{
int PortraitIndex = BubblePortraitIndexFromScreenBounds(ScreenPosBounds);
if(PortraitIndex != -1)
{
FrameBubblePortrait &CurPortrait = _BubblePortraits[PortraitIndex];
if(Info.Texture0->ID() == "PortraitBubble")
{
CurPortrait.Tex = Info.Texture1;
}
else
{
CurPortrait.Tex = Info.Texture0;
}
if(CurPortrait.Tex != NULL && CurPortrait.Tex->Unit() == NULL)
{
g_Context->Files.Assert << "No unit info found for portrait\n";
CurPortrait.Tex = NULL;
}
}
}
}
}
bool FrameHUDManager::ValidateSingleUnit(const String &Name) const
{
if(_SoloPortrait != NULL)
{
return (Name == _SoloPortrait->ID());
}
else
{
if(_BubblePortraits[0].Tex == NULL)
{
return false;
}
for(UINT PortraitIndex = 1; PortraitIndex < BubblePortraitCount; PortraitIndex++)
{
if(_BubblePortraits[PortraitIndex].Tex != NULL && _BubblePortraits[PortraitIndex].Tex->ID() != Name)
{
return false;
}
}
return true;
}
}
bool BuildingsEquivalent(const String &A, const String &B)
{
return (A == "Gateway" && B == "WarpGate");
}
bool FrameHUDManager::SingleUnitTypeSelected() const
{
if(_BubblePortraits[0].Tex == NULL)
{
return false;
}
for(UINT PortraitIndex = 1; PortraitIndex < BubblePortraitCount; PortraitIndex++)
{
if(_BubblePortraits[PortraitIndex].Tex != NULL)
{
if(_BubblePortraits[PortraitIndex].Tex == _BubblePortraits[0].Tex ||
BuildingsEquivalent(_BubblePortraits[PortraitIndex].Tex->ID(), _BubblePortraits[0].Tex->ID()) ||
BuildingsEquivalent(_BubblePortraits[0].Tex->ID(), _BubblePortraits[PortraitIndex].Tex->ID()))
{
}
else
{
return false;
}
}
}
return true;
}
bool FrameHUDManager::HaveResourcesForUnit(const UnitEntry &Entry) const
{
return (_Minerals >= int(Entry.MineralCost) && _Gas >= int(Entry.GasCost) && _SupplyCap - _SupplyCurrent >= int(Entry.SupplyCost));
}
Vec2i FrameHUDManager::ScreenCoordFromControlGroupLevel(UINT Level) const
{
const Vec2f TopLeft = g_Context->Constants.GetVec2fConstant(ScreenConstantVec2fControlGroupLevelTopLeft);
const Vec2f Displacement = g_Context->Constants.GetVec2fConstant(ScreenConstantVec2fControlGroupLevelDisplacement);
return (TopLeft + Displacement * float(Level)).RoundToVec2i();
}
Vec2i FrameHUDManager::ScreenCoordFromBubblePortraitIndex(UINT Index) const
{
const Vec2f TopLeft = g_Context->Constants.GetVec2fConstant(ScreenConstantVec2fBubblePortraitTopLeft);
const Vec2f ExpectedDimensions = g_Context->Constants.GetVec2fConstant(ScreenConstantVec2fBubblePortraitDimensions);
UINT Row = Index / 8;
UINT Col = Index - Row * 8;
Vec2f ScreenCoord = TopLeft + Vec2f(ExpectedDimensions.x * (Col + 0.5f), ExpectedDimensions.y * (Row + 0.5f));
return ScreenCoord.RoundToVec2i();
}
int FrameHUDManager::BubblePortraitIndexFromScreenBounds(const Rectangle2f &ScreenBounds) const
{
const Vec2f ObservedDimensions = ScreenBounds.Dimensions();
const Vec2f TopLeft = g_Context->Constants.GetVec2fConstant(ScreenConstantVec2fBubblePortraitTopLeft);
const Vec2f ExpectedDimensions = g_Context->Constants.GetVec2fConstant(ScreenConstantVec2fBubblePortraitDimensions);
if((ExpectedDimensions - ObservedDimensions).LengthSq() >= 4.0f)
{
g_Context->Files.Assert << "Invalid bubble portrait dimensions: Observed=" << ObservedDimensions.CommaSeparatedString() << " Expected=" << ExpectedDimensions.CommaSeparatedString() << endl;
return -1;
}
Vec2f Offset = ScreenBounds.Min - TopLeft;
int XIndex = Math::Round(Offset.x / ExpectedDimensions.x);
int YIndex = Math::Round(Offset.y / ExpectedDimensions.y);
if(XIndex < 0 || YIndex < 0 || XIndex >= 8 || YIndex >= 3)
{
g_Context->Files.Assert << "Invalid bubble portrait coordinates\n";
return -1;
}
return (YIndex * 8 + XIndex);
}
int FrameHUDManager::ActionButtonIndexFromScreenBounds(const Rectangle2f &ScreenBounds) const
{
const Vec2f ObservedDimensions = ScreenBounds.Dimensions();
const Vec2f TopLeft = g_Context->Constants.GetVec2fConstant(ScreenConstantVec2fActionButtonTopLeft);
const Vec2f ExpectedDimensions = g_Context->Constants.GetVec2fConstant(ScreenConstantVec2fActionButtonDimensions);
if((ExpectedDimensions - ObservedDimensions).LengthSq() >= 4.0f)
{
return -1;
}
Vec2f Offset = ScreenBounds.Min - TopLeft;
int XIndex = Math::Round(Offset.x / ExpectedDimensions.x);
int YIndex = Math::Round(Offset.y / ExpectedDimensions.y);
if(XIndex < 0 || YIndex < 0 || XIndex >= 5 || YIndex >= 3)
{
g_Context->Files.Assert << "Invalid action button coordinates\n";
return -1;
}
return (YIndex * 5 + XIndex);
}
void FrameHUDManager::ReportActionButtonBackground(const RenderInfo &Info, const Vector<ProcessedVertex> &ProcessedVertices)
{
if(Info.PrimitiveCount > 30 || (Info.PrimitiveCount & 1) == 1 || Info.PrimitiveType != D3DPT_TRIANGLELIST || ProcessedVertices.Length() != Info.PrimitiveCount * 2)
{
g_Context->Files.Assert << "Invalid action button background parameters\n";
return;
}
ActionButtonStateType State = ActionButtonStateNormal;
if(Info.Texture0->ID() == "ActionButtonSelectedBubble")
{
State = ActionButtonStateSelected;
}
const Vec4f CurColor = g_Context->Managers.State.PShaderFloatConstants[0];
if(Math::Abs(CurColor.x - 0.50196f) < 0.01f &&
Math::Abs(CurColor.y - 0.50196f) < 0.01f &&
Math::Abs(CurColor.z - 0.50196f) < 0.01f)
{
State = ActionButtonStateDisabled;
g_Context->Managers.State.PShaderFloatConstants[0] = Vec4f::Origin;
}
for(UINT ButtonIndex = 0; ButtonIndex < Info.PrimitiveCount / 2; ButtonIndex++)
{
Rectangle2f ScreenPosBounds;
for(UINT VertexIndex = 0; VertexIndex < 3; VertexIndex++)
{
const ProcessedVertex &CurVertex = ProcessedVertices[ButtonIndex * 4 + VertexIndex];
const Vec2f CurScreenPos = Vec2f(CurVertex.TransformedProjectionPos.x, CurVertex.TransformedProjectionPos.y);
if(VertexIndex == 0)
{
ScreenPosBounds.Min = CurScreenPos;
ScreenPosBounds.Max = CurScreenPos;
}
else
{
ScreenPosBounds.ExpandBoundingBox(CurScreenPos);
}
}
int ButtonIndex = ActionButtonIndexFromScreenBounds(ScreenPosBounds);
if(ButtonIndex != -1)
{
FrameActionButton &CurActionButton = _ActionButtons[ButtonIndex];
CurActionButton.State = State;
}
}
}
void FrameHUDManager::ReportActionButtonIcon(const RenderInfo &Info, const Vector<ProcessedVertex> &ProcessedVertices)
{
if(Info.PrimitiveCount != 2 || Info.PrimitiveType != D3DPT_TRIANGLELIST || ProcessedVertices.Length() != 4)
{
g_Context->Files.Assert << "Invalid action button icon parameters\n";
}
Rectangle2f ScreenPosBounds;
for(UINT VertexIndex = 0; VertexIndex < 3; VertexIndex++)
{
const ProcessedVertex &CurVertex = ProcessedVertices[VertexIndex];
const Vec2f CurScreenPos = Vec2f(CurVertex.TransformedProjectionPos.x, CurVertex.TransformedProjectionPos.y);
if(VertexIndex == 0)
{
ScreenPosBounds.Min = CurScreenPos;
ScreenPosBounds.Max = CurScreenPos;
}
else
{
ScreenPosBounds.ExpandBoundingBox(CurScreenPos);
}
}
int ButtonIndex = ActionButtonIndexFromScreenBounds(ScreenPosBounds);
if(ButtonIndex != -1)
{
FrameActionButton &CurActionButton = _ActionButtons[ButtonIndex];
CurActionButton.Tex = Info.Texture0;
if(Info.Texture1->Type() == RenderSpecial && Info.Texture1->ID() == "CooldownGrid")
{
CurActionButton.State = ActionButtonCoolingDown;
}
else {
const Vec4f CurColor = g_Context->Managers.State.PShaderFloatConstants[0];
if(Math::Abs(CurColor.x - 0.462744f) < 0.01f &&
Math::Abs(CurColor.y - 0.286274f) < 0.01f)
{
if(CurActionButton.State == ActionButtonStateNormal)
{
CurActionButton.State = ActionButtonStateNotEnoughEnergy;
g_Context->Managers.State.PShaderFloatConstants[0] = Vec4f::Origin;
}
}
}
}
}
bool FrameHUDManager::BuildQueueEntryPresent(const String &Name) const
{
for(UINT Index = 0; Index < BuildQueueCount; Index++)
{
if(_BuildQueue[Index].Tex != NULL && _BuildQueue[Index].Tex->ID() == Name)
{
return true;
}
}
return false;
}
bool FrameHUDManager::ActionButtonPresent(const String &Name, ActionButtonStateType &State) const
{
for(UINT Index = 0; Index < ActionButtonCount; Index++)
{
if(_ActionButtons[Index].Tex != NULL && _ActionButtons[Index].Tex->ID() == Name)
{
State = _ActionButtons[Index].State;
return true;
}
}
return false;
}
void FrameHUDManager::ProcessResourceString(const String &S)
{
Vector<String> Words, SupplyWords;
S.Partition('@', Words);
if(Words.Length() != 3)
{
return;
}
Words[0].Partition('/', SupplyWords);
if(SupplyWords.Length() != 2)
{
return;
}
_Minerals = Words[2].ConvertToInteger();
_Gas = Words[1].ConvertToInteger();
_SupplyCurrent = SupplyWords[0].ConvertToInteger();
_SupplyCap = SupplyWords[1].ConvertToInteger();
}
float FrameHUDManager::CurrentHealthPercentage() const
{
if(_CurHP <= 0 || _MaxHP <= 0)
{
return -1.0f;
}
int CurrentHealth = _CurHP;
int TotalHealth = _MaxHP;
if(_CurShields > 0 && _MaxShields > 0)
{
CurrentHealth += _CurShields;
TotalHealth += _MaxShields;
}
return float(CurrentHealth) / float(TotalHealth);
}
bool IsBarStatString(const String &S, int &Current, int &Max)
{
Vector<String> Words;
S.Partition('/', Words);
if(Words.Length() != 2)
{
return false;
}
Current = Words[0].ConvertToInteger();
Max = Words[1].ConvertToInteger();
return true;
}
void FrameHUDManager::ProcessUnitInfoString(const String &S)
{
Vector<String> Words, HPWords;
S.Partition('@', Words);
if(Words.Length() == 0)
{
return;
}
UINT BarCount = 0;
int BarCurrent[3], BarMax[3];
for(UINT WordIndex = 0; WordIndex < Words.Length(); WordIndex++)
{
int Current, Max;
const String &CurWord = Words[WordIndex];
if(IsBarStatString(CurWord, Current, Max))
{
if(BarCount < 3)
{
BarCurrent[BarCount] = Current;
BarMax[BarCount] = Max;
BarCount++;
}
}
}
if(BarCount == 0)
{
return;
}
bool Protoss = (g_Context->Managers.Knowledge.MyRace() == RaceProtoss);
if(BarCount == 1)
{
_CurHP = BarCurrent[0];
_MaxHP = BarMax[0];
}
else if(BarCount == 2)
{
if(Protoss)
{
_CurShields = BarCurrent[0];
_MaxShields = BarMax[0];
_CurHP = BarCurrent[1];
_MaxHP = BarMax[1];
}
else
{
_CurHP = BarCurrent[0];
_MaxHP = BarMax[0];
_CurEnergy = BarCurrent[1];
_MaxEnergy = BarMax[1];
}
}
else
{
_CurShields = BarCurrent[0];
_MaxShields = BarMax[0];
_CurHP = BarCurrent[1];
_MaxHP = BarMax[1];
_CurEnergy = BarCurrent[2];
_MaxEnergy = BarMax[2];
}
}