mirror of
https://gitlab.com/MaddoScientisto/cirnogodot.git
synced 2026-06-20 22:23:47 +00:00
Basic laser integration
This commit is contained in:
parent
d16f9b3b9d
commit
4011a7c6f6
15 changed files with 1028 additions and 284 deletions
|
|
@ -1634,3 +1634,17 @@
|
||||||
"origin" "-48 -24 28"
|
"origin" "-48 -24 28"
|
||||||
"timeline" "BBS_Test"
|
"timeline" "BBS_Test"
|
||||||
}
|
}
|
||||||
|
// entity 63
|
||||||
|
{
|
||||||
|
"classname" "actor_emitter_floor"
|
||||||
|
"origin" "-100 -132 20"
|
||||||
|
"script_path" "res://Resources/BulletScripts/Laser_Pattern_Test_1.tres"
|
||||||
|
"targetname" "laser_test_trap"
|
||||||
|
}
|
||||||
|
// entity 64
|
||||||
|
{
|
||||||
|
"classname" "actor_controlpad"
|
||||||
|
"origin" "-140 -100 36"
|
||||||
|
"target" "laser_test_trap"
|
||||||
|
"activationtype" "Toggle"
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,11 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="Export.ps1" />
|
<Content Include="Export.ps1" />
|
||||||
|
<Content Include="LASER_INTEGRATION.md" />
|
||||||
|
<Content Include="LASER_INTEGRATION_SUMMARY.md" />
|
||||||
|
<Content Include="LASER_NULL_FIX.md" />
|
||||||
|
<Content Include="LASER_PATTERN_FIX.md" />
|
||||||
|
<Content Include="LASER_WITH_EMITTERS.md" />
|
||||||
<Content Include="omnisharp.json" />
|
<Content Include="omnisharp.json" />
|
||||||
<Content Include="Publish.ps1" />
|
<Content Include="Publish.ps1" />
|
||||||
<Content Include="Scripts\Resources\Events\tsconfig.json" />
|
<Content Include="Scripts\Resources\Events\tsconfig.json" />
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1,4 +1,4 @@
|
||||||
[gd_resource type="Resource" script_class="BulletScript" load_steps=6 format=3 uid="uid://cmra7n6so1x4u"]
|
[gd_resource type="Resource" script_class="BulletScript" format=3 uid="uid://cmra7n6so1x4u"]
|
||||||
|
|
||||||
[ext_resource type="Resource" uid="uid://dqnvesdj0dk3v" path="res://Resources/Bullets/simple_enemy_bullet.tres" id="1_lkipe"]
|
[ext_resource type="Resource" uid="uid://dqnvesdj0dk3v" path="res://Resources/Bullets/simple_enemy_bullet.tres" id="1_lkipe"]
|
||||||
[ext_resource type="Script" uid="uid://b5s5mjuk1rng5" path="res://Scripts/Resources/TimeModifier.cs" id="2_omk5v"]
|
[ext_resource type="Script" uid="uid://b5s5mjuk1rng5" path="res://Scripts/Resources/TimeModifier.cs" id="2_omk5v"]
|
||||||
|
|
|
||||||
13
Resources/BulletScripts/LaserTest_1.tres
Normal file
13
Resources/BulletScripts/LaserTest_1.tres
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
[gd_resource type="Resource" script_class="LaserPattern" format=3 uid="uid://cdurqf6rk3utj"]
|
||||||
|
|
||||||
|
[ext_resource type="Resource" uid="uid://dgwsfxku5csp5" path="res://Resources/BulletScripts/Laser_Bullet_Resource_test.tres" id="1_22aun"]
|
||||||
|
[ext_resource type="Script" uid="uid://b5s5mjuk1rng5" path="res://Scripts/Resources/TimeModifier.cs" id="1_l746i"]
|
||||||
|
[ext_resource type="Script" uid="uid://ckcuhstelos4x" path="res://Scripts/AttackPatterns/LaserPattern.cs" id="2_22aun"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("2_22aun")
|
||||||
|
BulletResource = ExtResource("1_22aun")
|
||||||
|
bulletCount = 8
|
||||||
|
duration = 5.0
|
||||||
|
ShotsPerBurst = 1
|
||||||
|
metadata/_custom_type_script = "uid://ckcuhstelos4x"
|
||||||
18
Resources/BulletScripts/Laser_Bullet_Resource_test.tres
Normal file
18
Resources/BulletScripts/Laser_Bullet_Resource_test.tres
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
[gd_resource type="Resource" script_class="BulletResource" format=3 uid="uid://dgwsfxku5csp5"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://dslyrfcej3g2n" path="res://Scripts/Resources/BulletResource.cs" id="1_0ejwi"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bkv1sf54u3agr" path="res://Scenes/Weapons/Bullets/Laser_Bullet_3D.tscn" id="1_qd1t8"]
|
||||||
|
[ext_resource type="Script" uid="uid://5utr3255hbln" path="res://Scripts/Actors/3D/LaserConfig.cs" id="2_2g5bd"]
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_5e6ih"]
|
||||||
|
script = ExtResource("2_2g5bd")
|
||||||
|
metadata/_custom_type_script = "uid://5utr3255hbln"
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1_0ejwi")
|
||||||
|
BulletScene = ExtResource("1_qd1t8")
|
||||||
|
BulletSpeed = 0.0
|
||||||
|
TimeModifiers = []
|
||||||
|
IsLaser = true
|
||||||
|
LaserConfig = SubResource("Resource_5e6ih")
|
||||||
|
metadata/_custom_type_script = "uid://dslyrfcej3g2n"
|
||||||
9
Resources/BulletScripts/Laser_Pattern_Test_1.tres
Normal file
9
Resources/BulletScripts/Laser_Pattern_Test_1.tres
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
[gd_resource type="Resource" script_class="BulletScript3D" format=3 uid="uid://bog816y8fx3w5"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://w8hcpu68ssq" path="res://Scripts/Resources/BulletScripts/BulletScript3D.cs" id="1_70ed4"]
|
||||||
|
[ext_resource type="Resource" uid="uid://cdurqf6rk3utj" path="res://Resources/BulletScripts/LaserTest_1.tres" id="1_kfu43"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1_70ed4")
|
||||||
|
Patterns = Array[Object]([ExtResource("1_kfu43")])
|
||||||
|
metadata/_custom_type_script = "uid://w8hcpu68ssq"
|
||||||
33
Resources/Bullets/enemy_laser_3d.tres
Normal file
33
Resources/Bullets/enemy_laser_3d.tres
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
[gd_resource type="Resource" script_class="BulletResource" load_steps=4 format=3 uid="uid://laser3d_example"]
|
||||||
|
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bkv1sf54u3agr" path="res://Scenes/Weapons/Bullets/Laser_Bullet_3D.tscn" id="1_laser_scene"]
|
||||||
|
[ext_resource type="Script" uid="uid://dslyrfcej3g2n" path="res://Scripts/Resources/BulletResource.cs" id="2_bullet_res"]
|
||||||
|
[ext_resource type="Script" uid="uid://5utr3255hbln" path="res://Scripts/Actors/3D/LaserConfig.cs" id="3_laser_config"]
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="LaserConfig_example"]
|
||||||
|
script = ExtResource("3_laser_config")
|
||||||
|
MaxLength = 100.0
|
||||||
|
WarningRadius = 0.025
|
||||||
|
DamageRadius = 0.05
|
||||||
|
WarningDuration = 0.5
|
||||||
|
ExpansionDelay = 0.0
|
||||||
|
ExpansionDuration = 0.2
|
||||||
|
ActiveDuration = 2.0
|
||||||
|
GeometryLayer = 1
|
||||||
|
Damagelayer = 2
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("2_bullet_res")
|
||||||
|
BulletScene = ExtResource("1_laser_scene")
|
||||||
|
BulletSpeed = 0.0
|
||||||
|
BulletDamage = 10.0
|
||||||
|
MaxDamage = 10.0
|
||||||
|
Knockback = 0.0
|
||||||
|
LifeTime = 3.0
|
||||||
|
Owner = 2
|
||||||
|
DamageType = 0
|
||||||
|
GrazeValue = 0.5
|
||||||
|
Attributes = 0
|
||||||
|
TimeModifiers = []
|
||||||
|
IsLaser = true
|
||||||
|
LaserConfig = SubResource("LaserConfig_example")
|
||||||
47
Scenes/Weapons/Bullets/Laser_Bullet_3D.tscn
Normal file
47
Scenes/Weapons/Bullets/Laser_Bullet_3D.tscn
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
[gd_scene format=3 uid="uid://bkv1sf54u3agr"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://Scripts/Weapons/LaserBullet3D.cs" id="1_laser_bullet"]
|
||||||
|
[ext_resource type="Script" uid="uid://5utr3255hbln" path="res://Scripts/Actors/3D/LaserConfig.cs" id="2_l0r7c"]
|
||||||
|
[ext_resource type="Shader" uid="uid://cd8n7vsgxkfib" path="res://Shaders/minimal_laser.gdshader" id="3_6ff8g"]
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_awu2v"]
|
||||||
|
script = ExtResource("2_l0r7c")
|
||||||
|
MaxLength = 100.0
|
||||||
|
WarningRadius = 0.025
|
||||||
|
DamageRadius = 0.05
|
||||||
|
WarningDuration = 0.5
|
||||||
|
ExpansionDelay = 0.0
|
||||||
|
ExpansionDuration = 0.2
|
||||||
|
ActiveDuration = 1.0
|
||||||
|
GeometryLayer = 1
|
||||||
|
Damagelayer = 2
|
||||||
|
metadata/_custom_type_script = "uid://5utr3255hbln"
|
||||||
|
|
||||||
|
[sub_resource type="ShaderMaterial" id="ShaderMaterial_fmfkd"]
|
||||||
|
render_priority = 0
|
||||||
|
shader = ExtResource("3_6ff8g")
|
||||||
|
shader_parameter/beam_color = Color(1, 0.2, 0.2, 1)
|
||||||
|
shader_parameter/glow_radius = 0.2
|
||||||
|
shader_parameter/noise_speed = 1.0
|
||||||
|
shader_parameter/flicker_strength = 0.2
|
||||||
|
|
||||||
|
[sub_resource type="CylinderMesh" id="CylinderMesh_tqorp"]
|
||||||
|
|
||||||
|
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_tqorp"]
|
||||||
|
|
||||||
|
[node name="Laser" type="Area3D"]
|
||||||
|
script = ExtResource("1_laser_bullet")
|
||||||
|
|
||||||
|
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
|
||||||
|
transform = Transform3D(0.06, 0, 0, 0, 1, 0, 0, 0, 0.06, 0, 0, 0)
|
||||||
|
material_override = SubResource("ShaderMaterial_fmfkd")
|
||||||
|
mesh = SubResource("CylinderMesh_tqorp")
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
|
||||||
|
shape = SubResource("CapsuleShape3D_tqorp")
|
||||||
|
|
||||||
|
[node name="RayCast3D" type="RayCast3D" parent="."]
|
||||||
|
|
||||||
|
[connection signal="area_entered" from="." to="." method="_on_area_entered"]
|
||||||
|
[connection signal="body_entered" from="." to="." method="_on_body_entered"]
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
namespace Cirno.Scripts.Actors._3D;
|
namespace Cirno.Scripts.Actors._3D;
|
||||||
|
|
||||||
[GlobalClass]
|
[GlobalClass]
|
||||||
|
[Tool]
|
||||||
public partial class LaserConfig : Resource
|
public partial class LaserConfig : Resource
|
||||||
{
|
{
|
||||||
[Export] public float MaxLength = 50f;
|
[Export] public float MaxLength = 50f;
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,15 @@ namespace Cirno.Scripts.Actors;
|
||||||
|
|
||||||
public partial class BulletSpawner3D : Node3D
|
public partial class BulletSpawner3D : Node3D
|
||||||
{
|
{
|
||||||
|
|
||||||
public void SpawnBullet(BulletInfo bulletInfo, Vector3 position)
|
public void SpawnBullet(BulletInfo bulletInfo, Vector3 position)
|
||||||
{
|
{
|
||||||
|
// Check if this is a laser
|
||||||
|
if (bulletInfo.IsLaser && bulletInfo.LaserConfig != null)
|
||||||
|
{
|
||||||
|
SpawnLaser(bulletInfo, position);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int count = bulletInfo.BulletCount;
|
int count = bulletInfo.BulletCount;
|
||||||
|
|
||||||
// Choose base direction (defaults to +X)
|
// Choose base direction (defaults to +X)
|
||||||
|
|
@ -66,5 +72,28 @@ public partial class BulletSpawner3D : Node3D
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SpawnLaser(BulletInfo bulletInfo, Vector3 position)
|
||||||
|
{
|
||||||
|
// Lasers don't use bullet count/spread the same way
|
||||||
|
// Each laser is typically a single beam
|
||||||
|
LaserBullet3D laser = PoolingManager.Instance.SpawnBullet<LaserBullet3D>(bulletInfo.OriginalBulletResource);
|
||||||
|
laser.GlobalPosition = position;
|
||||||
|
|
||||||
|
// Apply modifiers if present
|
||||||
|
if (bulletInfo.Modifier is not null)
|
||||||
|
{
|
||||||
|
bulletInfo = bulletInfo.Modifier.ModifyBullet(bulletInfo, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
laser.Initialize(bulletInfo);
|
||||||
|
|
||||||
|
// Set the laser direction
|
||||||
|
Vector2 baseDirection = bulletInfo.Direction == Vector2.Zero
|
||||||
|
? Vector2.Right
|
||||||
|
: bulletInfo.Direction.Normalized();
|
||||||
|
|
||||||
|
laser.SetDirection(baseDirection);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,17 +1,20 @@
|
||||||
using Cirno.Scripts.Components;
|
using Cirno.Scripts.Actors;
|
||||||
|
using Cirno.Scripts.Components;
|
||||||
|
using Cirno.Scripts.Enums;
|
||||||
|
using Cirno.Scripts.Utils;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
namespace Cirno.Scripts.AttackPatterns;
|
namespace Cirno.Scripts.AttackPatterns;
|
||||||
|
|
||||||
[GlobalClass]
|
[GlobalClass]
|
||||||
[Tool]
|
[Tool]
|
||||||
public partial class LaserPattern : SpiralPattern
|
public partial class LaserPattern : ShootingPattern3D
|
||||||
{
|
{
|
||||||
[ExportGroup("Laser")][Export] public float SpawnDelay { get; set; } = 0.3f; // Delay before beam appears
|
[ExportGroup("Laser")][Export] public float SpawnDelay { get; set; } = 0.3f; // Delay before beam appears
|
||||||
[ExportGroup("Laser")][Export] public float PreFireTime { get; set; } = 0.5f; // Time before laser becomes lethal
|
[ExportGroup("Laser")][Export] public float PreFireTime { get; set; } = 0.5f; // Time before laser becomes lethal
|
||||||
[ExportGroup("Laser")][Export] public float LethalTime { get; set; } = 1.5f; // Time laser remains lethal
|
[ExportGroup("Laser")][Export] public float LethalTime { get; set; } = 1.5f; // Time laser remains lethal
|
||||||
[ExportGroup("Laser")][Export] public Color PreFireColor { get; set; } = new Color(1, 0, 0, 0.5f); // Thin red beam
|
[ExportGroup("Laser")][Export] public Color PreFireColor { get; set; } = new Color(1, 0, 0, 0.5f); // Thin red beam
|
||||||
[ExportGroup("Laser")][Export] public Color LethalColor { get; set; } = new Color(1, 0, 0, 1.0f); // Thicker beam
|
[ExportGroup("Laser")][Export] public Color LethalColor { get; set; } = Colors.Red; // Thicker beam
|
||||||
|
|
||||||
protected override BulletInfo MakeBullet(Vector2 position, int count = 1, float spread = 0f, float rotationOffset = 0f)
|
protected override BulletInfo MakeBullet(Vector2 position, int count = 1, float spread = 0f, float rotationOffset = 0f)
|
||||||
{
|
{
|
||||||
|
|
@ -26,4 +29,194 @@ public partial class LaserPattern : SpiralPattern
|
||||||
|
|
||||||
return bf;
|
return bf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override IPatternMachine MakeMachine(Node parent)
|
||||||
|
{
|
||||||
|
return new LaserPatternMachine(this, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LaserPatternMachine(LaserPattern pattern, Node parent) : IPatternMachine
|
||||||
|
{
|
||||||
|
public Node Parent => parent;
|
||||||
|
public IScriptHost3D ScriptHost { get; private set; }
|
||||||
|
private double _timer;
|
||||||
|
private double _burstTimer;
|
||||||
|
private BulletSpawner3D _spawner;
|
||||||
|
private ShootStatus _state = ShootStatus.Idle;
|
||||||
|
private int _burstBullets;
|
||||||
|
private int _currentBurstOffset;
|
||||||
|
|
||||||
|
public void Start()
|
||||||
|
{
|
||||||
|
ScriptHost = Parent as IScriptHost3D;
|
||||||
|
|
||||||
|
_timer = 0;
|
||||||
|
_burstBullets = pattern.ShotsPerBurst;
|
||||||
|
_burstTimer = pattern.burstInterval;
|
||||||
|
_spawner = parent.GetNode<BulletSpawner3D>("BulletSpawner3D");
|
||||||
|
|
||||||
|
_state = pattern.Delay == 0 ? ShootStatus.Shooting : ShootStatus.Idle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdatePattern(double delta)
|
||||||
|
{
|
||||||
|
switch (_state)
|
||||||
|
{
|
||||||
|
case ShootStatus.Idle:
|
||||||
|
IdleUpdate(delta);
|
||||||
|
break;
|
||||||
|
case ShootStatus.Done:
|
||||||
|
return;
|
||||||
|
case ShootStatus.Shooting:
|
||||||
|
ShootingUpdate(delta);
|
||||||
|
break;
|
||||||
|
case ShootStatus.WaitingBurst:
|
||||||
|
WaitingBurstUpdate(delta);
|
||||||
|
break;
|
||||||
|
case ShootStatus.WaitingReload:
|
||||||
|
WaitingReloadUpdate(delta);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pattern.duration > -1 && _timer >= pattern.duration)
|
||||||
|
{
|
||||||
|
_state = ShootStatus.Done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void IdleUpdate(double delta)
|
||||||
|
{
|
||||||
|
_timer += delta;
|
||||||
|
if (_timer >= pattern.Delay)
|
||||||
|
{
|
||||||
|
_state = ShootStatus.Shooting;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WaitingBurstUpdate(double delta)
|
||||||
|
{
|
||||||
|
_timer += delta;
|
||||||
|
_burstTimer += delta;
|
||||||
|
|
||||||
|
if (_burstTimer >= pattern.burstInterval)
|
||||||
|
{
|
||||||
|
_state = ShootStatus.Shooting;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WaitingReloadUpdate(double delta)
|
||||||
|
{
|
||||||
|
_timer += delta;
|
||||||
|
_burstTimer += delta;
|
||||||
|
|
||||||
|
if (_burstTimer >= pattern.BurstRate)
|
||||||
|
{
|
||||||
|
_burstBullets = pattern.ShotsPerBurst;
|
||||||
|
_state = ShootStatus.Shooting;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShootingUpdate(double delta)
|
||||||
|
{
|
||||||
|
_timer += delta;
|
||||||
|
_burstTimer = 0;
|
||||||
|
Shoot();
|
||||||
|
_burstBullets--;
|
||||||
|
|
||||||
|
if (_burstBullets <= 0)
|
||||||
|
{
|
||||||
|
if (pattern.LoopType == LoopType.PlayOnce)
|
||||||
|
{
|
||||||
|
_state = ShootStatus.Done;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_state = ShootStatus.WaitingReload;
|
||||||
|
_currentBurstOffset++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_state = ShootStatus.WaitingBurst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Shoot()
|
||||||
|
{
|
||||||
|
if (pattern.BulletResource == null)
|
||||||
|
{
|
||||||
|
GD.PushError("LaserPattern: BulletResource is null! Cannot spawn laser.");
|
||||||
|
_state = ShootStatus.Done;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
float angleOffset = pattern._rotationOffset + (float)(pattern.rotationSpeed * _timer) +
|
||||||
|
(float)pattern.BurstRotationSpeed * _currentBurstOffset;
|
||||||
|
|
||||||
|
Vector2 direction = pattern.BulletResource.Direction;
|
||||||
|
|
||||||
|
// Rotate with parent rotation
|
||||||
|
if (pattern.UseParentRotationOffset)
|
||||||
|
{
|
||||||
|
direction = direction.Rotated(-_spawner.GlobalRotation.Y + Mathf.DegToRad(90));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle player targeting for 3D
|
||||||
|
if (pattern._targetPlayer && GameController.Instance.PlayerPosition.HasValue)
|
||||||
|
{
|
||||||
|
if (pattern._predictPlayer && GameController.Instance.PlayerVelocity.HasValue)
|
||||||
|
{
|
||||||
|
var predictedDirection = MathFunctions.PredictInterceptPosition(
|
||||||
|
_spawner.GlobalPosition.ToVector2(),
|
||||||
|
GameController.Instance.PlayerPosition.Value.ToVector2(),
|
||||||
|
GameController.Instance.PlayerVelocity.Value.ToVector2(),
|
||||||
|
pattern.BulletResource.BulletSpeed);
|
||||||
|
|
||||||
|
if (predictedDirection.HasValue)
|
||||||
|
{
|
||||||
|
direction = (predictedDirection.Value - _spawner.GlobalPosition.ToVector2()).Normalized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
direction = (GameController.Instance.PlayerPosition.Value.ToVector2() -
|
||||||
|
_spawner.GlobalPosition.ToVector2()).Normalized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var spawnPosition = _spawner.GlobalPosition + pattern.EmitterOffset;
|
||||||
|
|
||||||
|
// Create laser bullet with laser-specific properties
|
||||||
|
var bullet = pattern.MakeBullet(
|
||||||
|
spawnPosition.ToVector2(),
|
||||||
|
pattern.bulletCount,
|
||||||
|
pattern.spread,
|
||||||
|
angleOffset);
|
||||||
|
|
||||||
|
bullet.Direction = direction;
|
||||||
|
|
||||||
|
_spawner.SpawnBullet(bullet, spawnPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsComplete()
|
||||||
|
{
|
||||||
|
if (!pattern.WaitForCompletion) return _state is ShootStatus.Done;
|
||||||
|
if (_state is not ShootStatus.Done) return false;
|
||||||
|
|
||||||
|
if (pattern.duration > -1)
|
||||||
|
{
|
||||||
|
return (_timer >= pattern.duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum ShootStatus
|
||||||
|
{
|
||||||
|
Idle,
|
||||||
|
Shooting,
|
||||||
|
WaitingBurst,
|
||||||
|
WaitingReload,
|
||||||
|
Done
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Cirno.Scripts.Actors._3D;
|
||||||
using Cirno.Scripts.Resources;
|
using Cirno.Scripts.Resources;
|
||||||
using Cirno.Scripts.Utils;
|
using Cirno.Scripts.Utils;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
@ -39,10 +40,13 @@ public class BulletInfo(BulletResource originalBulletResource)
|
||||||
|
|
||||||
#region Laser
|
#region Laser
|
||||||
public bool IsLaser { get; set; }
|
public bool IsLaser { get; set; }
|
||||||
public float SpawnDelay { get; set; } = 0.3f; // Delay before beam appears
|
public LaserConfig LaserConfig { get; set; } // For 3D lasers
|
||||||
public float PreFireTime { get; set; } = 0.5f; // Time before laser becomes lethal
|
|
||||||
public float LethalTime { get; set; } = 1.5f; // Time laser remains lethal
|
// 2D Laser properties (legacy support)
|
||||||
public Color PreFireColor { get; set; } = new Color(1, 0, 0, 0.5f); // Thin red beam
|
public float SpawnDelay { get; set; } = 0.3f;
|
||||||
public Color LethalColor { get; set; } = new Color(1, 0, 0, 1.0f); // Thicker beam
|
public float PreFireTime { get; set; } = 0.5f;
|
||||||
|
public float LethalTime { get; set; } = 1.5f;
|
||||||
|
public Color PreFireColor { get; set; } = new Color(1, 0, 0, 0.5f);
|
||||||
|
public Color LethalColor { get; set; } = new Color(1, 0, 0, 1.0f);
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Cirno.Scripts.Actors._3D;
|
||||||
using Cirno.Scripts.Components;
|
using Cirno.Scripts.Components;
|
||||||
using Cirno.Scripts.Utils;
|
using Cirno.Scripts.Utils;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
@ -33,6 +34,11 @@ public partial class BulletResource : Resource
|
||||||
public BulletCreationModifier Modifier;
|
public BulletCreationModifier Modifier;
|
||||||
[Export] public Array<TimeModifier> TimeModifiers;
|
[Export] public Array<TimeModifier> TimeModifiers;
|
||||||
|
|
||||||
|
// Laser-specific properties
|
||||||
|
[ExportGroup("Laser Settings")]
|
||||||
|
[Export] public bool IsLaser { get; set; }
|
||||||
|
[Export] public LaserConfig LaserConfig { get; set; }
|
||||||
|
|
||||||
public BulletInfo MakeBullet(Vector2 position, int count = 1, float spread = 0f, float rotationOffset = 0f)
|
public BulletInfo MakeBullet(Vector2 position, int count = 1, float spread = 0f, float rotationOffset = 0f)
|
||||||
{
|
{
|
||||||
return new BulletInfo(this)
|
return new BulletInfo(this)
|
||||||
|
|
@ -59,6 +65,8 @@ public partial class BulletResource : Resource
|
||||||
//Grazeable = Attributes.HasFlag(BulletFlags.Grazeable),
|
//Grazeable = Attributes.HasFlag(BulletFlags.Grazeable),
|
||||||
GrazeValue = GrazeValue,
|
GrazeValue = GrazeValue,
|
||||||
Attributes = Attributes,
|
Attributes = Attributes,
|
||||||
|
IsLaser = IsLaser,
|
||||||
|
LaserConfig = LaserConfig
|
||||||
// TimeModifiers = TimeModifiers?.Where(mod => mod is TimeModifier).Cast<TimeModifier>().Select(m => new ModifierWrapper()
|
// TimeModifiers = TimeModifiers?.Where(mod => mod is TimeModifier).Cast<TimeModifier>().Select(m => new ModifierWrapper()
|
||||||
// {
|
// {
|
||||||
// TimeModifier = m,
|
// TimeModifier = m,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,404 @@
|
||||||
namespace Cirno.Scripts.Weapons;
|
using Cirno.Scripts.Actors._3D;
|
||||||
|
using Cirno.Scripts.Components;
|
||||||
|
using Cirno.Scripts.Controllers;
|
||||||
|
using Cirno.Scripts.Utils;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
public partial class LaserBullet3D : Bullet3D
|
namespace Cirno.Scripts.Weapons;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Laser implementation that conforms to the IBullet interface.
|
||||||
|
/// Wraps the Laser class functionality to integrate with the bullet system.
|
||||||
|
/// </summary>
|
||||||
|
public partial class LaserBullet3D : Area3D, IBullet
|
||||||
{
|
{
|
||||||
//private line3d
|
private BulletInfo _bulletInfo;
|
||||||
|
|
||||||
|
public float Speed { get; set; }
|
||||||
|
public BulletOwner BulletOwner => _bulletInfo?.Owner ?? BulletOwner.None;
|
||||||
|
public float Damage => _bulletInfo?.Damage ?? 1;
|
||||||
|
public DamageType DamageType => _bulletInfo?.DamageType ?? DamageType.Neutral;
|
||||||
|
public BulletInfo BulletInfo => _bulletInfo;
|
||||||
|
public bool IsGrazed { get; set; }
|
||||||
|
public bool IsFrozen { get; private set; }
|
||||||
|
public bool Enabled { get; private set; }
|
||||||
|
|
||||||
|
private Vector2 _direction = Vector2.Right;
|
||||||
|
private double _elapsedTime;
|
||||||
|
|
||||||
|
private MeshInstance3D _mesh;
|
||||||
|
private CollisionShape3D _collision;
|
||||||
|
private RayCast3D _ray;
|
||||||
|
private ShaderMaterial _beamMaterial;
|
||||||
|
private float _currentRadius;
|
||||||
|
private Laser.LaserState _state = Laser.LaserState.Inactive;
|
||||||
|
private float _stateTimer;
|
||||||
|
private Vector3 _origin;
|
||||||
|
private Vector3 _laserDirection;
|
||||||
|
private float _currentLength;
|
||||||
|
|
||||||
|
[Signal]
|
||||||
|
public delegate void OnDestroyEventHandler();
|
||||||
|
|
||||||
|
[Signal]
|
||||||
|
public delegate void InitializedEventHandler();
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
_mesh = GetNode<MeshInstance3D>("MeshInstance3D");
|
||||||
|
_collision = GetNode<CollisionShape3D>("CollisionShape3D");
|
||||||
|
_ray = GetNode<RayCast3D>("RayCast3D");
|
||||||
|
_beamMaterial = _mesh.MaterialOverride as ShaderMaterial;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize(BulletInfo bulletInfo)
|
||||||
|
{
|
||||||
|
_bulletInfo = bulletInfo;
|
||||||
|
_elapsedTime = 0f;
|
||||||
|
_direction = bulletInfo.Direction.Normalized();
|
||||||
|
|
||||||
|
IsGrazed = false;
|
||||||
|
IsFrozen = false;
|
||||||
|
|
||||||
|
// Setup laser from direction
|
||||||
|
_origin = GlobalPosition;
|
||||||
|
_laserDirection = new Vector3(_direction.X, 0, _direction.Y).Normalized();
|
||||||
|
|
||||||
|
if (_bulletInfo.LaserConfig != null)
|
||||||
|
{
|
||||||
|
_ray.CollisionMask = _bulletInfo.LaserConfig.GeometryLayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChangeCollisionStateDeferred(true);
|
||||||
|
|
||||||
|
StartLaser();
|
||||||
|
EmitSignal(SignalName.Initialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartLaser()
|
||||||
|
{
|
||||||
|
_mesh.Scale = Vector3.One;
|
||||||
|
_collision.Scale = Vector3.One;
|
||||||
|
|
||||||
|
_stateTimer = 0f;
|
||||||
|
_state = _bulletInfo.LaserConfig.WarningDuration > 0
|
||||||
|
? Laser.LaserState.Warning
|
||||||
|
: Laser.LaserState.Expanding;
|
||||||
|
|
||||||
|
UpdateVisualOrientation();
|
||||||
|
SetRadius(_bulletInfo.LaserConfig.WarningRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Enable()
|
||||||
|
{
|
||||||
|
Enabled = true;
|
||||||
|
Show();
|
||||||
|
if (_collision != null)
|
||||||
|
{
|
||||||
|
_collision.SetDeferred(CollisionShape3D.PropertyName.Disabled, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Disable(bool hideSprite = true)
|
||||||
|
{
|
||||||
|
Enabled = false;
|
||||||
|
if (hideSprite)
|
||||||
|
{
|
||||||
|
Hide();
|
||||||
|
}
|
||||||
|
if (_collision != null)
|
||||||
|
{
|
||||||
|
_collision.SetDeferred(CollisionShape3D.PropertyName.Disabled, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Graze()
|
||||||
|
{
|
||||||
|
if (!Enabled) return;
|
||||||
|
IsGrazed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RotateBullet(float degrees)
|
||||||
|
{
|
||||||
|
float radians = Mathf.DegToRad(degrees);
|
||||||
|
_direction = _direction.Rotated(radians).Normalized();
|
||||||
|
_laserDirection = new Vector3(_direction.X, 0, _direction.Y).Normalized();
|
||||||
|
UpdateVisualOrientation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RotateSpriteDegrees(float degrees)
|
||||||
|
{
|
||||||
|
// Lasers don't rotate sprite independently
|
||||||
|
RotateBullet(degrees);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RotateSprite(float radians)
|
||||||
|
{
|
||||||
|
// Lasers don't rotate sprite independently
|
||||||
|
RotateBullet(Mathf.RadToDeg(radians));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FacePlayer()
|
||||||
|
{
|
||||||
|
if (GameController.Instance.PlayerPosition.HasValue)
|
||||||
|
{
|
||||||
|
_direction = (GameController.Instance.PlayerPosition.Value.ToVector2() - GlobalPosition.ToVector2()).Normalized();
|
||||||
|
_laserDirection = new Vector3(_direction.X, 0, _direction.Y).Normalized();
|
||||||
|
UpdateVisualOrientation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetDirection(Vector2 direction)
|
||||||
|
{
|
||||||
|
_direction = direction.Normalized();
|
||||||
|
_laserDirection = new Vector3(_direction.X, 0, _direction.Y).Normalized();
|
||||||
|
UpdateVisualOrientation();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanHit(BulletOwner bulletOwner, BulletOwner targetGroup)
|
||||||
|
{
|
||||||
|
if (bulletOwner == BulletOwner.None || targetGroup == BulletOwner.None)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return bulletOwner != targetGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RequestCollisionDestruction()
|
||||||
|
{
|
||||||
|
// Lasers typically don't get destroyed on collision
|
||||||
|
if (_bulletInfo.DestroyOnCollision)
|
||||||
|
{
|
||||||
|
Destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Freeze()
|
||||||
|
{
|
||||||
|
IsFrozen = true;
|
||||||
|
EmitSignal(SignalName.OnDestroy);
|
||||||
|
PoolingManager.Instance.DisableBullet(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _Process(double delta)
|
||||||
|
{
|
||||||
|
if (!Enabled) return;
|
||||||
|
|
||||||
|
_elapsedTime += delta;
|
||||||
|
|
||||||
|
if (_elapsedTime >= _bulletInfo.LifeTime)
|
||||||
|
{
|
||||||
|
Destroy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_state == Laser.LaserState.Finished)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_stateTimer += (float)delta;
|
||||||
|
|
||||||
|
UpdateRaycast();
|
||||||
|
UpdateBeam();
|
||||||
|
|
||||||
|
switch (_state)
|
||||||
|
{
|
||||||
|
case Laser.LaserState.Warning:
|
||||||
|
if (_stateTimer >= _bulletInfo.LaserConfig.WarningDuration)
|
||||||
|
TransitionTo(Laser.LaserState.Expanding);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Laser.LaserState.Expanding:
|
||||||
|
HandleExpansion();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Laser.LaserState.Active:
|
||||||
|
if (_bulletInfo.LaserConfig.ActiveDuration >= 0 &&
|
||||||
|
_stateTimer >= _bulletInfo.LaserConfig.ActiveDuration)
|
||||||
|
TransitionTo(Laser.LaserState.Finished);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TransitionTo(Laser.LaserState next)
|
||||||
|
{
|
||||||
|
_state = next;
|
||||||
|
_stateTimer = 0f;
|
||||||
|
|
||||||
|
switch (next)
|
||||||
|
{
|
||||||
|
case Laser.LaserState.Expanding:
|
||||||
|
if (_bulletInfo.LaserConfig.ExpansionDuration <= 0f)
|
||||||
|
{
|
||||||
|
SetRadius(_bulletInfo.LaserConfig.DamageRadius);
|
||||||
|
EnableCollision();
|
||||||
|
TransitionTo(Laser.LaserState.Active);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Laser.LaserState.Active:
|
||||||
|
EnableCollision();
|
||||||
|
SetRadius(_bulletInfo.LaserConfig.DamageRadius);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Laser.LaserState.Finished:
|
||||||
|
Destroy();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleExpansion()
|
||||||
|
{
|
||||||
|
if (_stateTimer < _bulletInfo.LaserConfig.ExpansionDelay)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var t = Mathf.Clamp(
|
||||||
|
(_stateTimer - _bulletInfo.LaserConfig.ExpansionDelay) /
|
||||||
|
Mathf.Max(_bulletInfo.LaserConfig.ExpansionDuration, 0.001f),
|
||||||
|
0f, 1f
|
||||||
|
);
|
||||||
|
|
||||||
|
var radius = Mathf.Lerp(_bulletInfo.LaserConfig.WarningRadius,
|
||||||
|
_bulletInfo.LaserConfig.DamageRadius, t);
|
||||||
|
SetRadius(radius);
|
||||||
|
|
||||||
|
if (t >= 1f)
|
||||||
|
TransitionTo(Laser.LaserState.Active);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateRaycast()
|
||||||
|
{
|
||||||
|
_ray.TargetPosition = _laserDirection * _bulletInfo.LaserConfig.MaxLength;
|
||||||
|
_ray.ForceRaycastUpdate();
|
||||||
|
|
||||||
|
_currentLength = _ray.IsColliding()
|
||||||
|
? GlobalPosition.DistanceTo(_ray.GetCollisionPoint())
|
||||||
|
: _bulletInfo.LaserConfig.MaxLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateVisualOrientation()
|
||||||
|
{
|
||||||
|
Basis look = Basis.LookingAt(_laserDirection, Vector3.Up);
|
||||||
|
Basis correction = new Basis(Vector3.Right, Mathf.Pi / 2f);
|
||||||
|
Basis finalBasis = look * correction;
|
||||||
|
|
||||||
|
_mesh.Basis = finalBasis;
|
||||||
|
_collision.Basis = finalBasis;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateBeam()
|
||||||
|
{
|
||||||
|
if (_mesh.Mesh is CylinderMesh cyl)
|
||||||
|
cyl.Height = _currentLength;
|
||||||
|
|
||||||
|
if (_collision.Shape is CapsuleShape3D capsule)
|
||||||
|
capsule.Height = _currentLength;
|
||||||
|
|
||||||
|
Vector3 center = _origin + _laserDirection * (_currentLength * 0.5f);
|
||||||
|
|
||||||
|
Transform3D meshXform = _mesh.GlobalTransform;
|
||||||
|
meshXform.Origin = center;
|
||||||
|
_mesh.GlobalTransform = meshXform;
|
||||||
|
|
||||||
|
Transform3D colXform = _collision.GlobalTransform;
|
||||||
|
colXform.Origin = center;
|
||||||
|
_collision.GlobalTransform = colXform;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetRadius(float radius)
|
||||||
|
{
|
||||||
|
_currentRadius = radius;
|
||||||
|
|
||||||
|
if (_mesh.Mesh is CylinderMesh cyl)
|
||||||
|
{
|
||||||
|
cyl.TopRadius = radius;
|
||||||
|
cyl.BottomRadius = radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_collision.Shape is CapsuleShape3D capsule)
|
||||||
|
capsule.Radius = radius;
|
||||||
|
|
||||||
|
UpdateMaterial();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnableCollision()
|
||||||
|
{
|
||||||
|
ChangeCollisionStateDeferred(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ChangeCollisionStateDeferred(bool value)
|
||||||
|
{
|
||||||
|
_collision.SetDeferred(CollisionShape3D.PropertyName.Disabled, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateMaterial()
|
||||||
|
{
|
||||||
|
if (_beamMaterial == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_beamMaterial.SetShaderParameter("beam_length", _currentLength);
|
||||||
|
_beamMaterial.SetShaderParameter("beam_radius", _currentRadius);
|
||||||
|
|
||||||
|
switch (_state)
|
||||||
|
{
|
||||||
|
case Laser.LaserState.Warning:
|
||||||
|
_beamMaterial.SetShaderParameter("beam_color", new Color(1f, 1f, 0.2f));
|
||||||
|
_beamMaterial.SetShaderParameter("intensity", 0.5f);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Laser.LaserState.Active:
|
||||||
|
_beamMaterial.SetShaderParameter("beam_color", new Color(1f, 0.2f, 0.2f));
|
||||||
|
_beamMaterial.SetShaderParameter("intensity", 1.0f);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsPointInsideBeam(Vector3 worldPoint)
|
||||||
|
{
|
||||||
|
if (_state != Laser.LaserState.Active)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Vector3 local = worldPoint - _origin;
|
||||||
|
float projection = local.Dot(_laserDirection);
|
||||||
|
|
||||||
|
if (projection < 0 || projection > _currentLength)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Vector3 closestPoint = _origin + _laserDirection * projection;
|
||||||
|
float distanceSq = worldPoint.DistanceSquaredTo(closestPoint);
|
||||||
|
|
||||||
|
float radius = _bulletInfo.LaserConfig.DamageRadius;
|
||||||
|
return distanceSq <= radius * radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void _on_area_entered(Area3D area)
|
||||||
|
{
|
||||||
|
if (!Enabled || _state != Laser.LaserState.Active) return;
|
||||||
|
|
||||||
|
if (area.IsInGroup("Destroyable") && area is IDestructible destructible &&
|
||||||
|
CanHit(BulletOwner, destructible.BulletGroup))
|
||||||
|
{
|
||||||
|
destructible.Hit(Damage, DamageType);
|
||||||
|
RequestCollisionDestruction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void _on_body_entered(Node3D body)
|
||||||
|
{
|
||||||
|
if (!Enabled || _state != Laser.LaserState.Active) return;
|
||||||
|
|
||||||
|
if (body.IsInGroup("Destroyable") && body is IDestructible destructible &&
|
||||||
|
CanHit(BulletOwner, destructible.BulletGroup))
|
||||||
|
{
|
||||||
|
destructible.Hit(Damage, DamageType);
|
||||||
|
RequestCollisionDestruction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Destroy()
|
||||||
|
{
|
||||||
|
EmitSignal(SignalName.OnDestroy);
|
||||||
|
PoolingManager.Instance.DisableBullet(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue