Repository: fltuna/lupercalia-mg-cs2
Pull request #36: feat: impl Rocket
Author: login: 2vg
Created: 2025-04-07T13:09:10Z | Merged: 2025-04-08T08:56:19Z

Rocketコマンドの追加
ZeisenのC++で作られたRocketからCSSharpへ移植


Diff:
diff --git a/LupercaliaMGCore/LupercaliaMGCore.cs b/LupercaliaMGCore/LupercaliaMGCore.cs
index 2717b22..9a7dc1f 100644
--- a/LupercaliaMGCore/LupercaliaMGCore.cs
+++ b/LupercaliaMGCore/LupercaliaMGCore.cs
@@ -68,6 +68,7 @@ public override void Load(bool hotReload)
         InitializeModule(new ExternalView(this));
         InitializeModule(new CourseWeapons(this));
         InitializeModule(new VelocityDisplay(this));
+        InitializeModule(new Rocket(this));
     }
 
     public override void OnAllPluginsLoaded(bool hotReload)
diff --git a/LupercaliaMGCore/PluginSettings.cs b/LupercaliaMGCore/PluginSettings.cs
index 241347b..0d12eeb 100644
--- a/LupercaliaMGCore/PluginSettings.cs
+++ b/LupercaliaMGCore/PluginSettings.cs
@@ -299,6 +299,12 @@ public readonly FakeConVar<bool>
     public readonly FakeConVar<bool> m_CVCourseWeaponEnabled =
         new("lp_mg_course_weapon_enabled", "Course weapon feature is enabled", false);
 
+    /*
+     *   Rocket commands
+     */
+    public readonly FakeConVar<bool> m_CVRocketEnabled =
+        new("lp_mg_rocket", "Is rocket command enabled?", false);
+
     private LupercaliaMGCore m_CSSPlugin;
 
     public PluginSettings(LupercaliaMGCore plugin)
@@ -542,6 +548,12 @@ private void generateCFG(string configPath)
         writeConVarConfig(config, m_CVCourseWeaponEnabled);
         config.WriteLine("\n");
 
+        /*
+         *   Rocket commands
+         */
+        writeConVarConfig(config, m_CVRocketEnabled);
+        config.WriteLine("\n");
+
         config.Close();
     }
 
