Skip to content

Commit

Permalink
Don't color CK3 map edge impassables for the bookmark map (#1729) #patch
Browse files Browse the repository at this point in the history
  • Loading branch information
IhateTrains authored Jan 25, 2024
1 parent 0404f04 commit 2d2406d
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 63 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
0;0;0;0;x;x;
1;42;3;128;VESTFIRDIR;x;
1;134;186;154;VESTFIRDIR;x;
2;84;6;1;REYKJAVIK;x;
3;126;9;129;STOKKSEYRI;x;
49 changes: 40 additions & 9 deletions ImperatorToCK3/CK3/Map/MapData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,12 @@ public override int GetHashCode() {
}

private SortedDictionary<ulong, HashSet<ulong>> NeighborsDict { get; } = [];
public ISet<ulong> ColorableImpassableProvinces { get; } = new HashSet<ulong>();
public ISet<ulong> ColorableImpassableProvinceIds { get; } = new HashSet<ulong>();
public IDictionary<ulong, ProvincePosition> ProvincePositions { get; } = new Dictionary<ulong, ProvincePosition>();
public ProvinceDefinitions ProvinceDefinitions { get; }

public MapData(ModFilesystem ck3ModFS) {
const string mapPath = "map_data/provinces.png";
var provincesMapPath = ck3ModFS.GetActualFileLocation(mapPath);
if (provincesMapPath is null) {
throw new FileNotFoundException($"{nameof(provincesMapPath)} not found!");
}
string provincesMapPath = GetProvincesMapPath(ck3ModFS);

Logger.Info("Loading province definitions...");
ProvinceDefinitions = new ProvinceDefinitions(ck3ModFS);
Expand All @@ -62,7 +58,17 @@ public MapData(ModFilesystem ck3ModFS) {
FindImpassables(ck3ModFS);
Logger.IncrementProgress();
}


private static string GetProvincesMapPath(ModFilesystem ck3ModFS) {
const string mapPath = "map_data/provinces.png";
var provincesMapPath = ck3ModFS.GetActualFileLocation(mapPath);
if (provincesMapPath is null) {
throw new FileNotFoundException($"{nameof(provincesMapPath)} not found!");
}

return provincesMapPath;
}

public double GetDistanceBetweenProvinces(ulong province1, ulong province2) {
if (!ProvincePositions.TryGetValue(province1, out var province1Position)) {
Logger.Warn($"Province {province1} has no position defined!");
Expand Down Expand Up @@ -153,14 +159,39 @@ private void FindImpassables(ModFilesystem ck3ModFS) {
var beginning = provIds[0];
var end = provIds.Last();
for (var id = beginning; id <= end; ++id) {
ColorableImpassableProvinces.Add(id);
ColorableImpassableProvinceIds.Add(id);
}
} else {
ColorableImpassableProvinces.UnionWith(provIds);
ColorableImpassableProvinceIds.UnionWith(provIds);
}
});
parser.IgnoreAndLogUnregisteredItems();
parser.ParseGameFile(filePath, ck3ModFS);

// Exclude impassable provinces that border the map edge from the colorable set.
using var mapPng = Image.Load<Rgb24>(GetProvincesMapPath(ck3ModFS));
var height = mapPng.Height;
var width = mapPng.Width;
var edgeProvinceIds = new HashSet<ulong>();
for (var y = 0; y < height; ++y) {
// Get left edge color.
var color = GetPixelColor(new Point(0, y), mapPng);
edgeProvinceIds.Add(ProvinceDefinitions.ColorToProvinceDict[color]);

// Get right edge color.
color = GetPixelColor(new Point(width - 1, y), mapPng);
edgeProvinceIds.Add(ProvinceDefinitions.ColorToProvinceDict[color]);
}
for (var x = 0; x < width; ++x) {
// Get top edge color.
var color = GetPixelColor(new Point(x, 0), mapPng);
edgeProvinceIds.Add(ProvinceDefinitions.ColorToProvinceDict[color]);

// Get bottom edge color.
color = GetPixelColor(new Point(x, height - 1), mapPng);
edgeProvinceIds.Add(ProvinceDefinitions.ColorToProvinceDict[color]);
}
ColorableImpassableProvinceIds.ExceptWith(edgeProvinceIds);
}

