mirror of
https://gitlab.com/MaddoScientisto/cirnogodot.git
synced 2026-06-11 00:35:54 +00:00
Enhanced loot drops system
This commit is contained in:
parent
ddde409dbf
commit
acc61f9a0e
12 changed files with 661 additions and 443 deletions
|
|
@ -1,4 +1,4 @@
|
|||
using Godot;
|
||||
using Godot;
|
||||
|
||||
namespace Cirno.Scripts.Resources.Loot;
|
||||
|
||||
|
|
@ -8,7 +8,10 @@ public partial class LootDrop : Resource
|
|||
{
|
||||
[Export]
|
||||
public LootItem Item { get; set; }
|
||||
|
||||
|
||||
[Export(PropertyHint.None, "suffix:%")]
|
||||
public float Chance { get; set; }
|
||||
|
||||
/// <summary>How many pickup instances to spawn when this drop is triggered.</summary>
|
||||
[Export] public int Count { get; set; } = 1;
|
||||
}
|
||||
68
Scripts/Resources/Loot/LootDropHelper.cs
Normal file
68
Scripts/Resources/Loot/LootDropHelper.cs
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Godot;
|
||||
|
||||
namespace Cirno.Scripts.Resources.Loot;
|
||||
|
||||
/// <summary>
|
||||
/// Shared logic for spawning and scattering <see cref="LootDrop"/> entries in the 3D world.
|
||||
/// Used by both destructibles and enemies so the distribution behaviour stays consistent.
|
||||
/// </summary>
|
||||
public static class LootDropHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Rolls each drop's chance, then spawns the resulting pickups scattered uniformly
|
||||
/// in a circle around <paramref name="center"/> with an outward + upward launch velocity
|
||||
/// so they arc through the air and fall to the floor.
|
||||
/// </summary>
|
||||
/// <param name="drops">The list of possible drops to evaluate.</param>
|
||||
/// <param name="anchor">Node used as the spawn parent / sibling reference.</param>
|
||||
/// <param name="center">World-space centre of the scatter circle.</param>
|
||||
/// <param name="scatterRadius">Radius of the circle in which items are spread.</param>
|
||||
/// <param name="launchUpSpeed">Initial upward speed applied to each pickup.</param>
|
||||
/// <param name="rng">Caller-owned RNG instance so seeds are consistent per object.</param>
|
||||
public static void SpawnDrops(
|
||||
IEnumerable<LootDrop> drops,
|
||||
Node3D anchor,
|
||||
Vector3 center,
|
||||
float scatterRadius,
|
||||
float launchUpSpeed,
|
||||
RandomNumberGenerator rng)
|
||||
{
|
||||
var dropList = drops?.ToList();
|
||||
if (dropList is not { Count: > 0 }) return;
|
||||
|
||||
// Count total spawns up front so golden-angle spacing is proportional to the full set.
|
||||
var totalSpawns = dropList
|
||||
.Where(d => d?.Item is not null)
|
||||
.Sum(d => Mathf.Max(d.Count, 1));
|
||||
|
||||
var itemIndex = 0;
|
||||
|
||||
foreach (var drop in dropList)
|
||||
{
|
||||
if (drop?.Item is null) continue;
|
||||
|
||||
var roll = rng.RandfRange(0f, 100f);
|
||||
if (roll > drop.Chance) continue;
|
||||
|
||||
var spawnCount = Mathf.Max(drop.Count, 1);
|
||||
for (var i = 0; i < spawnCount; i++)
|
||||
{
|
||||
// Golden-angle placement gives uniform, non-clustering distribution.
|
||||
itemIndex++;
|
||||
var angle = itemIndex * Mathf.Tau / 1.618033988f;
|
||||
var radius = scatterRadius * Mathf.Sqrt((float)itemIndex / Mathf.Max(totalSpawns, 1));
|
||||
var offset = new Vector3(Mathf.Cos(angle) * radius, 0f, Mathf.Sin(angle) * radius);
|
||||
var spawnPos = center + offset;
|
||||
|
||||
var lateralDir = offset.LengthSquared() > 0f ? offset.Normalized() : Vector3.Right;
|
||||
var launch = lateralDir * (scatterRadius * 0.5f) + Vector3.Up * launchUpSpeed;
|
||||
|
||||
drop.Item.Spawn3D(anchor, spawnPosition: spawnPos, launchVelocity: launch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1
Scripts/Resources/Loot/LootDropHelper.cs.uid
Normal file
1
Scripts/Resources/Loot/LootDropHelper.cs.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://cmpwy17x132io
|
||||
|
|
@ -55,31 +55,43 @@ public partial class LootItem : Resource
|
|||
return spawnedItem;
|
||||
}
|
||||
|
||||
public ItemPickup3D Spawn3D(Node3D sibling, bool dropAsChild = false)
|
||||
/// <summary>
|
||||
/// Spawns this item as an <see cref="ItemPickup3D"/> in the 3D world.
|
||||
/// </summary>
|
||||
/// <param name="sibling">Reference node used to determine parent and default spawn position.</param>
|
||||
/// <param name="dropAsChild">If true, spawns as a child of <paramref name="sibling"/>; otherwise as a sibling.</param>
|
||||
/// <param name="spawnPosition">
|
||||
/// Optional world-space position override. When null, the position of <paramref name="sibling"/> is used.
|
||||
/// Pass a custom value to scatter drops instead of stacking them all at the same point.
|
||||
/// </param>
|
||||
/// <param name="launchVelocity">Optional initial velocity applied to the pickup so it arcs and falls to the floor.</param>
|
||||
public ItemPickup3D Spawn3D(Node3D sibling, bool dropAsChild = false, Vector3? spawnPosition = null, Vector3? launchVelocity = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(DropScenePath3D)) return null;
|
||||
var itemScene = GD.Load<PackedScene>(DropScenePath3D);
|
||||
|
||||
var spawnedItem = itemScene.Instantiate<ItemPickup3D>();
|
||||
spawnedItem.Name = this.ItemKey;
|
||||
|
||||
var position = spawnPosition ?? sibling.GlobalPosition;
|
||||
|
||||
if (dropAsChild)
|
||||
{
|
||||
CallDeferred(MethodName.DeferredSpawn3D, sibling, spawnedItem, sibling.GlobalPosition);
|
||||
//sibling.CallDeferred(Node.MethodName.AddChild, spawnedItem);
|
||||
//sibling.AddChild(spawnedItem);
|
||||
CallDeferred(MethodName.DeferredSpawn3D, sibling, spawnedItem, position);
|
||||
}
|
||||
else
|
||||
{
|
||||
CallDeferred(MethodName.DeferredSpawn3D, sibling.GetParentNode3D(), spawnedItem, sibling.GlobalPosition);
|
||||
//sibling.GetParent().CallDeferred(Node.MethodName.AddChild, spawnedItem);
|
||||
//sibling.GetParent().AddChild(spawnedItem);
|
||||
CallDeferred(MethodName.DeferredSpawn3D, sibling.GetParentNode3D(), spawnedItem, position);
|
||||
}
|
||||
|
||||
//spawnedItem.GlobalPosition = sibling.GlobalPosition;
|
||||
|
||||
spawnedItem.LootTable.Add(this);
|
||||
spawnedItem.SetSprite(InventorySprite);
|
||||
|
||||
if (launchVelocity.HasValue)
|
||||
{
|
||||
spawnedItem.Launch(launchVelocity.Value);
|
||||
}
|
||||
|
||||
return spawnedItem;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue