Add Altas class & refactored Atlas editor

This commit is contained in:
miku-666
2025-11-06 20:38:43 +01:00
parent 444924bfd2
commit 9d04e5f2aa
22 changed files with 3032 additions and 2425 deletions

View File

@@ -291,7 +291,7 @@ namespace PckStudio.Controls
asset.SetSerializedData(animation, AnimationSerializer.DefaultSerializer);
});
using (AnimationEditor animationEditor = new AnimationEditor(animation, saveContext, displayname, internalName.ToLower().EqualsAny(specialTileNames)))
using (AnimationEditor animationEditor = new AnimationEditor(animation, saveContext, displayname, !internalName.ToLower().EqualsAny(specialTileNames)))
{
if (animationEditor.ShowDialog(this) == DialogResult.OK)
{
@@ -300,17 +300,17 @@ namespace PckStudio.Controls
}
}
break;
case ResourceCategory.ParticleAtlas:
case ResourceCategory.MoonPhaseAtlas:
case ResourceCategory.ItemAtlas:
case ResourceCategory.BlockAtlas:
case ResourceCategory.ParticleAtlas:
case ResourceCategory.BannerAtlas:
case ResourceCategory.PaintingAtlas:
case ResourceCategory.ExplosionAtlas:
case ResourceCategory.ExperienceOrbAtlas:
case ResourceCategory.MoonPhaseAtlas:
case ResourceCategory.MapIconAtlas:
case ResourceCategory.AdditionalMapIconsAtlas:
Image atlas = asset.GetTexture();
Atlas atlas = asset.GetDeserializedData(new AtlasDeserializer(resourceLocation));
ColorContainer colorContainer = default;
if (EditorValue.File.TryGetAsset("colours.col", PckAssetType.ColourTableFile, out PckAsset colAsset))
colorContainer = colAsset.GetData(new COLFileReader());
@@ -356,7 +356,7 @@ namespace PckStudio.Controls
return true;
});
ISaveContext<Image> textureAtlasSaveContext = new DelegatedSaveContext<Image>(Settings.Default.AutoSaveChanges, asset.SetTexture);
ISaveContext<Atlas> textureAtlasSaveContext = new DelegatedSaveContext<Atlas>(Settings.Default.AutoSaveChanges, atlas => asset.SetTexture(atlas));
var viewer = new TextureAtlasEditor(atlas, textureAtlasSaveContext, resourceLocation, colorContainer, tryGetAnimation, tryGetAnimationSaveContext);
if (viewer.ShowDialog(this) == DialogResult.OK)
@@ -1293,7 +1293,7 @@ namespace PckStudio.Controls
newAnimation = animation;
});
using AnimationEditor animationEditor = new AnimationEditor(Animation.CreateEmpty(), saveContext, diag.SelectedTile.DisplayName, diag.SelectedTile.InternalName.EqualsAny("clock", "compass"));
using AnimationEditor animationEditor = new AnimationEditor(Animation.CreateEmpty(), saveContext, diag.SelectedTile.DisplayName, !diag.SelectedTile.InternalName.EqualsAny("clock", "compass"));
if (animationEditor.ShowDialog() == DialogResult.OK && newAnimation is not null)
{
_wasModified = true;

View File

@@ -40,16 +40,16 @@ namespace PckStudio.Forms.Editor
{
public partial class AnimationEditor : EditorForm<Animation>
{
private bool _isSpecialTile;
private bool _editable;
internal AnimationEditor(Animation animation, ISaveContext<Animation> saveContext, string displayName, bool isSpecialTile = false)
internal AnimationEditor(Animation animation, ISaveContext<Animation> saveContext, string displayName, bool editable = true)
: base(animation, saveContext)
{
InitializeComponent();
saveToolStripMenuItem1.Available = !saveContext.AutoSave;
toolStripSeparator1.Available = !saveContext.AutoSave;
tileLabel.Text = displayName;
_isSpecialTile = isSpecialTile;
_editable = editable;
animationPictureBox.Image = animation.CreateAnimationImage();
}
@@ -62,10 +62,9 @@ namespace PckStudio.Forms.Editor
private void ValidateToolStrip()
{
bulkAnimationSpeedToolStripMenuItem.Enabled =
importToolStripMenuItem.Enabled =
exportAsToolStripMenuItem.Enabled =
InterpolationCheckbox.Visible = !_isSpecialTile;
editToolStripMenuItem.Visible =
importToolStripMenuItem.Visible =
InterpolationCheckbox.Visible = _editable;
}
private void AnimationEditor_Load(object sender, EventArgs e)
@@ -78,10 +77,10 @@ namespace PckStudio.Forms.Editor
{
if (EditorValue is null)
{
AnimationStartStopBtn.Enabled = false;
AnimationStartStopBtn.Visible = false;
return;
}
AnimationStartStopBtn.Enabled = true;
AnimationStartStopBtn.Visible = true;
InterpolationCheckbox.Checked = EditorValue.Interpolate;
TextureIcons.Images.Clear();
TextureIcons.Images.AddRange(EditorValue.GetTextures().ToArray());
@@ -103,7 +102,7 @@ namespace PckStudio.Forms.Editor
.Select(frame =>
{
var imageIndex = EditorValue.GetTextureIndex(frame.Texture);
return new TreeNode($"for {frame.Ticks} ticks", imageIndex, imageIndex);
return new TreeNode($"for {frame.Ticks} tick(s)", imageIndex, imageIndex);
})
.ToArray()
);
@@ -159,7 +158,7 @@ namespace PckStudio.Forms.Editor
private void saveToolStripMenuItem1_Click(object sender, EventArgs e)
{
if (!_isSpecialTile && EditorValue is not null && EditorValue.FrameCount > 0)
if (_editable && EditorValue is not null && EditorValue.FrameCount > 0)
{
Save();
DialogResult = DialogResult.OK;
@@ -271,7 +270,7 @@ namespace PckStudio.Forms.Editor
diag.SaveBtn.Text = "Add";
if (diag.ShowDialog(this) == DialogResult.OK)
{
EditorValue.AddFrame(diag.FrameTextureIndex, _isSpecialTile ? Animation.MinimumFrameTime : diag.FrameTime);
EditorValue.AddFrame(diag.FrameTextureIndex, _editable ? diag.FrameTime : Animation.MinimumFrameTime);
UpdateTreeView();
}
}

View File

@@ -36,6 +36,7 @@
this.viewToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.applyColorMaskToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.playAnimationsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.allowGroupsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.originalPictureBox = new PckStudio.ToolboxItems.InterpolationPictureBox();
this.selectTilePictureBox = new PckStudio.ToolboxItems.AnimationPictureBox();
@@ -102,7 +103,8 @@
//
this.viewToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.applyColorMaskToolStripMenuItem,
this.playAnimationsToolStripMenuItem});
this.playAnimationsToolStripMenuItem,
this.allowGroupsToolStripMenuItem});
this.viewToolStripMenuItem.ForeColor = System.Drawing.SystemColors.Control;
this.viewToolStripMenuItem.Name = "viewToolStripMenuItem";
this.viewToolStripMenuItem.Size = new System.Drawing.Size(44, 20);
@@ -114,7 +116,7 @@
this.applyColorMaskToolStripMenuItem.CheckOnClick = true;
this.applyColorMaskToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked;
this.applyColorMaskToolStripMenuItem.Name = "applyColorMaskToolStripMenuItem";
this.applyColorMaskToolStripMenuItem.Size = new System.Drawing.Size(168, 22);
this.applyColorMaskToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.applyColorMaskToolStripMenuItem.Text = "Apply Color Mask";
this.applyColorMaskToolStripMenuItem.CheckedChanged += new System.EventHandler(this.applyColorMaskToolStripMenuItem_CheckedChanged);
//
@@ -124,10 +126,20 @@
this.playAnimationsToolStripMenuItem.CheckOnClick = true;
this.playAnimationsToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked;
this.playAnimationsToolStripMenuItem.Name = "playAnimationsToolStripMenuItem";
this.playAnimationsToolStripMenuItem.Size = new System.Drawing.Size(168, 22);
this.playAnimationsToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.playAnimationsToolStripMenuItem.Text = "Play Animations";
this.playAnimationsToolStripMenuItem.CheckedChanged += new System.EventHandler(this.playAnimationsToolStripMenuItem_CheckedChanged);
//
// allowGroupsToolStripMenuItem
//
this.allowGroupsToolStripMenuItem.Checked = true;
this.allowGroupsToolStripMenuItem.CheckOnClick = true;
this.allowGroupsToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked;
this.allowGroupsToolStripMenuItem.Name = "allowGroupsToolStripMenuItem";
this.allowGroupsToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.allowGroupsToolStripMenuItem.Text = "Allow Groups";
this.allowGroupsToolStripMenuItem.CheckedChanged += new System.EventHandler(this.allowGroupsToolStripMenuItem_CheckedChanged);
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.AutoSize = true;
@@ -171,6 +183,7 @@
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.originalPictureBox.BackColor = System.Drawing.Color.Transparent;
this.originalPictureBox.BackgroundInterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Default;
this.originalPictureBox.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
this.originalPictureBox.Location = new System.Drawing.Point(217, 3);
this.originalPictureBox.Name = "originalPictureBox";
@@ -187,6 +200,7 @@
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.selectTilePictureBox.BackColor = System.Drawing.Color.Transparent;
this.selectTilePictureBox.BackgroundInterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Default;
this.selectTilePictureBox.BlendColor = System.Drawing.Color.White;
this.selectTilePictureBox.BlendMode = PckStudio.Core.Extensions.BlendMode.Multiply;
this.tableLayoutPanel1.SetColumnSpan(this.selectTilePictureBox, 2);
@@ -384,5 +398,6 @@
private MetroFramework.Controls.MetroTrackBar colorSlider;
private MetroFramework.Controls.MetroLabel colorSliderLabel;
private MetroFramework.Controls.MetroButton extractButton;
private System.Windows.Forms.ToolStripMenuItem allowGroupsToolStripMenuItem;
}
}

View File

