Feat/categorized editors (#264)

* Add support for categorized editors

* Rename functions

* Move utils to top

* Sort by category and add category tags

* Add all app_config editors

* Use client size, otherwise you'd have to enter the width/height of content + window border
This commit is contained in:
JelleInfinity
2022-10-02 21:23:51 +02:00
committed by GitHub
parent efb34f556b
commit 602e95a767
6 changed files with 804 additions and 340 deletions

View File

@@ -1,4 +1,4 @@
namespace pkNX.Game;
namespace pkNX.Game;
/// <summary>
/// Simple descriptor related to what purpose the game data serves.
@@ -197,32 +197,138 @@ public enum GameFile
/// <summary> All pokedex research tasks </summary>
DexResearch,
ThrowableParam,
ThrowParam,
ThrowableResourceSet,
ThrowableResource,
ThrowPermissionSet,
ThrowableParam,
ThrowableResource,
ThrowableResourceSet,
/// <summary> Gives bonus rolls based on met thresholds. </summary>
ShinyRolls,
WormholeConfig,
CaptureConfig,
BattleLogicConfig,
PlayerConfig,
EventFarmConfig,
FieldLandmarkConfig,
BattleViewConfig,
AICommonConfig,
FieldSpawnerConfig,
OutbreakConfig,
EvolutionConfig,
BallThrowConfig,
SizeScaleConfig,
AppConfigList,
HaShop,
NewHugeGroup,
NewHugeGroupLottery,
NewHugeLottery,
NewHugeTimeLimit,
}
AICommonConfig,
AIExcitingConfig,
AIFieldWazaConfig,
AISemiLegendConfig,
AITirednessConfig,
AppConfigList,
AppliHudConfig,
AppliStaffrollConfig,
AppliTipsConfig,
BattleCommonConfig,
BattleEndConfig,
BattleInConfig,
BattleLogicConfig,
BattleStartConfig,
BattleViewConfig,
BattleVsnsConfig,
BuddyBattleConfig,
BuddyConfig,
BuddyDirectItemConfig,
BuddyGroupTalkConfig,
BuddyLandmarkConfig,
BuddyNPCReactionConfig,
BuddyPlayerModeConfig,
BuddyWarpConfig,
CharacterBipedIkConfig,
CharacterBlinkConfig,
CharacterControllerConfig,
CharacterLookAtConfig,
CaptureConfig,
CommonGeneralConfig,
CommonItemConfig,
DemoConfig,
EnvPokeVoiceConfig,
EventBalloonrunConfig,
EventBalloonthrowConfig,
EventBanditConfig,
EventCullingConfig,
EventDitherConfig,
EventFarmConfig,
EventGameOverConfig,
EventItemConfig,
EventMkrgRewardConfig,
EventQuestBoardConfig,
EventRestrictionBattle,
EventWork,
FieldAnimationFramerateConfig,
FieldAreaSpeedConfig,
FieldCameraConfig,
FieldCaptureDirectorConfig,
FieldCharaViewerConfig,
FieldCommonConfig,
FieldDirectItemConfig,
FieldEnvConfig,
FieldItem,
FieldItemRespawn,
FieldLandmarkInciteConfig,
FieldLockonConfig,
FieldMyPokeBallHitNoneTargetConfig,
FieldObstructionWazaConfig,
FieldPokemonSlopeConfig,
FieldQuestDestinationConfig,
FieldShadowConfig,
FieldThrowConfig,
FieldThrowableAfterHitConfig,
FieldVigilanceBgmConfig,
FieldWeatheringConfig,
FieldWildPokemonConfig,
NPCAIConfig,
NPCControllerConfig,
NPCCreaterConfig,
NPCPokemonAIConfig,
NPCPopupConfig,
NPCTalkTableConfig,
PlayerCameraShakeConfig,
PlayerCollisionConfig,
PlayerConfig,
PlayerControllerConfig,
PlayerFaceConfig,
PokemonConfig,
PokemonControllerConfig,
EvolutionConfig,
PokemonFriendshipConfig,
/// <summary> Gives bonus rolls based on met thresholds. </summary>
ShinyRolls,
SizeScaleConfig,
RideBasuraoCollisionConfig,
RideBasuraoConfig,
RideChangeConfig,
RideCommonConfig,
RideNyuuraCollisionConfig,
RideNyuuraConfig,
RideNyuuraControllerConfig,
RideOdoshishiCollisionConfig,
RideOdoshishiConfig,
RideRingumaCollisionConfig,
RideRingumaConfig,
RideRingumaControllerConfig,
RideWhooguruCollisionConfig,
RideWhooguruConfig,
RideWhooguruControllerConfig,
SoundConfig,
WaterMotion,
}

View File

