// // DatabaseManager.cpp // // Stores all databases used by the AI // Written by Matthew Fisher // #include "Main.h" DatabaseHeader::DatabaseHeader(const String &Str) { Str.Partition('\t', Words); } UINT DatabaseHeader::GetFieldIndex(const String &FieldName) const { for(UINT i = 0; i < Words.Length(); i++) { if(Words[i] == FieldName) { return i; } } g_Context->Files.Assert << "Field not found: " << FieldName << endl; return 0; } UnitEntry::UnitEntry(const String &Str, const DatabaseHeader &Header) { Vector Words; Str.Partition('\t', Words); const float FastestGameSpeedFactor = 0.73f; Name = Words[Header.GetFieldIndex("Name")]; Prims = Words[Header.GetFieldIndex("Prims")].ConvertToInteger(); Race = GetRaceTypeFromString(String("Race") + Words[Header.GetFieldIndex("Race")]); PrimaryType = GetUnitPrimaryTypeFromString(String("UnitPrimary") + Words[Header.GetFieldIndex("Primary Type")]); SecondaryType = GetUnitSecondaryTypeFromString(String("UnitSecondary") + Words[Header.GetFieldIndex("Secondary Type")]); BuildClass = GetBuildClassTypeFromString(String("BuildClass") + Words[Header.GetFieldIndex("Build Class")]); BuiltAt = Words[Header.GetFieldIndex("Built At")]; Requirement = Words[Header.GetFieldIndex("Requirement")]; Complexity = GetComplexityTypeFromString(String("Complexity") + Words[Header.GetFieldIndex("Complexity")]); Hostile = Words[Header.GetFieldIndex("Hostile")].MakeLowercase().ConvertToBoolean(); AttackAir = Words[Header.GetFieldIndex("AttackAir")].MakeLowercase().ConvertToBoolean(); AttackGround = Words[Header.GetFieldIndex("AttackGround")].MakeLowercase().ConvertToBoolean(); Flying = Words[Header.GetFieldIndex("Flying")].MakeLowercase().ConvertToBoolean(); Range = Words[Header.GetFieldIndex("Range")].MakeLowercase().ConvertToInteger(); MineralCost = Words[Header.GetFieldIndex("Mineral Cost")].ConvertToUnsignedInteger(); GasCost = Words[Header.GetFieldIndex("Gas Cost")].ConvertToUnsignedInteger(); SupplyCost = Words[Header.GetFieldIndex("Supply Cost")].ConvertToUnsignedInteger(); BuildTime = Words[Header.GetFieldIndex("Build Time")].ConvertToUnsignedInteger() * FastestGameSpeedFactor; Hotkey = Words[Header.GetFieldIndex("Hotkey")][0]; Detector = (Name == "Observer" || Name == "PhotonCannon" || Name == "Raven" || Name == "MissleTurret" || Name == "Overseer" || Name == "SporeCrawler"); } void MinimapEntry::LoadBasesFromFile(const String &Filename) { Vector Lines, Words; Utility::GetFileLines(Filename, Lines); Lines[0].Partition(' ', Words); Bases.Allocate(Words[1].ConvertToUnsignedInteger()); for(UINT BaseIndex = 0; BaseIndex < Bases.Length(); BaseIndex++) { BaseInfo &CurBase = Bases[BaseIndex]; CurBase.ExtractorCount = 2; CurBase.Player = PlayerInvalid; CurBase.MyStartLocation = false; CurBase.EnemyStartLocation = false; } for(UINT LineIndex = 0; LineIndex < Lines.Length(); LineIndex++) { const String &CurLine = Lines[LineIndex]; CurLine.Partition(' ', Words); if(Words.Length() == 4 || Words.Length() == 5) { UINT BaseIndex = Words[1].ConvertToUnsignedInteger(); BaseInfo &CurBase = Bases[BaseIndex]; const String &Property = Words[2]; if(Words.Length() == 4) { bool Value = Words[3].MakeLowercase().ConvertToBoolean(); if(Property == "Debris") CurBase.Debris = Value; else if(Property == "StartLocation") CurBase.StartLocation = Value; else if(Property == "Island") CurBase.Island = Value; else g_Context->Files.Assert << "Unexpected field property\n"; } if (Words.Length() == 5) { Vec2f Value; Value.x = Words[3].ConvertToFloat(); Value.y = Words[4].ConvertToFloat(); if (Property == "AssaultLocation") CurBase.AssaultLocation = Value; else if (Property == "BaseLocation") CurBase.BaseLocation = Value; else if (Property == "BuildLocation") CurBase.BuildLocation = Value; else if (Property == "ExtractorLocation0") CurBase.ExtractorLocations[0] = Value; else if (Property == "ExtractorLocation1") CurBase.ExtractorLocations[1] = Value; else if (Property == "RampLocation") CurBase.RampLocation = Value; else g_Context->Files.Assert << "Unexpected field property\n"; } } } } // // Loads all database entries from the database file // void DatabaseManager::Init() { LoadUnitEntries(g_Context->Parameters.DatabaseLoadDirectory + String("Units.txt")); LoadMinimapEntries(); } void DatabaseManager::LoadUnitEntries(const String &DatabaseFilename) { if(Utility::FileExists(DatabaseFilename)) { Vector Lines; Utility::GetFileLines(DatabaseFilename, Lines); DatabaseHeader Header(Lines[0]); for(UINT LineIndex = 1; LineIndex < Lines.Length(); LineIndex++) { String &CurLine = Lines[LineIndex]; if(CurLine.Length() > 3) { _UnitEntries.PushEnd(new UnitEntry(CurLine, Header)); } } } else { g_Context->Files.Assert << "Unit database " << DatabaseFilename << " not found\n"; } } void DatabaseManager::LoadMinimapEntries() { String BaseDirectory = g_Context->Parameters.BaseDataDirectory + String("Minimaps\\"); Directory Dir(BaseDirectory); for(UINT FileIndex = 0; FileIndex < Dir.Files().Length(); FileIndex++) { const String &CurFilename = Dir.Files()[FileIndex]; if(CurFilename.EndsWith(".png")) { String Name = Dir.Files()[FileIndex].RemoveSuffix(".png"); String CurDataFilename = Name + String(".txt"); if(Utility::FileExists(Dir.DirectoryPath() + CurDataFilename)) { MinimapEntry *NewEntry = new MinimapEntry; NewEntry->Bmp.LoadPNG(Dir.DirectoryPath() + CurFilename); NewEntry->Name = Name; NewEntry->LoadBasesFromFile(Dir.DirectoryPath() + CurDataFilename); _MinimapEntries.PushEnd(NewEntry); } else { g_Context->Files.Assert << "Minimap annotation not found: " << Name << endl; } } } } UnitEntry* DatabaseManager::GetUnitEntry(const String &Name) const { for(UINT UnitIndex = 0; UnitIndex < _UnitEntries.Length(); UnitIndex++) { if(_UnitEntries[UnitIndex]->Name == Name) { return _UnitEntries[UnitIndex]; } } g_Context->Files.Assert << "Unit not found: " << Name << endl; return NULL; } UINT ComputeMinimapMatchError(const Bitmap &A, const Bitmap &B) { if(A.Dimensions() != B.Dimensions()) { g_Context->Files.Assert << "Minimap dimensions do not match\n"; return 200 * 200; } //A.SavePNG("C:\\SC2AICapture\\A.png"); //B.SavePNG("C:\\SC2AICapture\\B.png"); const UINT Width = A.Width(); const UINT Height = A.Height(); UINT Error = 0; for(UINT Y = 0; Y < Height; Y++) { for(UINT X = 0; X < Width; X++) { bool ATest = (A[Y][X] == MinimapResourceColor); bool BTest = (B[Y][X] == MinimapResourceColor); if(ATest && !BTest) { Error++; } if(!ATest && BTest) { Error++; } } } return Error; } MinimapEntry* DatabaseManager::FindClosestMinimap(const Bitmap &Bmp) const { MinimapEntry *BestMinimap = NULL; UINT BestError = 200 * 200; for(UINT MinimapIndex = 0; MinimapIndex < _MinimapEntries.Length(); MinimapIndex++) { MinimapEntry *CurMinimap = _MinimapEntries[MinimapIndex]; UINT CurError = ComputeMinimapMatchError(Bmp, CurMinimap->Bmp); if(CurError < BestError) { BestError = CurError; BestMinimap = CurMinimap; } } if(BestMinimap != NULL) { g_Context->WriteConsole(String("Minimap matched:") + BestMinimap->Name + String(",") + String(BestError), RGBColor::Yellow, OverlayPanelSystem); } return BestMinimap; }