// // MinimapManager.cpp // // Handles all minimap-related functions. // Written by Matthew Fisher // #include "Main.h" bool FrameUnitInfo::Clickable() const { return g_Context->Constants.Clickable(ScreenBound.Center().RoundToVec2i()); } void FrameUnitManager::StartFrame() { _UnitsThisFrame.DeleteMemory(); _HealthBars.FreeMemory(); } void FrameUnitManager::EndFrame() { if(!g_Context->Controller.ConsoleEnabled()) { return; } if(DisplayUnitsOnScreen) { struct StringColorPair { String Text; RGBColor Color; }; Vector AllLines; set OutputtedNameHashes; for(UINT UnitIndex = 0; UnitIndex < _UnitsThisFrame.Length(); UnitIndex++) { const FrameUnitInfo &CurUnit = *_UnitsThisFrame[UnitIndex]; UINT NameHash = CurUnit.Entry->Name.Hash32() + CurUnit.Owner; if(OutputtedNameHashes.count(NameHash) == 0) { OutputtedNameHashes.insert(NameHash); UINT Count = CountUnits(CurUnit.Entry->Name, CurUnit.Owner); RGBColor Color = RGBColor::Green; if(CurUnit.Owner == PlayerEnemy) { Color = RGBColor::Red; } if(CurUnit.Owner == PlayerInvalid) { Color = RGBColor::White; } StringColorPair *NewPair = new StringColorPair; NewPair->Text = CurUnit.Entry->Name + String(" ") + String(Count); NewPair->Color = Color; AllLines.PushEnd(NewPair); } } class StringColorPairComparison { public: bool operator() (const StringColorPair *Left, const StringColorPair *Right) { if(Left->Color == Right->Color) { return String::LexicographicComparison::Compare(Left->Text, Right->Text); } else { return (*((UINT*)&(Left->Color)) < *((UINT*)&(Right->Color))); } } }; StringColorPairComparison Comparator; AllLines.Sort(Comparator); for(UINT LineIndex = 0; LineIndex < AllLines.Length(); LineIndex++) { const StringColorPair &CurLine = *AllLines[LineIndex]; g_Context->WriteConsole(CurLine.Text, CurLine.Color, OverlayPanelStatus); } AllLines.DeleteMemory(); } if(DisplayUnitHealthBars) { struct FrameUnitInfoSorter { bool operator()(const FrameUnitInfo *Left, const FrameUnitInfo *Right) { return String::LexicographicComparison::Compare(Left->Entry->Name, Right->Entry->Name); } }; FrameUnitInfoSorter Sorter; _UnitsThisFrame.Sort(Sorter); for(UINT UnitIndex = 0; UnitIndex < _UnitsThisFrame.Length(); UnitIndex++) { const FrameUnitInfo &CurUnit = *_UnitsThisFrame[UnitIndex]; String BarDescription = "None"; if(CurUnit.HealthBar != NULL) { BarDescription = String(CurUnit.HealthBar->Health()); } g_Context->WriteConsole(CurUnit.Entry->Name + String(" ") + String(BarDescription), RGBColor::Green, OverlayPanelStatus); } g_Context->WriteConsole(String("Health Bars: ") + String(_HealthBars.Length()), RGBColor::Green, OverlayPanelStatus); } if(DisplayBattlePosition) { g_Context->WriteConsole(String("Closest Hostile: ") + String(_BattlePosition.ClosestEnemyHostile), RGBColor::Orange, OverlayPanelStatus); g_Context->WriteConsole(String("Blink Location: ") + _BattlePosition.SafestBlinkLocation.CommaSeparatedString(), RGBColor::Orange, OverlayPanelStatus); g_Context->WriteConsole(String("Retreat Location: ") + _BattlePosition.SafestRetreatLocation.CommaSeparatedString(), RGBColor::Orange, OverlayPanelStatus); } } UINT FrameUnitManager::CountUnits(const String &Name, PlayerType Owner) const { UINT Sum = 0; for(UINT UnitIndex = 0; UnitIndex < _UnitsThisFrame.Length(); UnitIndex++) { const FrameUnitInfo &CurUnit = *_UnitsThisFrame[UnitIndex]; if(CurUnit.Entry->Name == Name && CurUnit.Owner == Owner) { Sum++; } } return Sum; } PlayerType FrameUnitManager::PlayerFromColor(const Vec4f &Color) { Vec3f LocalColor(Color.x, Color.y, Color.z); if(Vec3f::DistSq(LocalColor, Vec3f(0.0f, 0.7333f, 0.0f)) < 0.01f) { return PlayerSelf; } if(Vec3f::DistSq(LocalColor, Vec3f(1.0f, 0.0f, 0.0f)) < 0.01f) { return PlayerEnemy; } return PlayerInvalid; } void FrameUnitManager::ReportUnitRender(const RenderInfo &Info) { const float BorderRegion = 2.0f; const float MinUnitSize = 5.0f; if(g_ReportingEvents) { g_Context->Files.CurrentFrameAllEvents << "FrameUnitManager::RecordUnitRender: " << Info.Texture0->ID() << endl; } UnitEntry *Unit = Info.Texture0->Unit(); if(Unit == NULL) { if(g_ReportingEvents) { g_Context->Files.CurrentFrameAllEvents << "Rejected: no unit entry\n"; } return; } if(Unit->Prims != Info.PrimitiveCount && Unit->Prims != -1) { if(g_ReportingEvents) { g_Context->Files.CurrentFrameAllEvents << "Rejected: incorrect number of primitives\n"; } return; } if(Unit->PrimaryType == UnitPrimaryMobile && Unit->Race == RaceZerg && Unit->Name == "Zergling" && Info.PrimitiveCount != 1056 && Info.PrimitiveCount != 900) { if(g_ReportingEvents) { g_Context->Files.CurrentFrameAllEvents << "Rejected: zergling with invalid primitive count\n"; } return; } if(Unit->PrimaryType == UnitPrimaryResource && (Unit->Name == "MineralBlue" || Unit->Name == "MineralGold") && Info.PrimitiveCount <= 44) { if(g_ReportingEvents) { g_Context->Files.CurrentFrameAllEvents << "Rejected: minerals are being carried by worker\n"; } return; } if(Unit->PrimaryType == UnitPrimaryResource && Unit->Name == "VespeneGeyser" && Info.PrimitiveCount != 553 && Info.PrimitiveCount != 5606) { if(g_ReportingEvents) { g_Context->Files.CurrentFrameAllEvents << "Rejected: Vespene Geyser with incorrect primitive count\n"; } return; } const Vec4f CurColor = g_Context->Managers.State.PShaderFloatConstants[1]; const PlayerType ActivePlayerColor = PlayerFromColor(CurColor); if(ActivePlayerColor == PlayerInvalid && Unit->PrimaryType != UnitPrimaryResource) { if(g_ReportingEvents) { g_Context->Files.CurrentFrameAllEvents << "Rejected: ActivePlayerColor != PlayerInvalid\n"; } return; } const Vec2f Center = Info.ScreenBound.Center(); if(Center.x < BorderRegion || Center.x > float(g_Context->Graphics.WindowDimensions().x) - BorderRegion) { if(g_ReportingEvents) { g_Context->Files.CurrentFrameAllEvents << "Rejected: X coord out of bounds\n"; } return; } if(Center.y < BorderRegion || Center.y > float(g_Context->Graphics.WindowDimensions().y) - BorderRegion) { if(g_ReportingEvents) { g_Context->Files.CurrentFrameAllEvents << "Rejected: Y coord out of bounds\n"; } return; } const Vec2f Dimensions = Info.ScreenBound.Dimensions(); if(Dimensions.x < MinUnitSize || Dimensions.y < MinUnitSize) { if(g_ReportingEvents) { g_Context->Files.CurrentFrameAllEvents << "Rejected: too small\n"; } return; } if(g_ReportingEvents) { g_Context->Files.CurrentFrameAllEvents << "Accepted\n"; } _UnitsThisFrame.PushEnd(new FrameUnitInfo(Unit, ActivePlayerColor, Info.ScreenBound)); } bool IsEnergyColor(RGBColor C) { //g_Context->Files.Thread << "Energy: " << C.CommaSeperatedString() << endl; const RGBColor BaseEnergyColor(220, 40, 150); // remember BGR vs. RGB swap return (RGBColor::DistL1(BaseEnergyColor, C) < 100); } void FrameUnitManager::ReportHealthBars(const RenderInfo &Info, const Vector &ProcessedVertices) { if((Info.PrimitiveCount & 1) == 1 || Info.PrimitiveType != D3DPT_TRIANGLELIST || ProcessedVertices.Length() != Info.PrimitiveCount * 2) { g_Context->Files.Assert << "Invalid health bar parameters\n"; return; } HealthBarInfo BaseHealthBar; BaseHealthBar.ColoredBlocks = 0; BaseHealthBar.GrayBlocks = 0; const RGBColor HealthBarGray(80, 80, 80); for(UINT HealthBarIndex = 0; HealthBarIndex < Info.PrimitiveCount / 2; HealthBarIndex++) { UINT ColorType = 0; Rectangle2f ScreenBound; for(UINT VertexIndex = 0; VertexIndex < 3; VertexIndex++) { const ProcessedVertex &CurVertex = ProcessedVertices[HealthBarIndex * 4 + VertexIndex]; const Vec2f CurScreenPos = Vec2f(CurVertex.TransformedProjectionPos.x, CurVertex.TransformedProjectionPos.y); if(VertexIndex == 0) { ScreenBound.Min = CurScreenPos; ScreenBound.Max = CurScreenPos; RGBColor Color = CurVertex.Diffuse; if(Color == RGBColor::Black) { ColorType = 0; } else if(RGBColor::DistL1(HealthBarGray, Color) < 25) { ColorType = 1; } else { if(IsEnergyColor(Color)) { BaseHealthBar.Energy = true; } ColorType = 2; } } else { ScreenBound.ExpandBoundingBox(CurScreenPos); } } if(ColorType == 0) { if(BaseHealthBar.ColoredBlocks > 0 || BaseHealthBar.GrayBlocks > 0) { RecordHealthBar(BaseHealthBar); } BaseHealthBar.ColoredBlocks = 0; BaseHealthBar.GrayBlocks = 0; BaseHealthBar.ScreenBound = ScreenBound; BaseHealthBar.Energy = false; } else if(ColorType == 1) { BaseHealthBar.GrayBlocks++; } else if(ColorType == 2) { BaseHealthBar.ColoredBlocks++; } } if(BaseHealthBar.ColoredBlocks > 0 || BaseHealthBar.GrayBlocks > 0) { RecordHealthBar(BaseHealthBar); } } void FrameUnitManager::RecordHealthBar(const HealthBarInfo &CurInfo) { _HealthBars.PushEnd(CurInfo); //g_Context->Files.Thread << CurInfo.ScreenBound.Min.CommaSeparatedString() << ' ' << CurInfo.ScreenBound.Dimensions().CommaSeparatedString() << ' ' << CurInfo.Health() << ' ' << String(CurInfo.Energy) << endl; } void FrameUnitManager::MatchHealthBars() { for(UINT UnitIndex = 0; UnitIndex < _UnitsThisFrame.Length(); UnitIndex++) { FrameUnitInfo &CurUnit = *_UnitsThisFrame[UnitIndex]; Vec2f BasePt = CurUnit.ScreenBound.Min + Vec2f(CurUnit.ScreenBound.Width() * 0.5f, 0.0f); float BestDistSq = 125.0f * 125.0f; for(UINT BarIndex = 0; BarIndex < _HealthBars.Length(); BarIndex++) { HealthBarInfo &CurBar = _HealthBars[BarIndex]; if(!CurBar.Energy) { float CurDistSq = Vec2f::DistSq(CurBar.ScreenBound.Center(), BasePt); if(CurDistSq < BestDistSq) { BestDistSq = CurDistSq; CurUnit.HealthBar = &CurBar; } } } } } float FrameUnitManager::ClosestHostileToPoint(const Vec2f &Pt) const { float BestDist = 1000.0f; for(UINT UnitIndex = 0; UnitIndex < _UnitsThisFrame.Length(); UnitIndex++) { const FrameUnitInfo &CurUnit = *_UnitsThisFrame[UnitIndex]; if(CurUnit.Owner == PlayerEnemy && CurUnit.Entry->Hostile && CurUnit.Entry->SecondaryType != UnitSecondaryWorker) { float CurDist = Vec2f::Dist(CurUnit.ScreenBound.Center(), Pt); if(CurDist < BestDist) { BestDist = CurDist; } } } return BestDist; } Vec2f FrameUnitManager::BestRetreatLocation(const Vec2f &Center, float Radius) const { const UINT ThetaDivisions = 20; float BestValue = -1.0f; Vec2f BestLocation = Center + Vec2f(Radius, 0.0f); for(UINT ThetaIndex = 0; ThetaIndex < ThetaDivisions; ThetaIndex++) { float Theta = float(ThetaIndex) / float(ThetaDivisions) * 2.0f * Math::PIf; Vec2f CurLocation = Center + Vec2f(cosf(Theta) * Radius, sinf(Theta) * Radius); float CurValue = ClosestHostileToPoint(CurLocation); if(CurValue > BestValue) { BestValue = CurValue; BestLocation = CurLocation; } } return BestLocation; } void FrameUnitManager::UpdateBattlePosition() { Vec2f ScreenCenter = g_Context->Constants.GetVec2fConstant(ScreenConstantVec2fScreenCenter); _BattlePosition.ClosestEnemyHostile = ClosestHostileToPoint(ScreenCenter); const float BlinkRange = 275.0f; const float RetreatRange = 150.0f; _BattlePosition.SafestRetreatLocation = BestRetreatLocation(ScreenCenter, RetreatRange).RoundToVec2i(); _BattlePosition.SafestBlinkLocation = BestRetreatLocation(ScreenCenter, BlinkRange).RoundToVec2i(); }