Trenchbroom support for health and destruction

This commit is contained in:
Marco 2025-08-27 18:17:17 +02:00
commit 823886b4ce
19 changed files with 2064 additions and 1398 deletions

View file

@ -12,122 +12,147 @@ namespace Cirno.Scripts.Actors;
[Tool]
public partial class Destructible3D : StaticBody3D, IDestructible
{
[Export] public bool Indestructible { get; private set; }
[Export] public float Health = 1f;
[Export] public bool Indestructible { get; protected set; }
[Export] public float Health { get; protected set; } = 1f;
[Export] public PackedScene DebrisScene { get; set; }
[Export] public PackedScene ExplosionParticles { get; set; }
[Export] public BulletResource ExplosionData { get; set; }
[Export] public BulletOwner BulletGroup { get; set; } = BulletOwner.None;
[Export] public Array<DamageResistance> DamageResistances { get; set; } = [];
[ExportCategory("On Death Activation")]
[Export]
public ActivationType ActivationType { get; private set; } = ActivationType.Toggle;
[Export]
public Node Target { get; private set;}
[Export] public string TargetGroup { get; private set; }
public ActivationType ActivationType { get; protected set; } = ActivationType.Toggle;
[Export] public Node Target { get; private set; }
[Export] public string TargetGroup { get; protected set; }
[Signal]
public delegate void ExplodedEventHandler();
private float _currentHealth = 0f;
private bool _isDestroyed = false;
public override void _Ready()
{
_currentHealth = Health;
}
public void _func_godot_apply_properties(Dictionary<string, string> props)
public virtual void _func_godot_apply_properties(Dictionary<string, Variant> props)
{
TargetGroup = props["target"];
if (props.TryGetValue("activationtype", out var type))
{
var t = Enum.TryParse(type, true, out ActivationType activationType);
if (t)
{
ActivationType = activationType;
}
}
SetProperties(props);
}
protected void SetProperties(Dictionary<string, Variant> props)
{
if (props.TryGetValue("target", out var target))
{
TargetGroup = target.AsString();
}
if (props.TryGetValue("activationtype", out var type))
{
var stringType = type.AsString();
var t = Enum.TryParse(stringType, true, out ActivationType activationType);
if (t)
{
ActivationType = activationType;
}
}
if (props.TryGetValue("indestructible", out var indestructible))
{
var ind = indestructible.AsBool();
Indestructible = ind;
}
if (props.TryGetValue("health", out var health))
{
var h = health.AsSingle();
Health = h;
}
}
private void Explode()
{
//ApplyExplosionDamage();
EmitSignal(SignalName.Exploded, this, _currentHealth);
ActivateOnDeath();
CreateExplosion();
CreateParticles();
CreateDebris();
QueueFree();
//GameManager.Instance.RebakeNavigation();
//GameManager.Instance.RecalculateTilemap(this.GlobalPosition);
}
{
//ApplyExplosionDamage();
EmitSignal(SignalName.Exploded, this, _currentHealth);
ActivateOnDeath();
CreateExplosion();
CreateParticles();
CreateDebris();
private void ActivateOnDeath()
{
if (Target is IActivable node)
{
node.Activate(ActivationType);
}
if (!string.IsNullOrWhiteSpace(TargetGroup))
{
ActivationHelper.UseTargets(this, TargetGroup, ActivationType);
}
}
QueueFree();
private void CreateExplosion()
{
if (ExplosionData == null) return;
var explosion = PoolingManager.Instance.SpawnBullet<Bullet3D>(ExplosionData);
explosion.GlobalPosition = this.GlobalPosition;
explosion.Speed = 0;
explosion.Initialize(ExplosionData.MakeBullet(new Vector2(this.GlobalPosition.X, this.GlobalPosition.Y)));
}
//GameManager.Instance.RebakeNavigation();
//GameManager.Instance.RecalculateTilemap(this.GlobalPosition);
}
private void CreateDebris()
{
if (DebrisScene == null) return;
this.CreateSibling<Destructible3D>(DebrisScene);
}
private void ActivateOnDeath()
{
if (Target is IActivable node)
{
node.Activate(ActivationType);
}
private void CreateParticles()
{
if (ExplosionParticles == null) {
return;
}
var particle = this.CreateSibling<GpuParticles3D>(ExplosionParticles);
if (particle == null) return;
if (!string.IsNullOrWhiteSpace(TargetGroup))
{
ActivationHelper.UseTargets(this, TargetGroup, ActivationType);
}
}
particle.Emitting = true;
}
private void CreateExplosion()
{
if (ExplosionData == null) return;
var explosion = PoolingManager.Instance.SpawnBullet<Bullet3D>(ExplosionData);
explosion.GlobalPosition = this.GlobalPosition;
explosion.Speed = 0;
explosion.Initialize(ExplosionData.MakeBullet(new Vector2(this.GlobalPosition.X, this.GlobalPosition.Y)));
}
private void CreateDebris()
{
if (DebrisScene == null) return;
this.CreateSibling<Destructible3D>(DebrisScene);
}
private void CreateParticles()
{
if (ExplosionParticles == null)
{
return;
}
var particle = this.CreateSibling<GpuParticles3D>(ExplosionParticles);
if (particle == null) return;
particle.Emitting = true;
}
public void Hit(float damage, DamageType damageType = DamageType.Neutral)
{
if (_isDestroyed || Indestructible) return;
var dmg = DamageResistances.Aggregate(damage, (current, resistance) => current * resistance.CalculateDamage(current, damageType));
_currentHealth -= dmg;
if (_currentHealth > 0) return;
_isDestroyed = true;
Explode();
}
public void Hit(float damage, DamageType damageType = DamageType.Neutral)
{
if (_isDestroyed || Indestructible) return;
public bool IsDestroyed()
{
return _isDestroyed;
}
var dmg = DamageResistances.Aggregate(damage,
(current, resistance) => current * resistance.CalculateDamage(current, damageType));
_currentHealth -= dmg;
if (_currentHealth > 0) return;
_isDestroyed = true;
Explode();
}
public bool IsDestroyed()
{
return _isDestroyed;
}
}