@@ -38,7 +38,7 @@ internal IFileContainer GetFile(GameFile file, int language)
Cache.Add(file, container);
return container;
}
internal void SaveAll()
{
foreach (var container in Cache)
@@ -232,36 +232,126 @@ internal void SaveAll()
new(GameFile.PokeMisc, ContainerType.SingleFile, "bin", "pokemon", "data", "poke_misc.bin"),
new(GameFile.Outbreak, ContainerType.SingleFile, "bin", "field", "encount", "huge_outbreak.bin"),
new(GameFile.MoveShop, ContainerType.SingleFile, "bin", "appli", "wazaremember", "bin", "wazashop_table.bin"),
new(GameFile.ThrowableParam, ContainerType.SingleFile, "bin", "capture", "throwable_param_table.bin"),
new(GameFile.ThrowParam, ContainerType.SingleFile, "bin", "capture", "throw_param_table.bin"),
new(GameFile.ThrowableResourceSet, ContainerType.SingleFile, "bin", "capture", "throwable_resourceset_dictionary.bin"),
new(GameFile.ThrowableResource, ContainerType.SingleFile, "bin", "capture", "throwable_resource_dictionary.bin"),
new(GameFile.ThrowPermissionSet, ContainerType.SingleFile, "bin", "capture", "throw_permissionset_dictionary.bin"),
new(GameFile.HaShop, ContainerType.SingleFile, "bin", "appli", "shop", "bin", "ha_shop_data.bin"),
new(GameFile.ShinyRolls, ContainerType.SingleFile, "bin", "misc", "app_config", "pokemon_rare.bin"),
new(GameFile.WormholeConfig, ContainerType.SingleFile, "bin", "misc", "app_config", "field_wormhole_config.bin"),
new(GameFile.CaptureConfig, ContainerType.SingleFile, "bin", "misc", "app_config", "common_capture_config.bin"),
new(GameFile.BattleLogicConfig, ContainerType.SingleFile, "bin", "misc", "app_config", "battle_logic_config.bin"),
new(GameFile.PlayerConfig, ContainerType.SingleFile, "bin", "misc", "app_config", "player_config.bin"),
new(GameFile.EventFarmConfig, ContainerType.SingleFile, "bin", "misc", "app_config", "event_farm_config.bin"),
new(GameFile.AppConfigList, ContainerType.SingleFile, "bin", "misc", "app_config", "app_config_list.bin"),
new(GameFile.FieldLandmarkConfig, ContainerType.SingleFile, "bin", "misc", "app_config", "field_landmark_config.bin"),
new(GameFile.BattleViewConfig, ContainerType.SingleFile, "bin", "misc", "app_config", "battle_view_config.bin"),
new(GameFile.AICommonConfig, ContainerType.SingleFile, "bin", "misc", "app_config", "ai_common_config.bin"),
new(GameFile.FieldSpawnerConfig, ContainerType.SingleFile, "bin", "misc", "app_config", "field_spawner_config.bin"),
new(GameFile.OutbreakConfig, ContainerType.SingleFile, "bin", "misc", "app_config", "field_huge_outbreak.bin"),
new(GameFile.EvolutionConfig, ContainerType.SingleFile, "bin", "misc", "app_config", "pokemon_evolution_config.bin"),
new(GameFile.BallThrowConfig, ContainerType.SingleFile, "bin", "misc", "app_config", "field_my_poke_ball_config.bin"),
new(GameFile.SizeScaleConfig, ContainerType.SingleFile, "bin", "misc", "app_config", "pokemon_size_category_adjust_scale_config.bin"),
new(GameFile.SymbolBehave, ContainerType.SingleFile, "bin", "pokemon", "data", "poke_ai.bin"),
new(GameFile.ThrowPermissionSet , ContainerType.SingleFile, "bin", "capture", "throw_permissionset_dictionary.bin"),
new(GameFile.ThrowableParam , ContainerType.SingleFile, "bin", "capture", "throwable_param_table.bin"),
new(GameFile.ThrowableResource , ContainerType.SingleFile, "bin", "capture", "throwable_resource_dictionary.bin"),
new(GameFile.ThrowableResourceSet , ContainerType.SingleFile, "bin", "capture", "throwable_resourceset_dictionary.bin"),
new(GameFile.NewHugeGroup, ContainerType.SingleFile, "bin", "field", "encount", "new_huge_outbreak_group.bin"),
new(GameFile.NewHugeGroupLottery, ContainerType.SingleFile, "bin", "field", "encount", "new_huge_outbreak_group_lottery.bin"),
new(GameFile.NewHugeLottery, ContainerType.SingleFile, "bin", "field", "encount", "new_huge_outbreak_lottery.bin"),
new(GameFile.NewHugeTimeLimit, ContainerType.SingleFile, "bin", "field", "encount", "new_huge_outbreak_time_limit.bin"),
new(GameFile.SymbolBehave , ContainerType.SingleFile, "bin", "pokemon", "data", "poke_ai.bin"),
new(GameFile.AICommonConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ai_common_config.bin"),
new(GameFile.AIExcitingConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ai_exciting_config.bin"),
new(GameFile.AIFieldWazaConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ai_field_waza_config.bin"),
new(GameFile.AISemiLegendConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ai_semi_legend_config.bin"),
new(GameFile.AITirednessConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ai_tiredness_config.bin"),
new(GameFile.AppConfigList , ContainerType.SingleFile, "bin", "misc", "app_config", "app_config_list.bin"),
new(GameFile.AppliHudConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "appli_hud_config.bin"),
new(GameFile.AppliStaffrollConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "appli_staffroll_config.bin"),
new(GameFile.AppliTipsConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "appli_tips_config.bin"),
new(GameFile.BattleCommonConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "battle_common_config.bin"),
new(GameFile.BattleEndConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "battle_end_config.bin"),
new(GameFile.BattleInConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "battle_in_config.bin"),
new(GameFile.BattleLogicConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "battle_logic_config.bin"),
new(GameFile.BattleStartConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "battle_start_config.bin"),
new(GameFile.BattleViewConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "battle_view_config.bin"),
new(GameFile.BattleVsnsConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "battle_vsns_config.bin"),
new(GameFile.BuddyBattleConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "buddy_battle_config.bin"),
new(GameFile.BuddyConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "buddy_config.bin"),
new(GameFile.BuddyDirectItemConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "buddy_direct_item_config.bin"),
new(GameFile.BuddyGroupTalkConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "buddy_group_talk_config.bin"),
new(GameFile.BuddyLandmarkConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "buddy_landmark_config.bin"),
new(GameFile.BuddyNPCReactionConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "buddy_npc_reaction_config.bin"),
new(GameFile.BuddyPlayerModeConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "buddy_player_mode_config.bin"),
new(GameFile.BuddyWarpConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "buddy_warp_config.bin"),
new(GameFile.CharacterBipedIkConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "character_biped_ik_config.bin"),
new(GameFile.CharacterBlinkConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "character_blink_config.bin"),
new(GameFile.CharacterControllerConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "character_controller_config.bin"),
new(GameFile.CharacterLookAtConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "character_look_at_config.bin"),
new(GameFile.CaptureConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "common_capture_config.bin"),
new(GameFile.CommonGeneralConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "common_general_config.bin"),
new(GameFile.CommonItemConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "common_item_config.bin"),
new(GameFile.DemoConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "demo_config.bin"),
new(GameFile.EnvPokeVoiceConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "env_poke_voice_config.bin"),
new(GameFile.EventBalloonrunConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "event_balloonrun_config.bin"),
new(GameFile.EventBalloonthrowConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "event_balloonthrow_config.bin"),
new(GameFile.EventBanditConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "event_bandit_config.bin"),
new(GameFile.EventCullingConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "event_culling_config.bin"),
new(GameFile.EventDitherConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "event_dither_config.bin"),
new(GameFile.EventFarmConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "event_farm_config.bin"),
new(GameFile.EventGameOverConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "event_game_over_config.bin"),
new(GameFile.EventItemConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "event_item_config.bin"),
new(GameFile.EventMkrgRewardConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "event_mkrg_reward_config.bin"),
new(GameFile.EventQuestBoardConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "event_quest_board_config.bin"),
new(GameFile.EventRestrictionBattle , ContainerType.SingleFile, "bin", "misc", "app_config", "event_restriction_battle.bin"),
new(GameFile.EventWork , ContainerType.SingleFile, "bin", "misc", "app_config", "event_work.bin"),
new(GameFile.FieldAnimationFramerateConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_anime_framerate_config.bin"),
new(GameFile.FieldAreaSpeedConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_area_speed_config.bin"),
new(GameFile.FieldCameraConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_camera_config.bin"),
new(GameFile.FieldCaptureDirectorConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_capture_director_config.bin"),
new(GameFile.FieldCharaViewerConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_chara_viewer_config.bin"),
new(GameFile.FieldCommonConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_common_config.bin"),
new(GameFile.FieldDirectItemConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_direct_item_config.bin"),
new(GameFile.FieldEnvConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_env_config.bin"),
new(GameFile.OutbreakConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_huge_outbreak.bin"),
new(GameFile.FieldItem , ContainerType.SingleFile, "bin", "misc", "app_config", "field_item.bin"),
new(GameFile.FieldItemRespawn , ContainerType.SingleFile, "bin", "misc", "app_config", "field_item_respawn.bin"),
new(GameFile.FieldLandmarkConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_landmark_config.bin"),
new(GameFile.FieldLandmarkInciteConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_landmark_incite_config.bin"),
new(GameFile.FieldLockonConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_lockon_config.bin"),
new(GameFile.BallThrowConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_my_poke_ball_config.bin"),
new(GameFile.FieldMyPokeBallHitNoneTargetConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_my_poke_ball_hit_none_target_config.bin"),
new(GameFile.FieldObstructionWazaConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_obstruction_waza_config.bin"),
new(GameFile.FieldPokemonSlopeConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_pokemon_slope_config.bin"),
new(GameFile.FieldQuestDestinationConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_quest_destination_config.bin"),
new(GameFile.FieldShadowConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_shadow_config.bin"),
new(GameFile.FieldSpawnerConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_spawner_config.bin"),
new(GameFile.FieldThrowConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_throw_config.bin"),
new(GameFile.FieldThrowableAfterHitConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_throwable_after_hit_config.bin"),
new(GameFile.FieldVigilanceBgmConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_vigilance_bgm_config.bin"),
new(GameFile.FieldWeatheringConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_weathering_config.bin"),
new(GameFile.FieldWildPokemonConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_wild_pokemon_config.bin"),
new(GameFile.WormholeConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "field_wormhole_config.bin"),
new(GameFile.NPCAIConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "npc_ai_config.bin"),
new(GameFile.NPCControllerConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "npc_controller_config.bin"),
new(GameFile.NPCCreaterConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "npc_creater_config.bin"),
new(GameFile.NPCPokemonAIConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "npc_pokemon_ai_config.bin"),
new(GameFile.NPCPopupConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "npc_popup_config.bin"),
new(GameFile.NPCTalkTableConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "npc_talk_table_config.bin"),
new(GameFile.PlayerCameraShakeConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "player_camera_shake_config.bin"),
new(GameFile.PlayerCollisionConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "player_collision_config.bin"),
new(GameFile.PlayerConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "player_config.bin"),
new(GameFile.PlayerControllerConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "player_controller_config.bin"),
new(GameFile.PlayerFaceConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "player_face_config.bin"),
new(GameFile.PokemonConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "pokemon_config.bin"),
new(GameFile.PokemonControllerConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "pokemon_controller_config.bin"),
new(GameFile.EvolutionConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "pokemon_evolution_config.bin"),
new(GameFile.PokemonFriendshipConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "pokemon_friendship_config.bin"),
new(GameFile.ShinyRolls , ContainerType.SingleFile, "bin", "misc", "app_config", "pokemon_rare.bin"),
new(GameFile.SizeScaleConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "pokemon_size_category_adjust_scale_config.bin"),
new(GameFile.RideBasuraoCollisionConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_basurao_collision_config.bin"),
new(GameFile.RideBasuraoConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_basurao_config.bin"),
new(GameFile.RideChangeConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_change_config.bin"),
new(GameFile.RideCommonConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_common_config.bin"),
new(GameFile.RideNyuuraCollisionConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_nyuura_collision_config.bin"),
new(GameFile.RideNyuuraConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_nyuura_config.bin"),
new(GameFile.RideNyuuraControllerConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_nyuura_controller_config.bin"),
new(GameFile.RideOdoshishiCollisionConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_odoshishi_collision_config.bin"),
new(GameFile.RideOdoshishiConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_odoshishi_config.bin"),
new(GameFile.RideRingumaCollisionConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_ringuma_collision_config.bin"),
new(GameFile.RideRingumaConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_ringuma_config.bin"),
new(GameFile.RideRingumaControllerConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_ringuma_controller_config.bin"),
new(GameFile.RideWhooguruCollisionConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_whooguru_collision_config.bin"),
new(GameFile.RideWhooguruConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_whooguru_config.bin"),
new(GameFile.RideWhooguruControllerConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "ride_whooguru_controller_config.bin"),
new(GameFile.SoundConfig , ContainerType.SingleFile, "bin", "misc", "app_config", "sound_config.bin"),
new(GameFile.WaterMotion , ContainerType.SingleFile, "bin", "misc", "app_config", "water_motion.bin"),
new(GameFile.NewHugeGroup , ContainerType.SingleFile, "bin", "field", "encount", "new_huge_outbreak_group.bin"),
new(GameFile.NewHugeGroupLottery , ContainerType.SingleFile, "bin", "field", "encount", "new_huge_outbreak_group_lottery.bin"),
new(GameFile.NewHugeLottery , ContainerType.SingleFile, "bin", "field", "encount", "new_huge_outbreak_lottery.bin"),
new(GameFile.NewHugeTimeLimit , ContainerType.SingleFile, "bin", "field", "encount", "new_huge_outbreak_time_limit.bin"),
// Cutscenes bin\demo
// Models bin\archive\pokemon
// pretty much everything is obviously named :)

View File

@@ -11,6 +11,10 @@ namespace pkNX.WinForms;
public partial class Main : Form
{
private const int ButtonWidth = 120;
private const int ButtonHeight = 40;
private const int ButtonPadding = 3;
public static readonly string ProgramSettingsPath = Path.Combine(Application.StartupPath, "settings.json");
public ProgramSettings Settings { get; }
@@ -173,19 +177,66 @@ private void OpenFolder(string path, GameVersion gameOverride)
}
}
private const int ButtonWidth = 120;
private const int ButtonHeight = 40;
private const int ButtonPadding = 3;
private void LoadEditorButtons(EditorCategory category = EditorCategory.None)
{
FLP_Controls.SuspendLayout();
FLP_Controls.Controls.Clear();
if (category == EditorCategory.None)
{
foreach (var c in (EditorCategory[])Enum.GetValues(typeof(EditorCategory)))
{
if (c == EditorCategory.None)
continue;
if (Editor!.CountControlsForCategory(c) == 0)
continue;
FLP_Controls.Controls.Add(CreateCategoryButton(c));
}
}
else
{
// Create back button
FLP_Controls.Controls.Add(CreateCategoryButton(EditorCategory.None));
}
AddEditorButtonsForCategory(category);
FLP_Controls.ResumeLayout();
}
private void AddEditorButtonsForCategory(EditorCategory category)
{
var ctrls = Editor!.GetControls(ButtonWidth, ButtonHeight, category).OrderBy(x => x.Text);
foreach (var ctrl in ctrls)
FLP_Controls.Controls.Add(ctrl);
}
public Button CreateCategoryButton(EditorCategory category)
{
var b = new Button
{
Width = ButtonWidth,
Height = ButtonHeight,
Name = $"B_OpenCategory{category}",
Text = ((category == EditorCategory.None) ? "Back" : $"Show {category} Editors"),
};
b.Click += (s, e) =>
{
LoadEditorButtons(category);
};
return b;
}
private void LoadROM(EditorBase editor)
{
Editor = editor;
var ctrl = Editor.GetControls(ButtonWidth, ButtonHeight).OrderBy(x => x.Text);
FLP_Controls.Controls.Clear();
foreach (var c in ctrl)
FLP_Controls.Controls.Add(c);
const int wp = ButtonWidth + (2 * ButtonPadding) + 3;
LoadEditorButtons();
const int wp = ButtonWidth + (2 * ButtonPadding);
const int hp = ButtonHeight + (2 * ButtonPadding);
const int area = wp * hp;
var count = FLP_Controls.Controls.Count;
@@ -194,8 +245,8 @@ private void LoadROM(EditorBase editor)
var squareSide = Math.Sqrt(totalArea);
var columns = (int)Math.Ceiling(squareSide / wp) + 1;
var rows = (count / columns) + 2;
Width = (columns * wp) + 6;
Height = FLP_Controls.Location.Y + (rows * hp) + 6;
ClientSize = new(columns * wp, FLP_Controls.Location.Y + (rows * hp));
CenterToScreen();
Text = $"{nameof(pkNX)} - {Editor.Game}";

View File

@@ -0,0 +1,44 @@
using System;
namespace pkNX.WinForms;
public enum EditorCategory
{
None,
Pokemon,
Battle,
Field,
Shops,
Dialog,
Graphics,
Misc,
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class EditorCallableAttribute : Attribute
{
public EditorCategory Category { get; set; }
public string EditorName { get; set; }
/// <summary>
/// Add this attribute to customize the button that will be displayed in the main editor
/// </summary>
/// <param name="category">The category of this editor. Add this to hide it under a sub editor button</param>
/// <param name="editorName">The name that should be displayed on the editor button. Leave empty for automatic conversion from the method name.</param>
public EditorCallableAttribute(EditorCategory category = EditorCategory.Misc, string editorName = "")
{
Category = category;
EditorName = editorName;
}
public bool HasCustomEditorName()
{
return !string.IsNullOrWhiteSpace(EditorName);
}
public bool HasCategory()
{
return Category != EditorCategory.None;
}
}

View File

@@ -32,79 +32,6 @@ private static void CheckOodleDllPresence()
WinFormsUtil.Alert($"{file} not found in the executable folder", "Some decompression functions may cause errors.");
}
public void EditCommon()
{
var text = ROM.GetFilteredFolder(GameFile.GameText, z => Path.GetExtension(z) == ".dat");
var config = new TextConfig(ROM.Game);
var tc = new TextContainer(text, config);
using var form = new TextEditor(tc, TextEditor.TextEditorMode.Common);
form.ShowDialog();
if (!form.Modified)
text.CancelEdits();
}
public void EditScript()
{
var text = ROM.GetFilteredFolder(GameFile.StoryText, z => Path.GetExtension(z) == ".dat");
var config = new TextConfig(ROM.Game);
var tc = new TextContainer(text, config);
using var form = new TextEditor(tc, TextEditor.TextEditorMode.Script);
form.ShowDialog();
if (!form.Modified)
text.CancelEdits();
}
public void EditTrainers()
{
var folder = ROM.GetFilteredFolder(GameFile.TrainerData);
var cache = new DataCache<TrData8a>(folder)
{
Create = FlatBufferConverter.DeserializeFrom<TrData8a>,
Write = FlatBufferConverter.SerializeFrom,
};
var names = folder.GetPaths().Select(Path.GetFileNameWithoutExtension).OfType<string>().ToArray();
using var form = new GenericEditor<TrData8a>(cache, names, "Trainers", Randomize, canSave: true);
form.ShowDialog();
void Randomize()
{
var settings = EditUtil.Settings.Species;
var rand = new SpeciesRandomizer(ROM.Info, Data.PersonalData);
rand.Initialize(settings, GetSpeciesBanlist());
for (int i = 0; i < cache.Length; i++)
{
foreach (var t in cache[i].Team)
{
t.Species = rand.GetRandomSpecies(t.Species);
t.Form = GetRandomForm(t.Species);
t.Gender = (int)FixedGender.Random;
t.Nature = NatureType8a.Random;
t.Move_01.Move = t.Move_02.Move = t.Move_03.Move = t.Move_04.Move = 0;
t.Move_01.Mastered = t.Move_02.Mastered = t.Move_03.Mastered = t.Move_04.Mastered = true;
t.Shiny = Randomization.Util.Random.Next(0, 100 + 1) < 3;
t.IsOybn = Randomization.Util.Random.Next(0, 100 + 1) < 3;
}
}
}
if (!form.Modified)
cache.CancelEdits();
else
cache.Save();
}
public void EditMoveShop()
{
var names = ROM.GetStrings(TextName.MoveNames);
var data = ROM.GetFile(GameFile.MoveShop);
var obj = FlatBufferConverter.DeserializeFrom<MoveShopTable8a>(data[0]);
var result = PopFlat(obj.Table, "Move Shop Editor", z => names[z.Move]);
if (!result)
return;
data[0] = FlatBufferConverter.SerializeFrom(obj);
}
public void PopFlat<T1, T2>(GameFile file, string title, Func<T2, string> getName, Action? rand = null, bool canSave = true) where T1 : class, IFlatBufferArchive<T2> where T2 : class
{
var obj = ROM.GetFile(file);
@@ -125,60 +52,149 @@ public void EditMoveShop()
return form.Modified;
}
public void EditThrowable_Param()
public void PopFlatConfig(GameFile file, string title)
{
var itemNames = ROM.GetStrings(TextName.ItemNames);
PopFlat<ThrowableParamTable8a, ThrowableParam8a>(GameFile.ThrowableParam, "Throwable Param Editor", z => itemNames[z.ItemID]);
}
public void EditThrow_Param()
{
PopFlat<ThrowParamTable8a, ThrowParam8a>(GameFile.ThrowParam, "Throw Param Editor", z => z.Hash.ToString("X16"));
}
public void EditThrow_ResourceSet_Dictionary()
{
PopFlat<ThrowableResourceSetDictionary8a, ThrowableResourceSetEntry8a>(GameFile.ThrowableResourceSet, "Throwable ResourceSet Dictionary Editor", z => z.Hash_00.ToString("X16"));
}
public void EditThrow_Resource_Dictionary()
{
PopFlat<ThrowableResourceDictionary8a, ThrowableResourceEntry8a>(GameFile.ThrowableResource, "Throwable Resource Dictionary Editor", z => z.Hash_00.ToString("X16"));
}
public void EditThrow_PermissionSet_Param()
{
PopFlat<ThrowPermissionSetDictionary8a, ThrowPermissionSetEntry8a>(GameFile.ThrowPermissionSet, "Throw Permission Editor", z => z.Hash_00.ToString("X16"));
}
public void EditHa_Shop_Data()
{
var names = ROM.GetStrings(TextName.ItemNames);
var data = ROM.GetFile(GameFile.HaShop);
var obj = FlatBufferConverter.DeserializeFrom<HaShopTable8a>(data[0]);
var result = PopFlat(obj.Table, "ha_shop_data Editor", z => names[z.ItemID], Randomize);
if (!result)
var obj = ROM.GetFile(file); // flatbuffer
var data = obj[0];
var root = FlatBufferConverter.DeserializeFrom<ConfigureTable8a>(data);
var cache = new DataCache<Configure8aEntry>(root.Table);
var names = root.Table.Select(z => z.Name).ToArray();
using var form = new GenericEditor<Configure8aEntry>(cache, names, title);
form.ShowDialog();
if (!form.Modified)
return;
obj[0] = FlatBufferConverter.SerializeFrom(root);
}
void Randomize()
public int[] GetSpeciesBanlist()
{
var pt = Data.PersonalData;
var hasForm = new HashSet<int>();
var banned = new HashSet<int>();
foreach (var pi in pt.Table.Cast<IPersonalMisc_1>())
{
foreach (var t in obj.Table)
if (pi.IsPresentInGame)
{
if (Legal.Pouch_Recipe_LA.Contains((ushort)t.ItemID)) // preserve recipes
continue;
t.ItemID = Legal.Pouch_Items_LA[Randomization.Util.Random.Next(Legal.Pouch_Items_LA.Length)];
banned.Remove(pi.ModelID);
hasForm.Add(pi.ModelID);
}
else if (!hasForm.Contains(pi.ModelID))
{
banned.Add(pi.ModelID);
}
}
data[0] = FlatBufferConverter.SerializeFrom(obj);
return banned.ToArray();
}
public void EditApp_Config_List()
public int GetRandomForm(int spec)
{
PopFlat<AppConfigList8a, AppconfigEntry8a>(GameFile.AppConfigList, "App Config List", z => z.OriginalPath);
var pt = Data.PersonalData;
var formRand = pt.Table.Cast<IPersonalMisc_1>()
.Where(z => z.IsPresentInGame && !(Legal.BattleExclusiveForms.Contains(z.ModelID) || Legal.BattleFusions.Contains(z.ModelID)))
.GroupBy(z => z.ModelID)
.ToDictionary(z => z.Key, z => z.ToList());
if (!formRand.TryGetValue((ushort)spec, out var entries))
return 0;
var count = entries.Count;
return (Species)spec switch
{
Growlithe or Arcanine or Voltorb or Electrode or Typhlosion or Qwilfish or Samurott or Lilligant or Zorua or Zoroark or Braviary or Sliggoo or Goodra or Avalugg or Decidueye => 1,
Basculin => 2,
Kleavor => 0,
_ => Randomization.Util.Random.Next(0, count),
};
}
public void EditArea_Weather()
#region Dialog Editors
[EditorCallable(EditorCategory.Dialog)]
public void EditCommon()
{
var text = ROM.GetFilteredFolder(GameFile.GameText, z => Path.GetExtension(z) == ".dat");
var config = new TextConfig(ROM.Game);
var tc = new TextContainer(text, config);
using var form = new TextEditor(tc, TextEditor.TextEditorMode.Common);
form.ShowDialog();
if (!form.Modified)
text.CancelEdits();
}
[EditorCallable(EditorCategory.Dialog)]
public void EditScript()
{
var text = ROM.GetFilteredFolder(GameFile.StoryText, z => Path.GetExtension(z) == ".dat");
var config = new TextConfig(ROM.Game);
var tc = new TextContainer(text, config);
using var form = new TextEditor(tc, TextEditor.TextEditorMode.Script);
form.ShowDialog();
if (!form.Modified)
text.CancelEdits();
}
#endregion
#region Pokemon Editors
[EditorCallable(EditorCategory.Pokemon)]
public void EditPokemon()
{
var editor = new PokeEditor8a
{
Personal = Data.PersonalData,
PokeMisc = Data.PokeMiscData,
Evolve = Data.EvolutionData,
Learn = Data.LevelUpData,
FieldDropTables = Data.FieldDrops,
BattleDropTabels = Data.BattleDrops,
DexResearch = Data.DexResearch
};
using var form = new PokeDataUI8a(editor, ROM, Data);
form.ShowDialog();
if (!form.Modified)
editor.CancelEdits();
else
editor.Save();
}
[EditorCallable(EditorCategory.Pokemon)]
public void EditMiscSpeciesInfo()
{
var names = ROM.GetStrings(TextName.SpeciesNames);
PopFlat<PokeMiscTable8a, PokeMisc8a>(GameFile.PokeMisc, "Misc Species Info Editor", z => $"{names[z.Species]}{(z.Form == 0 ? "" : $"-{z.Form}")} ~ {z.Value}");
}
[EditorCallable(EditorCategory.Pokemon)]
public void EditSymbolBehave()
{
var names = ROM.GetStrings(TextName.SpeciesNames);
PopFlat<PokeAIArchive8a, PokeAI8a>(GameFile.SymbolBehave, "Symbol Behavior Editor", z => $"{names[z.Species]}{(z.Form != 0 ? $"-{z.Form}" : "")}");
}
[EditorCallable(EditorCategory.Pokemon)]
public void EditEvolutions()
{
var names = ROM.GetStrings(TextName.SpeciesNames);
PopFlat<EvolutionTable8, EvolutionSet8a>(GameFile.Evolutions, "Evolution Editor",
z => $"{names[z.Species]}{(z.Form != 0 ? $"-{z.Form}" : "")}");
}
[EditorCallable(EditorCategory.Pokemon)]
public void EditLearnsetRaw()
{
var names = ROM.GetStrings(TextName.SpeciesNames);
PopFlat<Learnset8a, Learnset8aMeta>(GameFile.Learnsets, "Learnset Editor (Raw)", z => $"{names[z.Species]}{(z.Form == 0 ? "" : $"-{z.Form}")}");
}
[EditorCallable(EditorCategory.Pokemon)]
public void EditPersonalRaw()
{
var names = ROM.GetStrings(TextName.SpeciesNames);
PopFlat<PersonalTableLAfb, PersonalInfoLAfb>(GameFile.PersonalStats, "Personal Info Editor (Raw)", z => $"{names[z.Species]}{(z.Form == 0 ? "" : $"-{z.Form}")}");
}
#endregion
#region Field Editors
[EditorCallable(EditorCategory.Field)]
public void EditAreaWeather()
{
var gfp = (GFPack)ROM.GetFile(GameFile.Resident);
var data = gfp[2065];
@@ -189,7 +205,8 @@ public void EditArea_Weather()
gfp[2065] = FlatBufferConverter.SerializeFrom(obj);
}
public void EditStatic()
[EditorCallable(EditorCategory.Field)]
public void EditStaticEncounter()
{
var names = ROM.GetStrings(TextName.SpeciesNames);
var obj = ROM.GetFile(GameFile.EncounterStatic);
@@ -241,6 +258,204 @@ void Randomize(IEnumerable<EventEncount8a> arr)
}
}
[EditorCallable(EditorCategory.Field)]
public void EditMapViewer()
{
var resident = (GFPack)ROM.GetFile(GameFile.Resident);
using var form = new MapViewer8a((GameManagerPLA)ROM, resident);
form.ShowDialog();
}
[EditorCallable(EditorCategory.Field)]
public void EditAreas()
{
using var form = new AreaEditor8a((GameManagerPLA)ROM);
form.ShowDialog();
}
[EditorCallable(EditorCategory.Field)]
public void EditShinyRate() => PopFlatConfig(GameFile.ShinyRolls, "Shiny Rate Editor");
[EditorCallable(EditorCategory.Field)]
public void EditWormholeRate() => PopFlatConfig(GameFile.WormholeConfig, "Wormhole Config Editor");
[EditorCallable(EditorCategory.Field)]
public void EditOutbreakConfig() => PopFlatConfig(GameFile.OutbreakConfig, "Outbreak Configuration Editor");
[EditorCallable(EditorCategory.Field)]
public void EditOutbreakDetail()
=> PopFlat<MassOutbreakTable8a, MassOutbreak8a>(GameFile.Outbreak, "Outbreak Proc Editor", z => z.WorkValueName);
[EditorCallable(EditorCategory.Field)]
public void EditNewOutbreakGroup()
=> PopFlat<NewHugeOutbreakGroupArchive8a, NewHugeOutbreakGroup8a>(GameFile.NewHugeGroup, "New Outbreak Group Editor", z => z.Group.ToString("X16"));
[EditorCallable(EditorCategory.Field)]
public void EditNewOutbreakGroupLottery()
=> PopFlat<NewHugeOutbreakGroupLotteryArchive8a, NewHugeOutbreakGroupLottery8a>(GameFile.NewHugeGroupLottery, "New Outbreak Group Lottery Editor", z => z.LotteryGroup.ToString("X16"));
[EditorCallable(EditorCategory.Field)]
public void EditNewOutbreakLottery()
=> PopFlat<NewHugeOutbreakLotteryArchive8a, NewHugeOutbreakLottery8a>(GameFile.NewHugeLottery, "New Outbreak Lottery Editor", z => z.LotteryGroupString);
[EditorCallable(EditorCategory.Field)]
public void EditNewOutbreakTimeLimit()
=> PopFlat<NewHugeOutbreakTimeLimitArchive8a, NewHugeOutbreakTimeLimit8a>(GameFile.NewHugeTimeLimit, "New Outbreak Time Limit Editor", z => z.Duration.ToString());
[EditorCallable(EditorCategory.Field)]
public void EditFieldAttackConfig() => PopFlatConfig(GameFile.AIFieldWazaConfig, "AI Field Attack Config Editor");
#endregion
#region Battle Editors
[EditorCallable(EditorCategory.Battle)]
public void EditTrainers()
{
var folder = ROM.GetFilteredFolder(GameFile.TrainerData);
var cache = new DataCache<TrData8a>(folder)
{
Create = FlatBufferConverter.DeserializeFrom<TrData8a>,
Write = FlatBufferConverter.SerializeFrom,
};
var names = folder.GetPaths().Select(Path.GetFileNameWithoutExtension).ToArray();
using var form = new GenericEditor<TrData8a>(cache, names, "Trainers", Randomize, canSave: true);
form.ShowDialog();
void Randomize()
{
var settings = EditUtil.Settings.Species;
var rand = new SpeciesRandomizer(ROM.Info, Data.PersonalData);
rand.Initialize(settings, GetSpeciesBanlist());
for (int i = 0; i < cache.Length; i++)
{
foreach (var t in cache[i].Team)
{
t.Species = rand.GetRandomSpecies(t.Species);
t.Form = GetRandomForm(t.Species);
t.Gender = (int)FixedGender.Random;
t.Nature = NatureType8a.Random;
t.Move_01.Move = t.Move_02.Move = t.Move_03.Move = t.Move_04.Move = 0;
t.Move_01.Mastered = t.Move_02.Mastered = t.Move_03.Mastered = t.Move_04.Mastered = true;
t.Shiny = Randomization.Util.Random.Next(0, 100 + 1) < 3;
t.IsOybn = Randomization.Util.Random.Next(0, 100 + 1) < 3;
}
}
}
if (!form.Modified)
cache.CancelEdits();
else
cache.Save();
}
[EditorCallable(EditorCategory.Battle)]
public void EditMoves()
{
var obj = ROM[GameFile.MoveStats]; // folder
var cache = new DataCache<Waza8a>(obj)
{
Create = FlatBufferConverter.DeserializeFrom<Waza8a>,
Write = FlatBufferConverter.SerializeFrom,
};
using var form = new GenericEditor<Waza8a>(cache, ROM.GetStrings(TextName.MoveNames), "Move Editor");
form.ShowDialog();
if (!form.Modified)
{
cache.CancelEdits();
return;
}
cache.Save();
Data.MoveData.ClearAll(); // force reload if used again
}
#endregion
#region Shop Editors
[EditorCallable(EditorCategory.Shops)]
public void EditMoveShop()
{
var names = ROM.GetStrings(TextName.MoveNames);
var data = ROM.GetFile(GameFile.MoveShop);
var obj = FlatBufferConverter.DeserializeFrom<MoveShopTable8a>(data[0]);
var result = PopFlat(obj.Table, "Move Shop Editor", z => names[z.Move]);
if (!result)
return;
data[0] = FlatBufferConverter.SerializeFrom(obj);
}
[EditorCallable(EditorCategory.Shops)]
public void EditHaShopData()
{
var names = ROM.GetStrings(TextName.ItemNames);
var data = ROM.GetFile(GameFile.HaShop);
var obj = FlatBufferConverter.DeserializeFrom<HaShopTable8a>(data[0]);
var result = PopFlat(obj.Table, "ha_shop_data Editor", z => names[z.ItemID], Randomize);
if (!result)
return;
void Randomize()
{
foreach (var t in obj.Table)
{
if (Legal.Pouch_Recipe_LA.Contains((ushort)t.ItemID)) // preserve recipes
continue;
t.ItemID = Legal.Pouch_Items_LA[Randomization.Util.Random.Next(Legal.Pouch_Items_LA.Length)];
}
}
data[0] = FlatBufferConverter.SerializeFrom(obj);
}
#endregion
#region Graphics Editors
[EditorCallable(EditorCategory.Graphics)]
public void EditFieldAnimationFramerate() => PopFlatConfig(GameFile.FieldAnimationFramerateConfig, "Field Anime Framerate Config");
[EditorCallable(EditorCategory.Graphics)]
public void EditWaterMotion() => PopFlatConfig(GameFile.WaterMotion, "Water Motion Configuration");
#endregion
#region Other Editors
[EditorCallable]
public void EditThrowParam()
{
PopFlat<ThrowParamTable8a, ThrowParam8a>(GameFile.ThrowParam, "Throw Param Editor", z => z.Hash.ToString("X16"));
}
[EditorCallable]
public void EditThrowPermissionSetParam()
{
PopFlat<ThrowPermissionSetDictionary8a, ThrowPermissionSetEntry8a>(GameFile.ThrowPermissionSet, "Throw Permission Editor", z => z.Hash_00.ToString("X16"));
}
[EditorCallable]
public void EditThrowableParam()
{
var itemNames = ROM.GetStrings(TextName.ItemNames);
PopFlat<ThrowableParamTable8a, ThrowableParam8a>(GameFile.ThrowableParam, "Throwable Param Editor", z => itemNames[z.ItemID]);
}
[EditorCallable]
public void EditThrowResourceDictionary()
{
PopFlat<ThrowableResourceDictionary8a, ThrowableResourceEntry8a>(GameFile.ThrowableResource, "Throwable Resource Dictionary Editor", z => z.Hash_00.ToString("X16"));
}
[EditorCallable]
public void EditThrowResourceSetDictionary()
{
PopFlat<ThrowableResourceSetDictionary8a, ThrowableResourceSetEntry8a>(GameFile.ThrowableResourceSet, "Throwable Resource Set Dictionary Editor", z => z.Hash_00.ToString("X16"));
}
[EditorCallable]
public void EditAppConfigList()
{
PopFlat<AppConfigList8a, AppconfigEntry8a>(GameFile.AppConfigList, "App Config List", z => z.OriginalPath);
}
[EditorCallable]
public void EditGift()
{
var names = ROM.GetStrings(TextName.SpeciesNames);
@@ -272,98 +487,7 @@ void Randomize(IEnumerable<PokeAdd8a> arr)
}
}
public int[] GetSpeciesBanlist()
{
var pt = Data.PersonalData;
var hasForm = new HashSet<int>();
var banned = new HashSet<int>();
foreach (var pi in pt.Table.Cast<IPersonalMisc_1>())
{
if (pi.IsPresentInGame)
{
banned.Remove(pi.ModelID);
hasForm.Add(pi.ModelID);
}
else if (!hasForm.Contains(pi.ModelID))
{
banned.Add(pi.ModelID);
}
}
return banned.ToArray();
}
public int GetRandomForm(int spec)
{
var pt = Data.PersonalData;
var formRand = pt.Table.Cast<IPersonalMisc_1>()
.Where(z => z.IsPresentInGame && !(Legal.BattleExclusiveForms.Contains(z.ModelID) || Legal.BattleFusions.Contains(z.ModelID)))
.GroupBy(z => z.ModelID)
.ToDictionary(z => z.Key, z => z.ToList());
if (!formRand.TryGetValue((ushort)spec, out var entries))
return 0;
var count = entries.Count;
return (Species)spec switch
{
Growlithe or Arcanine or Voltorb or Electrode or Typhlosion or Qwilfish or Samurott or Lilligant or Zorua or Zoroark or Braviary or Sliggoo or Goodra or Avalugg or Decidueye => 1,
Basculin => 2,
Kleavor => 0,
_ => Randomization.Util.Random.Next(0, count),
};
}
public void EditPersonal_Raw()
{
var names = ROM.GetStrings(TextName.SpeciesNames);
PopFlat<PersonalTableLAfb, PersonalInfoLAfb>(GameFile.PersonalStats, "Personal Info Editor (Raw)", z => $"{names[z.Species]}{(z.Form == 0 ? "" : $"-{z.Form}")}");
}
public void EditLearnset_Raw()
{
var names = ROM.GetStrings(TextName.SpeciesNames);
PopFlat<Learnset8a, Learnset8aMeta>(GameFile.Learnsets, "Learnset Editor (Raw)", z => $"{names[z.Species]}{(z.Form == 0 ? "" : $"-{z.Form}")}");
}
public void EditMiscSpeciesInfo()
{
var names = ROM.GetStrings(TextName.SpeciesNames);
PopFlat<PokeMiscTable8a, PokeMisc8a>(GameFile.PokeMisc, "Misc Species Info Editor", z => $"{names[z.Species]}{(z.Form == 0 ? "" : $"-{z.Form}")} ~ {z.Value}");
}
public void EditMap_Viewer()
{
var resident = (GFPack)ROM.GetFile(GameFile.Resident);
using var form = new MapViewer8a(ROM, resident);
form.ShowDialog();
}
public void EditAreas()
{
using var form = new AreaEditor8a(ROM);
form.ShowDialog();
}
public void EditMoves()
{
var obj = ROM[GameFile.MoveStats]; // folder
var cache = new DataCache<Waza8a>(obj)
{
Create = FlatBufferConverter.DeserializeFrom<Waza8a>,
Write = FlatBufferConverter.SerializeFrom,
};
using var form = new GenericEditor<Waza8a>(cache, ROM.GetStrings(TextName.MoveNames), "Move Editor");
form.ShowDialog();
if (!form.Modified)
{
cache.CancelEdits();
return;
}
cache.Save();
Data.MoveData.ClearAll(); // force reload if used again
}
[EditorCallable]
public void EditItems()
{
var obj = ROM[GameFile.ItemStats]; // mini
@@ -381,86 +505,115 @@ public void EditItems()
obj[0] = Item8a.SetArray(items, data);
}
public void EditPokemon()
{
var editor = new PokeEditor8a
{
Personal = Data.PersonalData,
PokeMisc = Data.PokeMiscData,
Evolve = Data.EvolutionData,
Learn = Data.LevelUpData,
FieldDropTables = Data.FieldDrops,
BattleDropTabels = Data.BattleDrops,
DexResearch = Data.DexResearch,
};
using var form = new PokeDataUI8a(editor, ROM, Data);
form.ShowDialog();
if (!form.Modified)
editor.CancelEdits();
else
editor.Save();
}
[EditorCallable] public void EditAICommonConfig() => PopFlatConfig(GameFile.AICommonConfig, "AI Common Config Editor");
[EditorCallable] public void EditAIExcitingConfig() => PopFlatConfig(GameFile.AIExcitingConfig, "AI Exciting Config Editor");
public void PopFlatConfig(GameFile file, string title)
{
var obj = ROM.GetFile(file); // flatbuffer
var data = obj[0];
var root = FlatBufferConverter.DeserializeFrom<ConfigureTable8a>(data);
var cache = new DataCache<Configure8aEntry>(root.Table);
var names = root.Table.Select(z => z.Name).ToArray();
using var form = new GenericEditor<Configure8aEntry>(cache, names, title);
form.ShowDialog();
if (!form.Modified)
return;
obj[0] = FlatBufferConverter.SerializeFrom(root);
}
[EditorCallable] public void EditAISemiLegendConfig() => PopFlatConfig(GameFile.AISemiLegendConfig, "AI Semi Legend Config Editor");
[EditorCallable] public void EditAITirednessConfig() => PopFlatConfig(GameFile.AITirednessConfig, "AI Tiredness Config Editor");
[EditorCallable] public void EditAppliHudConfig() => PopFlatConfig(GameFile.AppliHudConfig, "Appli Hud Config Editor");
[EditorCallable] public void EditAppliStaffrollConfig() => PopFlatConfig(GameFile.AppliStaffrollConfig, "Appli Staffroll Config Editor");
[EditorCallable] public void EditAppliTipsConfig() => PopFlatConfig(GameFile.AppliTipsConfig, "Appli Tips Config Editor");
[EditorCallable] public void EditBattleCommonConfig() => PopFlatConfig(GameFile.BattleCommonConfig, "Battle Common Config Editor");
[EditorCallable] public void EditBattleEndConfig() => PopFlatConfig(GameFile.BattleEndConfig, "Battle End Config Editor");
[EditorCallable] public void EditBattleInConfig() => PopFlatConfig(GameFile.BattleInConfig, "Battle In Config Editor");
[EditorCallable] public void EditBattleLogicConfig() => PopFlatConfig(GameFile.BattleLogicConfig, "Battle Logic Config Editor");
[EditorCallable] public void EditBattleStartConfig() => PopFlatConfig(GameFile.BattleStartConfig, "Battle Start Config Editor");
[EditorCallable] public void EditBattleViewConfig() => PopFlatConfig(GameFile.BattleViewConfig, "Battle View Config Editor");
[EditorCallable] public void EditBattleVsnsConfig() => PopFlatConfig(GameFile.BattleVsnsConfig, "Battle Vsns Config Editor");
[EditorCallable] public void EditBuddyBattleConfig() => PopFlatConfig(GameFile.BuddyBattleConfig, "Buddy Battle Config Editor");
[EditorCallable] public void EditBuddyConfig() => PopFlatConfig(GameFile.BuddyConfig, "Buddy Config Editor");
[EditorCallable] public void EditBuddyDirectItemConfig() => PopFlatConfig(GameFile.BuddyDirectItemConfig, "Buddy Direct Item Config Editor");
[EditorCallable] public void EditBuddyGroupTalkConfig() => PopFlatConfig(GameFile.BuddyGroupTalkConfig, "Buddy Group Talk Config Editor");
[EditorCallable] public void EditBuddyLandmarkConfig() => PopFlatConfig(GameFile.BuddyLandmarkConfig, "Buddy Landmark Config Editor");
[EditorCallable] public void EditBuddyNPCReactionConfig() => PopFlatConfig(GameFile.BuddyNPCReactionConfig, "Buddy NPC Reaction Config Editor");
[EditorCallable] public void EditBuddyPlayerModeConfig() => PopFlatConfig(GameFile.BuddyPlayerModeConfig, "Buddy Player Mode Config Editor");
[EditorCallable] public void EditBuddyWarpConfig() => PopFlatConfig(GameFile.BuddyWarpConfig, "Buddy Warp Config Editor");
[EditorCallable] public void EditCharacterBipedIkConfig() => PopFlatConfig(GameFile.CharacterBipedIkConfig, "Character Biped Ik Config Editor");
[EditorCallable] public void EditCharacterBlinkConfig() => PopFlatConfig(GameFile.CharacterBlinkConfig, "Character Blink Config Editor");
[EditorCallable] public void EditCharacterControllerConfig() => PopFlatConfig(GameFile.CharacterControllerConfig, "Character Controller Config Editor");
[EditorCallable] public void EditCharacterLookAtConfig() => PopFlatConfig(GameFile.CharacterLookAtConfig, "Character Look At Config Editor");
[EditorCallable] public void EditCaptureConfig() => PopFlatConfig(GameFile.CaptureConfig, "Capture Config Editor");
[EditorCallable] public void EditCommonGeneralConfig() => PopFlatConfig(GameFile.CommonGeneralConfig, "Common General Config Editor");
[EditorCallable] public void EditCommonItemConfig() => PopFlatConfig(GameFile.CommonItemConfig, "Common Item Config Editor");
[EditorCallable] public void EditDemoConfig() => PopFlatConfig(GameFile.DemoConfig, "Demo Config Editor");
[EditorCallable] public void EditEnvPokeVoiceConfig() => PopFlatConfig(GameFile.EnvPokeVoiceConfig, "Env Poke Voice Config Editor");
[EditorCallable] public void EditEventBalloonrunConfig() => PopFlatConfig(GameFile.EventBalloonrunConfig, "Event Balloonrun Config Editor");
[EditorCallable] public void EditEventBalloonthrowConfig() => PopFlatConfig(GameFile.EventBalloonthrowConfig, "Event Balloonthrow Config Editor");
[EditorCallable] public void EditEventBanditConfig() => PopFlatConfig(GameFile.EventBanditConfig, "Event Bandit Config Editor");
[EditorCallable] public void EditEventCullingConfig() => PopFlatConfig(GameFile.EventCullingConfig, "Event Culling Config Editor");
[EditorCallable] public void EditEventDitherConfig() => PopFlatConfig(GameFile.EventDitherConfig, "Event Dither Config Editor");
[EditorCallable] public void EditEventFarmConfig() => PopFlatConfig(GameFile.EventFarmConfig, "Farm Config Editor");
[EditorCallable] public void EditEventGameOverConfig() => PopFlatConfig(GameFile.EventGameOverConfig, "Event Game Over Config Editor");
[EditorCallable] public void EditEventItemConfig() => PopFlatConfig(GameFile.EventItemConfig, "Event Item Config Editor");
[EditorCallable] public void EditEventMkrgRewardConfig() => PopFlatConfig(GameFile.EventMkrgRewardConfig, "Event Mkrg Reward Config Editor");
[EditorCallable] public void EditEventQuestBoardConfig() => PopFlatConfig(GameFile.EventQuestBoardConfig, "Event Quest Board Config Editor");
[EditorCallable] public void EditEventRestrictionBattle() => PopFlatConfig(GameFile.EventRestrictionBattle, "Event Restriction Battle Editor");
[EditorCallable] public void EditEventWork() => PopFlatConfig(GameFile.EventWork, "Event Work Editor");
public void EditShinyRate() => PopFlatConfig(GameFile.ShinyRolls, "Shiny Rate Editor");
public void EditWormholeRate() => PopFlatConfig(GameFile.WormholeConfig, "Wormhole Config Editor");
public void EditCapture_Config() => PopFlatConfig(GameFile.CaptureConfig, "CaptureConfig Editor");
public void EditBattle_Logic_Config() => PopFlatConfig(GameFile.BattleLogicConfig, "Battle Logic Config Editor");
public void EditEvent_Farm_Config() => PopFlatConfig(GameFile.EventFarmConfig, "Event Farm Config Editor");
public void EditPlayer_Config() => PopFlatConfig(GameFile.PlayerConfig, "Player Config Editor");
public void EditField_Landmark_Config() => PopFlatConfig(GameFile.FieldLandmarkConfig, "Field Landmark Config Editor");
public void EditBattle_View_Config() => PopFlatConfig(GameFile.BattleViewConfig, "Battle View Config Editor");
public void EditAICommon_Config() => PopFlatConfig(GameFile.AICommonConfig, "AI Common Config Editor");
public void EdiField_Spawner_Config() => PopFlatConfig(GameFile.FieldSpawnerConfig, "Field Spawner Config Editor");
public void EditOutbreak_Config() => PopFlatConfig(GameFile.OutbreakConfig, "Outbreak Config Editor");
public void EditEvolution_Config() => PopFlatConfig(GameFile.EvolutionConfig, "Evolution Config Editor");
public void EditBall_Throw_Config() => PopFlatConfig(GameFile.BallThrowConfig, "Ball Throw Config Editor");
public void EditSize_Scale_Config() => PopFlatConfig(GameFile.SizeScaleConfig, "Size Scale Config Editor");
[EditorCallable] public void EditNPCAIConfig() => PopFlatConfig(GameFile.NPCAIConfig, "NPC AI Config");
[EditorCallable] public void EditNPCControllerConfig() => PopFlatConfig(GameFile.NPCControllerConfig, "NPC Controller Config");
[EditorCallable] public void EditNPCCreaterConfig() => PopFlatConfig(GameFile.NPCCreaterConfig, "NPC Creater Config");
[EditorCallable] public void EditNPCPokemonAIConfig() => PopFlatConfig(GameFile.NPCPokemonAIConfig, "NPC Pokemon AI Config");
[EditorCallable] public void EditNPCPopupConfig() => PopFlatConfig(GameFile.NPCPopupConfig, "NPC Popup Config");
[EditorCallable] public void EditNPCTalkTableConfig() => PopFlatConfig(GameFile.NPCTalkTableConfig, "NPC Talk Table Config");
[EditorCallable] public void EditPlayerCameraShakeConfig() => PopFlatConfig(GameFile.PlayerCameraShakeConfig, "Player Camera Shake Config");
[EditorCallable] public void EditPlayerCollisionConfig() => PopFlatConfig(GameFile.PlayerCollisionConfig, "Player Collision Config");
[EditorCallable] public void EditPlayerConfig() => PopFlatConfig(GameFile.PlayerConfig, "Player Config Editor");
[EditorCallable] public void EditPlayerControllerConfig() => PopFlatConfig(GameFile.PlayerControllerConfig, "Player Controller Config");
[EditorCallable] public void EditPlayerFaceConfig() => PopFlatConfig(GameFile.PlayerFaceConfig, "Player Face Config");
[EditorCallable] public void EditPokemonConfig() => PopFlatConfig(GameFile.PokemonConfig, "Pokemon Config");
[EditorCallable] public void EditPokemonControllerConfig() => PopFlatConfig(GameFile.PokemonControllerConfig, "Pokemon Controller Config");
[EditorCallable] public void EditPokemonFriendshipConfig() => PopFlatConfig(GameFile.PokemonFriendshipConfig, "Pokemon Friendship Config");
public void EditEvolutions()
{
var names = ROM.GetStrings(TextName.SpeciesNames);
PopFlat<EvolutionTable8, EvolutionSet8a>(GameFile.Evolutions, "Evolution Editor",
z => $"{names[z.Species]}{(z.Form != 0 ? $"-{z.Form}" : "")}");
}
[EditorCallable] public void EditSizeScaleConfig() => PopFlatConfig(GameFile.SizeScaleConfig, "Size Scale Config");
[EditorCallable] public void EditRideBasuraoCollisionConfig() => PopFlatConfig(GameFile.RideBasuraoCollisionConfig, "Ride Basurao Collision Config");
[EditorCallable] public void EditRideBasuraoConfig() => PopFlatConfig(GameFile.RideBasuraoConfig, "Ride Basurao Config");
[EditorCallable] public void EditRideChangeConfig() => PopFlatConfig(GameFile.RideChangeConfig, "Ride Change Config");
[EditorCallable] public void EditRideCommonConfig() => PopFlatConfig(GameFile.RideCommonConfig, "Ride Common Config");
[EditorCallable] public void EditRideNyuuraCollisionConfig() => PopFlatConfig(GameFile.RideNyuuraCollisionConfig, "Ride Nyuura Collision Config");
[EditorCallable] public void EditRideNyuuraConfig() => PopFlatConfig(GameFile.RideNyuuraConfig, "Ride Nyuura Config");
[EditorCallable] public void EditRideNyuuraControllerConfig() => PopFlatConfig(GameFile.RideNyuuraControllerConfig, "Ride Nyuura Controller Config");
[EditorCallable] public void EditRideOdoshishiCollisionConfig() => PopFlatConfig(GameFile.RideOdoshishiCollisionConfig, "Ride Odoshishi Collision Config");
[EditorCallable] public void EditRideOdoshishiConfig() => PopFlatConfig(GameFile.RideOdoshishiConfig, "Ride Odoshishi Config");
[EditorCallable] public void EditRideRingumaCollisionConfig() => PopFlatConfig(GameFile.RideRingumaCollisionConfig, "Ride Ringuma Collision Config");
[EditorCallable] public void EditRideRingumaConfig() => PopFlatConfig(GameFile.RideRingumaConfig, "Ride Ringuma Config");
[EditorCallable] public void EditRideRingumaControllerConfig() => PopFlatConfig(GameFile.RideRingumaControllerConfig, "Ride Ringuma Controller Config");
[EditorCallable] public void EditRideWhooguruCollisionConfig() => PopFlatConfig(GameFile.RideWhooguruCollisionConfig, "Ride Whooguru Collision Config");
[EditorCallable] public void EditRideWhooguruConfig() => PopFlatConfig(GameFile.RideWhooguruConfig, "Ride Whooguru Config");
[EditorCallable] public void EditRideWhooguruControllerConfig() => PopFlatConfig(GameFile.RideWhooguruControllerConfig, "Ride Whooguru Controller Config");
[EditorCallable] public void EditSoundConfig() => PopFlatConfig(GameFile.SoundConfig, "Sound Config");
public void EditOutbreakDetail()
=> PopFlat<MassOutbreakTable8a, MassOutbreak8a>(GameFile.Outbreak, "Outbreak Proc Editor", z => z.WorkValueName);
[EditorCallable] public void EditFieldAreaSpeed() => PopFlatConfig(GameFile.FieldAreaSpeedConfig, "Field Area Speed Config");
[EditorCallable] public void EditFieldCameraConfig() => PopFlatConfig(GameFile.FieldCameraConfig, "Field Camera Config");
[EditorCallable] public void EditFieldCaptureDirectorConfig() => PopFlatConfig(GameFile.FieldCaptureDirectorConfig, "Field Capture Director Config");
[EditorCallable] public void EditFieldCharaViewerConfig() => PopFlatConfig(GameFile.FieldCharaViewerConfig, "Field Chara Viewer Config");
[EditorCallable] public void EditFieldCommonConfig() => PopFlatConfig(GameFile.FieldCommonConfig, "Field Common Config");
[EditorCallable] public void EditFieldDirectItemConfig() => PopFlatConfig(GameFile.FieldDirectItemConfig, "Field Direct Item Config");
[EditorCallable] public void EditFieldEnvConfig() => PopFlatConfig(GameFile.FieldEnvConfig, "Field Env Config");
[EditorCallable] public void EditFieldItem() => PopFlatConfig(GameFile.FieldItem, "Field Item");
[EditorCallable] public void EditFieldItemRespawn() => PopFlatConfig(GameFile.FieldItemRespawn, "Field Item Respawn");
[EditorCallable] public void EditFieldLandmarkInciteConfig() => PopFlatConfig(GameFile.FieldLandmarkInciteConfig, "Field Landmark Incite Config");
[EditorCallable] public void EditFieldLockonConfig() => PopFlatConfig(GameFile.FieldLockonConfig, "Field Lockon Config");
[EditorCallable] public void EditFieldMyPokeBallHitNoneTargetConfig() => PopFlatConfig(GameFile.FieldMyPokeBallHitNoneTargetConfig, "Field My Poke Ball Hit None Target Config");
[EditorCallable] public void EditFieldObstructionWazaConfig() => PopFlatConfig(GameFile.FieldObstructionWazaConfig, "Field Obstruction Waza Config");
[EditorCallable] public void EditFieldPokemonSlopeConfig() => PopFlatConfig(GameFile.FieldPokemonSlopeConfig, "Field Pokemon Slope Config");
[EditorCallable] public void EditFieldQuestDestinationConfig() => PopFlatConfig(GameFile.FieldQuestDestinationConfig, "Field Quest Destination Config");
[EditorCallable] public void EditFieldShadowConfig() => PopFlatConfig(GameFile.FieldShadowConfig, "Field Shadow Config");
[EditorCallable] public void EditFieldThrowConfig() => PopFlatConfig(GameFile.FieldThrowConfig, "Field Throw Config");
[EditorCallable] public void EditFieldThrowableAfterHitConfig() => PopFlatConfig(GameFile.FieldThrowableAfterHitConfig, "Field Throwable After Hit Config");
[EditorCallable] public void EditFieldVigilanceBgmConfig() => PopFlatConfig(GameFile.FieldVigilanceBgmConfig, "Field Vigilance Bgm Config");
[EditorCallable] public void EditFieldWeatheringConfig() => PopFlatConfig(GameFile.FieldWeatheringConfig, "Field Weathering Config");
[EditorCallable] public void EditFieldWildPokemonConfig() => PopFlatConfig(GameFile.FieldWildPokemonConfig, "Field Wild Pokemon Config");
public void EditNewOutbreak_Group()
=> PopFlat<NewHugeOutbreakGroupArchive8a, NewHugeOutbreakGroup8a>(GameFile.NewHugeGroup, "New Outbreak Group Editor", z => z.Group.ToString("X16"));
public void EditNewOutbreak_GroupLottery()
=> PopFlat<NewHugeOutbreakGroupLotteryArchive8a, NewHugeOutbreakGroupLottery8a>(GameFile.NewHugeGroupLottery, "New Outbreak Group Lottery Editor", z => z.LotteryGroup.ToString("X16"));
public void EditNewOutbreak_Lottery()
=> PopFlat<NewHugeOutbreakLotteryArchive8a, NewHugeOutbreakLottery8a>(GameFile.NewHugeLottery, "New Outbreak Lottery Editor", z => z.LotteryGroupString);
public void EditNewOutbreak_TimeLimit()
=> PopFlat<NewHugeOutbreakTimeLimitArchive8a, NewHugeOutbreakTimeLimit8a>(GameFile.NewHugeTimeLimit, "New Outbreak Time Limit Editor", z => z.Duration.ToString());
public void EditSymbolBehave()
{
var names = ROM.GetStrings(TextName.SpeciesNames);
PopFlat<PokeAIArchive8a, PokeAI8a>(GameFile.SymbolBehave, "Symbol Behavior Editor", z => $"{names[z.Species]}{(z.Form != 0 ? $"-{z.Form}" : "")}");
}
[EditorCallable] public void EditFieldLandmarkConfig() => PopFlatConfig(GameFile.FieldLandmarkConfig, "Field Landmark Config Editor");
[EditorCallable] public void EditField_Spawner_Config() => PopFlatConfig(GameFile.FieldSpawnerConfig, "Field Spawner Config Editor");
[EditorCallable] public void EditEvolutionConfig() => PopFlatConfig(GameFile.EvolutionConfig, "Evolution Config Editor");
[EditorCallable] public void EditBallThrowConfig() => PopFlatConfig(GameFile.BallThrowConfig, "Ball Throw Config Editor");
public void EditMasterDump()
{
using var md = new DumperPLA(ROM);
md.ShowDialog();
}
#endregion
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using pkNX.Game;
@@ -15,25 +16,44 @@ public abstract class EditorBase
public int Language { get => ROM.Language; set => ROM.Language = value; }
public string? Location { get; private set; }
private const string prefix = "Edit";
private MethodInfo[] editorMethods = Array.Empty<MethodInfo>();
private EditorCallableAttribute[] editorAttributes = Array.Empty<EditorCallableAttribute>();
protected EditorBase()
{
// Collect all methods that are marked as editors
// The method name needs to start with `Edit` or an EditorCallableAttribute should be added
var editors = GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance)
.Select(x => new { Method = x, Callable = x.GetCustomAttribute<EditorCallableAttribute>() })
.Where(x => x.Callable != null || x.Method.Name.StartsWith(prefix));
editorMethods = editors.Select(x => x.Method).ToArray();
editorAttributes = editors.Select(x => x.Callable ?? new EditorCallableAttribute(EditorCategory.None)).ToArray();
}
public void Initialize() => ROM.Initialize();
public IEnumerable<Button> GetControls(int width, int height)
public int CountControlsForCategory(EditorCategory category) => editorAttributes.Count(a => a.Category == category);
public IEnumerable<Button> GetControls(int width, int height, EditorCategory category = EditorCategory.None)
{
var type = GetType();
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance);
foreach (var m in methods)
for (int i = 0; i < editorMethods.Length; ++i)
{
const string prefix = "Edit";
if (!m.Name.StartsWith(prefix))
var m = editorMethods[i];
var callable = editorAttributes[i];
// Ignore all editors that are not of the requested category
if (callable.Category != category)
continue;
var name = m.Name.AsSpan(prefix.Length);
var name = m.Name.Replace(prefix, ""); // Might or might not contain prefix
var b = new Button
{
Width = width,
Height = height,
Name = $"B_{name}",
Text = WinFormsUtil.GetSpacedCapitalized(name),
Text = (callable?.HasCustomEditorName() ?? false) ? callable.EditorName : WinFormsUtil.GetSpacedCapitalized(name),
};
b.Click += (s, e) =>
{