@@ -32,22 +32,17 @@ using PckStudio.Core.Json;
namespace PckStudio.Forms.Editor
{
internal partial class TextureAtlasEditor : EditorForm<Image>
internal partial class TextureAtlasEditor : EditorForm<Atlas>
{
private readonly ITryGet<string, Animation> _tryGetAnimation;
private readonly ITryGet<string, ISaveContext<Animation>> _tryGetAnimationSaveContext;
private readonly ColorContainer _colourTable;
private readonly Size _tileAreaSize;
private readonly int _rowCount;
private readonly int _columnCount;
private readonly ResourceCategory _resourceLocationCategory;
private readonly List<AtlasTile> _tiles;
private readonly Atlas _atlas;
private AtlasTile _selectedTile;
// the "parent" tile for tiles that share name; i.e. parts of water_flow
private AtlasTile dataTile;
public TextureAtlasEditor(Image atlas, ISaveContext<Image> saveContext, ResourceLocation resourceLocation, ColorContainer colorContainer,
public TextureAtlasEditor(Atlas atlas, ISaveContext<Atlas> saveContext, ResourceLocation resourceLocation, ColorContainer colorContainer,
ITryGet<string, Animation> tryGetAnimation, ITryGet<string, ISaveContext<Animation>> tryGetAnimationSaveContext)
: base(atlas, saveContext)
{
@@ -56,20 +51,17 @@ namespace PckStudio.Forms.Editor
_ = atlas ?? throw new ArgumentNullException(nameof(atlas));
_ = resourceLocation ?? throw new ArgumentNullException(nameof(resourceLocation));
originalPictureBox.Image = new Bitmap(atlas);
originalPictureBox.Image = atlas;
_colourTable = colorContainer ?? new ColorContainer();
_tileAreaSize = resourceLocation.GetTileArea(atlas.Size);
_tryGetAnimation = tryGetAnimation;
_tryGetAnimationSaveContext = tryGetAnimationSaveContext;
_rowCount = atlas.Width / _tileAreaSize.Width;
_columnCount = atlas.Height / _tileAreaSize.Height;
_atlas = atlas;
_resourceLocationCategory = resourceLocation.Category;
_tiles = new List<AtlasTile>(CreateAtlasTiles());
_atlas.AllowGroups = allowGroupsToolStripMenuItem.Checked;
SelectedIndex = 0;
animationButton.Enabled =
_resourceLocationCategory == ResourceCategory.BlockAtlas ||
_resourceLocationCategory == ResourceCategory.ItemAtlas;
@@ -85,85 +77,16 @@ namespace PckStudio.Forms.Editor
}
}
private IEnumerable<AtlasTile> CreateAtlasTiles()
{
List<JsonTileInfo> tileInfos = _resourceLocationCategory switch
{
ResourceCategory.BlockAtlas => Tiles.BlockTileInfos,
ResourceCategory.ItemAtlas => Tiles.ItemTileInfos,
ResourceCategory.ParticleAtlas => Tiles.ParticleTileInfos,
ResourceCategory.MapIconAtlas => Tiles.MapIconTileInfos,
ResourceCategory.AdditionalMapIconsAtlas => Tiles.AdditionalMapIconTileInfos,
ResourceCategory.MoonPhaseAtlas => Tiles.MoonPhaseTileInfos,
ResourceCategory.ExperienceOrbAtlas => Tiles.ExperienceOrbTileInfos,
ResourceCategory.ExplosionAtlas => Tiles.ExplosionTileInfos,
ResourceCategory.PaintingAtlas => Tiles.PaintingTileInfos,
ResourceCategory.BannerAtlas => Tiles.BannerTileInfos,
_ => null,
};
IEnumerable<Image> images = EditorValue.Split(_tileAreaSize, _imageLayout);
AtlasTile MakeTile((int index, Image value) p)
{
int i = p.index;
JsonTileInfo tileInfo = tileInfos.IndexInRange(i) ? tileInfos[i] : null;
int tileWidth = tileInfo?.TileWidth ?? 1;
int tileHeight = tileInfo?.TileHeight ?? 1;
Rectangle atlasArea = GetAtlasArea(i, tileWidth, tileHeight, _rowCount, _columnCount, _tileAreaSize, _imageLayout);
// get texture for tiles that are not 1x1 tiles
Point selectedPoint = GetSelectedPoint(i, _rowCount, _columnCount, _imageLayout);
var textureLocation = new Point(selectedPoint.X * _tileAreaSize.Width, selectedPoint.Y * _tileAreaSize.Height);
var textureSize = new Size(tileWidth * _tileAreaSize.Width, tileHeight * _tileAreaSize.Height);
var textureArea = new Rectangle(textureLocation, textureSize);
Image texture = tileInfos.IndexInRange(i) ? EditorValue.GetArea(textureArea) : p.value;
return new AtlasTile(i, atlasArea, tileInfo, texture);
}
return images.enumerate().Select(MakeTile);
}
private sealed class AtlasTile
{
internal readonly int Index;
internal readonly Rectangle Area;
internal readonly JsonTileInfo Tile;
internal readonly Image Texture;
public AtlasTile(int index, Rectangle area, JsonTileInfo tile, Image texture)
{
Index = index;
Area = area;
Tile = tile;
Texture = texture;
}
public override bool Equals(object obj)
{
return obj is AtlasTile otherTile && Index == otherTile.Index && Area == otherTile.Area;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
private int SelectedIndex
{
set {
if (value < 0)
{
value += _tiles.Count;
value += _atlas.TileCount;
}
else if (value >= _tiles.Count)
else if (value >= _atlas.TileCount)
{
value -= _tiles.Count;
value -= _atlas.TileCount;
}
SetImageDisplayed(value);
}
@@ -183,12 +106,12 @@ namespace PckStudio.Forms.Editor
{
g.ApplyConfig(_graphicsConfig);
g.Clear(Color.Transparent);
g.DrawImage(EditorValue, 0, 0, EditorValue.Width, EditorValue.Height);
Image image = EditorValue;
g.DrawImage(image, 0, 0, image.Width, image.Height);
SolidBrush brush = new SolidBrush(Color.FromArgb(127, Color.White));
g.FillRectangle(brush, _selectedTile.Area);
g.FillRectangle(brush, _atlas.GetTileArea(_selectedTile));
}
originalPictureBox.Invalidate();
}
@@ -210,46 +133,42 @@ namespace PckStudio.Forms.Editor
selectTilePictureBox.Stop();
selectTilePictureBox.UseBlendColor = false;
selectTilePictureBox.Image = null;
_selectedTile = _atlas[index];
if (_tiles is null || !_tiles.IndexInRange(index) || (_selectedTile = _tiles[index]) is null)
if (_selectedTile is null)
return;
UpdateAtlasDisplay();
dataTile = _selectedTile;
if (string.IsNullOrEmpty(dataTile.Tile.DisplayName) && !string.IsNullOrEmpty(dataTile.Tile.InternalName))
{
dataTile = _tiles.Find(t => t.Tile.InternalName == _selectedTile.Tile.InternalName);
}
selectTilePictureBox.Image = dataTile.Texture;
selectTilePictureBox.Image = _selectedTile.Texture;
selectTilePictureBox.BlendColor = GetBlendColor();
selectTilePictureBox.UseBlendColor = applyColorMaskToolStripMenuItem.Checked;
tileNameLabel.Text = $"{dataTile.Tile.DisplayName}";
internalTileNameLabel.Text = $"{dataTile.Tile.InternalName}";
JsonTileInfo tileInfo = _selectedTile.GetUserDataOfType<JsonTileInfo>();
if (animationButton.Enabled)
tileNameLabel.Text = $"{tileInfo?.DisplayName}";
internalTileNameLabel.Text = $"{tileInfo?.InternalName}";
if (animationButton.Enabled && (_resourceLocationCategory == ResourceCategory.ItemAtlas || _resourceLocationCategory == ResourceCategory.BlockAtlas))
{
ResourceCategory animationResourceCategory = _resourceLocationCategory == ResourceCategory.ItemAtlas ? ResourceCategory.ItemAnimation : ResourceCategory.BlockAnimation;
string animationAssetPath = $"{ResourceLocation.GetPathFromCategory(animationResourceCategory)}/{dataTile.Tile.InternalName}";
string animationAssetPath = $"{ResourceLocation.GetPathFromCategory(animationResourceCategory)}/{tileInfo.InternalName}";
bool hasAnimation = _tryGetAnimation.TryGet(animationAssetPath, out Animation animation);
animationButton.Text = hasAnimation ? "Edit Animation" : "Create Animation";
if (playAnimationsToolStripMenuItem.Checked && hasAnimation)
{
selectTilePictureBox.Image = animation.CreateAnimationImage();
selectTilePictureBox.Image = animation.CreateAnimationImage(selectTilePictureBox.BlendColor);
selectTilePictureBox.Start();
}
}
setColorButton.Enabled = dataTile.Tile.AllowCustomColour;
setColorButton.Enabled = tileInfo.AllowCustomColour;
variantComboBox.Enabled = variantComboBox.Visible = dataTile.Tile.HasColourEntry && dataTile.Tile.ColourEntry?.Variants?.Length > 1;
variantComboBox.Enabled = variantComboBox.Visible = tileInfo.HasColourEntry && tileInfo.ColourEntry?.Variants?.Length > 1;
if (variantComboBox.Enabled)
{
if (dataTile.Tile.ColourEntry.IsWaterColour)
if (tileInfo.ColourEntry.IsWaterColour)
{
foreach (ColorContainer.WaterColor col in _colourTable.WaterColors)
{
@@ -259,11 +178,34 @@ namespace PckStudio.Forms.Editor
}
// TODO: only add variants that are available in the color table
variantComboBox.Items.AddRange(dataTile.Tile.ColourEntry.Variants);
variantComboBox.Items.AddRange(tileInfo.ColourEntry.Variants.Where(colorName => _colourTable.Colors.Any(c => c.Name == colorName) || _colourTable.WaterColors.Any(c => c.Name == colorName)).ToArray());
if (variantComboBox.Items.Count > 0)
variantComboBox.SelectedIndex = 0;
}
if (_selectedTile.IsPartOfGroup && allowGroupsToolStripMenuItem.Checked)
{
AtlasGroup group = _selectedTile.GetGroup();
tileNameLabel.Text = $"{group.Name}";
internalTileNameLabel.Text = string.Empty;
if (group.IsAnimation())
{
animationButton.Enabled = true;
animationButton.Text = "Edit as Animation";
if (playAnimationsToolStripMenuItem.Checked)
{
selectTilePictureBox.UseBlendColor = false;
selectTilePictureBox.Image = _atlas.GetAnimationFromGroup(group).CreateAnimationImage(selectTilePictureBox.BlendColor);
selectTilePictureBox.Start();
return;
}
}
if (group.IsLargeTile())
{
selectTilePictureBox.Image = _atlas.GetTileTexture(_selectedTile);
}
}
}
private static int GetSelectedImageIndex(
@@ -336,6 +278,7 @@ namespace PckStudio.Forms.Editor
default:
break;
}
Debug.WriteLine(result);
return GetSelectedIndex(result.X, result.Y, rowCount, columnCount, imageLayout);
}
@@ -349,47 +292,22 @@ namespace PckStudio.Forms.Editor
};
}
private static Rectangle GetAtlasArea(int index, int tileWidth, int tileHeight, int rowCount, int columnCount, Size size, ImageLayoutDirection imageLayout)
{
Point p = GetSelectedPoint(index, rowCount, columnCount, imageLayout);
var ap = new Point(p.X * size.Width, p.Y * size.Height);
return new Rectangle(ap, new Size(size.Width * tileWidth, size.Height * tileHeight));
}
private static Point GetSelectedPoint(int index, int rowCount, int columnCount, ImageLayoutDirection imageLayout)
{
int y = Math.DivRem(index, rowCount, out int x);
if (imageLayout == ImageLayoutDirection.Vertical)
x = Math.DivRem(index, columnCount, out y);
return new Point(x, y);
}
private void SetTile(Image texture)
{
if (texture.Size != _tileAreaSize)
texture = texture.Resize(_tileAreaSize, _graphicsConfig);
if (texture.Size != _atlas.TileSize)
texture = texture.Resize(_atlas.TileSize, _graphicsConfig);
using (var g = Graphics.FromImage(EditorValue))
{
g.ApplyConfig(_graphicsConfig);
g.Fill(dataTile.Area, Color.Transparent);
g.DrawImage(texture, dataTile.Area);
}
AtlasTile tile = _selectedTile != dataTile ? dataTile : _selectedTile;
_tiles[tile.Index] = new AtlasTile(tile.Index, tile.Area, tile.Tile, texture);
_selectedTile.Texture = texture;
selectTilePictureBox.Image = texture;
UpdateAtlasDisplay();
}
private Color GetBlendColor()
{
if (dataTile.Tile.HasColourEntry && dataTile.Tile.ColourEntry is not null)
if (_selectedTile.TryGetUserDataOfType(out JsonTileInfo tileInfo) && tileInfo.HasColourEntry)
{
Color col = FindBlendColorByKey(dataTile.Tile.ColourEntry.DefaultName);
return col;
return FindBlendColorByKey(tileInfo.ColourEntry.DefaultName);
}
return Color.White;
}
@@ -405,7 +323,7 @@ namespace PckStudio.Forms.Editor
var final_color = Color.FromArgb(colorSlider.Value, colorSlider.Value, colorSlider.Value);
// Enchanted hits are modified critical hit particles
if (dataTile.Tile.InternalName == "enchanted_hit")
if (_selectedTile.TryGetUserDataOfType(out JsonTileInfo tileInfo) && tileInfo.InternalName == "enchanted_hit")
{
// This is directly based on Java's source code for handling enchanted hits
// it just multiplies the red by 0.3 and green by .8 of the color assigned to the critical hit particle
@@ -420,10 +338,10 @@ namespace PckStudio.Forms.Editor
if (colorKey == "experience_orb" || colorKey == "critical_hit")
return GetSpecificBlendColor(colorKey);
if (dataTile.Tile.HasColourEntry && dataTile.Tile.ColourEntry is not null)
if (_selectedTile.TryGetUserDataOfType(out JsonTileInfo tileInfo) && tileInfo.HasColourEntry)
{
// basic way to check for classic water colors
if(!dataTile.Tile.ColourEntry.IsWaterColour || colorKey.StartsWith("Water_"))
if(!tileInfo.ColourEntry.IsWaterColour || colorKey.StartsWith("Water_"))
{
if (_colourTable.Colors.FirstOrDefault(entry => entry.Name == colorKey) is ColorContainer.Color color)
{
@@ -440,6 +358,7 @@ namespace PckStudio.Forms.Editor
return Color.White;
}
// TODO
protected override bool ProcessDialogKey(Keys keyData)
{
switch (keyData)
@@ -455,10 +374,10 @@ namespace PckStudio.Forms.Editor
SelectedIndex = _selectedTile.Index + 1;
return true;
case Keys.Up:
SelectedIndex = _selectedTile.Index - _rowCount;
SelectedIndex = _selectedTile.Index - _atlas.Rows;
return true;
case Keys.Down:
SelectedIndex = _selectedTile.Index + _rowCount;
SelectedIndex = _selectedTile.Index + _atlas.Rows;
return true;
}
@@ -474,8 +393,8 @@ namespace PckStudio.Forms.Editor
int index = GetSelectedImageIndex(
originalPictureBox.Size,
EditorValue.Size,
_tileAreaSize,
((Image)_atlas).Size,
_atlas.TileSize,
e.Location,
originalPictureBox.SizeMode,
_imageLayout);
@@ -509,14 +428,28 @@ namespace PckStudio.Forms.Editor
private void animationButton_Click(object sender, EventArgs e)
{
if (_selectedTile.IsPartOfGroup)
{
AtlasGroup group = _selectedTile.GetGroup();
Animation anim = _atlas.GetAnimationFromGroup(group);
ISaveContext<Animation> saveContext = new DelegatedSaveContext<Animation>(false, (animation) =>
{
// TODO
_atlas.SetGroupTilesFromAnimation(group, animation);
});
var aEditor = new AnimationEditor(anim, saveContext, group.Name, false);
aEditor.ShowDialog(this);
return;
}
JsonTileInfo tileInfo = _selectedTile.GetUserDataOfType<JsonTileInfo>();
ResourceCategory animationResourceCategory = _resourceLocationCategory == ResourceCategory.ItemAtlas ? ResourceCategory.ItemAnimation : ResourceCategory.BlockAnimation;
string animationAssetPath = $"{ResourceLocation.GetPathFromCategory(animationResourceCategory)}/{_selectedTile.Tile.InternalName}";
string animationAssetPath = $"{ResourceLocation.GetPathFromCategory(animationResourceCategory)}/{tileInfo.InternalName}";
bool hasAnimation = _tryGetAnimation.TryGet(animationAssetPath, out Animation animation);
bool isValidAnimationSaveContext = _tryGetAnimationSaveContext.TryGet(animationAssetPath, out ISaveContext<Animation> animationSaveContext);
Debug.Assert(isValidAnimationSaveContext, "Couldn't get valid animation save context.");
var animationEditor = new AnimationEditor(hasAnimation ? animation : Animation.CreateEmpty(), animationSaveContext, _selectedTile.Tile.DisplayName);
var animationEditor = new AnimationEditor(hasAnimation ? animation : Animation.CreateEmpty(), animationSaveContext, tileInfo.DisplayName);
if (animationEditor.ShowDialog(this) == DialogResult.OK)
{
// so animations can automatically update upon saving
@@ -524,27 +457,34 @@ namespace PckStudio.Forms.Editor
}
}
// TODO
private void extractTileToolStripMenuItem_Click(object sender, EventArgs e)
{
string filename = _selectedTile.TryGetUserDataOfType(out JsonTileInfo tileInfo) ? tileInfo.InternalName : "tile";
SaveFileDialog saveFileDialog = new SaveFileDialog()
{
Filter = "Tile Texture|*.png",
FileName = _selectedTile.Tile.InternalName
FileName = filename
};
if (saveFileDialog.ShowDialog(this) == DialogResult.OK)
{
dataTile.Texture.Save(saveFileDialog.FileName, ImageFormat.Png);
_selectedTile.Texture.Save(saveFileDialog.FileName, ImageFormat.Png);
}
}
private void variantComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (dataTile.Tile.ColourEntry is not null && variantComboBox.SelectedItem is not null)
if (_selectedTile.TryGetUserDataOfType(out JsonTileInfo tileInfo) && variantComboBox.SelectedItem is not null)
{
string colorKey = variantComboBox.SelectedItem.ToString();
selectTilePictureBox.BlendColor = FindBlendColorByKey(colorKey);
selectTilePictureBox.Image = dataTile.Texture;
Color blendColor = FindBlendColorByKey(colorKey);
if (_selectedTile.IsPartOfGroup && _selectedTile.GetGroup().IsAnimation())
{
selectTilePictureBox.Image = _atlas.GetAnimationFromGroup(_selectedTile.GetGroup()).CreateAnimationImage(blendColor);
selectTilePictureBox.Start();
return;
}
selectTilePictureBox.BlendColor = blendColor;
}
}
@@ -571,13 +511,12 @@ namespace PckStudio.Forms.Editor
colorPick.AnyColor = true;
colorPick.SolidColorOnly = true;
colorPick.CustomColors = GameConstants.DyeColors.Select(c => c.ToBGR()).ToArray();
colorPick.CustomColors = GameConstants.DyeColors.Select(ColorExtensions.ToBGR).ToArray();
if (colorPick.ShowDialog(this) != DialogResult.OK)
return;
selectTilePictureBox.BlendColor = colorPick.Color;
selectTilePictureBox.Image = dataTile.Texture;
variantComboBox.Enabled = false;
clearColorButton.Enabled = true;
}
@@ -594,7 +533,12 @@ namespace PckStudio.Forms.Editor
private void colorSlider_ValueChanged(object sender, EventArgs e)
{
selectTilePictureBox.BlendColor = GetBlendColor();
selectTilePictureBox.Image = dataTile.Texture;
}
private void allowGroupsToolStripMenuItem_CheckedChanged(object sender, EventArgs e)
{
_atlas.AllowGroups = allowGroupsToolStripMenuItem.Checked;
SelectedIndex = _selectedTile.Index;
}
}
}