private static Rgb24 GetCenterColor(Point position, Image<Rgb24> provincesMap) {
Expand Down
124 changes: 71 additions & 53 deletions ImperatorToCK3/Outputter/BookmarkOutputter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -202,68 +202,86 @@ private static void DrawBookmarkMap(Configuration config, List<Title> playerTitl
var mapData = ck3World.MapData;
var provDefs = mapData.ProvinceDefinitions;

foreach (var playerTitle in playerTitles) {
DrawPlayerTitleOnMap(config, ck3World, playerTitle, mapData, provincesImage, provDefs, bookmarkMapImage);
}

var outputPath = Path.Combine("output", config.OutputModName, "gfx/interface/bookmarks/bm_converted.png");
bookmarkMapImage.SaveAsPng(outputPath);
ResaveImageAsDDS(outputPath);

Logger.IncrementProgress();
}

private static void DrawPlayerTitleOnMap(
Configuration config,
World ck3World,
Title playerTitle,
MapData mapData,
Image provincesImage,
ProvinceDefinitions provDefs,
Image bookmarkMapImage
) {
Rgba32 black = Color.Black;

var colorOnMap = playerTitle.Color1 ?? new commonItems.Colors.Color(0, 0, 0);
var rgba32ColorOnMap = new Rgba32((byte)colorOnMap.R, (byte)colorOnMap.G, (byte)colorOnMap.B);
ISet<ulong> heldProvinces = playerTitle.GetProvincesInCountry(config.CK3BookmarkDate);

// Determine which impassables should be be colored by the country
HashSet<ulong> provincesToColor = GetImpassableProvincesToColor(mapData, heldProvinces);
int diff = provincesToColor.Count - heldProvinces.Count;
Logger.Debug($"Coloring {diff} impassable provinces with color of {playerTitle}...");

using var realmHighlightImage = provincesImage.CloneAs<Rgba32>();
IEnumerable<Rgb24> provinceColors = provincesToColor.Select(provId => provDefs.ProvinceToColorDict[provId]);
foreach (var provinceColor in provinceColors) {
// Make pixels of the province black.
var rgbaProvinceColor = new Rgba32();
provinceColor.ToRgba32(ref rgbaProvinceColor);
ReplaceColorOnImage(realmHighlightImage, rgbaProvinceColor, black);
}

foreach (var playerTitle in playerTitles) {
var colorOnMap = playerTitle.Color1 ?? new commonItems.Colors.Color(0, 0, 0);
var rgba32ColorOnMap = new Rgba32((byte)colorOnMap.R, (byte)colorOnMap.G, (byte)colorOnMap.B);
ISet<ulong> heldProvinces = playerTitle.GetProvincesInCountry(config.CK3BookmarkDate);
// Determine which impassables should be be colored by the country
var provincesToColor = new HashSet<ulong>(heldProvinces);
var impassables = mapData.ColorableImpassableProvinces;
foreach (var impassableId in impassables) {
var nonImpassableNeighborProvs = mapData.GetNeighborProvinceIds(impassableId)
.Except(impassables)
.ToHashSet();
if (nonImpassableNeighborProvs.Count == 0) {
continue;
}
// Make all non-black pixels transparent.
InverseTransparent(realmHighlightImage, black);

var heldNonImpassableNeighborProvs = nonImpassableNeighborProvs.Intersect(heldProvinces);
if ((double)heldNonImpassableNeighborProvs.Count() / nonImpassableNeighborProvs.Count > 0.5) {
// Realm controls more than half of non-impassable neighbors of the impassable.
provincesToColor.Add(impassableId);
}
}
// Replace black with title color.
ReplaceColorOnImage(realmHighlightImage, black, rgba32ColorOnMap);

// Create realm highlight file.
var holder = ck3World.Characters[playerTitle.GetHolderId(config.CK3BookmarkDate)];
var highlightPath = Path.Combine(
"output",
config.OutputModName,
$"gfx/interface/bookmarks/bm_converted_bm_converted_{holder.Id}.png"
);
realmHighlightImage.SaveAsPng(highlightPath);
ResaveImageAsDDS(highlightPath);

var diff = provincesToColor.Count - heldProvinces.Count;
Logger.Debug($"Coloring {diff} impassable provinces with color of {playerTitle}...");
// Add the image on top of blank map image.
// Make the realm on map semi-transparent.
bookmarkMapImage.Mutate(x => x.DrawImage(realmHighlightImage, 0.5f));
}

using var realmHighlightImage = provincesImage.CloneAs<Rgba32>();
foreach (var provinceColor in provincesToColor.Select(
province => provDefs.ProvinceToColorDict[province])) {
// Make pixels of the province black.
var rgbaProvinceColor = new Rgba32();
provinceColor.ToRgba32(ref rgbaProvinceColor);
ReplaceColorOnImage(realmHighlightImage, rgbaProvinceColor, black);
private static HashSet<ulong> GetImpassableProvincesToColor(MapData mapData, ISet<ulong> heldProvinceIds) {
var provinceIdsToColor = new HashSet<ulong>(heldProvinceIds);
var impassableIds = mapData.ColorableImpassableProvinceIds;
foreach (ulong impassableId in impassableIds) {
var nonImpassableNeighborProvIds = mapData.GetNeighborProvinceIds(impassableId)
.Except(impassableIds)
.ToHashSet();
if (nonImpassableNeighborProvIds.Count == 0) {
continue;
}

// Make all non-black pixels transparent.
InverseTransparent(realmHighlightImage, black);

// Replace black with title color.
ReplaceColorOnImage(realmHighlightImage, black, rgba32ColorOnMap);

// Create realm highlight file.
var holder = ck3World.Characters[playerTitle.GetHolderId(config.CK3BookmarkDate)];
var highlightPath = Path.Combine(
"output",
config.OutputModName,
$"gfx/interface/bookmarks/bm_converted_bm_converted_{holder.Id}.png"
);
realmHighlightImage.SaveAsPng(highlightPath);
ResaveImageAsDDS(highlightPath);

// Add the image on top of blank map image.
// Make the realm on map semi-transparent.
bookmarkMapImage.Mutate(x => x.DrawImage(realmHighlightImage, 0.5f));
var heldNonImpassableNeighborProvIds = nonImpassableNeighborProvIds.Intersect(heldProvinceIds);
if ((double)heldNonImpassableNeighborProvIds.Count() / nonImpassableNeighborProvIds.Count > 0.5) {
// Realm controls more than half of non-impassable neighbors of the impassable.
provinceIdsToColor.Add(impassableId);
}
}

var outputPath = Path.Combine("output", config.OutputModName, "gfx/interface/bookmarks/bm_converted.png");
bookmarkMapImage.SaveAsPng(outputPath);
ResaveImageAsDDS(outputPath);

Logger.IncrementProgress();
return provinceIdsToColor;
}

private static void ReplaceColorOnImage(Image<Rgba32> image, Rgba32 sourceColor, Rgba32 targetColor) {
Expand Down

0 comments on commit 2d2406d

Please sign in to comment.