XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

AshfallFrontier

【Unity】灰烬边境,一款生存策略游戏

公开
关注 0 Fork 0 Star 0
UTF-8
using System;
using System.Collections.Generic;
using System.Linq;

namespace Ashfall.Core
{
    public sealed partial class GameSimulation
    {
        private ActionResult Build(PlayerState p, GameCommand cmd)
        {
            if (p.location.spaceId != "base" || p.stationId != "") return ActionResult.Fail("原型建造区域位于前哨基地内部");
            if (!Enum.IsDefined(typeof(PieceKind), cmd.piece) || cmd.floor < 0 || cmd.floor > 3 || cmd.floor > p.location.floor + 1)
                return ActionResult.Fail("最多四层;只能建造当前层或上一层");
            V2 at = new V2((float)Math.Round(cmd.position.x), (float)Math.Round(cmd.position.y));
            if (V2.Distance(p.location.position, at) > 10 || !World.Space("base").floors[0].bounds.Contains(at, .8f)) return ActionResult.Fail("超出建造范围");
            var l = WorldFactory.At("base", cmd.floor, at);
            if (World.pieces.Any(b => b.location.SameFloor(l) && V2.Distance(b.location.position, at) < .1f && b.kind == cmd.piece)) return ActionResult.Fail("这里已有同类构件");
            if (cmd.piece != PieceKind.Floor && !World.pieces.Any(b => b.kind == PieceKind.Floor && b.location.SameFloor(l) && V2.Distance(b.location.position, at) < .1f)) return ActionResult.Fail("需要先铺设楼板");
            if (cmd.piece == PieceKind.Floor && cmd.floor > 0 && !World.pieces.Any(b => b.kind == PieceKind.Floor && b.location.floor == cmd.floor - 1
                && b.location.spaceId == "base" && V2.Distance(b.location.position, at) < .1f)) return ActionResult.Fail("缺少下层结构支持");
            if (cmd.piece != PieceKind.Floor)
            {
                if (World.facilities.Any(f => f.location.SameFloor(l) && V2.Distance(f.location.position, at) < 1.3f)
                    || World.portals.Any(portal => portal.from.SameFloor(l) && V2.Distance(portal.from.position, at) < 1.4f)
                    || World.players.Any(other => other.connected && other.location.SameFloor(l) && V2.Distance(other.location.position, at) < .75f))
                    return ActionResult.Fail("入口、设备或人物占用了此处");
            }
            int cost = cmd.piece == PieceKind.Floor ? 2 : cmd.piece == PieceKind.Stairs ? 24 : cmd.piece == PieceKind.Wall || cmd.piece == PieceKind.Door ? 4 : 20;
            string material = p.species == Species.Human ? "metal" : "biomass";
            if (!World.warehouse.Has(material, cost)) return ActionResult.Fail("共享仓库材料不足,需要 " + cost);
            if (cmd.piece == PieceKind.Stairs)
            {
                if (cmd.floor == 3) return ActionResult.Fail("已达到四层上限");
                V2 landing = at + new V2(0, 4);
                if (!World.Space("base").floors[0].bounds.Contains(landing, 1.6f)) return ActionResult.Fail("楼梯需要 2×4 米及安全平台");
                var landingLoc = WorldFactory.At("base", cmd.floor + 1, landing);
                if (World.pieces.Any(b => b.kind != PieceKind.Floor && b.location.SameFloor(landingLoc) && V2.Distance(b.location.position, landing) < 2)) return ActionResult.Fail("上层平台受阻");
            }
            var piece = new BuildPiece { id = World.NewId("build"), ownerId = "base", location = l, kind = cmd.piece,
                rotation = ((cmd.rotation % 4) + 4) % 4, family = p.species == Species.Human ? Family.Mechanical : Family.Biological };
            EnsureBaseFloor(cmd.floor);
            World.pieces.Add(piece);
            if (cmd.piece == PieceKind.Wall && !AllRoutesOpen(l.floor))
            { World.pieces.Remove(piece); return ActionResult.Fail("不能封死玩家或设施的唯一出口"); }
            World.warehouse.Take(material, cost);
            if (cmd.piece == PieceKind.Stairs)
            {
                EnsureBaseFloor(cmd.floor + 1); V2 landing = at + new V2(0, 4);
                for (int x = -1; x <= 1; x++) for (int y = -1; y <= 1; y++)
                {
                    var tile = WorldFactory.At("base", cmd.floor + 1, landing + new V2(x, y));
                    if (!World.pieces.Any(b => b.kind == PieceKind.Floor && b.location.SameFloor(tile) && V2.Distance(b.location.position, tile.position) < .1f))
                        World.pieces.Add(new BuildPiece { id = World.NewId("landing"), ownerId = piece.id, kind = PieceKind.Floor, location = tile, family = piece.family });
                }
                int start = World.portals.Count;
                WorldFactory.AddStairs(World, "base", cmd.floor, at, cmd.floor + 1, landing);
                // Stair ownership is distinct from vehicle ownership; keep a mapping in the ID.
                for (int i = start; i < World.portals.Count; i++) World.portals[i].id = piece.id + "." + i;
            }
            if (cmd.piece == PieceKind.Research || cmd.piece == PieceKind.Generator || cmd.piece == PieceKind.Storage)
            {
                FacilityKind kind = cmd.piece == PieceKind.Research ? FacilityKind.Research : cmd.piece == PieceKind.Generator ? FacilityKind.Generator : FacilityKind.Storage;
                WorldFactory.AddFacility(World, piece.id + ".facility", cmd.piece.ToString(), kind, "base", cmd.floor, at, true, piece.family);
            }
            return ActionResult.Ok("建造完成:" + cmd.piece);
        }
        private void EnsureBaseFloor(int floor)
        {
            if (World.Space("base").floors.Any(f => f.index == floor)) return;
            World.Space("base").floors.Add(new FloorState { index = floor, name = (floor + 1) + "F", playerBuilt = true, bounds = new Box2(new V2(), new V2(18, 18)) });
        }
        private bool AllRoutesOpen(int floor)
        {
            var exits = World.portals.Where(p => p.from.spaceId == "base" && p.from.floor == floor).Select(p => p.from.position).ToArray();
            if (exits.Length == 0) return false;
            var points = World.players.Where(p => p.connected && p.location.spaceId == "base" && p.location.floor == floor).Select(p => p.location.position)
                .Concat(World.facilities.Where(f => f.location.spaceId == "base" && f.location.floor == floor).Select(f => f.location.position + new V2(0, -1)));
            foreach (var point in points)
                if (!Reachable(point, exits, floor)) return false;
            return true;
        }
        private bool Reachable(V2 start, V2[] exits, int floor)
        {
            var pending = new Queue<V2>(); var seen = new HashSet<string>();
            pending.Enqueue(new V2((float)Math.Round(start.x), (float)Math.Round(start.y)));
            var dirs = new[] { new V2(1, 0), new V2(-1, 0), new V2(0, 1), new V2(0, -1) };
            while (pending.Count > 0)
            {
                var at = pending.Dequeue(); string key = at.x + ":" + at.y;
                if (!seen.Add(key) || !CanStand(WorldFactory.At("base", floor, at), .18f)) continue;
                if (exits.Any(e => V2.Distance(e, at) <= 1.2f)) return true;
                foreach (var d in dirs) pending.Enqueue(at + d);
            }
            return false;
        }
        private ActionResult Dismantle(PlayerState p, string id)
        {
            var b = World.pieces.Find(x => x.id == id);
            if (b == null || p.location.spaceId != "base" || !p.location.SameFloor(b.location) || V2.Distance(p.location.position, b.location.position) > 8) return ActionResult.Fail("构件不在可拆除范围内");
            if (b.id.StartsWith("tile-") || b.ownerId.StartsWith("build-")) return ActionResult.Fail("基础地基或楼梯平台不能单独拆除");
            if (b.kind == PieceKind.Stairs) return ActionResult.Fail("楼梯承载上层通路;请先清空上层,本原型保留楼梯结构");
            if (World.players.Any(x => x.connected && x.location.SameFloor(b.location) && V2.Distance(x.location.position, b.location.position) < 1.2f)) return ActionResult.Fail("人物占用了此处");
            if (World.portals.Any(x => x.from.SameFloor(b.location) && V2.Distance(x.from.position, b.location.position) < 1.6f)) return ActionResult.Fail("构件用于连接通道");
            if (World.facilities.Any(x => x.id == b.id + ".facility" && x.jobs.Count > 0)) return ActionResult.Fail("先完成生产队列");
            if (b.kind == PieceKind.Floor && World.pieces.Any(x => x.id != id && x.location.spaceId == "base"
                && x.location.floor >= b.location.floor && V2.Distance(x.location.position, b.location.position) < .2f)) return ActionResult.Fail("构件仍在承载设施或上层楼板");
            World.pieces.Remove(b);
            if (!AllRoutesOpen(b.location.floor)) { World.pieces.Add(b); return ActionResult.Fail("不能切断唯一通路"); }
            World.facilities.RemoveAll(x => x.id == id + ".facility");
            return ActionResult.Ok("已拆除构件");
        }
    }
}