View File

@@ -35,10 +35,10 @@ namespace PckStudio.ToolboxItems
get => base.Image;
set
{
base.Image = value;
this.Animate(false);
if (value is null)
return;
this.Animate(false);
base.Image = value;
value.SelectActiveFrame(new FrameDimension(value.FrameDimensionsList[0]), 0);
}
}

View File

@@ -20,8 +20,13 @@ namespace PckStudio.ToolboxItems
public Color BlendColor
{
get => _blendColor;
set => _blendColor = value;
set
{
_blendColor = value;
Image = _image;
}
}
[DefaultValue(typeof(BlendMode), "BlendMode.Add")]
[Category("Blending")]
@@ -33,6 +38,7 @@ namespace PckStudio.ToolboxItems
private bool _useBlendColor = false;
private Color _blendColor = Color.White;
private Image _image;
private BlendMode _blendMode = BlendMode.Add;
public new Image Image
@@ -41,7 +47,9 @@ namespace PckStudio.ToolboxItems
set {
if (value is null)
return;
base.Image = UseBlendColor && BlendColor != Color.White ? value.Blend(BlendColor, BlendMode) : value;
_image = value;
base.Image = UseBlendColor && BlendColor != Color.White ? _image.Blend(BlendColor, BlendMode) : _image;
Invalidate();
}
}
}

