Skip to content

Commit

Permalink
Add character editor to Avalonia branch (#447)
Browse files Browse the repository at this point in the history
  • Loading branch information
jonko0493 authored Jan 25, 2025
1 parent 4d0b25f commit e6c02ad
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 5 deletions.
3 changes: 2 additions & 1 deletion src/SerialLoops.Lib/Items/ItemDescription.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ public ItemDescription(string name, ItemType type, string displayName)
}
}

public void Rename(string newName)
public void Rename(string newName, Project project)
{
DisplayName = newName;
project.ItemNames[Name] = DisplayName;
}

// Enum with values for each type of item
Expand Down
2 changes: 1 addition & 1 deletion src/SerialLoops.Lib/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -986,7 +986,7 @@ public LoadProjectResult LoadArchives(ILogger log, IProgressTracker tracker)
{
if (ItemNames.TryGetValue(Items[i].Name, out string value))
{
Items[i].Rename(value);
Items[i].DisplayName = value;
}
else
{
Expand Down
148 changes: 148 additions & 0 deletions src/SerialLoops/ViewModels/Editors/CharacterEditorViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Platform;
using HaruhiChokuretsuLib.Util;
using ReactiveUI;
using ReactiveUI.Fody.Helpers;
using SerialLoops.Lib.Items;
using SerialLoops.Utility;
using SerialLoops.ViewModels.Panels;
using SkiaSharp;

namespace SerialLoops.ViewModels.Editors;

public class CharacterEditorViewModel : EditorViewModel
{
private CharacterItem _character;

public EditorTabsPanelViewModel Tabs { get; }

public string CharacterName
{
get => _character.NameplateProperties.Name;
set
{
_character.NameplateProperties.Name = value;
this.RaisePropertyChanged();
UpdateNameplateBitmap();
_character.UnsavedChanges = true;
}
}

public SKColor TextColor
{
get => _character.NameplateProperties.NameColor;
set
{
_character.NameplateProperties.NameColor = value;
this.RaisePropertyChanged();
UpdateNameplateBitmap();
_character.UnsavedChanges = true;
}
}

public SKColor PlateColor
{
get => _character.NameplateProperties.PlateColor;
set
{
_character.NameplateProperties.PlateColor = value;
this.RaisePropertyChanged();
UpdateNameplateBitmap();
_character.UnsavedChanges = true;
}
}

public SKColor OutlineColor
{
get => _character.NameplateProperties.OutlineColor;
set
{
_character.NameplateProperties.OutlineColor = value;
this.RaisePropertyChanged();
UpdateNameplateBitmap();
_character.UnsavedChanges = true;
}
}

public bool HasOutline
{
get => _character.NameplateProperties.HasOutline;
set
{
_character.NameplateProperties.HasOutline = value;
this.RaisePropertyChanged();
UpdateNameplateBitmap();
_character.UnsavedChanges = true;
}
}

public ObservableCollection<SfxItem> Sfxs { get; }
private SfxItem _voiceFont;
public SfxItem VoiceFont
{
get => _voiceFont;
set
{
this.RaiseAndSetIfChanged(ref _voiceFont, value);
_character.MessageInfo.VoiceFont = _voiceFont.Index;
_character.UnsavedChanges = true;
}
}

public short TextTimer
{
get => _character.MessageInfo.TextTimer;
set
{
_character.MessageInfo.TextTimer = value;
this.RaisePropertyChanged();
_character.UnsavedChanges = true;
}
}

[Reactive]
public SKBitmap NameplateBitmap { get; set; }

private SKBitmap _blankNameplateBitmap;
private SKBitmap _blankNameplateBaseArrowBitmap;

public CharacterColorPalette ColorPalette { get; } = new();

public CharacterEditorViewModel(CharacterItem character, MainWindowViewModel window, ILogger log) : base(character, window, log)
{
_character = character;
Tabs = window.EditorTabs;

Sfxs = new(window.OpenProject.Items.Where(i => i.Type == ItemDescription.ItemType.SFX).Cast<SfxItem>());
_voiceFont = (SfxItem)window.OpenProject.Items.First(i =>
i.Type == ItemDescription.ItemType.SFX && ((SfxItem)i).Index == _character.MessageInfo.VoiceFont);

using Stream blankNameplateStream = AssetLoader.Open(new("avares://SerialLoops/Assets/Graphics/BlankNameplate.png"));
_blankNameplateBitmap = SKBitmap.Decode(blankNameplateStream);
using Stream blankNameplateBaseArrowStream = AssetLoader.Open(new("avares://SerialLoops/Assets/Graphics/BlankNameplateBaseArrow.png"));
_blankNameplateBaseArrowBitmap = SKBitmap.Decode(blankNameplateBaseArrowStream);

NameplateBitmap = new(64, 16);
window.OpenProject.NameplateBitmap.ExtractSubset(NameplateBitmap,
new(0, 16 * ((int)_character.MessageInfo.Character - 1), 64, 16 * (int)_character.MessageInfo.Character));
}

private void UpdateNameplateBitmap()
{
NameplateBitmap =
_character.GetNewNameplate(_blankNameplateBitmap, _blankNameplateBaseArrowBitmap, Window.OpenProject);
}
}

public class CharacterColorPalette : IColorPalette
{
public Color GetColor(int colorIndex, int shadeIndex) =>
CharacterItem.BuiltInColors[CharacterItem.BuiltInColors.Keys.ElementAt(colorIndex)].ToAvalonia();

public int ColorCount => CharacterItem.BuiltInColors.Count;
public int ShadeCount => 1;
}
25 changes: 24 additions & 1 deletion src/SerialLoops/ViewModels/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,30 @@ public void SaveProject_Executed()
savedExtra = true;
}
break;
case ItemDescription.ItemType.Character:
CharacterItem characterItem = (CharacterItem)item;
if (characterItem.NameplateProperties.Name != item.DisplayName[4..])
{
characterItem.Rename($"CHR_{characterItem.NameplateProperties.Name}", OpenProject);
nameplateCanvas.DrawBitmap(
characterItem.GetNewNameplate(_blankNameplate, _blankNameplateBaseArrow, OpenProject),
new SKRect(0, 16 * ((int)characterItem.MessageInfo.Character - 1), 64,
16 * ((int)characterItem.MessageInfo.Character)));
speakerCanvas.DrawBitmap(
characterItem.GetNewNameplate(_blankNameplate, _blankNameplateBaseArrow, OpenProject,
transparent: true),
new SKRect(0, 16 * ((int)characterItem.MessageInfo.Character - 1), 64,
16 * ((int)characterItem.MessageInfo.Character)));
changedNameplates = true;
}

if (!savedMessInfo)
{
IO.WriteStringFile(Path.Combine("assets", "data", $"{OpenProject.MessInfo.Index:X3}.s"),
OpenProject.MessInfo.GetSource([]), OpenProject, Log);
savedMessInfo = true;
}
break;
case ItemDescription.ItemType.Character_Sprite:
if (!savedChrData)
{
Expand Down Expand Up @@ -837,7 +861,6 @@ public void SaveProject_Executed()
changedTopics = true;
break;
case ItemDescription.ItemType.Voice:
VoicedLineItem vce = (VoicedLineItem)item;
if (OpenProject.VoiceMap is not null)
{
changedSubs = true;
Expand Down
6 changes: 4 additions & 2 deletions src/SerialLoops/ViewModels/Panels/EditorTabsPanelViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ private EditorViewModel CreateTab(ItemDescription item)
return new BackgroundEditorViewModel((BackgroundItem)item, MainWindow, _project, _log);
case ItemDescription.ItemType.BGM:
return new BackgroundMusicEditorViewModel((BackgroundMusicItem)item, MainWindow, _project, _log);
case ItemDescription.ItemType.Scenario:
return new ScenarioEditorViewModel((ScenarioItem)item, MainWindow, _log);
case ItemDescription.ItemType.Character:
return new CharacterEditorViewModel((CharacterItem)item, MainWindow, _log);
case ItemDescription.ItemType.Character_Sprite:
return new CharacterSpriteEditorViewModel((CharacterSpriteItem)item, MainWindow, _log);
case ItemDescription.ItemType.Chess_Puzzle:
Expand All @@ -77,6 +77,8 @@ private EditorViewModel CreateTab(ItemDescription item)
return new MapEditorViewModel((MapItem)item, MainWindow, _log);
case ItemDescription.ItemType.Puzzle:
return new PuzzleEditorViewModel((PuzzleItem)item, MainWindow, _log);
case ItemDescription.ItemType.Scenario:
return new ScenarioEditorViewModel((ScenarioItem)item, MainWindow, _log);
case ItemDescription.ItemType.Script:
return new ScriptEditorViewModel((ScriptItem)item, MainWindow, _log);
case ItemDescription.ItemType.SFX:
Expand Down
58 changes: 58 additions & 0 deletions src/SerialLoops/Views/Editors/CharacterEditorView.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="using:SerialLoops.ViewModels.Editors"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:assets="using:SerialLoops.Assets"
xmlns:controls="using:SerialLoops.Controls"
xmlns:li="using:SerialLoops.Lib.Items"
xmlns:utility="using:SerialLoops.Utility"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:DataType="vm:CharacterEditorViewModel"
x:Class="SerialLoops.Views.Editors.CharacterEditorView">
<UserControl.Resources>
<utility:SKAvaloniaColorConverter x:Key="ColorConverter"/>
</UserControl.Resources>

<Grid ColumnDefinitions="Auto,Auto,Auto" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto">
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Text="{x:Static assets:Strings.Character}"/>
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding CharacterName}"/>

<TextBlock Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Text="{x:Static assets:Strings.Text_Color}"/>
<ColorPicker Grid.Row="1" Grid.Column="1" Color="{Binding TextColor, Converter={StaticResource ColorConverter}}"
IsColorPaletteVisible="True" Palette="{Binding ColorPalette}"/>

<TextBlock Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Text="{x:Static assets:Strings.Plate_Color}"/>
<ColorPicker Grid.Row="2" Grid.Column="1" Color="{Binding PlateColor, Converter={StaticResource ColorConverter}}"
IsColorPaletteVisible="True" Palette="{Binding ColorPalette}"/>

<TextBlock Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Text="{x:Static assets:Strings.Outline_Color}"/>
<ColorPicker Grid.Row="3" Grid.Column="1" Color="{Binding OutlineColor, Converter={StaticResource ColorConverter}}"/>
<CheckBox Grid.Row="3" Grid.Column="2" VerticalAlignment="Center" Content="{x:Static assets:Strings.Has_Outline_}"
IsChecked="{Binding HasOutline}"/>

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

<TextBlock Grid.Row="5" Grid.Column="0" VerticalAlignment="Center" Text="{x:Static assets:Strings.Voice_Font}"/>
<ComboBox Grid.Row="5" Grid.Column="1" ItemsSource="{Binding Sfxs}" SelectedItem="{Binding VoiceFont}">
<ComboBox.ItemTemplate>
<DataTemplate DataType="li:SfxItem">
<TextBlock Text="{Binding DisplayName}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerTheme>
<ControlTheme TargetType="ComboBoxItem" x:DataType="li:SfxItem" BasedOn="{StaticResource {x:Type ComboBoxItem}}">
<Setter Property="TextSearch.Text" Value="{Binding DisplayName}" />
</ControlTheme>
</ComboBox.ItemContainerTheme>
</ComboBox>
<controls:ItemLink Grid.Row="5" Grid.Column="2" Item="{Binding VoiceFont}"
Tabs="{Binding Tabs}"/>

<TextBlock Grid.Row="6" Grid.Column="0" VerticalAlignment="Center" Text="{x:Static assets:Strings.Text_Timer}"/>
<NumericUpDown Grid.Row="6" Grid.Column="1" Value="{Binding TextTimer}"
FormatString="N0" Increment="1" ParsingNumberStyle="Integer"/>
</Grid>
</UserControl>

12 changes: 12 additions & 0 deletions src/SerialLoops/Views/Editors/CharacterEditorView.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Avalonia.Controls;

namespace SerialLoops.Views.Editors;

public partial class CharacterEditorView : UserControl
{
public CharacterEditorView()
{
InitializeComponent();
}
}

0 comments on commit e6c02ad

Please sign in to comment.