using Godot; using System; using System.Linq; namespace Gmtk24 { public partial class Table : Interactible { [Export] public Hud Hud; [Export(PropertyHint.NodeType, "FuncGodotMap")] public Node3D TrenchbroomMap; [ExportCategory("Spawn Options")] [Export] public float RelativeScale = 0.05f; [Export] public Node3D SpawnPoint; [Export(PropertyHint.LayersAvoidance)] public uint BlockMask; [Export(PropertyHint.LayersAvoidance)] public uint GhostOnlyMask = 0b100000; [Export(PropertyHint.LayersAvoidance)] public uint PlaneOnlyLayer = 0b1000000; [ExportCategory("Block Materials")] [Export] public ShaderMaterial BlockMaterial; [Export] public ShaderMaterial BlockHoverMaterial; [Export] public ShaderMaterial BlockGhostMaterial; [ExportCategory("Scene Items")] [Export] public Orbit Orbit; [Export] public StaticBody3D Plane; public BuildingBlock GhostBlock; private Vector3 GhostBlockNormal; public bool IsEnabled { get => Visible == true; } // Called when the node enters the scene tree for the first time. public override void _Ready() { Orbit.Hud = Hud; Vector3 SpawnPos = Vector3.Zero; if (SpawnPoint != null) SpawnPos = SpawnPoint.GlobalPosition - GlobalPosition; SpawnBlocks(SpawnPos); Orbit.BlockRelease += OnBlockRelease; Orbit.DraggingHeldBlock += OnBlockDrag; Plane.CollisionLayer = PlaneOnlyLayer; Plane.CollisionMask = PlaneOnlyLayer; DisablePlane(); } public void SpawnBlocks(Vector3 position) { var rng = new RandomNumberGenerator(); var children = TrenchbroomMap.FindChildren("*_buildingblock"); foreach (StaticBody3D buildingBlockStaticBody in children.Cast()) { var baseBlock = new BuildingBlock.BaseBlock(buildingBlockStaticBody); var block = new BuildingBlock() { Base = baseBlock, Material = BlockMaterial, HoverMaterial = BlockHoverMaterial, Position = position + Vector3.One * (rng.Randf() * 0.5f - 0.25f) + Vector3.Up * rng.Randf(), PhysicalModeLayer = BlockMask, RelativeScale = RelativeScale, }; AddChild(block); block.PickUp += OnBlockPickup; } } public void Interact(InputEvent _) { Orbit.SetEnabled(true); } public override void _Process(double delta) { GhostBlockNormal = Vector3.Up; if (GhostBlock != null) { var res = Util.RaycastFromMouse(Orbit.Camera, 0b10100 | PlaneOnlyLayer); if (res is RaycastResult results) { Vector3 LocalPos = results.Position - GlobalPosition; GhostBlockNormal = results.Normal; Vector3 offset = GhostBlock.OffsetFrom(results.Normal, GhostOnlyMask); LocalPos += offset; GhostBlock.Position = LocalPos * Basis; } if (Orbit.HeldBlock != null) GhostBlock.Rotation = Orbit.HeldBlock.Rotation; } } public void OnBlockPickup(BuildingBlock block) { Orbit.HoldBlock(block); Vector3 LocalPos = Vector3.Zero; GhostBlock = new BuildingBlock() { Base = block.Base, Material = BlockGhostMaterial, PhysicalModeLayer = 0, NonPhysicalModeLayer = GhostOnlyMask, RelativeScale = RelativeScale, Position = LocalPos, Mode = BuildingBlock.BlockMode.NonPhysical, }; AddChild(GhostBlock); Hud.InteractionPaused = true; } public void OnBlockRelease(BuildingBlock block, uint typeUint) { Hud.InteractionPaused = false; BlockReleaseType type = (BlockReleaseType)typeUint; DisablePlane(); block.Reparent(this); switch (type) { case BlockReleaseType.Throw: { block.SetMode(BuildingBlock.BlockMode.Physical); var normal = Orbit.Camera.ProjectRayNormal(Orbit.Camera.GetViewport().GetMousePosition()); var direction = normal * 10; var res = Util.RaycastFromMouse(Orbit.Camera, 0b1111); if (res is RaycastResult results) { direction = (results.Position - block.GlobalPosition + Vector3.Up * 0.5f) * 3; } block.ApplyImpulse(direction); break; } case BlockReleaseType.Place: { block.SetMode(BuildingBlock.BlockMode.Placed); block.Transform = GhostBlock.Transform; break; } default: break; } if (GhostBlock != null) { GhostBlock.QueueFree(); GhostBlock = null; } } private void OnBlockDrag(BuildingBlock _) { var offset = GhostBlock.OffsetFrom(GhostBlockNormal); EnablePlane(GhostBlock.Position - offset, new Quaternion(Vector3.Up, GhostBlockNormal)); } private void EnablePlane(Vector3 pos, Quaternion rot) { Plane.Visible = true; Plane.ProcessMode = ProcessModeEnum.Pausable; Plane.Position = pos; Plane.Quaternion = rot; } private void DisablePlane() { Plane.Visible = false; Plane.ProcessMode = ProcessModeEnum.Disabled; } public void DisableTable() { Visible = false; ProcessMode = ProcessModeEnum.Disabled; } public void SpawnTable(Vector3 position, Vector3 lookAt) { if (IsEnabled) return; Visible = true; ProcessMode = ProcessModeEnum.Pausable; Position = position; lookAt.Y = 0; LookAt(lookAt); Rotate(Vector3.Up, (float)Math.PI / 2); } } }