View File

@@ -41,11 +41,11 @@ namespace PckStudio.Core
private object _syncLock = new object();
public Animation(IEnumerable<Image> textures, bool initFramesFromTextures = false)
public Animation(IEnumerable<Image> textures, bool initFramesFromTextures = false, int frameTime = MinimumFrameTime)
{
_textures = new List<Image>(textures);
if (initFramesFromTextures)
AddTexturesAsFrames(MinimumFrameTime);
AddTexturesAsFrames(frameTime);
}
public class Frame

227
PckStudio.Core/Atlas.cs Normal file
View File

@@ -0,0 +1,227 @@
/* Copyright (c) 2025-present miku-666
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1.The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using PckStudio.Core.Extensions;
namespace PckStudio.Core
{
public sealed class Atlas
{
public string Name { get; set; }
public int Rows { get; }
public int Columns { get; }
public Size TileSize { get; }
public int TileCount => _tiles.Length;
public bool AllowGroups { get; set; } = default;
private readonly AtlasTile[] _tiles;
private readonly ImageLayoutDirection _layoutDirection;
private readonly List<AtlasGroup> _groups;
public static implicit operator Image(Atlas atlas) => atlas.BuildFinalImage();
private Atlas(string name, int rows, int columns)
{
Name = name;
Rows = rows;
Columns = columns;
_tiles = new AtlasTile[rows * columns];
_groups = new List<AtlasGroup>();
}
private Atlas(string name, int rows, int columns, IEnumerable<AtlasTile> tiles, Size tileSize, ImageLayoutDirection layoutDirection)
: this(name, rows, columns)
{
_tiles = tiles.Take(rows * columns).ToArray();
TileSize = tileSize;
_layoutDirection = layoutDirection;
}
public static Atlas FromResourceLocation(Image source, ResourceLocation resourceLocation, ImageLayoutDirection imageLayout = default)
{
Json.JsonTileInfo[] tilesInfo = resourceLocation.TilesInfo.ToArray();
Size tileArea = resourceLocation.GetTileArea(source.Size);
int rows = source.Width / tileArea.Width;
int columns = source.Height / tileArea.Height;
IEnumerable<AtlasTile> tiles = source.Split(tileArea, imageLayout).enumerate().Select(((int index, Image img) data) => new AtlasTile(data.img, GetSelectedPoint(data.index, out int col, rows, columns, imageLayout), col, index: data.index, userData: tilesInfo.IndexInRange(data.index) ? tilesInfo[data.index] : default));
var atlas = new Atlas(resourceLocation.Path, rows, columns, tiles, tileArea, imageLayout);
atlas.AddGroups(resourceLocation.AtlasGroups);
return atlas;
}
private static int GetSelectedPoint(int index, out int col, int rows, int columns, ImageLayoutDirection layoutDirection)
{
int y = Math.DivRem(index, rows, out int x);
if (layoutDirection == ImageLayoutDirection.Vertical)
x = Math.DivRem(index, columns, out y);
col = y;
return x;
}
public void AddGroups(IEnumerable<AtlasGroup> groups)
{
foreach (AtlasGroup group in groups)
{
AddGroup(group);
}
}
public AtlasTile this[int row, int col]
{
get => this[(col * Rows) + row];
set => this[(col * Rows) + row] = value;
}
public AtlasTile this[int index]
{
get => _tiles.IndexInRange(index) ? _tiles[index] : throw new IndexOutOfRangeException(index.ToString());
set
{
if (_tiles.IndexInRange(index))
_tiles[index] = value;
}
}
public IEnumerable<AtlasTile> GetRange(int row, int col, int count, ImageLayoutDirection direction)
{
return GetRange(row, col, direction == ImageLayoutDirection.Horizontal ? count : 1, direction == ImageLayoutDirection.Vertical ? count : 1);
}
public IEnumerable<AtlasTile> GetRange(int row, int col, int rowCount, int columnCount)
{
for (int j = 0; j < columnCount; j++)
{
for (int i = 0; i < rowCount; i++)
{
if ((row + i) < Rows && (col + j) < Columns)
{
yield return this[row + i, col + j];
}
}
}
yield break;
}
private void SetRange(int row, int col, int count, IEnumerable<Image> tiles)
{
Image[] atlasTiles = tiles.ToArray();
if (atlasTiles.Length < count)
return;
count = count < atlasTiles.Length ? count : atlasTiles.Length;
for (int i = 0; i < count; i++)
{
if (row < Rows)
{
this[row++, col].Texture = atlasTiles[i];
}
else
{
row %= Rows;
this[row, col++].Texture = atlasTiles[i];
}
}
}
private int AddGroup(AtlasGroup group)
{
IEnumerable<AtlasTile> tiles = InternalGetTilesFromGroup(group, out int _, out _);
foreach (AtlasTile tile in tiles)
{
tile.SetGroup(group);
}
int groupId = _groups.Count;
_groups.Add(group);
return groupId;
}
public Animation GetAnimationFromGroup(AtlasGroup group)
{
if (!group.IsAnimation())
return Animation.CreateEmpty();
if (group is AtlasGroupLargeTileAnimation largeTileAnimation)
return GetLargeAnimation(largeTileAnimation);
return GetAnimation(group as AtlasGroupAnimation);
}
private Animation GetLargeAnimation(AtlasGroupLargeTileAnimation group)
{
return new Animation(GetLargeAnimationTiles(group).Select(largeTileParts => largeTileParts.Select(t => t.Texture).Combine(group.RowSpan, group.ColumnSpan, _layoutDirection)), true, group.FrameTime);
}
private IEnumerable<IEnumerable<AtlasTile>> GetLargeAnimationTiles(AtlasGroupLargeTileAnimation group) => group.GetLargeTiles().Select(GetLargeTile);
private Animation GetAnimation(AtlasGroupAnimation groupAnimation) => new Animation(GetRange(groupAnimation.Row, groupAnimation.Column, groupAnimation.Direction == ImageLayoutDirection.Horizontal ? groupAnimation.Count : 1, groupAnimation.Direction == ImageLayoutDirection.Vertical ? groupAnimation.Count : 1).Select(t => t.Texture), true, groupAnimation.FrameTime);
private Image BuildFinalImage() => _tiles.Select(t => t.Texture).Combine(Rows, Columns, _layoutDirection);
public IReadOnlyCollection<AtlasTile> GetTiles() => _tiles;
public void SetGroupTilesFromAnimation(AtlasGroup group, Animation animation)
{
SetRange(group.Row, group.Column, group.Count, animation.GetFrames().Select(f => f.Texture));
}
private IEnumerable<AtlasTile> GetLargeTile(AtlasGroupLargeTile group) => GetRange(group.Row, group.Column, group.RowSpan, group.ColumnSpan);
public Image GetTileTexture(AtlasTile tile)
{
if (!tile.IsPartOfGroup)
return tile;
AtlasGroup atlasGroup = tile.GetGroup();
if (!atlasGroup.IsLargeTile())
return tile;
AtlasGroupLargeTile largeTile = atlasGroup is AtlasGroupLargeTileAnimation largeTileAnimation ? largeTileAnimation.GetTile(tile.Row, tile.Column) : atlasGroup as AtlasGroupLargeTile;
return GetLargeTile(largeTile).Select(t => t.Texture).Combine(largeTile.RowSpan, largeTile.ColumnSpan, _layoutDirection);
}
private IEnumerable<AtlasTile> InternalGetTilesFromGroup(AtlasGroup atlasGroup, out int rowSpan, out int columnSpan)
{
if (atlasGroup is AtlasGroupLargeTileAnimation largeTileAnimation)
{
rowSpan = largeTileAnimation.RowSpan;
columnSpan = largeTileAnimation.ColumnSpan;
return largeTileAnimation.GetLargeTiles().SelectMany(GetLargeTile);
}
if (atlasGroup is AtlasGroupLargeTile largeTile)
{
rowSpan = largeTile.RowSpan;
columnSpan = largeTile.ColumnSpan;
return GetLargeTile(largeTile);
}
rowSpan = 1;
columnSpan = 1;
return GetRange(atlasGroup.Row, atlasGroup.Column, atlasGroup.Count, atlasGroup.Direction);
}
public Rectangle GetTileArea(AtlasTile tile)
{
if (!tile.IsPartOfGroup || !AllowGroups)
return new Rectangle(new Point(tile.Row * TileSize.Width, tile.Column * TileSize.Height), TileSize);
AtlasGroup group = tile.GetGroup();
return new Rectangle(new Point(group.Row * TileSize.Width, group.Column * TileSize.Height), group.GetSize(TileSize));
}
}
}

View File

@@ -0,0 +1,48 @@
/* Copyright (c) 2025-present miku-666
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1.The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
using System;
using System.Drawing;
namespace PckStudio.Core
{
public abstract class AtlasGroup
{
public string Name { get; set; }
public int Row { get; }
public int Column { get; }
public virtual int Count => 1;
protected abstract bool isAnimation { get; }
protected abstract bool isLargeTile { get; }
public virtual ImageLayoutDirection Direction => Column > Row ? ImageLayoutDirection.Vertical : ImageLayoutDirection.Horizontal;
public AtlasGroup(string name, int row, int column)
{
Name = name;
Row = Math.Max(0, row);
Column = Math.Max(0, column);
}
public bool IsAnimation() => isAnimation;
public bool IsLargeTile() => isLargeTile;
internal abstract Size GetSize(Size tileSize);
}
}

View File

@@ -0,0 +1,42 @@
/* Copyright (c) 2025-present miku-666
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1.The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
using System.Drawing;
namespace PckStudio.Core
{
internal sealed class AtlasGroupAnimation : AtlasGroup
{
public int FrameTime { get; }
public override int Count { get; }
public override ImageLayoutDirection Direction { get; }
protected override bool isAnimation => true;
protected override bool isLargeTile => false;
internal override Size GetSize(Size tileSize) => new Size(tileSize.Width * (Direction == ImageLayoutDirection.Horizontal ? Count : 1), tileSize.Height * (Direction == ImageLayoutDirection.Vertical ? Count : 1));
public AtlasGroupAnimation(string name, int row, int column, int frameCount, ImageLayoutDirection direction, int frameTime = Animation.MinimumFrameTime)
: base(name, row, column)
{
Count = frameCount;
Direction = direction;
FrameTime = frameTime;
}
}
}

View File

@@ -0,0 +1,43 @@
/* Copyright (c) 2025-present miku-666
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1.The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
using System;
using System.Drawing;
namespace PckStudio.Core
{
internal sealed class AtlasGroupLargeTile : AtlasGroup
{
public int RowSpan { get; }
public int ColumnSpan { get; }
public override int Count => RowSpan * ColumnSpan;
protected override bool isAnimation => false;
protected override bool isLargeTile => true;
internal override Size GetSize(Size tileSize) => new Size(RowSpan * tileSize.Width, ColumnSpan * tileSize.Height);
public AtlasGroupLargeTile(string name, int row, int column, int rowSpan, int columnSpan)
: base(name, row, column)
{
RowSpan = Math.Max(1, rowSpan);
ColumnSpan = Math.Max(1, columnSpan);
}
}
}

View File

@@ -0,0 +1,79 @@
/* Copyright (c) 2025-present miku-666
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1.The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace PckStudio.Core
{
internal sealed class AtlasGroupLargeTileAnimation : AtlasGroup
{
private AtlasGroupLargeTile[] _largeTiles;
private int _frameCount;
public int RowSpan { get; }
public int ColumnSpan { get; }
public int FrameTime { get; }
public override int Count => RowSpan * ColumnSpan * _frameCount;
public override ImageLayoutDirection Direction { get; }
protected override bool isAnimation => true;
protected override bool isLargeTile => true;
internal override Size GetSize(Size tileSize) => new Size(RowSpan * tileSize.Width * (Direction == ImageLayoutDirection.Horizontal ? _frameCount : 1), ColumnSpan * tileSize.Height * (Direction == ImageLayoutDirection.Vertical ? _frameCount : 1));
public AtlasGroupLargeTileAnimation(string name, int row, int column, int rowSpan, int columnSpan, int frameCount, ImageLayoutDirection direction, int frameTime = Animation.MinimumFrameTime)
: base(name, row, column)
{
_frameCount = Math.Abs(frameCount);
RowSpan = Math.Max(1, rowSpan);
ColumnSpan = Math.Max(1, columnSpan);
Direction = direction;
FrameTime = frameTime;
_largeTiles = InternalGetLargeTiles().ToArray();
}
private IEnumerable<AtlasGroupLargeTile> InternalGetLargeTiles()
{
for (int i = 0; i < _frameCount; i++)
{
yield return new AtlasGroupLargeTile($"{Name}_{i}", Row + (Direction == ImageLayoutDirection.Horizontal ? i * RowSpan : 0), Column + (Direction == ImageLayoutDirection.Vertical ? i * ColumnSpan : 0), RowSpan, ColumnSpan);
}
yield break;
}
internal AtlasGroupLargeTile GetTile(int row, int col)
{
if (!IsInRange(row, col))
return default;
int i = (Direction == ImageLayoutDirection.Horizontal) ? Math.DivRem((row - Row), RowSpan, out _) : Math.DivRem((col - Column), ColumnSpan, out _);
return (i >= 0 && i < _frameCount) ? _largeTiles[i] : _largeTiles[0];
}
private bool IsInRange(int row, int col)
{
return Row <= row && row < (Row + (RowSpan * (Direction == ImageLayoutDirection.Horizontal ? _frameCount : 1))) && Column <= col && col < (Column + (ColumnSpan * (Direction == ImageLayoutDirection.Horizontal ? _frameCount : 1)));
}
internal IEnumerable<AtlasGroupLargeTile> GetLargeTiles() => _largeTiles;
}
}

View File

@@ -0,0 +1,68 @@
/* Copyright (c) 2025-present miku-666, MattNL
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1.The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
using System.Drawing;
namespace PckStudio.Core
{
public sealed class AtlasTile
{
public int Index { get; }
public int Row { get; }
public int Column { get; }
public object UserData;
private Image texture;
public bool IsPartOfGroup => _group != null;
public Image Texture { get => texture; set => texture = value; }
private AtlasGroup _group;
public AtlasTile(Image texture, int row, int column, int index, object userData)
{
Texture = texture;
Row = row;
Column = column;
Index = index;
UserData = userData;
}
internal void SetGroup(AtlasGroup group)
{
_group = group;
}
public AtlasGroup GetGroup() => _group;
public static implicit operator Image(AtlasTile tile) => tile.Texture;
public bool IsUserDataOfType<T>() where T : class => UserData is T _;
public T GetUserDataOfType<T>() where T : class => UserData as T;
public bool TryGetUserDataOfType<T>(out T value) where T : class
{
if (UserData is T val)
{
value = val;
return true;
}
value = default;
return false;
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OMI.Formats.Pck;
using PckStudio.Core.Extensions;
using PckStudio.Interfaces;
namespace PckStudio.Core.Deserializer
{
public sealed class AtlasDeserializer : IPckAssetDeserializer<Atlas>
{
private readonly ResourceLocation _resourceLocation;
public AtlasDeserializer(ResourceLocation resourceLocation)
{
_resourceLocation = resourceLocation;
}
public Atlas Deserialize(PckAsset asset) => Atlas.FromResourceLocation(asset.GetTexture(), _resourceLocation);
}
}

View File

@@ -5,7 +5,9 @@ namespace PckStudio.Core.Extensions
{
public static class AnimationExtensions
{
public static Image CreateAnimationImage(this Animation animation)
public static Image CreateAnimationImage(this Animation animation) => animation.CreateAnimationImage(Color.Black);
public static Image CreateAnimationImage(this Animation animation, Color blendColor)
{
if (animation.FrameCount == 0)
{
@@ -15,7 +17,8 @@ namespace PckStudio.Core.Extensions
var generateor = new AnimatedGifCreator(ms, GameConstants.GameTickInMilliseconds, 0);
foreach (Animation.Frame frame in animation.GetInterpolatedFrames())
{
generateor.AddFrame(frame.Texture, frame.Ticks * GameConstants.GameTickInMilliseconds, GifQuality.Bit8);
Image texture = (blendColor == Color.Black || blendColor == Color.White) ? frame.Texture : frame.Texture.Blend(blendColor, BlendMode.Multiply);
generateor.AddFrame(texture, frame.Ticks * GameConstants.GameTickInMilliseconds, GifQuality.Bit8);
}
ms.Position = 0;
return Image.FromStream(ms);

View File

@@ -16,14 +16,15 @@
* 3. This notice may not be removed or altered from any source distribution.
**/
using System;
using System.Drawing;
using System.Diagnostics;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Data.Common;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace PckStudio.Core.Extensions
@@ -71,12 +72,13 @@ namespace PckStudio.Core.Extensions
int rowCount = source.Width / size.Width;
int columnCount = source.Height / size.Height;
Debug.WriteLine($"Image size: {source.Size}, Area size: {size}, col num: {columnCount}, row num: {rowCount}");
bool vertical = imageLayout == ImageLayoutDirection.Vertical;
for (int i = 0; i < columnCount * rowCount; i++)
{
int row = Math.DivRem(i, rowCount, out int column);
if (imageLayout == ImageLayoutDirection.Vertical)
column = Math.DivRem(i, columnCount, out row);
Rectangle tileArea = new Rectangle(new Point(column * size.Width, row * size.Height), size);
int column = Math.DivRem(i, rowCount, out int row);
if (vertical)
row = Math.DivRem(i, columnCount, out column);
Rectangle tileArea = new Rectangle(new Point(row * size.Width, column * size.Height), size);
yield return source.GetArea(tileArea);
}
yield break;
@@ -94,38 +96,48 @@ namespace PckStudio.Core.Extensions
public static Image Combine(this IEnumerable<Image> sources, ImageLayoutDirection layoutDirection)
{
Size imageSize = CalculateImageSize(sources, layoutDirection);
bool horizontal = layoutDirection == ImageLayoutDirection.Horizontal;
int imgCount = sources.Count();
int rows = horizontal ? imgCount : 1;
int columns = horizontal ? 1 : imgCount;
return sources.Combine(rows, columns, layoutDirection);
}
public static Image Combine(this IEnumerable<Image> sources, int rows, int columns, ImageLayoutDirection layoutDirection)
{
Size imageSize = CalculateImageSize(sources, rows, columns);
var image = new Bitmap(imageSize.Width, imageSize.Height);
bool horizontal = layoutDirection == ImageLayoutDirection.Horizontal;
using (var graphic = Graphics.FromImage(image))
{
foreach ((int i, Image texture) in sources.enumerate())
{
var info = new ImageSection(texture.Size, i, layoutDirection);
graphic.DrawImage(texture, info.Point);
int x = Math.DivRem(i, columns, out int y);
if (horizontal)
y = Math.DivRem(i, rows, out x);
graphic.DrawImage(texture, new Point(x * texture.Width, y * texture.Height));
}
}
return image;
}
private static Size CalculateImageSize(IEnumerable<Image> sources, ImageLayoutDirection layoutDirection)
private static Size CalculateImageSize(IEnumerable<Image> sources, int rows, int columns)
{
Size size = sources.First().Size;
int count = sources.Count();
if (sources == null)
return Size.Empty;
Image[] imgs = sources.ToArray();
if (count < 2)
return count < 1 ? Size.Empty : size;
if (imgs.Length < rows * columns)
throw new ArgumentOutOfRangeException("Insufficent soure images provided.");
var horizontal = layoutDirection == ImageLayoutDirection.Horizontal;
Size size = imgs[0].Size;
if (!sources.All(img => img.Size == size))
if (!imgs.All(img => img.Size == size))
throw new InvalidOperationException("Images must have the same width and height.");
if (horizontal)
size.Width *= count;
else
size.Height *= count;
size.Width *= rows;
size.Height *= columns;
return size;
}

