### 先行列車位置の上書き — `IPreTrainPatchFactory` Source: https://context7.com/automatic9045/bveex/llms.txt 他列車の走行位置を任意のロジックで置き換えるパッチ機能です。`IPreTrainLocationConverter` を実装したコンバーターを渡すことで、`PreTrainLocation` を任意の値に変換できます。コンバーターは、本来の先行列車位置を `source` として受け取り、変換後の `PreTrainLocation` を返します。 ```csharp using BveTypes.ClassWrappers; using BveEx.Extensions.PreTrainPatch; using BveEx.PluginHost.Plugins; [Plugin(PluginType.MapPlugin)] public class PreTrainOverride : AssemblyPluginBase { private Train ControlledTrain; private PreTrainPatch Patch; public PreTrainOverride(PluginBuilder builder) : base(builder) { BveHacker.ScenarioCreated += OnScenarioCreated; } private void OnScenarioCreated(ScenarioCreatedEventArgs e) { ControlledTrain = e.Scenario.Trains["lead"]; // マップで定義した他列車キー ControlledTrain.Location = 100.0; // 初期位置 100m IPreTrainPatchFactory factory = Extensions.GetExtension(); Patch = factory.Patch( "PreTrainOverride", e.Scenario.SectionManager, new MyConverter(ControlledTrain, e.Scenario.SectionManager)); } public override void Tick(TimeSpan elapsed) { // 範囲外にならないよう制限 if (ControlledTrain.Location < 10) ControlledTrain.Location = 10; if (ControlledTrain.Location > 2000) ControlledTrain.Location = 2000; } public override void Dispose() { Patch?.Dispose(); BveHacker.ScenarioCreated -= OnScenarioCreated; } private class MyConverter : IPreTrainLocationConverter { private readonly Train Train; private readonly SectionManager Sm; public MyConverter(Train train, SectionManager sm) { Train = train; Sm = sm; } // source: 本来の先行列車位置。Train の現在位置に基づいて変換して返す。 public PreTrainLocation Convert(PreTrainLocation source) => Train.TrainInfo.TrackKey == "0" ? PreTrainLocation.FromLocation(Train.Location, Sm) : source; } } ``` -------------------------------- ### 車掌挙動の上書きプラグイン Source: https://context7.com/automatic9045/bveex/llms.txt 車掌の自動制御ロジックを `ConductorBase` 派生クラスで置き換えます。`MethodOverrideMode.SkipOriginal` を返すと本来の処理をスキップします。ドア開閉や停車位置確認などのイベントを手動でも発火できます。 ```csharp using BveTypes.ClassWrappers; using BveEx.Extensions.ConductorPatch; using BveEx.PluginHost.Plugins; [Plugin(PluginType.VehiclePlugin)] public class ConductorOverridePlugin : AssemblyPluginBase { private MyConductor Conductor; private ConductorPatch Patch; public ConductorOverridePlugin(PluginBuilder builder) : base(builder) { BveHacker.ScenarioCreated += OnScenarioCreated; } private void OnScenarioCreated(ScenarioCreatedEventArgs e) { IConductorPatchFactory factory = Extensions.GetExtension(); Conductor = new MyConductor(e.Scenario.Vehicle.Conductor); Patch = factory.Patch(Conductor, DeclarationPriority.Sequentially); } public override void Dispose() { if (Patch != null) Extensions.GetExtension().Unpatch(Patch); BveHacker.ScenarioCreated -= OnScenarioCreated; } public override void Tick(TimeSpan elapsed) { } } internal class MyConductor : ConductorBase { protected override event EventHandler FixStopPositionRequested; protected override event EventHandler StopPositionChecked; protected override event EventHandler DoorOpening; protected override event EventHandler DepartureSoundPlaying; protected override event EventHandler DoorClosing; protected override event EventHandler DoorClosed; public MyConductor(Conductor original) : base(original) { } protected override MethodOverrideMode OnTick() { // 次駅が通過駅なら自動的に進める if (Original.Stations.CurrentIndex + 1 < Original.Stations.Count) { Station next = Original.Stations[Original.Stations.CurrentIndex + 1] as Station; if (next != null && next.Pass) Original.Stations.GoToByIndex(Original.Stations.CurrentIndex + 1); } return MethodOverrideMode.SkipOriginal; } protected override MethodOverrideMode OnDoorStateChanged() { if (Original.Doors.AreAllClosed) DoorClosed?.Invoke(this, EventArgs.Empty); return MethodOverrideMode.SkipOriginal; } } ``` -------------------------------- ### BveEX マップステートメント解析プラグイン Source: https://context7.com/automatic9045/bveex/llms.txt マップファイル内のカスタムステートメントをプラグインから読み取ります。`FindUserStatements` でフィルタリングし、`Statement` オブジェクトから位置・引数・キーを取得できます。 ```csharp // マップファイル側の記述例: // BveEx.User.MyPlugin.Announce['hello'].Play(1000, 'msg'); using BveEx.Extensions.MapStatements; using BveEx.PluginHost.Plugins; [Plugin(PluginType.MapPlugin)] public class MapStatementReader : AssemblyPluginBase { public MapStatementReader(PluginBuilder builder) : base(builder) { IStatementSet statementSet = Extensions.GetExtension(); statementSet.LoadingCompleted += (_, _) => OnLoadingCompleted(statementSet); } private void OnLoadingCompleted(IStatementSet statementSet) { // BveEx.User.MyPlugin.Announce[key].Play(delay, message) を検索 ClauseFilter[] filters = { new ClauseFilter("Announce", ClauseType.Element), new ClauseFilter("Play", ClauseType.Function), }; foreach (Statement stmt in statementSet.FindUserStatements("MyPlugin", filters)) { double location = stmt.Source.Location; string key = stmt.Source.Clauses[^2].Keys.Count > 0 ? stmt.Source.Clauses[^2].Keys[0]?.ToString() : "(なし)"; object delay = stmt.Source.Clauses[^1].Args.Count > 0 ? stmt.Source.Clauses[^1].Args[0] : null; object message = stmt.Source.Clauses[^1].Args.Count > 1 ? stmt.Source.Clauses[^1].Args[1] : null; Console.WriteLine($"{location}m: key={key}, delay={delay}, msg={message}"); } } public override void Dispose() { } public override void Tick(TimeSpan elapsed) { } } ``` -------------------------------- ### Basic BveEX Vehicle Plugin Structure Source: https://context7.com/automatic9045/bveex/llms.txt Implement a basic vehicle plugin by inheriting from AssemblyPluginBase and using the [Plugin] attribute. Subscribe to scenario events and implement per-frame logic in Tick(). ```csharp using BveEx.PluginHost.Plugins; // [Plugin] 属性でプラグイン種別を指定。MinRequiredVersion で最低 BveEX バージョンを制限可能。 [Plugin(PluginType.VehiclePlugin, MinRequiredVersion = "2.0")] public class MyVehiclePlugin : AssemblyPluginBase { // PluginBuilder は BveEX から自動的に渡される public MyVehiclePlugin(PluginBuilder builder) : base(builder) { // BveHacker : BVE 本体内部へのアクセサ (IBveHacker) // Extensions : 読み込み済み拡張機能のセット (IExtensionSet) // Plugins : 読み込み済みプラグインの一覧 (IPluginSet) BveHacker.ScenarioCreated += OnScenarioCreated; } private void OnScenarioCreated(ScenarioCreatedEventArgs e) { // e.Scenario から現在のシナリオ情報にアクセス double speed = e.Scenario.VehicleLocation.Speed; // m/s } public override void Dispose() { BveHacker.ScenarioCreated -= OnScenarioCreated; } // 毎フレーム呼び出される (ATS の Elapse 相当) public override void Tick(TimeSpan elapsed) { if (!BveHacker.IsScenarioCreated) return; double speedKmh = BveHacker.Scenario.VehicleLocation.Speed * 3.6; } } ``` -------------------------------- ### 信号現示の上書き — `ISignalPatchFactory` Source: https://context7.com/automatic9045/bveex/llms.txt 指定した閉塞区間の信号現示インデックスを任意の値に変更します。`Patch` メソッドでデリゲートを登録し、戻り値 `null` の場合は本来の値が使用されます。`SignalPatch.Dispose()` でパッチを解除します。 ```csharp using BveTypes.ClassWrappers; using BveEx.Extensions.SignalPatch; using BveEx.PluginHost.Plugins; [Plugin(PluginType.MapPlugin)] public class SignalOverridePlugin : AssemblyPluginBase { private SignalPatch Patch; private int ForcedSignal = 0; // 0=R, 1=YY, 2=Y, 3=YG, 4=G public SignalOverridePlugin(PluginBuilder builder) : base(builder) { BveHacker.ScenarioCreated += OnScenarioCreated; } private void OnScenarioCreated(ScenarioCreatedEventArgs e) { // 閉塞インデックス 2 の信号を上書き Section targetSection = e.Scenario.SectionManager.Sections[2] as Section; ISignalPatchFactory factory = Extensions.GetExtension(); Patch = factory.Patch( "MySignalOverride", targetSection, source => ForcedSignal // source: 本来の信号インデックス ); } public override void Tick(TimeSpan elapsed) { // キー操作などで ForcedSignal を変更すると即時反映される } public override void Dispose() { Patch?.Dispose(); BveHacker.ScenarioCreated -= OnScenarioCreated; } } ``` -------------------------------- ### Accessing ex_include System Variables Source: https://github.com/automatic9045/bveex/blob/main/_out/Scenarios/BveEx.Samples/Maps/Basic/Samples/MapGrammar/SubMap.txt This snippet shows how to access system variables set by the ex_include syntax, such as ex_argdistance, ex_arg0, ex_arg1, ex_arg2, and ex_arg3. These variables are populated based on the arguments provided in the ex_include statement. ```BveTs ex_argdistance; // 今回の場合は「ex_include 'SubMap.txt', 500, 'hello!';」と記述されているので、 // ex_arg1 = 500、ex_arg2 = 'hello!'、ex_arg3 = 0 (使用していないため) となります。 BveEx.Dialog.Show('distance=' + distance + ', ex_argdistance=' + ex_argdistance + ', ' + 'ex_arg0=' + ex_arg0 + ', ex_arg1=' + ex_arg1 + ', ex_arg2=' + ex_arg2 + ', ex_arg3=' + ex_arg3); ``` -------------------------------- ### Accessing BVE Internal State with IBveHacker Source: https://context7.com/automatic9045/bveex/llms.txt Utilize the IBveHacker interface to access BVE's internal states like the main form, scenario details, and DirectSound device. Subscribe to scenario lifecycle events and frame hooks. ```csharp [Plugin(PluginType.MapPlugin)] public class BveHackerDemo : AssemblyPluginBase { public BveHackerDemo(PluginBuilder builder) : base(builder) { // シナリオ読込開始 BveHacker.ScenarioOpened += e => { string mapFile = e.ScenarioInfo.RouteFiles.SelectedFile.Path; Console.WriteLine($"マップ読込: {mapFile}"); }; // Scenario インスタンス生成後 BveHacker.ScenarioCreated += e => { Scenario s = e.Scenario; Console.WriteLine($"駅数: {s.Stations.Count}"); Console.WriteLine($"他列車数: {s.Trains.Count}"); }; // シナリオ終了 BveHacker.ScenarioClosed += _ => Console.WriteLine("シナリオ終了"); } public override void Tick(TimeSpan elapsed) { if (!BveHacker.IsScenarioCreated) return; // BVE メインフォーム操作 System.Windows.Forms.Form mainForm = BveHacker.MainFormSource; mainForm.Text = $"速度: {BveHacker.Scenario.VehicleLocation.Speed * 3.6:F1} km/h"; // DirectSound デバイス取得 SlimDX.DirectSound.DirectSound ds = BveHacker.DirectSound; // BVE バージョン確認 Version bveVer = App.Instance.BveVersion; Version bveExVer = App.Instance.BveExVersion; } public override void Dispose() { } } ``` -------------------------------- ### Handle ATS Plugin Events with INative Extension Source: https://context7.com/automatic9045/bveex/llms.txt Use the INative extension to access vehicle data and handle ATS key events, beacon passes, horn blows, and door operations. Register event handlers in the constructor after obtaining the INative instance. ```csharp using BveEx.Extensions.Native; using BveEx.Extensions.Native.Input; using BveEx.PluginHost.Input; using BveEx.PluginHost.Plugins; [Plugin(PluginType.VehiclePlugin)] public class SimpleAts : AssemblyPluginBase { private readonly INative Native; public SimpleAts(PluginBuilder builder) : base(builder) { Native = Extensions.GetExtension(); // 車両諸元がセットされたタイミングでキー入力を登録 Native.VehicleSpecLoaded += (sender, e) => { Console.WriteLine($ ``` -------------------------------- ### Add Custom Items to Right-Click Menu with IContextMenuHacker Source: https://context7.com/automatic9045/bveex/llms.txt Utilize the IContextMenuHacker extension to add custom clickable items, checkable items, and separators to the BVE main form's right-click context menu. Specify the section using ContextMenuItemType. ```csharp using System.Windows.Forms; using BveEx.Extensions.ContextMenuHacker; using BveEx.PluginHost.Plugins; [Plugin(PluginType.MapPlugin)] public class ContextMenuDemo : AssemblyPluginBase { private readonly ToolStripMenuItem ToggleItem; private readonly ToolStripMenuItem ActionItem; public ContextMenuDemo(PluginBuilder builder) : base(builder) { IContextMenuHacker cmh = Extensions.GetExtension(); // チェック可能な項目(ウィンドウの表示/非表示トグルなど) ToggleItem = cmh.AddCheckableMenuItem( "デバッグウィンドウを表示", OnToggleCheckedChanged, ContextMenuItemType.Plugins); // クリック可能な項目 ActionItem = cmh.AddClickableMenuItem( "データをリロード", OnActionClick, ContextMenuItemType.Plugins); // 区切り線 cmh.AddSeparator(ContextMenuItemType.Plugins); } private void OnToggleCheckedChanged(object sender, EventArgs e) { if (ToggleItem.Checked) MessageBox.Show("ウィンドウ表示"); else MessageBox.Show("ウィンドウ非表示"); } private void OnActionClick(object sender, EventArgs e) => MessageBox.Show("リロード実行"); public override void Dispose() { } public override void Tick(TimeSpan elapsed) { } } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.