diff --git a/LupercaliaMGCore/Rocket.cs b/LupercaliaMGCore/Rocket.cs
new file mode 100644
index 0000000..6363dd7
--- /dev/null
+++ b/LupercaliaMGCore/Rocket.cs
@@ -0,0 +1,107 @@
+﻿using CounterStrikeSharp.API;
+using CounterStrikeSharp.API.Core;
+using CounterStrikeSharp.API.Modules.Commands;
+using CounterStrikeSharp.API.Modules.Timers;
+using CounterStrikeSharp.API.Modules.Utils;
+using LupercaliaMGCore.model;
+using Timer = CounterStrikeSharp.API.Modules.Timers.Timer;
+
+namespace LupercaliaMGCore;
+
+public class Rocket(LupercaliaMGCore plugin) : PluginModuleBase(plugin)
+{
+    public override string PluginModuleName => "Rocket";
+    public readonly string ChatPrefix = $" {ChatColors.Gold}[Rocket]{ChatColors.Default}";
+
+    private Dictionary<CCSPlayerController, bool> isRocketLaunched = new Dictionary<CCSPlayerController, bool>();
+
+    public override void Initialize()
+    {
+        Plugin.RegisterEventHandler<EventPlayerConnectFull>(OnPlayerConnectFull);
+        Plugin.AddCommand("css_rocket", "launch to space.", CommandRocket);
+
+        Plugin.AddTimer(0.1F, () =>
+        {
+            SimpleLogging.LogDebug("Late initialization for hot reloading rocket.");
+            foreach (CCSPlayerController client in Utilities.GetPlayers())
+            {
+                if (!client.IsValid || client.IsBot || client.IsHLTV)
+                    continue;
+
+                isRocketLaunched[client] = false;
+            }
+        });
+    }
+
+    public override void UnloadModule()
+    {
+        Plugin.RemoveCommand("css_rocket", CommandRocket);
+        Plugin.DeregisterEventHandler<EventPlayerConnectFull>(OnPlayerConnectFull);
+
+        isRocketLaunched.Clear();
+    }
+
+    private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info)
+    {
+        CCSPlayerController? client = @event.Userid;
+
+        if (client == null)
+            return HookResult.Continue;
+
+        isRocketLaunched[client] = false;
+        return HookResult.Continue;
+    }
+
+    private void CommandRocket(CCSPlayerController? client, CommandInfo info)
+    {
+        if (client == null || !PlayerUtil.IsPlayerAlive(client) || isRocketLaunched[client])
+            return;
+
+        RocketPerform(client);
+    }
+
+    private void RocketPerform(CCSPlayerController client)
+    {
+        if (!PlayerUtil.IsPlayerAlive(client) || isRocketLaunched[client]) return;
+
+        CBasePlayerPawn pawn = client.Pawn.Value!;
+
+        Server.PrintToChatAll($" {ChatColors.Lime} {client.PlayerName} {ChatColors.Default}lancuhed to space!");
+
+        var vel = new Vector(0.0f, 0.0f, 350f);
+        pawn.EmitSound("C4.ExplodeWarning");
+        pawn.Teleport(null, null, vel);
+        pawn.GravityScale = 0.1f;
+
+        isRocketLaunched[client] = true;
+
+        Timer timer = new Timer(2.0f, () =>
+        {
+            if (!PlayerUtil.IsPlayerAlive(client)) return;
+
+            pawn.GravityScale = 1.0f;
+            pawn.CommitSuicide(false, true);
+            pawn.EmitSound("c4.explode");
+            MakeExplosive(client);
+            isRocketLaunched[client] = false;
+        });
+    }
+
+    private void MakeExplosive(CCSPlayerController client)
+    {
+        if (!PlayerUtil.IsPlayerAlive(client)) return;
+
+        CBasePlayerPawn pawn = client.Pawn.Value!;
+        var pEffect = Utilities.CreateEntityByName<CParticleSystem>("info_particle_system")!;
+        var vecOrign = pawn.AbsOrigin!;
+
+        pEffect.EffectName = "particles/explosions_fx/explosion_basic.vpcf";
+        pEffect.AbsOrigin!.X = vecOrign.X;
+        pEffect.AbsOrigin.Y = vecOrign.Y;
+        pEffect.AbsOrigin.Z = vecOrign.Z + 10;
+        pEffect.StartActive = true;
+        pEffect.DispatchSpawn();
+
+        pEffect.AddEntityIOEvent("kill", null, null, "", 2.0f);
+    }
+}


Reviews:
user: login: 2vg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T00:21:22Z
user: login: fltuna
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-08T00:59:35Z
user: login: fltuna
body: 修正ありがとうございます！ 一つだけ私の言い忘れによって、修正が必要な点がありましたので確認をお願いします！
state: COMMENTED
author_association: OWNER
submitted_at: 2025-04-08T07:31:09Z
user: login: fltuna
body: めっちゃいい感じ！！！ありがとう！
state: APPROVED
author_association: OWNER
submitted_at: 2025-04-08T08:48:41Z

