mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-04-24 23:57:12 -05:00
Fix all 9 critical audit issues: data loss, legality, buttons, labels
Critical fixes: - Data loss: write back IsShiny/IsInfected/IsCured in PreparePKM - IV/EV changes now trigger UpdateLegality (12 handlers fixed) - EXP changes now trigger UpdateLegality - FriendshipLabel swaps to "Hatch Counter:" when IsEgg=true - IsEgg now refreshes sprite display - Shiny toggle (☆) and RerollPID buttons added to Main tab - IsNicknamed checkbox added to Nickname row - RTC Editor now supports Gen 2 saves (new SAVRTC2 ViewModel/View)
This commit is contained in:
parent
0ca0ea0404
commit
6652a8d337
|
|
@ -89,6 +89,11 @@ public partial class PKMEditorViewModel : ObservableObject
|
|||
[ObservableProperty] private bool _isEgg;
|
||||
[ObservableProperty] private bool _isNicknamed;
|
||||
|
||||
/// <summary>
|
||||
/// Returns "Hatch Counter:" when the PKM is an egg, otherwise "Friendship:".
|
||||
/// </summary>
|
||||
public string FriendshipLabel => IsEgg ? "Hatch Counter:" : "Friendship:";
|
||||
|
||||
// Pokerus
|
||||
[ObservableProperty] private bool _isInfected;
|
||||
[ObservableProperty] private bool _isCured;
|
||||
|
|
@ -429,6 +434,41 @@ private void RerollEc()
|
|||
EncryptionConstantHex = ec.ToString("X8");
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Shinytize()
|
||||
{
|
||||
if (Entity is null) return;
|
||||
if (Entity.IsShiny)
|
||||
{
|
||||
Entity.SetPIDGender(Entity.Gender);
|
||||
IsShiny = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity.SetShiny();
|
||||
IsShiny = true;
|
||||
}
|
||||
PidHex = Entity.PID.ToString("X8");
|
||||
IsShinyDisplay = Entity.IsShiny;
|
||||
IsSquareShiny = Entity.IsShiny && Entity.ShinyXor == 0;
|
||||
UpdateSprite();
|
||||
UpdateLegality();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void RerollPid()
|
||||
{
|
||||
if (Entity is null) return;
|
||||
var rng = new Random();
|
||||
Entity.PID = (uint)rng.Next();
|
||||
PidHex = Entity.PID.ToString("X8");
|
||||
IsShiny = Entity.IsShiny;
|
||||
IsShinyDisplay = Entity.IsShiny;
|
||||
IsSquareShiny = Entity.IsShiny && Entity.ShinyXor == 0;
|
||||
UpdateSprite();
|
||||
UpdateLegality();
|
||||
}
|
||||
|
||||
// Home Tracker
|
||||
[ObservableProperty] private string _homeTrackerHex = "0000000000000000";
|
||||
[ObservableProperty] private bool _hasHomeTracker;
|
||||
|
|
@ -1609,7 +1649,15 @@ public void PopulateFields(PKM pk)
|
|||
Entity.IsEgg = IsEgg;
|
||||
Entity.IsNicknamed = IsNicknamed;
|
||||
|
||||
// Shiny
|
||||
if (IsShiny && !Entity.IsShiny)
|
||||
Entity.SetShiny();
|
||||
else if (!IsShiny && Entity.IsShiny)
|
||||
Entity.SetPIDGender(Entity.Gender);
|
||||
|
||||
// Pokerus
|
||||
Entity.IsPokerusInfected = IsInfected;
|
||||
Entity.IsPokerusCured = IsCured;
|
||||
Entity.PokerusStrain = PkrsStrain;
|
||||
Entity.PokerusDays = PkrsDays;
|
||||
|
||||
|
|
@ -1721,6 +1769,7 @@ public void PopulateFields(PKM pk)
|
|||
{
|
||||
scSave.ShadowID = (ushort)ShadowId;
|
||||
scSave.Purification = Purification;
|
||||
// IsShadow is derived from ShadowID/Purification, no separate write needed
|
||||
}
|
||||
|
||||
// Gen-specific: Catch Rate (Gen 1)
|
||||
|
|
@ -1953,14 +2002,19 @@ private async Task ShowFullLegalityReport()
|
|||
try { Level = Experience.GetLevel(value, growth); }
|
||||
finally { _isPopulating = false; }
|
||||
RecalcStats();
|
||||
UpdateLegality();
|
||||
}
|
||||
|
||||
// --- IsEgg change triggers legality ---
|
||||
// --- IsEgg change triggers legality + sprite + label ---
|
||||
|
||||
partial void OnIsEggChanged(bool value)
|
||||
{
|
||||
if (!_isPopulating)
|
||||
{
|
||||
UpdateLegality();
|
||||
UpdateSprite();
|
||||
}
|
||||
OnPropertyChanged(nameof(FriendshipLabel));
|
||||
}
|
||||
|
||||
partial void OnIsShinyChanged(bool value)
|
||||
|
|
@ -1971,21 +2025,21 @@ private async Task ShowFullLegalityReport()
|
|||
|
||||
// --- IV changed handlers → recalc stats + legality ---
|
||||
|
||||
partial void OnIv_HPChanged(int value) { if (!_isPopulating) RecalcStats(); }
|
||||
partial void OnIv_ATKChanged(int value) { if (!_isPopulating) RecalcStats(); }
|
||||
partial void OnIv_DEFChanged(int value) { if (!_isPopulating) RecalcStats(); }
|
||||
partial void OnIv_SPAChanged(int value) { if (!_isPopulating) RecalcStats(); }
|
||||
partial void OnIv_SPDChanged(int value) { if (!_isPopulating) RecalcStats(); }
|
||||
partial void OnIv_SPEChanged(int value) { if (!_isPopulating) RecalcStats(); }
|
||||
partial void OnIv_HPChanged(int value) { if (!_isPopulating) { RecalcStats(); UpdateLegality(); } }
|
||||
partial void OnIv_ATKChanged(int value) { if (!_isPopulating) { RecalcStats(); UpdateLegality(); } }
|
||||
partial void OnIv_DEFChanged(int value) { if (!_isPopulating) { RecalcStats(); UpdateLegality(); } }
|
||||
partial void OnIv_SPAChanged(int value) { if (!_isPopulating) { RecalcStats(); UpdateLegality(); } }
|
||||
partial void OnIv_SPDChanged(int value) { if (!_isPopulating) { RecalcStats(); UpdateLegality(); } }
|
||||
partial void OnIv_SPEChanged(int value) { if (!_isPopulating) { RecalcStats(); UpdateLegality(); } }
|
||||
|
||||
// --- EV changed handlers → recalc stats ---
|
||||
// --- EV changed handlers → recalc stats + legality ---
|
||||
|
||||
partial void OnEv_HPChanged(int value) { if (!_isPopulating) RecalcStats(); }
|
||||
partial void OnEv_ATKChanged(int value) { if (!_isPopulating) RecalcStats(); }
|
||||
partial void OnEv_DEFChanged(int value) { if (!_isPopulating) RecalcStats(); }
|
||||
partial void OnEv_SPAChanged(int value) { if (!_isPopulating) RecalcStats(); }
|
||||
partial void OnEv_SPDChanged(int value) { if (!_isPopulating) RecalcStats(); }
|
||||
partial void OnEv_SPEChanged(int value) { if (!_isPopulating) RecalcStats(); }
|
||||
partial void OnEv_HPChanged(int value) { if (!_isPopulating) { RecalcStats(); UpdateLegality(); } }
|
||||
partial void OnEv_ATKChanged(int value) { if (!_isPopulating) { RecalcStats(); UpdateLegality(); } }
|
||||
partial void OnEv_DEFChanged(int value) { if (!_isPopulating) { RecalcStats(); UpdateLegality(); } }
|
||||
partial void OnEv_SPAChanged(int value) { if (!_isPopulating) { RecalcStats(); UpdateLegality(); } }
|
||||
partial void OnEv_SPDChanged(int value) { if (!_isPopulating) { RecalcStats(); UpdateLegality(); } }
|
||||
partial void OnEv_SPEChanged(int value) { if (!_isPopulating) { RecalcStats(); UpdateLegality(); } }
|
||||
|
||||
// --- Stat auto-recalculation ---
|
||||
|
||||
|
|
|
|||
|
|
@ -129,8 +129,10 @@ public static List<SAVToolDescriptor> GetAllTools()
|
|||
|
||||
// --- RTC ---
|
||||
new("RTC Editor",
|
||||
sav => sav is SAV3 { SmallBlock: ISaveBlock3SmallHoenn },
|
||||
sav => WithView<SAVRTC3ViewModel, SAVRTC3View>(new SAVRTC3ViewModel((SAV3)sav))),
|
||||
sav => (sav.Generation == 2 && sav is not SAV2Stadium) || sav is SAV3 { SmallBlock: ISaveBlock3SmallHoenn },
|
||||
sav => sav is SAV3 s3
|
||||
? WithView<SAVRTC3ViewModel, SAVRTC3View>(new SAVRTC3ViewModel(s3))
|
||||
: WithView<SAVRTC2ViewModel, SAVRTC2View>(new SAVRTC2ViewModel((SAV2)sav))),
|
||||
|
||||
// --- DLC (Gen 5) ---
|
||||
new("DLC Content",
|
||||
|
|
|
|||
35
PKHeX.Avalonia/ViewModels/Subforms/SAVRTC2ViewModel.cs
Normal file
35
PKHeX.Avalonia/ViewModels/Subforms/SAVRTC2ViewModel.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using PKHeX.Core;
|
||||
|
||||
namespace PKHeX.Avalonia.ViewModels.Subforms;
|
||||
|
||||
/// <summary>
|
||||
/// ViewModel for the Gen 2 Real-Time Clock editor.
|
||||
/// Gen 2 Crystal has an RTC flag that can be reset to force re-initialization.
|
||||
/// </summary>
|
||||
public partial class SAVRTC2ViewModel : SaveEditorViewModelBase
|
||||
{
|
||||
private readonly SAV2 _sav;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _statusText = "RTC is running normally. Use Reset to force clock re-initialization on next boot.";
|
||||
|
||||
public SAVRTC2ViewModel(SAV2 sav) : base(sav)
|
||||
{
|
||||
_sav = sav;
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void ResetRtc()
|
||||
{
|
||||
_sav.ResetRTC();
|
||||
StatusText = "RTC reset flag has been SET. The clock will be re-initialized on next boot.";
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Save()
|
||||
{
|
||||
Modified = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -60,7 +60,11 @@
|
|||
ColumnDefinitions="104,*">
|
||||
<!-- PID -->
|
||||
<TextBlock Grid.Row="0" Grid.Column="0" Text="PID:" TextAlignment="Right" VerticalAlignment="Center" Margin="0,0,6,0" />
|
||||
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding PidHex}" Height="25" Width="72" HorizontalAlignment="Left" FontFamily="Courier New,Consolas,monospace" />
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<TextBox Text="{Binding PidHex}" Height="25" Width="72" FontFamily="Courier New,Consolas,monospace" />
|
||||
<Button Content="☆" Command="{Binding ShinytizeCommand}" Width="24" Height="25" Padding="0" FontSize="14" Margin="4,0,0,0" ToolTip.Tip="Toggle Shiny" />
|
||||
<Button Content="Reroll" Command="{Binding RerollPidCommand}" Height="25" Padding="4,0" FontSize="9" Margin="2,0,0,0" />
|
||||
</StackPanel>
|
||||
|
||||
<!-- Species + Gender -->
|
||||
<TextBlock Grid.Row="1" Grid.Column="0" Text="Species:" TextAlignment="Right" VerticalAlignment="Center" Margin="0,0,6,0" />
|
||||
|
|
@ -74,7 +78,8 @@
|
|||
<!-- Nickname -->
|
||||
<TextBlock Grid.Row="2" Grid.Column="0" Text="Nickname:" TextAlignment="Right" VerticalAlignment="Center" Margin="0,0,6,0" />
|
||||
<StackPanel Grid.Row="2" Grid.Column="1" Orientation="Horizontal" VerticalAlignment="Center">
|
||||
<TextBox Text="{Binding Nickname}" Height="25" Width="144" />
|
||||
<CheckBox IsChecked="{Binding IsNicknamed}" Margin="0,0,4,0" ToolTip.Tip="Is Nicknamed" />
|
||||
<TextBox Text="{Binding Nickname}" Height="25" Width="130" />
|
||||
<Button Content="?" Command="{Binding ShowNicknameWarningCommand}"
|
||||
Width="20" Height="20" Padding="0" FontSize="10" Margin="2,0,0,0"
|
||||
ToolTip.Tip="Check nickname font compatibility" />
|
||||
|
|
@ -115,8 +120,8 @@
|
|||
<ComboBox Grid.Row="8" Grid.Column="1" ItemsSource="{Binding AbilityList}" SelectedItem="{Binding SelectedAbility}"
|
||||
DisplayMemberBinding="{Binding Text, DataType=core:ComboItem}" Height="25" Width="144" HorizontalAlignment="Left" />
|
||||
|
||||
<!-- Friendship -->
|
||||
<TextBlock Grid.Row="9" Grid.Column="0" Text="Friendship:" TextAlignment="Right" VerticalAlignment="Center" Margin="0,0,6,0" />
|
||||
<!-- Friendship / Hatch Counter -->
|
||||
<TextBlock Grid.Row="9" Grid.Column="0" Text="{Binding FriendshipLabel}" TextAlignment="Right" VerticalAlignment="Center" Margin="0,0,6,0" />
|
||||
<NumericUpDown Grid.Row="9" Grid.Column="1" Value="{Binding Friendship}" Minimum="0" Maximum="255" Height="25" Width="60" HorizontalAlignment="Left" />
|
||||
|
||||
<!-- Language -->
|
||||
|
|
|
|||
31
PKHeX.Avalonia/Views/Subforms/SAVRTC2View.axaml
Normal file
31
PKHeX.Avalonia/Views/Subforms/SAVRTC2View.axaml
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<views:SubformWindow xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:views="using:PKHeX.Avalonia.Views"
|
||||
xmlns:vm="using:PKHeX.Avalonia.ViewModels.Subforms"
|
||||
x:Class="PKHeX.Avalonia.Views.Subforms.SAVRTC2View"
|
||||
x:DataType="vm:SAVRTC2ViewModel"
|
||||
Title="RTC Editor (Gen 2)"
|
||||
Width="380" Height="200"
|
||||
MinWidth="300" MinHeight="160"
|
||||
FontFamily="Microsoft Sans Serif,Geneva,Helvetica,Arial,sans-serif"
|
||||
FontSize="11"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
CanResize="False">
|
||||
|
||||
<DockPanel Margin="8">
|
||||
<!-- Bottom buttons -->
|
||||
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal"
|
||||
HorizontalAlignment="Right" Spacing="8" Margin="0,8,0,0">
|
||||
<Button Content="Reset RTC" Command="{Binding ResetRtcCommand}" MinWidth="85" Padding="8,4"
|
||||
ToolTip.Tip="Set the RTC reset flag so the clock re-initializes on next boot" />
|
||||
<Button Content="OK" Click="OnOkClick" MinWidth="75" Padding="8,4" />
|
||||
<Button Content="Cancel" Click="OnCancelClick" MinWidth="75" Padding="8,4" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Spacing="8" VerticalAlignment="Center">
|
||||
<TextBlock Text="Gen 2 Crystal Real-Time Clock" FontWeight="SemiBold" FontSize="12" />
|
||||
<TextBlock Text="{Binding StatusText}" TextWrapping="Wrap" FontSize="11" />
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
|
||||
</views:SubformWindow>
|
||||
24
PKHeX.Avalonia/Views/Subforms/SAVRTC2View.axaml.cs
Normal file
24
PKHeX.Avalonia/Views/Subforms/SAVRTC2View.axaml.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
using Avalonia.Interactivity;
|
||||
using PKHeX.Avalonia.ViewModels.Subforms;
|
||||
|
||||
namespace PKHeX.Avalonia.Views.Subforms;
|
||||
|
||||
public partial class SAVRTC2View : SubformWindow
|
||||
{
|
||||
public SAVRTC2View()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void OnOkClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
if (DataContext is SAVRTC2ViewModel vm)
|
||||
vm.SaveCommand.Execute(null);
|
||||
CloseWithResult(true);
|
||||
}
|
||||
|
||||
private void OnCancelClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
CloseWithResult(false);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user