using System.Collections.Generic; using System.Linq; using Godot; namespace Cirno.Scripts.Resources.Loot; /// /// Shared logic for spawning and scattering entries in the 3D world. /// Used by both destructibles and enemies so the distribution behaviour stays consistent. /// public static class LootDropHelper { /// /// Rolls each drop's chance, then spawns the resulting pickups scattered uniformly /// in a circle around with an outward + upward launch velocity /// so they arc through the air and fall to the floor. /// /// The list of possible drops to evaluate. /// Node used as the spawn parent / sibling reference. /// World-space centre of the scatter circle. /// Radius of the circle in which items are spread. /// Initial upward speed applied to each pickup. /// Caller-owned RNG instance so seeds are consistent per object. public static void SpawnDrops( IEnumerable 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); } } } }