2using System.Collections.Generic;
30 for (
int i = 0; i < span; i++)
33 if (num >= 0 && num <
used.Length)
64 private static readonly (
string source, Func<IEnumerable<TileRow>> rows)[] _sourceData =
new(string, Func<IEnumerable<TileRow>>)[5]
81 private static readonly Dictionary<ProceduralMesh, Vector2>
_grownMesh =
new Dictionary<ProceduralMesh, Vector2>();
83 private static readonly Dictionary<Material, Vector4>
_grownMat =
new Dictionary<Material, Vector4>();
98 Dictionary<MeshPass, TextureData> dictionary =
BuildPassData();
99 Dictionary<TextureData, TextureData> dictionary2 =
new Dictionary<TextureData, TextureData>();
104 if ((
bool)
item.snowPass && dictionary.TryGetValue(
item.snowPass, out var value) && value != value2)
106 dictionary2[value2] = value;
111 Dictionary<TextureData, Grid> grids =
new Dictionary<TextureData, Grid>();
112 (string, Func<IEnumerable<TileRow>>)[] sourceData = _sourceData;
113 for (
int i = 0; i < sourceData.Length; i++)
115 var (text, func) = sourceData[i];
123 if (tiles !=
null && tiles.Length != 0 && ModUtil.FindSourceRowPackage(entry.
row) ==
null)
125 Debug.LogWarning(
"#tile " + text +
"/" + entry.
alias +
" is a base game row; to reskin it put the sprite in 'Texture Replace' as <texture_sheet>_<cell>.png instead of " + entry.
file.FullName.ShortPath() +
"; or use in game Texture Viewer");
131 Allocate(entry, dictionary, dictionary2, grids);
135 Debug.LogError(
$"#tile failed to allocate {text}/{entry.alias} ({entry.file.FullName.ShortPath()})\n{ex}");
140 sourceData = _sourceData;
141 for (
int i = 0; i < sourceData.Length; i++)
143 (string, Func<IEnumerable<TileRow>>) tuple2 = sourceData[i];
144 var (text2, _) = tuple2;
145 foreach (
TileRow row
in tuple2.Item2())
147 int[] tiles2 = row.
tiles;
148 if (tiles2 !=
null && tiles2.Length == 0 &&
_allocated.All((
Entry e) => e.
row != row))
150 Debug.LogWarning(
"#tile " + text2 +
"/" + row.
alias +
" has empty tiles and no Texture/" + text2 +
"/" + row.
alias +
".png");
160 TileRow tileRow = _sourceData.First(((
string source, Func<IEnumerable<TileRow>> rows) t) => t.source == entry.
source).rows().FirstOrDefault((
TileRow r) =>
string.Equals(r.
alias, entry.
alias, StringComparison.OrdinalIgnoreCase));
176 private static List<Entry>
ParseTiles(
string source, IEnumerable<TileRow> rows)
178 string text = source +
"/";
179 Dictionary<string, FileInfo> dictionary =
new Dictionary<string, FileInfo>(StringComparer.OrdinalIgnoreCase);
183 dictModItem.Deconstruct(out var key, out key2);
186 if (text2.StartsWith(text, StringComparison.OrdinalIgnoreCase))
188 string text4 = text2[text.Length..];
189 if (text4.IndexOf(
'/') < 0)
191 dictionary[text4] =
new FileInfo(text3 +
".png");
195 List<TileRow> source2 = rows.ToList();
196 Dictionary<string, Entry> dictionary2 =
new Dictionary<string, Entry>(StringComparer.OrdinalIgnoreCase);
198 foreach (KeyValuePair<string, FileInfo>
item in dictionary)
200 item.Deconstruct(out key2, out value);
202 FileInfo file = value;
203 TileRow tileRow = source2.FirstOrDefault((
TileRow r) =>
string.Equals(r.
alias, name, StringComparison.OrdinalIgnoreCase));
206 dictionary2[name] =
new Entry
215 foreach (KeyValuePair<string, FileInfo> item2
in dictionary)
217 item2.Deconstruct(out key2, out value);
219 FileInfo fileInfo = value;
220 if (!dictionary2.ContainsKey(text5))
222 if (text5.EndsWith(
"_snow", StringComparison.OrdinalIgnoreCase) && dictionary2.TryGetValue(text5[..^5], out var value2))
224 value2.snowFile = fileInfo;
227 Debug.LogWarning(
"#tile no " + source +
" row with alias '" + text5 +
"' for " + fileInfo.FullName.ShortPath());
230 return dictionary2.Values.ToList();
237 width = Math.Max(1, frames)
240 if (anime !=
null && anime.Length != 0)
242 result.width = Math.Max(result.
width, anime[0]);
250 result.width = Math.Max(result.
width, 16);
255 result.width = Math.Max(Math.Max(1, frames - 2), 3);
257 if (row2.idRoof != 0)
259 result.width = Math.Max(result.
width, 12);
265 result.width = Math.Max(result.
width, 8);
273 result.down = tileRow.tileType.blockRenderMode
switch
275 BlockRenderMode.WallOrFence => 3,
276 BlockRenderMode.Pillar => Math.Max(result.
down, 1),
283 private static void Allocate(
Entry entry, Dictionary<MeshPass, TextureData> passData, Dictionary<TextureData, TextureData> snowOf, Dictionary<TextureData, Grid> grids)
286 string text = entry.source +
"/" + entry.alias +
" (" + entry.
file.FullName.ShortPath() +
")";
288 if (!renderData || !renderData.
pass || !passData.TryGetValue(renderData.
pass, out var value))
290 Debug.LogWarning(
"#tile " + text +
" has invalid render data");
295 Debug.LogWarning(
$"#tile {text} sprite must be n*{value.tileW} x m*{value.tileH} (n < 100, m <= 4)");
298 TextureData textureData = snowOf.TryGetValue(value);
303 if (textureData ==
null)
305 Debug.LogWarning(
"#tile " + text +
" ignore snow");
306 entry.snowFile =
null;
308 else if (!
TryMeasure(entry.
snowFile, value, out frames2, out rows2) || frames2 != frames || rows2 != rows)
310 Debug.LogWarning(
"#tile " + text +
" base and snow sprites size mismatch");
311 entry.snowFile =
null;
315 if (tileBounds.
width > frames)
317 Debug.LogWarning(
$"#tile {text} uses {tileBounds.width} cells but sprite has {frames}");
319 if (tileBounds.
up > 0 && rows < 1 + tileBounds.
up)
321 Debug.LogWarning(
$"#tile {text} multiSize needs {1 + tileBounds.up} cells tall");
323 if (tileBounds.
down > 0 && rows < 2)
325 Debug.LogWarning(
"#tile " + text +
" wall/fence should include the pillar rows below the base row");
329 Debug.LogWarning(
$"#tile {text} crop is 5 frames (stages 0..4, the 3rd is the base tile), got {frames}");
331 if (!grids.TryGetValue(value, out var value2))
333 value2 = (grids[value] =
BuildGrid(value, textureData, passData));
335 int num = Math.Max(1 + tileBounds.
up + tileBounds.
down, rows);
336 int num2 = tileBounds.left + tileBounds.
width;
338 if (num3 < 0 && num2 <= value2.cols &&
TryAddRows(value, textureData, value2, num, text))
344 Debug.LogError(
$"#tile no free {num2}x{num} region left in '{value.id}' for {text}");
347 int index = num3 - tileBounds.left - Math.Min(tileBounds.
up, rows - 1) * 100;
353 entry.tiles =
GetTiles(entry, row, num3, frames);
356 Debug.Log(
$"#tile {entry.source}/{entry.alias} -> {value.id}#{num3} x{frames}");
362 if (anime !=
null && anime.Length != 0)
364 return new int[1] { baseIndex };
370 return new int[1] { baseIndex };
377 return new int[1] {
MakeGrowthTile(entry, row2, baseIndex, frames) };
379 return new int[1] { baseIndex };
382 return Enumerable.Range(baseIndex, frames).ToArray();
387 string tag = entry.source +
"/" + entry.
alias;
388 string[] array = (
string[])obj._growth.Clone();
389 if (array.Length > 1)
391 array[1] =
string.Join(
"/", from v in array[1].Split(
'/')
394 int num = ((array.Length > 2 && array[2].ToInt() > 0) ?
Cell(array[2],
"harvest") : 0);
395 if (array.Length > 2)
397 array[2] = num.ToString();
399 entry.growth = array;
401 int num2 = obj.renderData.ConvertTile(baseIndex);
402 int num3 = obj.growth.stages.Where((
GrowSystem.
Stage st) => st.renderData == obj.renderData).SelectMany((
GrowSystem.
Stage st) => st.tiles).DefaultIfEmpty(num2)
406 num3 = Math.Max(num3, obj.growth.harvestTile + ((obj.growth is
GrowSystemWheat) ? 1 : 0));
408 if (num3 - num2 + 1 > frames)
410 Debug.LogWarning(
$"#tile {tag} the growth stages has {num3 - num2 + 1} frames but the sprite has {frames}");
416 return baseIndex + Math.Max(0, frames - 2);
420 return baseIndex + Math.Min(2, frames - 1);
421 int Cell(
string s,
string what)
423 int num4 = s.ToInt();
424 if (num4 < 0 || num4 >= frames)
426 Debug.LogWarning(
$"#tile {tag} _growth {what} offset {num4} is outside {frames}-frame");
427 num4 = Mathf.Clamp(num4, 0, frames - 1);
429 return baseIndex + num4;
435 string[] growth = obj._growth;
436 obj._growth = resolved;
439 obj.growth.Init(obj);
443 obj._growth = growth;
450 row._tiles = Array.Empty<
int>();
462 source = TextureReplace.Source.Mod
471 Texture2D texture2D = IO.LoadPNG(file.FullName);
476 frames = texture2D.width / data.
tileW;
477 rows = texture2D.height / data.
tileH;
480 if (num >= 1 && num < 100)
483 if (num >= 1 && num <= 4 && texture2D.width % data.
tileW == 0)
485 result = ((texture2D.height % data.tileH == 0) ? 1 : 0);
492 UnityEngine.Object.Destroy(texture2D);
493 return (
byte)result != 0;
498 Color32[] px = tex.GetPixels32();
499 int num = Math.Min(grid.
rows, tex.height / tileH);
500 int num2 = Math.Min(grid.
cols, tex.width / tileW);
501 for (
int i = 0; i < num; i++)
503 int y = tex.height - (i + 1) * tileH;
504 for (
int j = 0; j < num2; j++)
506 int num3 = i * grid.cols + j;
507 if (!grid.
used[num3])
509 grid.
used[num3] = HasAnyPx(tex.width, j * tileW, y, tileW, tileH);
513 bool HasAnyPx(
int texW,
int x0,
int num4,
int w,
int h)
515 for (
int k = num4; k < num4 + h; k++)
517 int num5 = k * texW + x0;
518 for (
int l = 0; l < w; l++)
520 if (px[num5 + l].a != 0)
534 cols = data.tex.width / data.
tileW,
535 rows = data.tex.height / data.tileH
537 grid.used =
new bool[grid.cols * grid.
rows];
546 if (!renderData || !renderData.
pass || passData.TryGetValue(renderData.
pass) != data || allTileRow.
_tiles ==
null)
551 int[] tiles = allTileRow.
_tiles;
552 for (
int i = 0; i < tiles.Length; i++)
554 int num = Math.Abs(tiles[i]) - tileBounds.
left;
555 for (
int j = -tileBounds.
up; j <= tileBounds.
down; j++)
557 grid.SetUsedSequence(num + j * grid.cols, tileBounds.
left + tileBounds.
width);
560 if (allTileRow is
SourceObj.
Row { growth: { stages: var stages } growth })
564 if (!stage.renderData || passData.TryGetValue(stage.renderData.pass) != data)
569 foreach (
int num2
in tiles)
571 for (
int l = -tileBounds.up; l <= 0; l++)
573 grid.SetUsedSequence(num2 + l * grid.cols, 1);
577 if (growth.harvestTile != 0 && (
bool)growth.RenderHarvest && passData.TryGetValue(growth.RenderHarvest.pass) == data)
579 grid.SetUsedSequence(growth.harvestTile, 2);
591 grid.SetUsedSequence(renderData.ConvertTile(allTileRow.
snowTile), 12);
596 grid.SetUsedSequence(renderData.ConvertTile(allTileRow.
snowTile), 16);
621 if (
item.autotile > 0)
628 if (row.autotile > 0 && (
bool)row.renderData && (
bool)row.renderData.pass && passData.TryGetValue(row.renderData.pass) == data)
635 private static int AllocateRect(
Grid grid,
int cols,
int rows,
int baseRowOffset,
int baseColOffset)
637 for (
int i = 0; i + rows <= grid.
rows; i++)
639 for (
int j = 0; j + cols <= grid.
cols; j++)
641 if (IsRectFree(grid, i, j, cols, rows))
643 for (
int k = 0; k < rows; k++)
647 return (i + baseRowOffset) * 100 + j + baseColOffset;
656 for (
int i = 0; i < rows; i++)
658 int num = (r + i) * grid.
cols + c;
659 for (
int j = 0; j < cols; j++)
661 if (grid.
used[num + j])
672 int cols = grid.
cols;
673 int rows = grid.
rows;
674 int num = Math.Min(10000 / cols, SystemInfo.maxTextureSize / data.
tileH);
675 if (rows + rowsNeeded > num)
677 Debug.LogError(
$"#tile '{data.id}' has no empty rows ({rows} rows of {cols}, max {num}), cannot add {tag}");
680 int num2 = Math.Min(Math.Max(rowsNeeded, 8), num - rows);
681 List<MeshPass> passes = GetPasses(data, snow);
685 if (textureData !=
null)
687 ReinitTextureH(textureData, num2 * textureData.
tileH);
693 if ((
bool)pmesh && Mathf.RoundToInt(pmesh.
tiling.x) == cols && Mathf.RoundToInt(pmesh.
tiling.y) == rows)
695 if (!_grownMesh.ContainsKey(pmesh))
697 _grownMesh[pmesh] = pmesh.
tiling;
699 pmesh.tiling.y = rows + num2;
703 if (!mat || !mat.HasProperty(_tiling))
707 Vector4 vector = mat.GetVector(_tiling);
708 if (Mathf.RoundToInt(vector.x) == cols && Mathf.RoundToInt(vector.y) == rows)
710 _grownMat.TryAdd(mat, vector);
711 float num3 = 1f / (float)rows;
712 if (vector.z > 0f && vector.z < num3)
714 vector.z *= (float)rows / (
float)(rows + num2);
716 if (vector.w > 0f && vector.w < num3)
718 vector.w *= (float)rows / (
float)(rows + num2);
720 vector.y = rows + num2;
721 mat.SetVector(_tiling, vector);
726 if ((
bool)item2.
pmesh)
733 Debug.Log(
$"#tile '{data.id}' added {num2} rows to {grid.rows}x{cols} ({data.tex.width}x{data.tex.height}) for {tag}");
739 Texture2D tex = d.
tex;
740 int width = tex.width;
741 int height = tex.height;
742 Color32[] pixels = tex.GetPixels32();
743 if (!_grownTex.ContainsKey(d))
745 _grownTex[d] = (width, height);
747 bool flag = tex.mipmapCount > 1;
748 if (!tex.Reinitialize(width, height + addH, tex.format, flag))
750 throw new InvalidOperationException(
$"could not reinitialize '{d.id}' to {width}x{height + addH}");
752 tex.SetPixels32(0, addH, width, height, pixels);
753 tex.SetPixels32(0, 0, width, addH,
new Color32[width * addH]);
754 tex.Apply(flag, makeNoLongerReadable:
false);
755 d.extraRows += addH / d.
tileH;
760 HashSet<MeshPass> hashSet =
new HashSet<MeshPass>();
764 if (textureData ==
null)
771 if ((
bool)
item.subPass)
773 hashSet.Add(
item.subPass);
775 if ((
bool)
item.snowPass)
777 hashSet.Add(
item.snowPass);
779 if ((
bool)
item.shadowPass)
781 hashSet.Add(
item.shadowPass);
785 return hashSet.ToList();
790 Dictionary<MeshPass, TextureData> dictionary =
new Dictionary<MeshPass, TextureData>();
795 dictionary[
item] = value;
override string ToString()
static SourceManager sources
SourceCellEffect cellEffects
static Dictionary< string, string > dictModItems
List< MeshPass > listPass
void AddReplace(TextureReplace r)
Dictionary< string, TextureData > texMap
void TryRefresh(bool force)
void SetUsedSequence(int linear, int span)
static TileBounds GetTileBounds(RenderRow row, int frames=1)
static void AllocateTiles()
static readonly Dictionary< ProceduralMesh, Vector2 > _grownMesh
static void Blit(TextureData data, FileInfo file, int index)
static bool IsRectFree(Grid grid, int r, int c, int cols, int rows)
static bool TryAddRows(TextureData data, TextureData snow, Grid grid, int rowsNeeded, string tag)
static Grid BuildGrid(TextureData data, TextureData snow, Dictionary< MeshPass, TextureData > passData)
static int MakeGrowthTile(Entry entry, SourceObj.Row obj, int baseIndex, int frames)
static Dictionary< MeshPass, TextureData > BuildPassData()
static void InitGrowth(SourceObj.Row obj, string[] resolved)
static IEnumerable< RenderRow > GetAllTileRows()
static void CheckUsedPx(Grid grid, Texture2D tex, int tileW, int tileH)
static int[] GetTiles(Entry entry, RenderRow row, int baseIndex, int frames)
static readonly int _tiling
static readonly List< Entry > _allocated
static void SetBuiltinSequence(Grid grid, TextureData data, Dictionary< MeshPass, TextureData > passData)
static readonly(string source, Func< IEnumerable< TileRow > > rows)[] _sourceData
static List< Entry > ParseTiles(string source, IEnumerable< TileRow > rows)
static void ReinitTextureH(TextureData d, int addH)
static void Allocate(Entry entry, Dictionary< MeshPass, TextureData > passData, Dictionary< TextureData, TextureData > snowOf, Dictionary< TextureData, Grid > grids)
static void ReapplyRows()
static List< MeshPass > GetPasses(TextureData data, TextureData snow)
static readonly Dictionary< TextureData,(int w, int h)> _grownTex
static readonly Dictionary< Material, Vector4 > _grownMat
static int AllocateRect(Grid grid, int cols, int rows, int baseRowOffset, int baseColOffset)
static bool TryMeasure(FileInfo file, TextureData data, out int frames, out int rows)
static void ApplyRow(RenderRow row, int[] tiles)
static int[] GetAnime(RenderRow row)