View File

@@ -52,10 +52,17 @@
<Compile Include="Animation.cs" />
<Compile Include="App\SettingsManager.cs" />
<Compile Include="App\Updater.cs" />
<Compile Include="Atlas.cs" />
<Compile Include="AtlasGroup.cs" />
<Compile Include="AtlasGroupAnimation.cs" />
<Compile Include="AtlasGroupLargeTile.cs" />
<Compile Include="AtlasGroupLargeTileAnimation.cs" />
<Compile Include="AtlasTile.cs" />
<Compile Include="BoundingBox.cs" />
<Compile Include="DelegatedFileSaveContext.cs" />
<Compile Include="DelegatedSaveContext.cs" />
<Compile Include="Deserializer\AnimationDeserializer.cs" />
<Compile Include="Deserializer\AtlasDeserializer.cs" />
<Compile Include="Deserializer\ImageDeserializer.cs" />
<Compile Include="Extensions\AnimationExtensions.cs" />
<Compile Include="Extensions\BlendMode.cs" />
@@ -120,6 +127,7 @@
<Compile Include="ResourceCategory.cs" />
<Compile Include="ResourceLocation.cs" />
<Compile Include="Serializer\AnimationSerializer.cs" />
<Compile Include="Serializer\AtlasSerializer.cs" />
<Compile Include="Serializer\ImageSerializer.cs" />
<Compile Include="Skin\Skin.cs" />
<Compile Include="Skin\SkinANIM.cs" />