Review comments:
diff_hunk: @@ -0,0 +1,107 @@
+﻿using CounterStrikeSharp.API;
+using CounterStrikeSharp.API.Core;
+using CounterStrikeSharp.API.Modules.Commands;
+using CounterStrikeSharp.API.Modules.Timers;
+using CounterStrikeSharp.API.Modules.Utils;
+using LupercaliaMGCore.model;
+using Timer = CounterStrikeSharp.API.Modules.Timers.Timer;
+
+namespace LupercaliaMGCore;
+
+public class Rocket: PluginModuleBase
+{
+    public override string PluginModuleName => "Rocket";
+    public readonly string ChatPrefix = $" {ChatColors.Gold}[Rocket]{ChatColors.Default}";
+
+    private Dictionary<CCSPlayerController, bool> isRocketLaunched = new Dictionary<CCSPlayerController, bool>();
+
+    public Rocket(LupercaliaMGCore plugin) : base(plugin)
+    {
+        Plugin.RegisterEventHandler<EventPlayerConnectFull>(OnPlayerConnectFull);
+        Plugin.AddCommand("css_rocket", "launch to space.", CommandRocket);
+
+        Plugin.AddTimer(0.1F, () =>
+        {
+            SimpleLogging.LogDebug("Late initialization for hot reloading rocket.");
+            foreach (CCSPlayerController client in Utilities.GetPlayers())
+            {
+                if (!client.IsValid || client.IsBot || client.IsHLTV)
+                    continue;
+
+                isRocketLaunched[client] = false;
+            }
+        });
+    }
+
+    public override void UnloadModule()
+    {
+        Plugin.RemoveCommand("css_rocket", CommandRocket);
+        Plugin.DeregisterEventHandler<EventPlayerConnectFull>(OnPlayerConnectFull);
+
+        isRocketLaunched.Clear();
+    }
+
+    private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info)
+    {
+        CCSPlayerController? client = @event.Userid;
+
+        if (client == null)
+            return HookResult.Continue;
+
+        isRocketLaunched[client] = false;
+        return HookResult.Continue;
+    }
+
+    private void CommandRocket(CCSPlayerController? client, CommandInfo info)
+    {
+        if (client == null || !client.PawnIsAlive || isRocketLaunched[client])
+            return;
+
+        RocketPerform(client);
+    }
path: LupercaliaMGCore/Rocket.cs
user: login: fltuna
body: もしよければ、PluginSettings.csにRocketの有効無効についてのCVarを追加してもらえると助かります！
created_at: 2025-04-07T15:42:36Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,107 @@
+﻿using CounterStrikeSharp.API;
+using CounterStrikeSharp.API.Core;
+using CounterStrikeSharp.API.Modules.Commands;
+using CounterStrikeSharp.API.Modules.Timers;
+using CounterStrikeSharp.API.Modules.Utils;
+using LupercaliaMGCore.model;
+using Timer = CounterStrikeSharp.API.Modules.Timers.Timer;
+
+namespace LupercaliaMGCore;
+
+public class Rocket: PluginModuleBase
+{
+    public override string PluginModuleName => "Rocket";
+    public readonly string ChatPrefix = $" {ChatColors.Gold}[Rocket]{ChatColors.Default}";
+
+    private Dictionary<CCSPlayerController, bool> isRocketLaunched = new Dictionary<CCSPlayerController, bool>();
+
+    public Rocket(LupercaliaMGCore plugin) : base(plugin)
+    {
+        Plugin.RegisterEventHandler<EventPlayerConnectFull>(OnPlayerConnectFull);
+        Plugin.AddCommand("css_rocket", "launch to space.", CommandRocket);
+
+        Plugin.AddTimer(0.1F, () =>
+        {
+            SimpleLogging.LogDebug("Late initialization for hot reloading rocket.");
+            foreach (CCSPlayerController client in Utilities.GetPlayers())
+            {
+                if (!client.IsValid || client.IsBot || client.IsHLTV)
+                    continue;
+
+                isRocketLaunched[client] = false;
+            }
+        });
+    }
path: LupercaliaMGCore/Rocket.cs
user: login: fltuna
body: コンストラクタでモジュールを初期化するのではなく
```cs
public override void Initialize(){}
```
内に書いてもらえると助かります！
created_at: 2025-04-07T15:42:55Z
author_association: OWNER
diff_hunk: @@ -0,0 +1,107 @@
+﻿using CounterStrikeSharp.API;
+using CounterStrikeSharp.API.Core;
+using CounterStrikeSharp.API.Modules.Commands;
+using CounterStrikeSharp.API.Modules.Timers;
+using CounterStrikeSharp.API.Modules.Utils;
+using LupercaliaMGCore.model;
+using Timer = CounterStrikeSharp.API.Modules.Timers.Timer;
+
+namespace LupercaliaMGCore;
+
+public class Rocket: PluginModuleBase
+{
+    public override string PluginModuleName => "Rocket";
+    public readonly string ChatPrefix = $" {ChatColors.Gold}[Rocket]{ChatColors.Default}";
+
+    private Dictionary<CCSPlayerController, bool> isRocketLaunched = new Dictionary<CCSPlayerController, bool>();
+
+    public Rocket(LupercaliaMGCore plugin) : base(plugin)
+    {
+        Plugin.RegisterEventHandler<EventPlayerConnectFull>(OnPlayerConnectFull);
+        Plugin.AddCommand("css_rocket", "launch to space.", CommandRocket);
+
+        Plugin.AddTimer(0.1F, () =>
+        {
+            SimpleLogging.LogDebug("Late initialization for hot reloading rocket.");
+            foreach (CCSPlayerController client in Utilities.GetPlayers())
+            {
+                if (!client.IsValid || client.IsBot || client.IsHLTV)
+                    continue;
+
+                isRocketLaunched[client] = false;
+            }
+        });
+    }
+
+    public override void UnloadModule()
+    {
+        Plugin.RemoveCommand("css_rocket", CommandRocket);
+        Plugin.DeregisterEventHandler<EventPlayerConnectFull>(OnPlayerConnectFull);
+
+        isRocketLaunched.Clear();
+    }
+
+    private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info)
+    {
+        CCSPlayerController? client = @event.Userid;
+
+        if (client == null)
+            return HookResult.Continue;
+
+        isRocketLaunched[client] = false;
+        return HookResult.Continue;
+    }
+
+    private void CommandRocket(CCSPlayerController? client, CommandInfo info)
+    {
+        if (client == null || !client.PawnIsAlive || isRocketLaunched[client])
path: LupercaliaMGCore/Rocket.cs
user: login: fltuna
body: 細かいことではあるのですが、プレイヤーの生死判定はPlayerUtil.csの PlayerUtil::IsPlayerAlive()で判定するようにするとコードがスッキリします！
https://github.com/fltuna/lupercalia-mg-cs2/blob/e48968ce83eec5502da0472820f72f0d251f74fa/LupercaliaMGCore/util/PlayerUtil.cs#L8-L19
created_at: 2025-04-07T15:44:45Z
author_association: OWNER
diff_hunk: @@ -68,6 +68,7 @@ public override void Load(bool hotReload)
         InitializeModule(new ExternalView(this));
         InitializeModule(new CourseWeapons(this));
         InitializeModule(new VelocityDisplay(this));
+        InitializeModule(new Rocket(this));
path: LupercaliaMGCore/LupercaliaMGCore.cs
user: login: 2vg
body: test
こういう感じでコメント残せる！
created_at: 2025-04-08T00:20:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,15 +54,15 @@ private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventI
 
     private void CommandRocket(CCSPlayerController? client, CommandInfo info)
     {
-        if (client == null || !client.PawnIsAlive || isRocketLaunched[client])
+        if (client == null || !PlayerUtil.IsPlayerAlive(client) || isRocketLaunched[client])
             return;
 
         RocketPerform(client);
     }
 
     private void RocketPerform(CCSPlayerController client)
     {
-        if (!client.PawnIsAlive || client.Pawn.Value == null || isRocketLaunched[client]) return;
+        if (!PlayerUtil.IsPlayerAlive(client)|| client.Pawn.Value == null || isRocketLaunched[client]) return;
path: LupercaliaMGCore/Rocket.cs
user: login: fltuna
body: ごめんなさい！一つ言い忘れてました...
PlayerUtil::IsPlayerAlive()はCCSPlayerControllerはもちろん、CBasePlayerPawnがnullの場合もプレイヤーが生きていない事が確定しているため、暗黙的にfalseを返します。なので、`client.Pawn.Value == null`の処理は必要なかったりします...!

変更後、おそらくコンパイラやIDEの静的解析で、ここより下のコードにある
```cs
CBasePlayerPawn pawn = client.Pawn.Value;
```
が警告を出すと思うので
```cs
CBasePlayerPawn pawn = client.Pawn.Value!;
```
と`!`をつけて回避してくれると嬉しいです！

また、他の同じ処理も同じようにしてもらえると助かります...!

(コードの範囲うまく指定できなくて申し訳ない....!)
created_at: 2025-04-08T07:29:34Z
author_association: OWNER

PR comments:
user: login: fltuna
created_at: 2025-04-07T15:40:49Z
body: 修正してほしい点が複数ありますが、面倒であればこっちの方でマージ後に修正します！
author_association: OWNER
user: login: 2vg
created_at: 2025-04-08T07:19:12Z
body: 3つのコメント指摘を修正済み: https://github.com/fltuna/lupercalia-mg-cs2/pull/36/commits/e1ee1c7f6beb2bc0d74dbc5422f7bcace0d72436
author_association: CONTRIBUTOR
user: login: 2vg
created_at: 2025-04-08T07:38:06Z
body: 修正済み: https://github.com/fltuna/lupercalia-mg-cs2/pull/36/commits/c037808bdd66627a47720f46a68ae47e838ebbba
他のWarnも消えてるはず...！
```
1>------ Build started: Project: LupercaliaMGCore, Configuration: Debug|AnyCPU ------
1>  LupercaliaMGCore -> C:\Users\...\Documents\GitHub\lupercalia-mg-cs2\LupercaliaMGCore\bin\Debug\net8.0\LupercaliaMGCore.dll
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
Build succeeded.
```
author_association: CONTRIBUTOR