public static BitmapSheet FromImage(string path, int scale, int pieceScale, int pieceSize, bool saveSheet, bool savePieces = false, bool animated = false) { var bs = new BitmapSheet { Bitmaps = new Dictionary <ushort, Bitmap>() }; var bitmap = AImage.FromFile(path); var totalBitmap = new Bitmap(bitmap.Width * scale, bitmap.Height * scale); var index = 0; for (var y = 0; y < bitmap.Height; y += pieceSize) { for (var x = 0; x < bitmap.Width; x += pieceSize) { var crop = new Crop(new Rectangle(x, y, pieceSize, pieceSize)); var piece = crop.Apply(bitmap); var resizeW = System.Math.Min(32, pieceSize * 4); var resizeH = System.Math.Min(32, pieceSize * 4); var outW = System.Math.Min(40, pieceSize * 5); var outH = System.Math.Min(40, pieceSize * 5); var resize = new ResizeNearestNeighbor(resizeW, resizeH); piece = resize.Apply(piece); var outBitmap = new Bitmap(outW, outH); outBitmap.Add(piece); outBitmap = outBitmap.OutlineGlow(0, 0, .8, 1.4, 10); if (savePieces) { outBitmap.Save($"{path} - 0x{index:x2}.png", ImageFormat.Png); } totalBitmap.Add(outBitmap, x * 5, y * 5); bs.Bitmaps.Add((ushort)index, outBitmap); index++; } } if (saveSheet) { totalBitmap.SavePng($"{path} - {(int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds}.png"); } return(bs); }
/// <summary> /// Encapsulates the entire parking lot state /// change, triggerd by Camera changes /// </summary> public void Update() { if (Baseline != null && Baseline.SameSize(Camera.CurrentImage)) { lock (this) { // Reset percentDifferences to zero Annotations.ForEach(a => a.PercentDifference = 0); // Maps annotations to their area in pixels Dictionary <Annotation, int> annotationPixelAreas = Annotations.ToDictionary(a => a, a => 0); // Get the raw pixel difference (color sensitive) Bitmap baseline = Baseline; Bitmap current = Camera.CurrentImage; Bitmap difference = baseline.Difference( current.Add( // Adjust the current image by the average light difference // between the baseline and current image AverageDifference( baseline, current, Annotations.Where(a => a.Type == Annotation.AnnotationType.Constant ) ) ) ); // Sum up difference percentage contained by each annotation for (int x = 0; x < difference.Width; x++) { for (int y = 0; y < difference.Height; y++) { Vector2 pixelPoint = new Vector2((double)x / difference.Width, (double)y / difference.Height); foreach (Annotation annotation in Annotations.Where(a => a.Contains(pixelPoint))) { annotationPixelAreas[annotation]++; annotation.PercentDifference += difference.GetPixel(x, y).Value(); } } } // Normalize the difference percentages by dividing out the total pixel area foreach (var pixelArea in annotationPixelAreas.Where(p => p.Value > 0)) { pixelArea.Key.PercentDifference /= pixelArea.Value; } } } }
private static void Main(string[] args) { Console.ForegroundColor = ConsoleColor.White; /* new epicHiveObjectsCXML(), new lostHallsObjectsCXML(), new oryxScribeObjectsCXML(), * new cnidarianReefObjectsCXML(), new gemLordObjectCXML(), new testAtrapperObjectCXML(), new stSorcAndHuntObjectsCXML(), * new bearCaveObjectCXML(), new goblinLairObjectCXML(), new KrathTestObjectsCXML(), new KrathMuTestObjectsCXML()];*/ var objectXmlPaths = new[] { "projectiles.xml", "equip.xml", "skins.xml", "dungeons/testAtrapper/testAtrapperSkins.xml", "dungeons/stSorcAndHunt/stSorcAndHuntSkins.xml", "dyes.xml", "textiles.xml", "permapets.xml", "token.xml", "testing/willemTesting.xml", "testing/ttesting.xml", "testing/btesting.xml", "testing/stesting.xml", "testing/mtesting.xml", "testing/ktesting.xml", "players.xml", "containers.xml", "objects.xml", "portals.xml", "testingObjects.xml", "staticobjects.xml", "tutorial/tutorialObjects.xml", "tutorial/tutorialMonsters.xml", "allies.xml", "heroes.xml", "playersZombies.xml", "pets.xml", "npc.xml", "realm/shore.xml", "realm/low.xml", "realm/mid.xml", "realm/high.xml", "realm/mountains.xml", "encounters.xml", "arena.xml", "dungeons/oryxCastle.xml", "dungeons/tombOfTheAncients.xml", "dungeons/spriteWorld.xml", "dungeons/undeadLair.xml", "dungeons/oceanTrench.xml", "dungeons/forbiddenJungle.xml", "dungeons/oryxChamber.xml", "dungeons/oryxChickenChamber.xml", "dungeons/oryxWineCellar.xml", "dungeons/manorOfTheImmortals.xml", "dungeons/pirateCave.xml", "dungeons/snakePit.xml", "dungeons/spiderDen.xml", "dungeons/abyssOfDemons.xml", "dungeons/ghostShip.xml", "dungeons/madLab.xml", "dungeons/caveOfAThousandTreasures.xml", "dungeons/candyLand.xml", "dungeons/hauntedCemetery.xml", "dungeons/forestMaze.xml", "dungeons/epicForestMaze.xml", "dungeons/epicPirateCave.xml", "dungeons/epicSpiderDen.xml", "dungeons/nexusDestroyed.xml", "dungeons/miniDungeonHub.xml", "dungeons/lairOfDraconis.xml", "dungeons/lairOfShaitan.xml", "dungeons/shatters.xml", "dungeons/belladonna.xml", "dungeons/puppetMaster.xml", "dungeons/iceCave.xml", "dungeons/theHive.xml", "dungeons/toxicSewers.xml", "dungeons/puppetMasterEncore.xml", "dungeons/iceTomb.xml", "dungeons/parasiteDen/parasiteDenObjects.xml", "dungeons/stPatricks/stPatricksObjects.xml", "dungeons/buffedBunny/buffedBunnyObjects.xml", "dungeons/hanamiNexus/hanamiNexusObjects.xml", "dungeons/mountainTemple/mountainTempleObjects.xml", "dungeons/oryxHorde/oryxHordeObjects.xml", "dungeons/summerNexus/summerNexusObjects.xml", "dungeons/autumnNexus/autumnNexusObjects.xml", "dungeons/epicHive/epicHiveObjects.xml", "dungeons/lostHalls/lostHallsObjects.xml", "dungeons/oryxScribe/oryxScribeObjects.xml", "dungeons/cnidarianReef/cnidarianReefObjects.xml", "dungeons/magicWoods/magicWoodsObjects.xml", "dungeons/santaWorkshop/santaWorkshopObjects.xml", "dungeons/gemLord/gemLordObjects.xml", "dungeons/testAtrapper/testAtrapperObjects.xml", "dungeons/stSorcAndHunt/stSorcAndHuntObjects.xml", "dungeons/bearCave/bearCaveObjects.xml", "dungeons/goblinLair/goblinLairObjects.xml", "dungeons/krathTest/krathTestObjects.xml", "dungeons/krathTest/krathMuTestObjects.xml" }; foreach (var file in objectXmlPaths.Select(o => new FileInfo($"xml/{o}"))) { if (!file.Exists) { Log($"ERROR: {file.FullName} doesn't exist"); continue; } load(file); } Log($"{items.Count} items"); var rendersBitmap = new Bitmap(40 * 25, Math.Max(items.Count / 25, 1) * 40); Log( $"Output bitmap: {rendersBitmap.Width} * {rendersBitmap.Height} ({rendersBitmap.Width * rendersBitmap.Height}px)"); var dict = new Dictionary <int, List <object> > { { -1, new List <object> { "empty slot", 0, -1, 0, 0, 0, 0 } } }; var bitmapSheetByName = new Dictionary <string, BitmapSheet>(); var addedSprites = new Dictionary <string, Dictionary <ushort, int> >(); var index = 1; foreach (var item in items) { var increment = true; var sheet = new FileInfo($"sheets/{item.Sheet}.png"); if (!sheet.Exists) { Log($"ERROR: sheet {sheet.Name} doesn't exist"); break; } var sheetBitmap = AImage.FromFile(sheet.FullName); if (!bitmapSheetByName.ContainsKey(item.Sheet)) { Log( $"{item.Sheet} bitmap: {sheetBitmap.Width} * {sheetBitmap.Height} ({sheetBitmap.Width * sheetBitmap.Height}px)"); int size; bool animated; switch (sheetBitmap.Width) { case 8 * 16: size = 8; animated = false; break; case 8 * 7: size = 8; animated = true; break; case 16 * 16: size = 16; animated = false; break; case 16 * 7: size = 16; animated = true; break; default: Log($"ERROR: unknown width: {sheetBitmap.Width}"); continue; } bitmapSheetByName.Add(item.Sheet, BitmapSheet.FromImage(sheet.FullName, 5, 4, size, false, false, false)); Log( $"{item.Sheet} - size {size} animated {animated} indexes {bitmapSheetByName[item.Sheet].Bitmaps.Keys.Max()}"); } // Log($"{item.Id}: {item.Sheet}.{item.Index}"); if (!addedSprites.ContainsKey(item.Sheet)) { addedSprites.Add(item.Sheet, new Dictionary <ushort, int>()); } int x, y; if (!addedSprites[item.Sheet].ContainsKey(item.Index)) { addedSprites[item.Sheet].Add(item.Index, index); indexToCoords(index, rendersBitmap.Width, out x, out y); rendersBitmap.Add( bitmapSheetByName[item.Sheet].Bitmaps[ item.Animated ? (ushort)(item.Index * (item.Sheet.Contains("player") ? 21 : 7)) : item.Index], x, y); } else { indexToCoords(addedSprites[item.Sheet][item.Index], rendersBitmap.Width, out x, out y); increment = false; } dict.Add(item.Type, new List <object> { item.Id, item.SlotType, item.Tier, x, y, item.FameBonus, item.FeedPower }); if (increment) { index++; } } rendersBitmap.SavePng("renders.png"); rendersBitmap.Dispose(); var classesDict = classes.ToDictionary <ClassData, int, List <object> >(classData => classData.Type, classData => new List <object> { classData.Id ?? "", classData.Starts ?? new ushort[0], classData.Averages ?? new ushort[0], classData.Maxes ?? new ushort[0], classData.Slots ?? new ushort[0] }); File.WriteAllText("constants.js", $@"items = { JsonConvert.SerializeObject(dict, new JsonSerializerSettings {Formatting = Formatting.Indented}) } classes = {JsonConvert.SerializeObject(classesDict, new JsonSerializerSettings {Formatting = Formatting.Indented})} skins = {{ {string.Join("\n", skins.Select(skin => $" 0x{skin.Type:x}: {skin.Index}, // {skin.Id}"))} }}"); var skinBitmap = new Bitmap(56, (skins.Count + 14) * 24); var maskBitmap = new Bitmap(56, (skins.Count + 14) * 24); addedSprites.Clear(); index = 0; foreach (var classData in classes) { var sheet = new FileInfo($"sheets/{classData.Sheet}.png"); if (!sheet.Exists) { Log($"ERROR: sheet {sheet.Name} doesn't exist"); break; } var mask = new FileInfo($"sheets/{classData.Sheet}Mask.png"); var maskSheet = AImage.FromFile(mask.FullName); var sheetBitmap = AImage.FromFile(sheet.FullName); var crop = new Crop(new Rectangle(0, classData.SheetIndex * 24, 56, 24)); var piece = crop.Apply(sheetBitmap); skinBitmap.Add(piece, 0, index * 24); crop = new Crop(new Rectangle(0, classData.SheetIndex * 24, 56, 24)); piece = crop.Apply(maskSheet); maskBitmap.Add(piece, 0, index * 24); index++; } index = 0; foreach (var skinData in skins.OrderBy(skin => skin.Index)) { var sheet = new FileInfo($"sheets/{skinData.Sheet}.png"); if (!sheet.Exists) { Log($"ERROR: sheet {sheet.Name} doesn't exist"); break; } var mask = new FileInfo($"sheets/{skinData.Sheet}Mask.png"); var maskSheet = AImage.FromFile(mask.FullName); var sheetBitmap = AImage.FromFile(sheet.FullName); var crop = new Crop(new Rectangle(0, skinData.SheetIndex * 24, 56, 24)); var piece = crop.Apply(sheetBitmap); skinBitmap.Add(piece, 0, (index + 14) * 24); crop = new Crop(new Rectangle(0, skinData.SheetIndex * 24, 56, 24)); piece = crop.Apply(maskSheet); maskBitmap.Add(piece, 0, (index + 14) * 24); index++; } using (var ms = new MemoryStream()) { skinBitmap.Save(ms, ImageFormat.Png); File.WriteAllText("sheets-skin.js", $"data:image/png;base64,{Convert.ToBase64String(ms.ToArray())}"); } using (var ms = new MemoryStream()) { maskBitmap.Save(ms, ImageFormat.Png); File.WriteAllText("sheets-skinmask.js", $"data:image/png;base64,{Convert.ToBase64String(ms.ToArray())}"); } skinBitmap.SavePng("skins.png"); maskBitmap.SavePng("skinsmask.png"); }