using System; using Godot; using GodotStateCharts; namespace Movementtests.systems; public partial class WeaponSystem : RigidBody3D { [Signal] public delegate void WeaponThrownEventHandler(); [Signal] public delegate void WeaponRetrievedEventHandler(); [Export(PropertyHint.Range, "0,100,1,or_greater")] public float ThrowForce { get; set; } = 1f; [Export(PropertyHint.Range, "0,0.2,0.01,or_greater")] public float StraightThrowDuration { get; set; } = 0.1f; private StateChart _weaponState; public StateChartState InHandState; public StateChartState FlyingState; public StateChartState PlantedState; private Node3D _head; private ShapeCast3D _dashCast3D; private Camera3D _camera; private TweenQueueSystem _tweenQueueSystem; private Transform3D _startTransform; private Vector3 _throwDirection; public Vector3 PlantLocation { get; set; } public Vector3 PlantNormal { get; set; } public MeshInstance3D WeaponLocationIndicator { get; set; } public StandardMaterial3D WeaponLocationIndicatorMaterial { get; set; } public MeshInstance3D WeaponMesh { get; set; } public StandardMaterial3D WeaponMaterial { get; set; } public void Init(Node3D head, Camera3D camera) { _head = head; _camera = camera; _weaponState = StateChart.Of(GetNode("StateChart")); InHandState = StateChartState.Of(GetNode("StateChart/Root/InHand")); FlyingState = StateChartState.Of(GetNode("StateChart/Root/Flying")); PlantedState = StateChartState.Of(GetNode("StateChart/Root/Planted")); WeaponLocationIndicator = GetNode("WeaponLocationIndicator"); WeaponLocationIndicator.Visible = false; WeaponLocationIndicatorMaterial = WeaponLocationIndicator.GetActiveMaterial(0) as StandardMaterial3D; WeaponMesh = GetNode("Weapon"); WeaponMaterial = WeaponMesh.GetActiveMaterial(0) as StandardMaterial3D; _tweenQueueSystem = GetNode("TweenQueueSystem"); _tweenQueueSystem.Init(this); _startTransform = Transform; Freeze = true; BodyEntered += OnThrownWeaponReachesGround; InHandState.StateExited += WeaponLeft; InHandState.StateEntered += WeaponBack; } public void WeaponLeft() { WeaponLocationIndicator.Visible = true; WeaponMaterial!.UseFovOverride = false; EmitSignalWeaponThrown(); } public void WeaponBack() { WeaponLocationIndicator.Visible = false; WeaponMaterial!.UseFovOverride = true; EmitSignalWeaponRetrieved(); } public void PlaceWeaponForTutorial(Vector3 location) { _weaponState.SendEvent("plant"); Freeze = true; GlobalPosition = location; PlantLocation = location; Visible = false; } public void ThrowWeapon(Vector3 end, bool hasHit, Vector3 collisionLocation, Vector3 collisionNormal) { _weaponState.SendEvent("throw"); WeaponLocationIndicatorMaterial.StencilColor = new Color(1f, 1f, 1f); _throwDirection = (end - GlobalPosition).Normalized(); PlantLocation = collisionLocation; PlantNormal = collisionNormal; LookAt(end); var tween = _tweenQueueSystem.TweenToLocation(new TweenQueueSystem.TweenInputs(end, StraightThrowDuration)); if (hasHit) tween.Finished += PlantWeaponInWall; else tween.Finished += ThrowWeaponOnCurve; } public void ThrowWeaponOnCurve() { Freeze = false; ApplyImpulse(_throwDirection * ThrowForce); } public void PlantWeaponInWall() { _weaponState.SendEvent("plant"); WeaponLocationIndicatorMaterial.StencilColor = new Color(1f, 0.2f, 0.2f); Freeze = true; GlobalPosition = PlantLocation; LookAt(GlobalTransform.Origin + PlantNormal, Vector3.Up, true); } public void OnThrownWeaponReachesGround(Node other) { PlantWeaponInWall(); } public void ResetWeapon() { _weaponState.SendEvent("recover"); Transform = _startTransform; Freeze = true; Visible = true; } public override void _IntegrateForces(PhysicsDirectBodyState3D state) { base._IntegrateForces(state); if (!Freeze && state.GetContactCount() > 0) { PlantLocation = state.GetContactLocalPosition(0); PlantNormal = state.GetContactLocalNormal(0); } } public bool IsPlantedUnderPlatform() { return PlantedState.Active && GlobalRotation.X > 1 && Math.Abs(GlobalRotation.Y) > 1; } public bool IsPlantedInWall() { return PlantedState.Active && Math.Abs(GlobalRotation.X) + Math.Abs(GlobalRotation.Z) < 0.3; } }