View file

@ -1,4 +1,5 @@
using Cirno.Scripts.AttackPatterns;
using System;
using Cirno.Scripts.AttackPatterns;
using Cirno.Scripts.Resources;
using Cirno.Scripts.Resources.BulletScripts;
using Godot;
@ -7,7 +8,7 @@ using Godot.Collections;
namespace Cirno.Scripts.Actors;
[Tool]
public partial class ScriptableBulletsEmitter3D : Node3D, IActivable, IScriptHost3D
public partial class ScriptableBulletsEmitter3D : Destructible3D, IActivable, IScriptHost3D
{
public Node3D ParentObject => this;
@ -21,6 +22,13 @@ public partial class ScriptableBulletsEmitter3D : Node3D, IActivable, IScriptHos
[Export]
public bool EmitOnStart { get; set; } = false;
// [Export] public bool Indestructible { get; private set; } = false;
// [Export] public float Health = 1f;
//
// [Export] public BulletResource ExplosionData { get; set; }
// [Export] public BulletOwner BulletGroup { get; set; } = BulletOwner.None;
// [Export] public Array<DamageResistance> DamageResistances { get; set; } = [];
[Signal]
public delegate void StateChangedEventHandler(bool isEmitting);
@ -53,17 +61,26 @@ public partial class ScriptableBulletsEmitter3D : Node3D, IActivable, IScriptHos
EmitSignal(SignalName.StateChanged, _isActive);
}
public virtual void _func_godot_apply_properties(Dictionary<string, Variant> props)
public override void _func_godot_apply_properties(Dictionary<string, Variant> props)
{
ActivationGroup = props["targetname"].AsString();
var scriptPath = props["script_path"].AsString();
Script = GD.Load<BulletScript3D>(scriptPath);
if (!string.IsNullOrWhiteSpace(scriptPath))
{
Script = GD.Load<BulletScript3D>(scriptPath);
}
else
{
GD.PushWarning("Emitter has no script assigned");
}
EmitOnStart = props["emit_on_start"].AsBool();
InvertSignal = props["invert_signal"].AsBool();
//TargetFunc = props["targetfunc"];
//TargetName = props["targetname"];
SetProperties(props);
}
public bool Activate(ActivationType activationType = ActivationType.Toggle)