add the concept of 'comment segments' for tables

This can allow the table to insert comments when writing the content as a string.

Also, update the formatting for wild pokemon summary
This commit is contained in:
haven1433 2023-06-20 23:39:46 -05:00
parent a397cf1c71
commit 2ac3f88541
5 changed files with 87 additions and 17 deletions

View File

@ -7,6 +7,7 @@ using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Windows.Input;
namespace HavenSoft.HexManiac.Core {
@ -182,6 +183,21 @@ namespace HavenSoft.HexManiac.Core {
return input.Substring(0, index) + replacement + input.Substring(index + search.Length);
}
public static StringBuilder TrimEnd(this StringBuilder sb) {
if (sb == null || sb.Length == 0) return sb;
int i = sb.Length - 1;
for (; i >= 0; i--)
if (!char.IsWhiteSpace(sb[i]))
break;
if (i < sb.Length - 1)
sb.Length = i + 1;
return sb;
}
public static bool IsAny<T>(this T self, params T[] options) {
Debug.Assert(self is IEquatable<T> || self is Enum);
return options.Contains(self);

View File

@ -215,7 +215,7 @@ data.trainers.rematches, , , , , ,
data.trainers.rematches, 082C00, 082C00, 082C20, 082C20, , , , , , [base:data.trainers.stats rematch1:data.trainers.stats rematch2:data.trainers.stats rematch3:data.trainers.stats rematch4:data.trainers.stats mapBank: mapNum: unused:]56
data.trainers.multibattle.steven.team, , , , , , , , , 165534, [species:data.pokemon.names iv. level. nature.data.pokemon.natures.names hpEV. atkEV. defEV. speedEV. spAtkEV. spDefEV. unused. [move:data.pokemon.moves.names]4]3
data.decorations.stats, 0B3AC8, 0B3AC8, 0B3AE8, 0B3AE8, 00014C, 00014C, 00014C, 00014C, 00014C, [id. name""16 permission.decorpermissions shape.decorshape category.decorcategory price: unused: description<""> graphics<>]
data.pokemon.wild, 084D9C, 084D9C, 084DBC, 084DBC, 082990, 082964, 0829A4, 082978, 0B4D48, [bank. map. unused: grass<[rate:: list<[lowLevel. highLevel. species:data.pokemon.names]12>]1> surf<[rate:: list<[lowLevel. highLevel. species:data.pokemon.names]5>]1> tree<[rate:: list<[lowLevel. highLevel. species:data.pokemon.names]5>]1> fish<[rate:: list<[lowLevel. highLevel. species:data.pokemon.names]10>]1>]!FFFF
data.pokemon.wild, 084D9C, 084D9C, 084DBC, 084DBC, 082990, 082964, 0829A4, 082978, 0B4D48, [bank. map. unused: grass<[rate:: list<[lowLevel. highLevel. species:data.pokemon.names]12>]1> surf<[rate:: list<[lowLevel. highLevel. species:data.pokemon.names]5>]1> tree<[rate:: list<[lowLevel. highLevel. species:data.pokemon.names]5>]1> fish<[rate:: list<[old|comment=0|old_rod: good|comment=2|good_rod: super|comment=5|super_rod: lowLevel. highLevel. species:data.pokemon.names]10>]1>]!FFFF
data.pokemon.trades, 04D8D4, 04D8D4, 04D8F4, 04D8F4, , , , , , [nickname""12 receive:data.pokemon.names hp. attack. defense. speed. spatk. spdef. abilitynum:: trainerid:: cool. tough. beauty. smart. cute. unused. unused: personality:: helditem:data.items.stats mailnum. trainername""11 trainergender.trainergender sheen. give::data.pokemon.names]3
data.pokemon.trades, , , , , 053AD4, 053AD4, 053AE8, 053AE8, , [nickname""12 receive:data.pokemon.names hp. attack. defense. speed. spatk. spdef. abilitynum:: trainerid:: cool. tough. beauty. smart. cute. unused. unused: personality:: helditem:data.items.stats mailnum. trainername""11 trainergender.trainergender sheen. give::data.pokemon.names]9
data.pokemon.trades, , , , , , , , , 07E774, [nickname""12 receive:data.pokemon.names hp. attack. defense. speed. spatk. spdef. abilitynum:: trainerid:: cool. tough. beauty. smart. cute. unused. unused: personality:: helditem:data.items.stats mailnum. trainername""11 trainergender.trainergender sheen. give::data.pokemon.names]4

View File

@ -52,7 +52,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
}
public static bool IsUnused(this ArrayRunElementSegment segment) {
return segment.Name.StartsWith("unused") || segment.Name.StartsWith("padding");
return segment is ArrayRunCommentSegment || segment.Name.StartsWith("unused") || segment.Name.StartsWith("padding");
}
public static IDataFormat CreateSegmentDataFormat(this ITableRun self, IDataModel data, int index) {
@ -128,7 +128,20 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
var names = self.ElementNames;
var offsets = self.ConvertByteOffsetToArrayOffset(start);
length += offsets.SegmentOffset;
var comments = new Dictionary<int, string>();
foreach (var seg in self.ElementContent) {
if (seg is not ArrayRunCommentSegment comment) continue;
if (comments.ContainsKey(comment.Index)) {
comments[comment.Index] += Environment.NewLine + comment.RenderCommentLine();
} else {
comments[comment.Index] = comment.RenderCommentLine();
}
}
for (int i = offsets.ElementIndex; i < self.ElementCount && length > 0; i++) {
if (comments.TryGetValue(i, out var comment)) {
if (i != 0) text.AppendLine();
text.AppendLine(comment);
}
var offset = offsets.SegmentStart;
var couldBeExtension = offsets.ElementIndex > 0 || self is TableStreamRun streamRun && streamRun.AllowsZeroElements;
if (offsets.SegmentIndex == 0 && couldBeExtension) text.Append(ArrayRun.ExtendArray);
@ -492,6 +505,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
public const string ColorFormatString = "|c";
public const string CalculatedFormatString = "|=";
public const string RenderFormatString = "|render=";
public const string CommentFormatString = "|comment=";
public const string SplitterFormatString = "|";
private const int JunkLimit = 80;
@ -1338,6 +1352,12 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
var tupleContract = segments.Slice(TupleFormatString.Length, endOfToken - TupleFormatString.Length);
segments = segments.Slice(endOfToken).Trim();
list.Add(new ArrayRunTupleSegment(name, tupleContract.ToString(), segmentLength));
} else if (segments.StartsWith(CommentFormatString)) {
var endOfToken = segments.IndexOf(' ');
if (endOfToken == -1) endOfToken = segments.Length;
var contract = segments.Slice(CommentFormatString.Length, endOfToken - CommentFormatString.Length);
segments = segments.Slice(endOfToken).Trim();
list.Add(new ArrayRunCommentSegment(name, contract.ToString()));
} else if (segments.StartsWith(ColorFormatString)) {
var endOfToken = segments.IndexOf(' ');
if (endOfToken == -1) endOfToken = segments.Length;
@ -1414,6 +1434,8 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
return (ElementContentType.Integer, 0, 0);
} else if (segments.StartsWith(RenderFormatString)) {
return (ElementContentType.Integer, 0, 0);
} else if (segments.StartsWith(CommentFormatString)) {
return (ElementContentType.Integer, 0, 0);
} else if (segments.StartsWith(DoubleByteIntegerFormat + string.Empty + DoubleByteIntegerFormat)) {
return (ElementContentType.Integer, 2, 4);
} else if (segments.StartsWith(DoubleByteIntegerFormat + string.Empty + SingleByteIntegerFormat) || segments.StartsWith(".:")) {
@ -1581,6 +1603,18 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
}
}
public class ArrayRunCommentSegment : ArrayRunElementSegment {
public int Index { get; private set; }
public string Comment { get; private set; }
public override string SerializeFormat => $"{Name}|comment={Index}|{Comment}";
public ArrayRunCommentSegment(string name, string contract) : base(name, ElementContentType.Integer, 0) {
var parts = contract.Split('|', 2);
if (parts[0].TryParseInt(out var index)) Index = index;
Comment = parts[1];
}
public string RenderCommentLine() => $"# {Comment.Replace('_', ' ')}";
}
public class ArrayRunParseException : Exception {
public ArrayRunParseException(string message) : base(message) { }
}

View File

@ -209,7 +209,7 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
var self = this;
if (lengthOverride != ElementCount) self = new TableStreamRun(model, Start, PointerSources, FormatString, ElementContent, endStream, lengthOverride);
var changedAddresses = new List<int>();
var lines = content.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
var lines = content.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries).Where(line => !line.Trim().StartsWith("#")).ToArray();
if (lines.Length == 0 && !AllowsZeroElements) lines = content.Split(Environment.NewLine);
var newRun = self;
var appendCount = Math.Max(lines.Length, 1) - lengthOverride;
@ -271,6 +271,8 @@ namespace HavenSoft.HexManiac.Core.Models.Runs {
if (pointerValue == Pointer.NULL) value = "<null>";
} else if (segment.Type == ElementContentType.PCS) {
value = model.TextConverter.Convert(model, offset, segment.Length);
} else if (segment.Length == 0) {
continue;
}
var extraWhitespace = new string(' ', longestLabel - segment.Name.Length);
result.Append($"{segment.Name}:{extraWhitespace} {value}");

View File

@ -2,6 +2,7 @@
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
@ -53,6 +54,7 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
// extend wild table
var wildTable = model.GetTable(HardcodeTablesModel.WildTableName);
if (wildTable == null) return;
var originalStart = wildTable.Start;
wildTable = model.RelocateForExpansion(token, wildTable, wildTable.Length + wildTable.ElementLength);
wildTable = wildTable.Append(token, 1);
@ -83,30 +85,46 @@ namespace HavenSoft.HexManiac.Core.ViewModels.Map {
// grass<[rate:: list<>]1> surf<[rate:: list<>]1> tree<[rate:: list<>]1> fish<[rate:: list<>]1>
var text = new StringBuilder();
if (wildDataIndex < 0) return text.ToString();
BuildWildTooltip(text, wild[wildDataIndex], "grass");
text.AppendLine();
BuildWildTooltip(text, wild[wildDataIndex], "surf");
text.AppendLine();
BuildWildTooltip(text, wild[wildDataIndex], "tree");
text.AppendLine();
if (BuildWildTooltip(text, wild[wildDataIndex], "grass")) text.AppendLine();
if (BuildWildTooltip(text, wild[wildDataIndex], "surf")) text.AppendLine();
if (BuildWildTooltip(text, wild[wildDataIndex], "tree")) text.AppendLine();
BuildWildTooltip(text, wild[wildDataIndex], "fish");
wildText = text.ToString();
wildText = text.TrimEnd().ToString();
if (string.IsNullOrWhiteSpace(wildText)) wildText = "No Wild Pokemon (yet!)";
return wildText;
}
}
private static void BuildWildTooltip(StringBuilder text, ModelArrayElement wild, string type) {
private static bool BuildWildTooltip(StringBuilder text, ModelArrayElement wild, string type) {
// list<[low. high. species:]n>
var terrain = wild.GetSubTable(type);
if (terrain == null) return;
if (terrain == null) return false;
var list = terrain[0].GetSubTable("list");
if (list == null) return;
if (list == null) return false;
text.Append(type);
text.Append(": ");
var content = list.Select(element => element.GetEnumValue("species")).ToHistogram();
text.AppendJoin(", ", content.Keys.Select(pokemon => {
if (content[pokemon] == 1) return pokemon;
return $"{pokemon} x{content[pokemon]}";
text.AppendLine(":");
if (type == "fish") {
text.Append($"old rod: ");
AppendHistogram(text, list.Take(2), "species");
text.AppendLine();
text.Append($"good rod: ");
AppendHistogram(text, list.Skip(2).Take(3), "species");
text.AppendLine();
text.Append($"super rod: ");
AppendHistogram(text, list.Skip(5), "species");
} else {
AppendHistogram(text, list, "species");
}
text.AppendLine();
return true;
}
private static void AppendHistogram(StringBuilder text, IEnumerable<ModelArrayElement> elements, string fieldName) {
var histogram = elements.Select(element => element.GetEnumValue(fieldName)).ToHistogram();
text.AppendJoin(", ", histogram.Keys.Select(key => {
if (histogram[key] == 1) return key;
return $"{key} x{histogram[key]}";
}));
}