View File

@@ -19,6 +19,8 @@ using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using PckStudio.Core.Json;
using PckStudio.Json;
namespace PckStudio.Core
{
@@ -27,42 +29,116 @@ namespace PckStudio.Core
private static List<ResourceLocation> ResourceGroups = new List<ResourceLocation>();
private static readonly ResourceLocation Unknown = new ResourceLocation(string.Empty, ResourceCategory.Unknown, -1);
private static AtlasGroup[] _particaleAtlasGroups =
{
new AtlasGroupAnimation("generic" , row: 0, column: 0, frameCount: 8, ImageLayoutDirection.Horizontal, 2),
new AtlasGroupAnimation("splash" , row: 3, column: 1, frameCount: 4, ImageLayoutDirection.Horizontal, 2),
new AtlasGroupAnimation("drip" , row: 0, column: 7, frameCount: 3, ImageLayoutDirection.Horizontal, 4),
new AtlasGroupAnimation("effect" , row: 0, column: 8, frameCount: 8, ImageLayoutDirection.Horizontal, 2),
new AtlasGroupAnimation("splash_effect" , row: 0, column: 9, frameCount: 8, ImageLayoutDirection.Horizontal, 2),
new AtlasGroupAnimation("firework_spark" , row: 0, column: 10, frameCount: 8, ImageLayoutDirection.Horizontal, 2),
new AtlasGroupAnimation("glitter" , row: 0, column: 11, frameCount: 8, ImageLayoutDirection.Horizontal, 2),
new AtlasGroupAnimation("BE_explosion" , row: 0, column: 12, frameCount: 16, ImageLayoutDirection.Horizontal),
new AtlasGroupLargeTile("flash" , row: 4, column: 2, rowSpan: 4, columnSpan: 4),
new AtlasGroupLargeTileAnimation("bubble_pop", row: 6, column: 6, rowSpan: 2, columnSpan: 2, frameCount: 5, ImageLayoutDirection.Horizontal, 2),
};
private static AtlasGroup[] _terrainAtlasGroups =
{
new AtlasGroupLargeTile("Oak Door" , row: 1, column: 5, rowSpan: 1, columnSpan: 2),
new AtlasGroupLargeTile("Iron Door" , row: 2, column: 5, rowSpan: 1, columnSpan: 2),
new AtlasGroupLargeTile("Acacia Door" , row: 0, column: 23, rowSpan: 1, columnSpan: 2),
new AtlasGroupLargeTile("Birch Door" , row: 1, column: 23, rowSpan: 1, columnSpan: 2),
new AtlasGroupLargeTile("Dark Oak Door", row: 2, column: 23, rowSpan: 1, columnSpan: 2),
new AtlasGroupLargeTile("Jungle Door" , row: 3, column: 23, rowSpan: 1, columnSpan: 2),
new AtlasGroupLargeTile("Spruce Door" , row: 4, column: 23, rowSpan: 1, columnSpan: 2),
new AtlasGroupLargeTile("Large Fern" , row: 0, column: 20, rowSpan: 1, columnSpan: 2),
new AtlasGroupLargeTile("Double Tall Grass", row: 1, column: 20, rowSpan: 1, columnSpan: 2),
new AtlasGroupLargeTile("Poeny" , row: 2, column: 20, rowSpan: 1, columnSpan: 2),
new AtlasGroupLargeTile("Rose Bush" , row: 3, column: 20, rowSpan: 1, columnSpan: 2),
new AtlasGroupLargeTile("Lilac" , row: 4, column: 20, rowSpan: 1, columnSpan: 2),
new AtlasGroupAnimation("Wheat" , row: 8, column: 5, frameCount: 8, ImageLayoutDirection.Horizontal, 5),
new AtlasGroupAnimation("Potatoes" , row: 9, column: 16, frameCount: 4, ImageLayoutDirection.Horizontal, 5),
new AtlasGroupAnimation("Carrots" , row: 8, column: 12, frameCount: 4, ImageLayoutDirection.Horizontal, 5),
new AtlasGroupAnimation("Beetroots" , row: 0, column: 25, frameCount: 4, ImageLayoutDirection.Horizontal, 5),
new AtlasGroupAnimation("Nether Wart", row: 2, column: 14, frameCount: 3, ImageLayoutDirection.Horizontal, 5),
new AtlasGroupAnimation("Destroy" , row: 0, column: 15, frameCount: 10, ImageLayoutDirection.Horizontal, 3),
};
private static AtlasGroup[] _itemsAtlasGroups =
{
new AtlasGroupAnimation("Bow Pulling", row: 5, column: 6, frameCount: 3, ImageLayoutDirection.Vertical, 6),
};
private static AtlasGroup[] _paintingAtlasGroups =
{
new AtlasGroupLargeTile("The Pool" , row: 0, column: 2, rowSpan: 2, columnSpan: 1),
new AtlasGroupLargeTile("Bonjour Monsiuer Courbet", row: 2, column: 2, rowSpan: 2, columnSpan: 1),
new AtlasGroupLargeTile("Seaside" , row: 4, column: 2, rowSpan: 2, columnSpan: 1),
new AtlasGroupLargeTile("sunset_dense" , row: 6, column: 2, rowSpan: 2, columnSpan: 1),
new AtlasGroupLargeTile("Creebet" , row: 8, column: 2, rowSpan: 2, columnSpan: 1),
new AtlasGroupLargeTile("Wanderer" , row: 0, column: 4, rowSpan: 1, columnSpan: 2),
new AtlasGroupLargeTile("Graham" , row: 1, column: 4, rowSpan: 1, columnSpan: 2),
new AtlasGroupLargeTile("Fighters" , row: 0, column: 6, rowSpan: 4, columnSpan: 2),
new AtlasGroupLargeTile("Match" , row: 0, column: 8, rowSpan: 2, columnSpan: 2),
new AtlasGroupLargeTile("Bust" , row: 2, column: 8, rowSpan: 2, columnSpan: 2),
new AtlasGroupLargeTile("The stage is set" , row: 4, column: 8, rowSpan: 2, columnSpan: 2),
new AtlasGroupLargeTile("The Void" , row: 6, column: 8, rowSpan: 2, columnSpan: 2),
new AtlasGroupLargeTile("Skull and Roses" , row: 8, column: 8, rowSpan: 2, columnSpan: 2),
new AtlasGroupLargeTile("Wither" , row: 10, column: 8, rowSpan: 2, columnSpan: 2),
new AtlasGroupLargeTile("Mortal Coil" , row: 12, column: 4, rowSpan: 4, columnSpan: 3),
new AtlasGroupLargeTile("Kong" , row: 12, column: 7, rowSpan: 4, columnSpan: 3),
new AtlasGroupLargeTile("Back Texture" , row: 12, column: 0, rowSpan: 4, columnSpan: 4),
new AtlasGroupLargeTile("Pointer" , row: 0, column: 12, rowSpan: 4, columnSpan: 4),
new AtlasGroupLargeTile("Pigscene" , row: 4, column: 12, rowSpan: 4, columnSpan: 4),
new AtlasGroupLargeTile("Skull On Fire" , row: 8, column: 12, rowSpan: 4, columnSpan: 4),
};
private static readonly Dictionary<string, ResourceLocation> _categoryLookUp = new Dictionary<string, ResourceLocation>()
{
["textures/items"] = new ResourceLocation("textures/items", ResourceCategory.ItemAnimation, 16, isGroup: true),
["textures/blocks"] = new ResourceLocation("textures/blocks", ResourceCategory.BlockAnimation, 16, isGroup: true),
["mob"] = new ResourceLocation("mob", ResourceCategory.MobEntityTextures, 1, isGroup: true),
["item"] = new ResourceLocation("item", ResourceCategory.ItemEntityTextures, 1, isGroup: true),
["terrain.png"] = new ResourceLocation("terrain.png", ResourceCategory.BlockAtlas, 16),
["items.png"] = new ResourceLocation("items.png", ResourceCategory.ItemAtlas, 16),
["particles.png"] = new ResourceLocation("particles.png", ResourceCategory.ParticleAtlas, 16),
["item/banner/Banner_Atlas.png"] = new ResourceLocation("item/banner/Banner_Atlas.png", ResourceCategory.BannerAtlas, new Size(6, 7), TillingMode.WidthAndHeight),
["art/kz.png"] = new ResourceLocation("art/kz.png", ResourceCategory.PaintingAtlas, 16),
["misc/explosion.png"] = new ResourceLocation("misc/explosion.png", ResourceCategory.ExplosionAtlas, 4),
["item/xporb.png"] = new ResourceLocation("item/xporb.png", ResourceCategory.ExperienceOrbAtlas, 4),
["terrain/moon_phases.png"] = new ResourceLocation("terrain/moon_phases.png", ResourceCategory.MoonPhaseAtlas, 4),
["misc/mapicons.png"] = new ResourceLocation("misc/mapicons.png", ResourceCategory.MapIconAtlas, 4),
["misc/additionalmapicons.png"] = new ResourceLocation("misc/additionalmapicons.png", ResourceCategory.AdditionalMapIconsAtlas, 4),
["textures/items"] = new ResourceLocation("textures/items", ResourceCategory.ItemAnimation, 16, isGroup: true),
["textures/blocks"] = new ResourceLocation("textures/blocks", ResourceCategory.BlockAnimation, 16, isGroup: true),
["mob"] = new ResourceLocation("mob", ResourceCategory.MobEntityTextures, 1, isGroup: true),
["item"] = new ResourceLocation("item", ResourceCategory.ItemEntityTextures, 1, isGroup: true),
["terrain.png"] = new ResourceLocation("terrain.png", ResourceCategory.BlockAtlas, 16, tilesInfo: Tiles.BlockTileInfos, atlasGroups: _terrainAtlasGroups),
["items.png"] = new ResourceLocation("items.png", ResourceCategory.ItemAtlas, 16, tilesInfo: Tiles.ItemTileInfos, atlasGroups: _itemsAtlasGroups),
["particles.png"] = new ResourceLocation("particles.png", ResourceCategory.ParticleAtlas, 16, tilesInfo: Tiles.ParticleTileInfos, atlasGroups: _particaleAtlasGroups),
//============ TODO ============//
["item/banner/Banner_Atlas.png"] = new ResourceLocation("item/banner/Banner_Atlas.png", ResourceCategory.BannerAtlas, new Size(6, 7), TillingMode.WidthAndHeight, tilesInfo: Tiles.BannerTileInfos),
//==============================//
["art/kz.png"] = new ResourceLocation("art/kz.png", ResourceCategory.PaintingAtlas, 16, tilesInfo: Tiles.PaintingTileInfos, atlasGroups: _paintingAtlasGroups),
["misc/explosion.png"] = new ResourceLocation("misc/explosion.png", ResourceCategory.ExplosionAtlas, 16, tilesInfo: Tiles.ExplosionTileInfos),
["item/xporb.png"] = new ResourceLocation("item/xporb.png", ResourceCategory.ExperienceOrbAtlas, 4, tilesInfo: Tiles.ExperienceOrbTileInfos),
["terrain/moon_phases.png"] = new ResourceLocation("terrain/moon_phases.png", ResourceCategory.MoonPhaseAtlas, 4, tilesInfo: Tiles.MoonPhaseTileInfos),
["misc/mapicons.png"] = new ResourceLocation("misc/mapicons.png", ResourceCategory.MapIconAtlas, 4, tilesInfo: Tiles.MapIconTileInfos),
["misc/additionalmapicons.png"] = new ResourceLocation("misc/additionalmapicons.png", ResourceCategory.AdditionalMapIconsAtlas, 4, tilesInfo: Tiles.AdditionalMapIconTileInfos),
};
public static string GetPathFromCategory(ResourceCategory category)
{
return category switch
{
ResourceCategory.ItemAnimation => _categoryLookUp["textures/items"].ToString(),
ResourceCategory.BlockAnimation => _categoryLookUp["textures/blocks"].ToString(),
ResourceCategory.MobEntityTextures => _categoryLookUp["mob"].ToString(),
ResourceCategory.ItemEntityTextures => _categoryLookUp["item"].ToString(),
ResourceCategory.BlockAtlas => _categoryLookUp["terrain.png"].ToString(),
ResourceCategory.ItemAtlas => _categoryLookUp["items.png"].ToString(),
ResourceCategory.ParticleAtlas => _categoryLookUp["particles.png"].ToString(),
ResourceCategory.BannerAtlas => _categoryLookUp["item/banner/Banner_Atlas.png"].ToString(),
ResourceCategory.PaintingAtlas => _categoryLookUp["art/kz.png"].ToString(),
ResourceCategory.ExplosionAtlas => _categoryLookUp["misc/explosion.png"].ToString(),
ResourceCategory.ExperienceOrbAtlas => _categoryLookUp["item/xporb.png"].ToString(),
ResourceCategory.MoonPhaseAtlas => _categoryLookUp["terrain/moon_phases.png"].ToString(),
ResourceCategory.MapIconAtlas => _categoryLookUp["misc/mapicons.png"].ToString(),
ResourceCategory.AdditionalMapIconsAtlas => _categoryLookUp["misc/additionalmapicons.png"].ToString(),
ResourceCategory.ItemAnimation => _categoryLookUp["textures/items"].ToString(),
ResourceCategory.BlockAnimation => _categoryLookUp["textures/blocks"].ToString(),
ResourceCategory.MobEntityTextures => _categoryLookUp["mob"].ToString(),
ResourceCategory.ItemEntityTextures => _categoryLookUp["item"].ToString(),
ResourceCategory.BlockAtlas => _categoryLookUp["terrain.png"].ToString(),
ResourceCategory.ItemAtlas => _categoryLookUp["items.png"].ToString(),
ResourceCategory.ParticleAtlas => _categoryLookUp["particles.png"].ToString(),
ResourceCategory.BannerAtlas => _categoryLookUp["item/banner/Banner_Atlas.png"].ToString(),
ResourceCategory.PaintingAtlas => _categoryLookUp["art/kz.png"].ToString(),
ResourceCategory.ExplosionAtlas => _categoryLookUp["misc/explosion.png"].ToString(),
ResourceCategory.ExperienceOrbAtlas => _categoryLookUp["item/xporb.png"].ToString(),
ResourceCategory.MoonPhaseAtlas => _categoryLookUp["terrain/moon_phases.png"].ToString(),
ResourceCategory.MapIconAtlas => _categoryLookUp["misc/mapicons.png"].ToString(),
ResourceCategory.AdditionalMapIconsAtlas => _categoryLookUp["misc/additionalmapicons.png"].ToString(),
_ => string.Empty
};
}
@@ -80,43 +156,46 @@ namespace PckStudio.Core
}
public enum TillingMode
{
{
Width,
Height,
WidthAndHeight
}
public readonly string Path;
public readonly ResourceCategory Category;
public readonly Size TillingFactor;
public readonly TillingMode Tilling;
public readonly bool IsGroup;
public string Path { get; }
public ResourceCategory Category { get; }
public Size TillingFactor { get; }
public TillingMode Tilling { get; }
public bool IsGroup { get; }
public IEnumerable<JsonTileInfo> TilesInfo { get; }
public IEnumerable<AtlasGroup> AtlasGroups { get; }
public Size GetTileArea(Size imgSize)
{
int tileFactorWidth = Math.Max(1, TillingFactor.Width);
int tileFactorHeight = Math.Max(1, TillingFactor.Height);
return Tilling switch
{
TillingMode.Width => new Size(imgSize.Width / tileFactorWidth, imgSize.Width / tileFactorHeight),
TillingMode.Height => new Size(imgSize.Height / tileFactorWidth, imgSize.Height / tileFactorHeight),
TillingMode.WidthAndHeight => new Size(imgSize.Width / tileFactorWidth, imgSize.Height / tileFactorHeight),
TillingMode.Width => new Size(imgSize.Width / TillingFactor.Width, imgSize.Width / TillingFactor.Height),
TillingMode.Height => new Size(imgSize.Height / TillingFactor.Width, imgSize.Height / TillingFactor.Height),
TillingMode.WidthAndHeight => new Size(imgSize.Width / TillingFactor.Width, imgSize.Height / TillingFactor.Height),
_ => Size.Empty,
};
}
private ResourceLocation(string path, ResourceCategory category, int tillingFactor, TillingMode tilling = TillingMode.Width, bool isGroup = false)
: this(path, category, new Size(tillingFactor, tillingFactor), tilling, isGroup)
private ResourceLocation(string path, ResourceCategory category, int tillingFactor, TillingMode tilling = TillingMode.Width, bool isGroup = false, IEnumerable<JsonTileInfo> tilesInfo = default, IEnumerable<AtlasGroup> atlasGroups = default)
: this(path, category, new Size(tillingFactor, tillingFactor), tilling, isGroup, tilesInfo, atlasGroups)
{
}
private ResourceLocation(string path, ResourceCategory category, Size tillingFactor, TillingMode tilling = TillingMode.Width, bool isGroup = false)
private ResourceLocation(string path, ResourceCategory category, Size tillingFactor, TillingMode tilling = TillingMode.Width, bool isGroup = false, IEnumerable<JsonTileInfo> tilesInfo = default, IEnumerable<AtlasGroup> atlasGroups = default)
{
Path = path;
Category = category;
TillingFactor = tillingFactor;
TillingFactor = new Size(Math.Max(1, tillingFactor.Width), Math.Max(1, tillingFactor.Height));
Tilling = tilling;
IsGroup = isGroup;
TilesInfo = tilesInfo ?? Enumerable.Empty<JsonTileInfo>();
AtlasGroups = atlasGroups ?? Enumerable.Empty<AtlasGroup>();
if (isGroup)
ResourceGroups.Add(this);
}

View File

@@ -4,7 +4,7 @@
{
"internalName": "base",
"displayName": "Base",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -30,7 +30,7 @@
{
"internalName": "border",
"displayName": "Bordure",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -56,7 +56,7 @@
{
"internalName": "bricks",
"displayName": "Field Masoned",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -82,7 +82,7 @@
{
"internalName": "circle",
"displayName": "Roundel",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -108,7 +108,7 @@
{
"internalName": "creeper",
"displayName": "Creeper Charge",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -134,7 +134,7 @@
{
"internalName": "cross",
"displayName": "Saltire",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -160,7 +160,7 @@
{
"internalName": "curly_border",
"displayName": "Bordure Indented",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -186,7 +186,7 @@
{
"internalName": "diagonal_left",
"displayName": "Per Bend Sinister",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -212,7 +212,7 @@
{
"internalName": "diagonal_right",
"displayName": "Per Bend",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -238,7 +238,7 @@
{
"internalName": "diagonal_up_left",
"displayName": "Per Bend Inverted",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -264,7 +264,7 @@
{
"internalName": "diagonal_up_right",
"displayName": "Per Bend Sinister Inverted",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -290,7 +290,7 @@
{
"internalName": "flower",
"displayName": "Flower Charge",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -316,7 +316,7 @@
{
"internalName": "gradient",
"displayName": "Gradient",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -342,7 +342,7 @@
{
"internalName": "gradient_up",
"displayName": "Base Gradient",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -368,7 +368,7 @@
{
"internalName": "half_horizontal",
"displayName": "Per Fess",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -394,7 +394,7 @@
{
"internalName": "half_horizontal_bottom",
"displayName": "Per Fess Inverted",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -420,7 +420,7 @@
{
"internalName": "half_vertical",
"displayName": "Per Pale",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -446,7 +446,7 @@
{
"internalName": "half_vertical_right",
"displayName": "Per Pale Inverted",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -472,7 +472,7 @@
{
"internalName": "mojang",
"displayName": "Thing",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -498,7 +498,7 @@
{
"internalName": "rhombus",
"displayName": "Lozenge",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -524,7 +524,7 @@
{
"internalName": "skull",
"displayName": "Skull Charge",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -550,7 +550,7 @@
{
"internalName": "small_stripes",
"displayName": "Paly",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -576,7 +576,7 @@
{
"internalName": "square_bottom_left",
"displayName": "Base Dexter Canton",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -602,7 +602,7 @@
{
"internalName": "square_bottom_right",
"displayName": "Base Sinister Canton",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -628,7 +628,7 @@
{
"internalName": "square_top_left",
"displayName": "Chief Dexter Canton",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -654,7 +654,7 @@
{
"internalName": "square_top_right",
"displayName": "Chief Sinister Canton",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -680,7 +680,7 @@
{
"internalName": "straight_cross",
"displayName": "Cross",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -706,7 +706,7 @@
{
"internalName": "stripe_bottom",
"displayName": "Base Fess",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -732,7 +732,7 @@
{
"internalName": "stripe_center",
"displayName": "Pale",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -758,7 +758,7 @@
{
"internalName": "strip_downleft",
"displayName": "Bend Sinister",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -784,7 +784,7 @@
{
"internalName": "stripe_downright",
"displayName": "Bend",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -810,7 +810,7 @@
{
"internalName": "stripe_left",
"displayName": "Pale Dexter",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -836,7 +836,7 @@
{
"internalName": "stripe_middle",
"displayName": "Fess",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -862,7 +862,7 @@
{
"internalName": "stripe_right",
"displayName": "Pale Sinister",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -888,7 +888,7 @@
{
"internalName": "stripe_top",
"displayName": "Chief Fess",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -914,7 +914,7 @@
{
"internalName": "triangle_bottom",
"displayName": "Chevron",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -940,7 +940,7 @@
{
"internalName": "triangle_top",
"displayName": "Inverted Chevron",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -966,7 +966,7 @@
{
"internalName": "triangles_bottom",
"displayName": "Base Indented",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -992,7 +992,7 @@
{
"internalName": "triangles_top",
"displayName": "Chief Indented",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [
@@ -1026,7 +1026,7 @@
{
"internalName": "globe",
"displayName": "Globe [PS4 ONLY]",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Banner_White",
"variants": [

View File

@@ -5,7 +5,7 @@
{
"internalName": "grass_top",
"displayName": "Grass Block (Top)",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Grass_Common",
"variants": [
@@ -167,7 +167,7 @@
{
"internalName": "grass_side_overlay",
"displayName": "Grass Side (Overlay)",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Grass_Common",
"variants": [
@@ -181,7 +181,7 @@
{
"internalName": "tallgrass",
"displayName": "Tall Grass",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Grass_Common",
"variants": [
@@ -243,7 +243,7 @@
{
"internalName": "leaves",
"displayName": "Oak Leaves",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Foliage_Default",
"variants": [
@@ -258,7 +258,7 @@
{
"internalName": "leaves_opaque",
"displayName": "Oak Leaves (Opaque)",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Foliage_Default",
"variants": [
@@ -281,7 +281,7 @@
{
"internalName": "fern",
"displayName": "Fern",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Grass_Common",
"variants": [
@@ -371,7 +371,7 @@
{
"internalName": "waterlily",
"displayName": "Lily Pad",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Tile_WaterLily",
"variants": [ "Tile_WaterLily" ]
@@ -516,7 +516,7 @@
{
"internalName": "stem_straight",
"displayName": "Stem",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Tile_StemMin",
"variants": [
@@ -588,7 +588,7 @@
{
"internalName": "stem_bent",
"displayName": "Stem (Attached)",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Tile_StemMin",
"variants": [
@@ -616,7 +616,7 @@
{
"internalName": "leaves_spruce",
"displayName": "Spruce Leaves",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Foliage_Evergreen",
"variants": [
@@ -631,7 +631,7 @@
{
"internalName": "leaves_spruce_opaque",
"displayName": "Spruce Leaves (Opaque)",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Foliage_Evergreen",
"variants": [
@@ -682,7 +682,7 @@
{
"internalName": "vine",
"displayName": "Vines",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Foliage_Default",
"variants": [
@@ -777,7 +777,7 @@
{
"internalName": "redstone_dust_cross",
"displayName": "Redstone Dust (Cross)",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Tile_RedstoneDust",
"variants": [
@@ -791,7 +791,7 @@
{
"internalName": "redstone_dust_line",
"displayName": "Redstone Dust (Line)",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Tile_RedstoneDust",
"variants": [
@@ -925,7 +925,7 @@
{
"internalName": "leaves_jungle",
"displayName": "Jungle Leaves",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Foliage_Default",
"variants": [
@@ -940,7 +940,7 @@
{
"internalName": "leaves_jungle_opaque",
"displayName": "Jungle Leaves (Opaque)",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Foliage_Default",
"variants": [
@@ -983,7 +983,7 @@
{
"internalName": "water",
"displayName": "Water",
"hasColourEntry": true,
"colourEntry": {
"isWaterColour": true,
"defaultName": "Water_Plains",
@@ -1034,7 +1034,7 @@
{
"internalName": "water_flow",
"displayName": "Flowing Water",
"hasColourEntry": true,
"width": 2,
"height": 2,
"colourEntry": {
@@ -1541,7 +1541,7 @@
{
"internalName": "double_plant_fern_top",
"displayName": "Large Fern (Top)",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Grass_Common",
"variants": [
@@ -1555,7 +1555,7 @@
{
"internalName": "double_plant_grass_top",
"displayName": "Double Tall Grass (Top)",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Grass_Common",
"variants": [
@@ -1605,7 +1605,7 @@
{
"internalName": "leaves_acacia",
"displayName": "Acacia Leaves",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Foliage_Default",
"variants": [
@@ -1620,7 +1620,7 @@
{
"internalName": "leaves_acacia_fast",
"displayName": "Acacia Leaves (Opaque)",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Foliage_Default",
"variants": [
@@ -1647,7 +1647,7 @@
{
"internalName": "double_plant_fern_bottom",
"displayName": "Large Fern (Bottom)",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Grass_Common",
"variants": [
@@ -1661,7 +1661,7 @@
{
"internalName": "double_plant_grass_bottom",
"displayName": "Double Tall Grass (Bottom)",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Grass_Common",
"variants": [
@@ -1711,7 +1711,7 @@
{
"internalName": "leaves_big_oak",
"displayName": "Dark Oak Leaves",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Foliage_Default",
"variants": [
@@ -1726,7 +1726,7 @@
{
"internalName": "leaves_big_oak_fast",
"displayName": "Dark Oak Leaves (Opaque)",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Foliage_Default",
"variants": [
@@ -1797,7 +1797,7 @@
{
"internalName": "leaves_birch",
"displayName": "Birch Leaves",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Foliage_Birch",
"variants": [
@@ -1812,7 +1812,7 @@
{
"internalName": "leaves_birch_fast",
"displayName": "Birch Leaves (Opaque)",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Foliage_Birch",
"variants": [
@@ -2287,7 +2287,7 @@
{
"internalName": "shulker_top",
"displayName": "Shulker Box (Break Particles)",
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Shulker_Box_Purple",
"variants": [
@@ -2318,7 +2318,7 @@
"internalName": "cauldron_water",
"displayName": "Cauldron Water",
"allowCustomColour": true,
"hasColourEntry": true,
"colourEntry": {
"defaultName": "Cauldron_Water",
"variants": [

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OMI.Formats.Pck;
using PckStudio.Core.Extensions;
using PckStudio.Interfaces;
namespace PckStudio.Core.Serializer
{
internal sealed class AtlasSerializer : IPckAssetSerializer<Atlas>
{
public void Serialize(Atlas atlas, ref PckAsset asset)
{
asset.SetTexture(atlas);
}
}
}