Skip to content

Commit f97ceee

Browse files
authored
Make character nameplate overridable, add four more layouts (#530)
1 parent fa9b8e2 commit f97ceee

File tree

8 files changed

+200
-23
lines changed

8 files changed

+200
-23
lines changed

src/SerialLoops.Lib/Items/CharacterItem.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ public class CharacterItem : Item
1212
{
1313
public MessageInfo MessageInfo { get; set; }
1414
public NameplateProperties NameplateProperties { get; set; }
15+
public SKBitmap NameplateOverride { get; set; }
1516

1617
public CharacterItem(MessageInfo character, NameplateProperties nameplateProperties, Project project) : base($"CHR_{project.Characters[(int)character.Character].Name}", ItemType.Character)
1718
{

src/SerialLoops.Lib/Project.cs

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -892,7 +892,7 @@ public LoadProjectResult LoadArchives(ILogger log, IProgressTracker tracker)
892892
try
893893
{
894894
LayoutFiles.Clear();
895-
tracker.Focus("Layouts", 22);
895+
tracker.Focus("Layouts", 23);
896896

897897
// Puzzle phase layouts
898898
LayoutFiles.Add(0xC45, Grp.GetFileByIndex(0xC45));
@@ -921,7 +921,71 @@ public LoadProjectResult LoadArchives(ILogger log, IProgressTracker tracker)
921921
Grp.GetFileByIndex(0xC67),
922922
];
923923

924-
Items.Add(new LayoutItem(0xC45, puzzlePhaseGraphics, 54, 13, "LYT_ACCIDENT_OUTBREAK", this));
924+
// ADV bottom screen layouts
925+
LayoutFiles.Add(0xB5B, Grp.GetFileByIndex(0xB5B));
926+
LayoutFiles.Add(0xE42, Grp.GetFileByIndex(0xE42));
927+
928+
List <GraphicsFile> advBottomGraphics =
929+
[
930+
Grp.GetFileByIndex(0xB73),
931+
Grp.GetFileByIndex(0xB73),
932+
Grp.GetFileByIndex(0xB75),
933+
Grp.GetFileByIndex(0xB77),
934+
Grp.GetFileByIndex(0xB79),
935+
Grp.GetFileByIndex(0xB7B),
936+
Grp.GetFileByIndex(0xB7C),
937+
Grp.GetFileByIndex(0xB7E),
938+
Grp.GetFileByIndex(0xB80),
939+
Grp.GetFileByIndex(0xB82),
940+
Grp.GetFileByIndex(0xB82),
941+
Grp.GetFileByIndex(0xB85),
942+
Grp.GetFileByIndex(0xB87),
943+
Grp.GetFileByIndex(0xB89),
944+
Grp.GetFileByIndex(0xB8B),
945+
Grp.GetFileByIndex(0xB90),
946+
Grp.GetFileByIndex(0xB90),
947+
Grp.GetFileByIndex(0xB93),
948+
Grp.GetFileByIndex(0xB96),
949+
Grp.GetFileByIndex(0xB98),
950+
Grp.GetFileByIndex(0xB9A),
951+
Grp.GetFileByIndex(0xB9C),
952+
Grp.GetFileByIndex(0xB9E),
953+
Grp.GetFileByIndex(0xBA2),
954+
Grp.GetFileByIndex(0xBA4),
955+
Grp.GetFileByIndex(0xBA6),
956+
Grp.GetFileByIndex(0xBA8),
957+
Grp.GetFileByIndex(0xBAA),
958+
Grp.GetFileByIndex(0xBAC),
959+
Grp.GetFileByIndex(0xBAE),
960+
Grp.GetFileByIndex(0xBB0),
961+
Grp.GetFileByIndex(0xBB2),
962+
Grp.GetFileByIndex(0xBB4),
963+
Grp.GetFileByIndex(0xBB6),
964+
Grp.GetFileByIndex(0xBB8),
965+
Grp.GetFileByIndex(0xBBA),
966+
Grp.GetFileByIndex(0xBBD),
967+
Grp.GetFileByIndex(0xBBF),
968+
Grp.GetFileByIndex(0xBC0),
969+
Grp.GetFileByIndex(0xBC1),
970+
Grp.GetFileByIndex(0xBC2),
971+
Grp.GetFileByIndex(0xBC3),
972+
Grp.GetFileByIndex(0xBC4),
973+
Grp.GetFileByIndex(0xBC5),
974+
Grp.GetFileByIndex(0xBC6),
975+
Grp.GetFileByIndex(0xBC7),
976+
Grp.GetFileByIndex(0xBC8),
977+
Grp.GetFileByIndex(0xB5C),
978+
Grp.GetFileByIndex(0xB5D),
979+
Grp.GetFileByIndex(0xB5E),
980+
];
981+
SystemTextureFile systexFile = Dat.GetFileByName("SYSTEXS").CastTo<SystemTextureFile>();
982+
List<GraphicsFile> xtrTpcGraphicFiles = systexFile.LoadOrders[0x120..0x12A]
983+
.Select(l => Grp.GetFileByIndex(systexFile.SystemTextures[l].GrpIndex)).ToList();
984+
xtrTpcGraphicFiles.Add(Grp.GetFileByIndex(systexFile.SystemTextures[0x5F].GrpIndex));
985+
xtrTpcGraphicFiles = xtrTpcGraphicFiles.OrderBy(g => g.Name).ToList();
986+
xtrTpcGraphicFiles.Insert(7, xtrTpcGraphicFiles[7]);
987+
988+
Items.Add(new LayoutItem(0xC45, puzzlePhaseGraphics, 54, 13, "LYT_ACCIDENT_OCCURRED", this));
925989
tracker.Finished++;
926990
Items.Add(new LayoutItem(0xC45, puzzlePhaseGraphics, 67, 5, "LYT_MAIN_TOPIC_DELAYED", this));
927991
tracker.Finished++;
@@ -963,6 +1027,13 @@ public LoadProjectResult LoadArchives(ILogger log, IProgressTracker tracker)
9631027
tracker.Finished++;
9641028
Items.Add(new LayoutItem(0xC45, puzzlePhaseGraphics, 307, 1, "LYT_MIN_ERASED_GOAL", this));
9651029
tracker.Finished++;
1030+
1031+
Items.Add(new LayoutItem(0xB5B, advBottomGraphics, 0, 13, "LYT_CHOICE_SELECT_BOX", this));
1032+
Items.Add(new LayoutItem(0xB5B, advBottomGraphics, 13, 2, "LYT_DIALOGUE_BOX", this));
1033+
1034+
Items.Add(new LayoutItem(0xE42, xtrTpcGraphicFiles, 0, 104, "LYT_EXTRA_TOPIC_VIEWER", this));
1035+
Items.Add(new LayoutItem(0xE42, xtrTpcGraphicFiles, 104, 36, "LYT_EXTRA_TOPIC_CHAR_SELECT", this));
1036+
tracker.Finished++;
9661037
}
9671038
catch (Exception ex)
9681039
{

src/SerialLoops/Assets/Strings.Designer.cs

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/SerialLoops/Assets/Strings.resx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3207,6 +3207,15 @@ item in the dropdown!</value>
32073207
<data name="LoadSaveErrorMessage" xml:space="preserve">
32083208
<value>Unable to load project save file. Please ensure you have set your emulator to melonDS and have built and run at least once.</value>
32093209
</data>
3210+
<data name="CharacterEditorOverrideNameplateText" xml:space="preserve">
3211+
<value>Override Nameplate</value>
3212+
</data>
3213+
<data name="CharacterEditorSelectNameplateFilePickerTitle" xml:space="preserve">
3214+
<value>Select nameplate image</value>
3215+
</data>
3216+
<data name="CharacterEditorNameplateReplaceErrorMessage" xml:space="preserve">
3217+
<value>Failed to replace nameplate for character {0} with image {1}</value>
3218+
</data>
32103219
<data name="CharacterSpriteEditorSelectBase" xml:space="preserve">
32113220
<value>Select sprite base</value>
32123221
</data>

src/SerialLoops/ViewModels/Editors/CharacterEditorViewModel.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
using System.Collections.ObjectModel;
22
using System.IO;
33
using System.Linq;
4+
using System.Threading.Tasks;
45
using System.Windows.Input;
56
using Avalonia.Controls;
67
using Avalonia.Input;
78
using Avalonia.Media;
89
using Avalonia.Platform;
10+
using Avalonia.Platform.Storage;
911
using HaruhiChokuretsuLib.Util;
1012
using ReactiveHistory;
1113
using ReactiveUI;
1214
using ReactiveUI.Fody.Helpers;
15+
using SerialLoops.Assets;
1316
using SerialLoops.Lib.Items;
1417
using SerialLoops.Utility;
18+
using SerialLoops.ViewModels.Dialogs;
1519
using SerialLoops.ViewModels.Panels;
20+
using SerialLoops.Views.Dialogs;
1621
using SkiaSharp;
1722

1823
namespace SerialLoops.ViewModels.Editors;
@@ -21,6 +26,17 @@ public class CharacterEditorViewModel : EditorViewModel
2126
{
2227
private readonly CharacterItem _character;
2328

29+
public bool OverrideGeneratedNameplate
30+
{
31+
get => _character.NameplateOverride is not null;
32+
set
33+
{
34+
_character.NameplateOverride = value ? NameplateBitmap : null;
35+
this.RaisePropertyChanged();
36+
Description.UnsavedChanges = true;
37+
}
38+
}
39+
2440
public EditorTabsPanelViewModel Tabs { get; }
2541

2642
public string CharacterName
@@ -113,6 +129,8 @@ public short TextTimer
113129
private readonly SKBitmap _blankNameplateBitmap;
114130
private readonly SKBitmap _blankNameplateBaseArrowBitmap;
115131

132+
public ICommand OverrideNameplateCommand { get; }
133+
116134
private StackHistory _history;
117135
public ICommand UndoCommand { get; }
118136
public ICommand RedoCommand { get; }
@@ -141,6 +159,8 @@ public CharacterEditorViewModel(CharacterItem character, MainWindowViewModel win
141159
window.OpenProject.NameplateBitmap.ExtractSubset(NameplateBitmap,
142160
new(0, 16 * ((int)_character.MessageInfo.Character - 1), 64, 16 * (int)_character.MessageInfo.Character));
143161

162+
OverrideNameplateCommand = ReactiveCommand.CreateFromTask(OverrideNameplate);
163+
144164
this.WhenAnyValue(c => c.TextColor).ObserveWithHistory(t => TextColor = t, TextColor, _history);
145165
this.WhenAnyValue(c => c.PlateColor).ObserveWithHistory(p => PlateColor = p, PlateColor, _history);
146166
this.WhenAnyValue(c => c.OutlineColor).ObserveWithHistory(o => OutlineColor = o, OutlineColor, _history);
@@ -154,8 +174,36 @@ public CharacterEditorViewModel(CharacterItem character, MainWindowViewModel win
154174
RedoGesture = GuiExtensions.CreatePlatformAgnosticCtrlGesture(Key.Y);
155175
}
156176

177+
private async Task OverrideNameplate()
178+
{
179+
string nameplateFile = (await Window.Window.ShowOpenFilePickerAsync(Strings.CharacterEditorSelectNameplateFilePickerTitle,
180+
[new(Strings.FiletypeSupportedImages) { Patterns = Shared.SupportedImageFiletypes }]))?.TryGetLocalPath();
181+
if (string.IsNullOrEmpty(nameplateFile))
182+
{
183+
return;
184+
}
185+
186+
ImageCropResizeDialogViewModel cropResizeDialogViewModel = new(nameplateFile, NameplateBitmap.Width, NameplateBitmap.Height, _log);
187+
SKBitmap finalImage = await new ImageCropResizeDialog
188+
{
189+
DataContext = cropResizeDialogViewModel,
190+
}.ShowDialog<SKBitmap>(Window.Window);
191+
if (finalImage is null)
192+
{
193+
return;
194+
}
195+
196+
NameplateBitmap = finalImage;
197+
_character.NameplateOverride = NameplateBitmap;
198+
Description.UnsavedChanges = true;
199+
}
200+
157201
private void UpdateNameplateBitmap()
158202
{
203+
if (OverrideGeneratedNameplate)
204+
{
205+
return;
206+
}
159207
NameplateBitmap =
160208
_character.GetNewNameplate(_blankNameplateBitmap, _blankNameplateBaseArrowBitmap, Window.OpenProject);
161209
}

src/SerialLoops/ViewModels/MainWindowViewModel.cs

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,18 +1111,36 @@ public void SaveProject_Executed()
11111111
break;
11121112
case ItemDescription.ItemType.Character:
11131113
CharacterItem characterItem = (CharacterItem)item;
1114-
if (characterItem.NameplateProperties.Name != item.DisplayName[4..])
1114+
if (characterItem.NameplateProperties.Name != item.DisplayName[4..] || characterItem.NameplateOverride is not null)
11151115
{
11161116
characterItem.Rename($"CHR_{characterItem.NameplateProperties.Name}", OpenProject);
1117-
nameplateCanvas.DrawBitmap(
1118-
characterItem.GetNewNameplate(_blankNameplate, _blankNameplateBaseArrow, OpenProject),
1119-
new SKRect(0, 16 * ((int)characterItem.MessageInfo.Character - 1), 64,
1120-
16 * ((int)characterItem.MessageInfo.Character)));
1121-
speakerCanvas.DrawBitmap(
1122-
characterItem.GetNewNameplate(_blankNameplate, _blankNameplateBaseArrow, OpenProject,
1123-
transparent: true),
1124-
new SKRect(0, 16 * ((int)characterItem.MessageInfo.Character - 1), 64,
1125-
16 * ((int)characterItem.MessageInfo.Character)));
1117+
if (characterItem.NameplateOverride is not null)
1118+
{
1119+
nameplateCanvas.DrawRect(new(0, 16 * ((int)characterItem.MessageInfo.Character - 1), 64,
1120+
16 * (int)characterItem.MessageInfo.Character), new() { Color = new(0, 128, 0)});
1121+
OpenProject.SpeakerBitmap.Erase(SKColors.Transparent, new(0, 16 * ((int)characterItem.MessageInfo.Character - 1), 64,
1122+
16 * (int)characterItem.MessageInfo.Character));
1123+
nameplateCanvas.DrawBitmap(
1124+
characterItem.NameplateOverride,
1125+
new SKRect(0, 16 * ((int)characterItem.MessageInfo.Character - 1), 64,
1126+
16 * (int)characterItem.MessageInfo.Character));
1127+
speakerCanvas.DrawBitmap(
1128+
characterItem.NameplateOverride,
1129+
new SKRect(0, 16 * ((int)characterItem.MessageInfo.Character - 1), 64,
1130+
16 * (int)characterItem.MessageInfo.Character));
1131+
}
1132+
else
1133+
{
1134+
nameplateCanvas.DrawBitmap(
1135+
characterItem.GetNewNameplate(_blankNameplate, _blankNameplateBaseArrow, OpenProject),
1136+
new SKRect(0, 16 * ((int)characterItem.MessageInfo.Character - 1), 64,
1137+
16 * (int)characterItem.MessageInfo.Character));
1138+
speakerCanvas.DrawBitmap(
1139+
characterItem.GetNewNameplate(_blankNameplate, _blankNameplateBaseArrow, OpenProject,
1140+
transparent: true),
1141+
new SKRect(0, 16 * ((int)characterItem.MessageInfo.Character - 1), 64,
1142+
16 * (int)characterItem.MessageInfo.Character));
1143+
}
11261144
changedNameplates = true;
11271145
}
11281146

src/SerialLoops/Views/Dialogs/LogViewerDialog.axaml.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using Avalonia;
21
using Avalonia.Controls;
3-
using Avalonia.Markup.Xaml;
42

53
namespace SerialLoops.Views.Dialogs;
64

src/SerialLoops/Views/Editors/CharacterEditorView.axaml

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
<utility:SKAvaloniaColorConverter x:Key="ColorConverter"/>
1414
</UserControl.Resources>
1515

16-
<Grid ColumnDefinitions="Auto,Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto">
16+
<Grid ColumnDefinitions="Auto,Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto">
1717
<Panel IsVisible="False">
1818
<Button Command="{Binding UndoCommand}" HotKey="{Binding UndoGesture}"/>
1919
<Button Command="{Binding RedoCommand}" HotKey="{Binding RedoGesture}"/>
@@ -24,33 +24,38 @@
2424

2525
<TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Text="{x:Static assets:Strings.CharacterEditorNameplateTextColor}"/>
2626
<ColorPicker Grid.Row="1" Grid.Column="1" Color="{Binding TextColor, Converter={StaticResource ColorConverter}}"
27-
IsColorPaletteVisible="True" Palette="{Binding ColorPalette}"/>
27+
IsColorPaletteVisible="True" Palette="{Binding ColorPalette}" IsEnabled="{Binding !OverrideGeneratedNameplate}"/>
2828

2929
<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Text="{x:Static assets:Strings.CharacterEditorNameplateColor}"/>
3030
<ColorPicker Grid.Row="2" Grid.Column="1" Color="{Binding PlateColor, Converter={StaticResource ColorConverter}}"
31-
IsColorPaletteVisible="True" Palette="{Binding ColorPalette}"/>
31+
IsColorPaletteVisible="True" Palette="{Binding ColorPalette}" IsEnabled="{Binding !OverrideGeneratedNameplate}"/>
3232

3333
<TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Text="{x:Static assets:Strings.CharacterEditorNameOutlineColor}"/>
3434
<ColorPicker Grid.Row="3" Grid.Column="1" Color="{Binding OutlineColor, Converter={StaticResource ColorConverter}}"/>
3535
<CheckBox Grid.Row="3" Grid.Column="2" VerticalAlignment="Center" Content="{x:Static assets:Strings.CharacterEditorHasOutlineLabel}"
36-
IsChecked="{Binding HasOutline}"/>
36+
IsChecked="{Binding HasOutline}" IsEnabled="{Binding !OverrideGeneratedNameplate}"/>
3737

3838
<Image Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" HorizontalAlignment="Center" Stretch="None" Margin="15"
3939
Source="{Binding NameplateBitmap, Converter={x:Static utility:SLConverters.SKBitmapToAvaloniaConverter}}"/>
4040

41-
<StackPanel Grid.Row="5" Grid.Column="0" Orientation="Horizontal" Spacing="3" Margin="0 0 10 0">
41+
<CheckBox Grid.Row="5" Grid.Column="0" Content="{x:Static assets:Strings.CharacterEditorOverrideNameplateText}"
42+
IsChecked="{Binding OverrideGeneratedNameplate}"/>
43+
<Button Grid.Row="5" Grid.Column="1" Content="{x:Static assets:Strings.Replace}" IsEnabled="{Binding OverrideGeneratedNameplate}"
44+
Command="{Binding OverrideNameplateCommand}"/>
45+
46+
<StackPanel Grid.Row="6" Grid.Column="0" Orientation="Horizontal" Spacing="3" Margin="0 0 10 0">
4247
<TextBlock VerticalAlignment="Center" Text="{x:Static assets:Strings.ScriptParamTextVoiceFont}"/>
4348
<Svg Path="avares://SerialLoops/Assets/Icons/Help.svg" Width="16" VerticalAlignment="Center" ToolTip.Tip="{x:Static assets:Strings.CharacterEditorVoiceFontHelp}"/>
4449
</StackPanel>
45-
<ComboBox Grid.Row="5" Grid.Column="1" ItemsSource="{Binding Sfxs}" SelectedItem="{Binding VoiceFont}"/>
46-
<controls:ItemLink Grid.Row="5" Grid.Column="2" Item="{Binding VoiceFont}"
50+
<ComboBox Grid.Row="6" Grid.Column="1" ItemsSource="{Binding Sfxs}" SelectedItem="{Binding VoiceFont}"/>
51+
<controls:ItemLink Grid.Row="6" Grid.Column="2" Item="{Binding VoiceFont}"
4752
Tabs="{Binding Tabs}"/>
4853

49-
<StackPanel Grid.Row="6" Grid.Column="0" Orientation="Horizontal" Spacing="3" Margin="0 0 10 0">
54+
<StackPanel Grid.Row="7" Grid.Column="0" Orientation="Horizontal" Spacing="3" Margin="0 0 10 0">
5055
<TextBlock VerticalAlignment="Center" Text="{x:Static assets:Strings.CharacterEditorTextTimer}"/>
5156
<Svg Path="avares://SerialLoops/Assets/Icons/Help.svg" Width="16" VerticalAlignment="Center" ToolTip.Tip="{x:Static assets:Strings.CharacterEditorTextTimerHelp}"/>
5257
</StackPanel>
53-
<NumericUpDown Grid.Row="6" Grid.Column="1" Value="{Binding TextTimer}"
58+
<NumericUpDown Grid.Row="7" Grid.Column="1" Value="{Binding TextTimer}"
5459
FormatString="N0" Increment="1" ParsingNumberStyle="Integer"/>
5560
</Grid>
5661
</UserControl>

0 commit comments

Comments
 (0)