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("已拆除构件");
}
}
}
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("已拆除构件");
}
}
}