using System; using System.Collections.Generic; using System.Linq; namespace Ashfall.Core { public sealed partial class GameSimulation { public ActionResult Execute(string authenticatedPlayerId, GameCommand cmd) { var p = World.Player(authenticatedPlayerId); if (p == null || !p.connected) return ActionResult.Fail("玩家未连接"); if (cmd == null || cmd.sequence <= p.lastCommand) return ActionResult.Fail("重复或过期指令"); if (!cmd.position.Finite || !Enum.IsDefined(typeof(CommandKind), cmd.kind) || (cmd.targetId?.Length ?? 0) > 128 || (cmd.value?.Length ?? 0) > 128 || (cmd.slot?.Length ?? 0) > 128) return ActionResult.Fail("无效指令"); p.lastCommand = cmd.sequence; if (p.transitionId != "") return ActionResult.Fail("正在通过连接通道"); ActionResult result; switch (cmd.kind) { case CommandKind.Interact: result = Interact(p, cmd.targetId); break; case CommandKind.LeaveStation: ReleaseStation(p); result = ActionResult.Ok("已离开操作台"); break; case CommandKind.InstallModule: result = Install(p, cmd); break; case CommandKind.Build: result = Build(p, cmd); break; case CommandKind.Dismantle: result = Dismantle(p, cmd.targetId); break; case CommandKind.Research: result = Research(p, cmd.targetId); break; case CommandKind.Craft: result = Craft(p, cmd); break; case CommandKind.Transfer: result = Transfer(p, cmd); break; case CommandKind.Cruise: result = SetCruise(p); break; case CommandKind.ChangeAltitude: result = ChangeAltitude(p); break; case CommandKind.Repair: result = Repair(p, cmd.targetId); break; case CommandKind.SwitchSpecies: result = SwitchSpecies(p, cmd.value); break; case CommandKind.Ability: result = Ability(p); break; case CommandKind.Collect: result = Collect(p, cmd.targetId); break; case CommandKind.Evolve: result = Evolve(p, cmd.targetId); break; default: result = ActionResult.Fail("未实现的指令"); break; } if (result.success) CriticalChange?.Invoke(); return result; } private ActionResult Interact(PlayerState p, string target) { if (p.stationId != "") { ReleaseStation(p); return ActionResult.Ok("已离开操作台"); } var seat = FindStation(target); if (seat != null) { if (!Near(p, seat.location)) return ActionResult.Fail("请走到操作台旁"); if (seat.playerId != "") return ActionResult.Fail("岗位已被队友占用"); if (p.burrowUntil > World.time || p.location.height > 0) return ActionResult.Fail("当前移动状态不能操作设备"); seat.playerId = p.id; p.stationId = seat.id; p.preferredStationId = seat.id; p.location.position = seat.location.position; p.input = new PlayerInput { sequence = p.input.sequence }; return ActionResult.Ok(seat.role == StationRole.Driver ? "WASD 驾驶 · 鼠标独立瞄准 · E 返回舱内" : "已接管主武器组 · E 返回舱内"); } var f = World.facilities.Find(x => x.id == target); if (f != null) { if (!Near(p, f.location)) return ActionResult.Fail("请靠近设施"); if (f.kind == FacilityKind.Generator) { f.powered = !f.powered; foreach (var other in World.facilities.Where(x => x.location.spaceId == f.location.spaceId)) other.powered = f.powered; return ActionResult.Ok(f.powered ? "供电已恢复" : "供电已关闭"); } if (f.kind == FacilityKind.Medical) { if (!f.powered) return ActionResult.Fail("设施缺少供能"); p.health = p.maxHealth; p.energy = 100; return ActionResult.Ok("生命与能量已恢复"); } return ActionResult.Ok("已连接:" + f.name); } return Collect(p, target); } private ActionResult Collect(PlayerState p, string id) { if (p.stationId != "") return ActionResult.Fail("先离开操作台"); var node = World.resources.Find(x => x.id == id); if (node == null || !Near(p, node.location, 2)) return ActionResult.Fail("附近没有可采集物"); if (!p.inventory.Add(node.itemId, node.amount)) return ActionResult.Fail("背包空间不足"); World.resources.Remove(node); return ActionResult.Ok("获得 " + node.itemId + " × " + node.amount); } private ActionResult Research(PlayerState p, string target) { var f = World.facilities.Find(x => x.id == target && x.kind == FacilityKind.Research); if (f == null || !Near(p, f.location)) return ActionResult.Fail("请在研究设施旁操作"); if (!f.powered) return ActionResult.Fail("请先恢复供电"); var family = p.species == Species.Human ? Family.Mechanical : Family.Biological; if (f.family != family) return ActionResult.Fail("研究由对应种族进行,研究成果全队共享"); string research = family == Family.Mechanical ? "mechanical.weapons" : "biological.weapons"; if (World.research.Contains(research)) return ActionResult.Fail("已完成这项研究"); var storage = Supply(p); string material = family == Family.Mechanical ? "metal" : "biomass"; if (!storage.Has("sample", 2) || !storage.Has(material, 20)) return ActionResult.Fail("设施仓储需要样本 ×2 和材料 ×20"); storage.Take("sample", 2); storage.Take(material, 20); World.research.Add(research); return ActionResult.Ok("已解锁重型武器改装及制造"); } private ActionResult Evolve(PlayerState p, string target) { if (!p.IsWorm) return ActionResult.Fail("只有虫族蠕虫幼体需要首次蜕变"); var f = World.facilities.Find(x => x.id == target && x.family == Family.Biological && (x.kind == FacilityKind.Medical || x.kind == FacilityKind.Research)); if (f == null || !Near(p, f.location) || !f.powered) return ActionResult.Fail("请靠近已供能的再生茧或进化设施"); if (!p.inventory.Has("biomass", 20) || !p.inventory.Has("sample", 1)) return ActionResult.Fail("随身需要生物质 ×20、样本 ×1"); p.inventory.Take("biomass", 20); p.inventory.Take("sample", 1); p.broodForm = BroodForm.Adult; p.burrowUntil = 0; p.energy = 100; RefreshHealth(p, true); return ActionResult.Ok("蜕变完成:掘袭猎手 · 解锁骨针与完整钻地"); } private ActionResult Craft(PlayerState p, GameCommand cmd) { var f = World.facilities.Find(x => x.id == cmd.targetId && x.kind == FacilityKind.Workshop); var recipe = Content.Recipe(cmd.value); if (f == null || recipe == null || !Near(p, f.location)) return ActionResult.Fail("请靠近对应制造设施"); if (!f.powered || f.health <= 0) return ActionResult.Fail("设施不可用"); if (recipe.family != f.family) return ActionResult.Fail("制造体系不兼容"); if (f.jobs.Count >= 8) return ActionResult.Fail("生产队列已满"); var module = Content.Module(recipe.id); if (module != null && !string.IsNullOrEmpty(module.researchId) && !World.research.Contains(module.researchId)) return ActionResult.Fail("需要先研究重型武器"); var storage = Supply(p); if (recipe.inputs.Any(i => !storage.Has(i.id, i.amount))) return ActionResult.Fail("设施仓储材料不足"); foreach (var item in recipe.inputs) storage.Take(item.id, item.amount); f.jobs.Add(new ProductionJob { id = World.NewId("job"), recipeId = recipe.id, outputId = recipe.outputId, outputAmount = recipe.outputAmount, remaining = recipe.seconds }); return ActionResult.Ok("已加入生产队列"); } private void TickProduction(float dt) { foreach (var f in World.facilities) { var vehicle = World.VehicleForSpace(f.location.spaceId); if (!f.powered || f.health <= 0 || f.jobs.Count == 0 || (vehicle != null && (vehicle.power <= 0 || vehicle.energy <= 0))) continue; var job = f.jobs[0]; job.remaining = Math.Max(0, job.remaining - dt); if (job.remaining > 0) continue; var storage = vehicle?.cargo ?? World.warehouse; // Retain a completed job when storage is full. No double spend and no lost output. if (storage.Add(job.outputId, job.outputAmount)) f.jobs.RemoveAt(0); } } private ActionResult Transfer(PlayerState p, GameCommand cmd) { var f = World.facilities.Find(x => x.id == cmd.targetId && x.kind == FacilityKind.Storage); if (f == null || !Near(p, f.location)) return ActionResult.Fail("请靠近仓储设施"); if (cmd.amount <= 0 || cmd.amount > 10000) return ActionResult.Fail("数量无效"); var storage = World.VehicleForSpace(f.location.spaceId)?.cargo ?? World.warehouse; bool success = cmd.slot == "deposit" ? Inventory.Transfer(p.inventory, storage, cmd.value, cmd.amount) : cmd.slot == "withdraw" && Inventory.Transfer(storage, p.inventory, cmd.value, cmd.amount); return success ? ActionResult.Ok("物资转移完成") : ActionResult.Fail("数量不足或目标仓储已满"); } public ActionResult ValidateModule(VehicleState v, HardpointState slot, ModuleDefinition module) { if (v == null || slot == null || module == null) return ActionResult.Fail("载具、挂点或模块不存在"); if (module.family != v.family) return ActionResult.Fail("机械与生物模块不能混装"); if (module.size > slot.size) return ActionResult.Fail("挂点尺寸不足"); if (module.researchId != null && !World.research.Contains(module.researchId)) return ActionResult.Fail("尚未解锁对应研究"); float mass = module.mass, power = module.power; foreach (var hp in v.hardpoints.Where(h => h.id != slot.id)) { var m = Content.Module(hp.moduleId); if (m != null) { mass += m.mass; power += m.power; } } if (mass > v.massLimit) return ActionResult.Fail("超过模块承重"); if (power > v.powerBudget) return ActionResult.Fail("超过供能预算"); return ActionResult.Ok(); } private ActionResult Install(PlayerState p, GameCommand cmd) { var v = World.Vehicle(cmd.targetId); if (v == null || p.location.spaceId != v.spaceId || p.stationId != "") return ActionResult.Fail("请进入载具并离开操作台"); if (!World.facilities.Any(f => f.location.spaceId == v.spaceId && f.kind == FacilityKind.Workshop && Near(p, f.location))) return ActionResult.Fail("请前往维修制造台"); if (Math.Abs(v.speed) > .5f || v.flightMode != FlightMode.Ground) return ActionResult.Fail("载具必须落地停驻"); var slot = v.hardpoints.Find(h => h.id == cmd.slot); var module = Content.Module(cmd.value); var validation = ValidateModule(v, slot, module); if (!validation.success) return validation; if (slot.moduleId == module.id) return ActionResult.Fail("已安装该模块"); if (!v.cargo.Has(module.itemId, 1)) return ActionResult.Fail("货仓缺少该模块"); var old = Content.Module(slot.moduleId); v.cargo.Take(module.itemId, 1); if (old != null) v.cargo.Add(old.itemId, 1); slot.moduleId = module.id; slot.health = 100; slot.readyAt = World.time + .3; return ActionResult.Ok("模块安装完成"); } private ActionResult SetCruise(PlayerState p) { var s = FindStation(p.stationId); var v = StationVehicle(p.stationId); if (s == null || s.role != StationRole.Driver) return ActionResult.Fail("需在驾驶台操作"); v.cruise = !v.cruise; return ActionResult.Ok(v.cruise ? "巡航开启,可离开驾驶台" : "巡航关闭"); } private ActionResult ChangeAltitude(PlayerState p) { var s = FindStation(p.stationId); var v = StationVehicle(p.stationId); if (s == null || s.role != StationRole.Driver || !v.flying) return ActionResult.Fail("需在航空载具驾驶台操作"); if (Math.Abs(v.speed) > .5f) return ActionResult.Fail("请先悬停"); FlightMode next = (FlightMode)(((int)v.flightMode + 1) % 3), old = v.flightMode; v.flightMode = next; if (!CanVehicleOccupy(v, v.position, v.heading)) { v.flightMode = old; return ActionResult.Fail("此处不能降落"); } v.doorOpen = next == FlightMode.Ground; return ActionResult.Ok("高度状态:" + next); } private ActionResult Repair(PlayerState p, string id) { var v = World.Vehicle(id); if (v == null || p.location.spaceId != v.spaceId || !World.facilities.Any(f => f.kind == FacilityKind.Workshop && Near(p, f.location))) return ActionResult.Fail("请前往载具维修台"); string material = v.family == Family.Mechanical ? "metal" : "biomass"; if (!v.cargo.Take(material, 10)) return ActionResult.Fail("货仓维修材料不足"); v.hull = Math.Min(v.maxHull, v.hull + 200); v.engine = Math.Min(100, v.engine + 40); v.power = Math.Min(100, v.power + 40); v.energy = Math.Min(v.maxEnergy, v.energy + 25); foreach (var hp in v.hardpoints) hp.health = Math.Min(100, hp.health + 40); return ActionResult.Ok("维修及补给完成"); } private ActionResult SwitchSpecies(PlayerState p, string value) { if (p.stationId != "" || !World.facilities.Any(f => f.kind == FacilityKind.Medical && Near(p, f.location))) return ActionResult.Fail("只能在复苏设施旁切换种族"); if (!Enum.TryParse(value, out Species species) || !Enum.IsDefined(typeof(Species), species)) return ActionResult.Fail("无效种族"); p.species = species; p.burrowUntil = 0; p.location.height = 0; p.energy = 100; RefreshHealth(p, true); return ActionResult.Ok(p.IsWorm ? "虫族:蠕虫幼体" : "角色切换完成"); } private ActionResult Ability(PlayerState p) { if (p.stationId != "" || World.time < p.abilityReadyAt || p.energy < 25) return ActionResult.Fail("技能不可用"); if (p.species == Species.Brood) { if (p.location.spaceId != "world" || p.location.height > 0) return ActionResult.Fail("钻地只适用于室外自然地表"); p.burrowUntil = World.time + (p.IsWorm ? 3 : 8); p.abilityReadyAt = p.burrowUntil + 7; } else { p.buffUntil = World.time + 6; p.abilityReadyAt = World.time + 12; } p.energy -= 25; return ActionResult.Ok(p.species == Species.Brood ? "钻入地表" : "战斗超频"); } } }