diff --git a/Content.Server/NPC/Components/NPCRangedCombatComponent.cs b/Content.Server/NPC/Components/NPCRangedCombatComponent.cs index 2e4fcf5298..7ae9b47dae 100644 --- a/Content.Server/NPC/Components/NPCRangedCombatComponent.cs +++ b/Content.Server/NPC/Components/NPCRangedCombatComponent.cs @@ -34,6 +34,12 @@ public sealed partial class NPCRangedCombatComponent : Component [ViewVariables(VVAccess.ReadWrite)] public float LOSAccumulator = 0f; + /// + /// Does this predict where the target is moving towards, and then fires there? + /// + [ViewVariables(VVAccess.ReadWrite)] + public bool Advanced = false; + /// /// Is the target still considered in LOS since the last check. /// diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Ranged/GunOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Ranged/GunOperator.cs index 53c5ed1952..177152d85a 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Ranged/GunOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Ranged/GunOperator.cs @@ -58,6 +58,7 @@ public override void Startup(NPCBlackboard blackboard) base.Startup(blackboard); var ranged = _entManager.EnsureComponent(blackboard.GetValue(NPCBlackboard.Owner)); ranged.Target = blackboard.GetValue(TargetKey); + ranged.Advanced = blackboard.GetValueOrDefault("AdvancedTargeting", _entManager); if (blackboard.TryGetValue(NPCBlackboard.RotateSpeed, out var rotSpeed, _entManager)) { diff --git a/Content.Server/NPC/NPCBlackboard.cs b/Content.Server/NPC/NPCBlackboard.cs index 322ff0f85b..9919f2eb6a 100644 --- a/Content.Server/NPC/NPCBlackboard.cs +++ b/Content.Server/NPC/NPCBlackboard.cs @@ -32,6 +32,7 @@ public sealed partial class NPCBlackboard : IEnumerable _physicsQuery; private EntityQuery _xformQuery; - // TODO: Don't predict for hitscan - private const float ShootSpeed = 20f; - /// /// Cooldown on raycasting to check LOS. /// @@ -121,20 +119,42 @@ private void UpdateRanged(float frameTime) comp.LOSAccumulator -= frameTime; - var worldPos = _transform.GetWorldPosition(xform); - var targetPos = _transform.GetWorldPosition(targetXform); + var (x, worldRot) = _transform.GetWorldPositionRotation(xform); + var v = gun.ProjectileSpeed; // bullet velocity + var (xt, targetRot) = _transform.GetWorldPositionRotation(targetXform); + var vt = targetBody.LinearVelocity; // target velocity - // We'll work out the projected spot of the target and shoot there instead of where they are. - var distance = (targetPos - worldPos).Length(); - var oldInLos = comp.TargetInLOS; + Vector2 targetSpot; + Angle goalRotation; + var dx = xt - x; // target displacement from gun + var distance = dx.Length(); // distance to target + + if (comp.Advanced) + { + var phi = (-dx).ToWorldAngle() - vt.ToWorldAngle(); + var theta = Math.Asin(vt.Length() / v * Math.Sin(phi.Theta)); + goalRotation = dx.ToWorldAngle() + theta; + var psi = Math.PI - phi - theta; + var intercept_dist = (float)(distance * Math.Sin(theta)/Math.Sin(psi)); + targetSpot = xt + vt.Normalized() * intercept_dist; + } + else + { + // We'll work out the projected spot of the target and shoot there instead of where they are. + targetSpot = xt + vt * distance / v; + goalRotation = (targetSpot - x).ToWorldAngle(); + } // TODO: Should be doing these raycasts in parallel // Ideally we'd have 2 steps, 1. to go over the normal details for shooting and then 2. to handle beep / rotate / shoot + var oldInLos = comp.TargetInLOS; + if (comp.LOSAccumulator < 0f) { comp.LOSAccumulator += UnoccludedCooldown; // For consistency with NPC steering. - comp.TargetInLOS = _interaction.InRangeUnobstructed(uid, Transform(comp.Target).Coordinates, distance + 0.1f); + comp.TargetInLOS = _interaction.InRangeUnobstructed(comp.Owner, comp.Target, distance + 0.1f) && + (!comp.Advanced | _interaction.InRangeUnobstructed(comp.Owner, new MapCoordinates(targetSpot, xform.MapID), distance + 0.1f)); } if (!comp.TargetInLOS) @@ -162,11 +182,6 @@ private void UpdateRanged(float frameTime) continue; } - var mapVelocity = targetBody.LinearVelocity; - var targetSpot = targetPos + mapVelocity * distance / ShootSpeed; - - // If we have a max rotation speed then do that. - var goalRotation = (targetSpot - worldPos).ToWorldAngle(); var rotationSpeed = comp.RotationSpeed; if (!_rotate.TryRotateTo(uid, goalRotation, frameTime, comp.AccuracyThreshold, rotationSpeed?.Theta ?? double.MaxValue, xform)) @@ -187,7 +202,7 @@ private void UpdateRanged(float frameTime) EntityCoordinates targetCordinates; - if (_mapManager.TryFindGridAt(xform.MapID, targetPos, out var gridUid, out var mapGrid)) + if (_mapManager.TryFindGridAt(xform.MapID, xt, out var gridUid, out var mapGrid)) { targetCordinates = new EntityCoordinates(gridUid, mapGrid.WorldToLocal(targetSpot)); } diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index 7f7c7ba855..df4c83f8c9 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -304,8 +304,6 @@ private void ShootOrThrow(EntityUid uid, Vector2 mapDirection, Vector2 gunVeloci if (!HasComp(uid)) { RemoveShootable(uid); - // TODO: Someone can probably yeet this a billion miles so need to pre-validate input somewhere up the call stack. - ThrowingSystem.TryThrow(uid, mapDirection, gun.ProjectileSpeedModified, user); return; } diff --git a/Content.Server/_FTL/HeatSeeking/HeatSeekingComponent.cs b/Content.Server/_FTL/HeatSeeking/HeatSeekingComponent.cs new file mode 100644 index 0000000000..06d7c9e14d --- /dev/null +++ b/Content.Server/_FTL/HeatSeeking/HeatSeekingComponent.cs @@ -0,0 +1,41 @@ +namespace Content.Server._FTL.HeatSeeking; + +/// +/// This is used for... +/// +[RegisterComponent] +public sealed partial class HeatSeekingComponent : Component +{ + /// + /// How far does this fire a raycast onto? + /// + [DataField("seekRange")] + public float DefaultSeekingRange = 100f; + + /// + /// Should this lock onto ONE entity only? + /// + [DataField] + public bool LockedIn; + + [DataField] + public Angle WeaponArc = Angle.FromDegrees(360); + + /// + /// If null it will instantly turn. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public Angle? RotationSpeed; + + /// + /// What is this entity targeting? + /// + [DataField] + public EntityUid? TargetEntity; + + /// + /// How fast does the missile accelerate? + /// + [DataField] + public float Acceleration = 10f; +} diff --git a/Content.Server/_FTL/HeatSeeking/HeatSeekingSystem.cs b/Content.Server/_FTL/HeatSeeking/HeatSeekingSystem.cs new file mode 100644 index 0000000000..86daf9d5db --- /dev/null +++ b/Content.Server/_FTL/HeatSeeking/HeatSeekingSystem.cs @@ -0,0 +1,59 @@ +using System.Linq; +using System.Numerics; +using Content.Shared.Interaction; +using Content.Shared.Physics; +using Robust.Server.GameObjects; +using Robust.Shared.Physics; +using Robust.Shared.Random; + +namespace Content.Server._FTL.HeatSeeking; + +/// +/// This handles... +/// +public sealed class HeatSeekingSystem : EntitySystem +{ + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly RotateToFaceSystem _rotate = default!; + [Dependency] private readonly PhysicsSystem _physics = default!; + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp, out var xform)) + { + if (comp.TargetEntity.HasValue) + { + var entXform = Transform(comp.TargetEntity.Value); + var angle = ( + _transform.ToMapCoordinates(xform.Coordinates).Position - + _transform.ToMapCoordinates(entXform.Coordinates).Position + ).ToWorldAngle(); + + _transform.SetLocalRotationNoLerp(uid, angle, xform); + + if (!_rotate.TryRotateTo(uid, angle, frameTime, comp.WeaponArc, comp.RotationSpeed?.Theta ?? double.MaxValue, xform)) + { + continue; + } + + _physics.ApplyForce(uid, xform.LocalRotation.RotateVec(new Vector2(0, 1)) * comp.Acceleration); + return; + } + + var ray = new CollisionRay(_transform.GetMapCoordinates(uid, xform).Position, + xform.LocalRotation.ToWorldVec(), + (int) (CollisionGroup.Impassable | CollisionGroup.BulletImpassable)); + var results = _physics.IntersectRay(xform.MapID, ray, comp.DefaultSeekingRange, uid).ToList(); + if (results.Count <= 0) + return; // nothing to heatseek ykwim + + if (comp is { LockedIn: true, TargetEntity: not null }) + return; // Don't reassign target entity if we have one AND we have the LockedIn property + + comp.TargetEntity = results[0].HitEntity; + } + } +} diff --git a/Resources/Prototypes/_FTL/Catalog/Fills/Crates/missiles.yml b/Resources/Prototypes/_FTL/Catalog/Fills/Crates/missiles.yml index 71d42cbf69..010c1df026 100644 --- a/Resources/Prototypes/_FTL/Catalog/Fills/Crates/missiles.yml +++ b/Resources/Prototypes/_FTL/Catalog/Fills/Crates/missiles.yml @@ -4,7 +4,7 @@ components: - type: StorageFill contents: - - id: MissileSDM + - id: MissileTomahawk amount: 10 - type: entity @@ -13,7 +13,7 @@ components: - type: StorageFill contents: - - id: MissileSAM + - id: MissileTomahawk amount: 10 - type: entity @@ -22,5 +22,5 @@ components: - type: StorageFill contents: - - id: MissileTAD + - id: MissileGR amount: 10 diff --git a/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Ammunition/base.yml b/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Ammunition/base.yml index 728918f93c..f68d401a57 100644 --- a/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Ammunition/base.yml +++ b/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Ammunition/base.yml @@ -42,7 +42,7 @@ tags: - Missile - type: CartridgeAmmo - proto: BulletSDM + proto: BulletTomahawk deleteOnSpawn: true - type: StaticPrice price: 150 diff --git a/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Ammunition/explosive.yml b/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Ammunition/explosive.yml index 50a00c82df..29942cb32f 100644 --- a/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Ammunition/explosive.yml +++ b/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Ammunition/explosive.yml @@ -1,18 +1,20 @@ - type: entity - id: MissileSDM - name: SDM-VC-SR40 + id: MissileTomahawk + name: tomahawk-class assault missile + suffix: Missile parent: BaseMissile - description: A structural damage missile that requires visual contact and has a short range of 40 kilometers. + description: A basic ship assault missile. Cheap, abundant, quantity over quality - contains basic heatseeking. components: - type: Sprite sprite: _FTL/Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi state: sdm - type: entity - id: MissileSAM - name: SAM-RC-LR40 + id: MissileWolf + name: wolf-class attack missile + suffix: Missile parent: BaseMissile - description: A ship attack missile that requires radar contact and has a long range of 4000 kilometers. + description: A standard attack missile with basic heat-seeking capabilities. components: - type: Sprite sprite: _FTL/Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi @@ -21,14 +23,15 @@ tags: - Missile - type: CartridgeAmmo - proto: BulletSAM + proto: BulletWolf deleteOnSpawn: true - type: entity - id: MissileTAD - name: TAD-FB-IR-R50 + id: MissileGR + name: goldenrod-class enhanced attack missile + suffix: Missile parent: BaseMissile - description: A total area destruction device that you should fire blindly with infinite range and has an expected blast radius of 50 meters. + description: An attack missile with enhanced payload and heat-seeking capabilities. components: - type: Sprite sprite: _FTL/Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi @@ -37,14 +40,15 @@ tags: - Missile - type: CartridgeAmmo - proto: BulletTAD + proto: BulletGR deleteOnSpawn: true - type: entity id: MissileTND - name: TND-FB-IR-R150 + name: trinity-class nuclear missile + suffix: Missile + description: A rudimentary nuclear missile. Use with caution. parent: BaseMissile - description: A total nuclear destruction device that you should fire blindly with infinite range and has an expected blast radius of 150 meters. components: - type: Sprite sprite: _FTL/Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi diff --git a/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Naval/Ballistic/20mm.yml b/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Naval/Ballistic/20mm.yml index d7edd828d5..9cf81cf129 100644 --- a/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Naval/Ballistic/20mm.yml +++ b/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Naval/Ballistic/20mm.yml @@ -20,3 +20,35 @@ - Cartridge20mm - type: Gun fireRate: 16 + +- type: entity + parent: BaseWeaponTurret + id: Weapon20mmPD + name: 20mm point defense gun + description: This PD gun is completely independent of any ammo systems and of any control. Make sure vicinity is clear of missiles before crossing. + components: + - type: NpcFactionMember + factions: + - PointDefenseGun + - type: BallisticAmmoProvider + proto: Cartridge20mm + capacity: 1500 + - type: Gun + fireRate: 20 + selectedMode: FullAuto + availableModes: + - FullAuto + - type: HTN + rootTask: + task: TurretCompound + blackboard: + RotateSpeed: !type:Single + 15.705 # 3.141 * 5 + SoundTargetInLOS: !type:SoundPathSpecifier + path: /Audio/Effects/double_beep.ogg + AdvancedTargeting: !type:Bool + true + RangedRange: !type:Single + 60.0 + VisionRadius: !type:Single + 100.0 diff --git a/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Naval/missile.yml b/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Naval/missile.yml index a9d2bacbb1..3958ea501c 100644 --- a/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Naval/missile.yml +++ b/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Naval/missile.yml @@ -3,17 +3,44 @@ id: WeaponMissileLauncher name: I.S.T "Artemis" Missile Launcher description: The IncSek Technologies "Artemis" Missile Launcher is a high duty missile launcher used throughout many ships and even in some CG designs. + # Yeah go away lore piecers, IncSek is canon - do I care? No. placement: mode: SnapgridCenter components: - - type: ItemSlots - slots: - gun_magazine: - startingItem: null - whitelist: - tags: [] - gun_chamber: - startingItem: MissileSDM - whitelist: - tags: - - Missile + - type: Gun + projectileSpeed: 0 + - type: ItemSlots + slots: + gun_magazine: + startingItem: null + whitelist: + tags: [] + gun_chamber: + startingItem: MissileTomahawk + whitelist: + tags: + - Missile + +- type: entity + parent: WeaponMissileLauncher + id: WeaponMissileLauncherDebug + name: hand missile cannon + description: It's the Artemis BUT IN YOUR %%%%%% HAND! + suffix: DEBUG + placement: + mode: SnapgridCenter + components: + - type: Item + - type: Gun + projectileSpeed: 0 + - type: ItemSlots + slots: + gun_magazine: + startingItem: null + whitelist: + tags: [] + gun_chamber: + startingItem: MissileTomahawk + whitelist: + tags: + - Missile diff --git a/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Projectiles/explosive.yml b/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Projectiles/explosive.yml index 0a6d19e9c4..17bd09ed8a 100644 --- a/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Projectiles/explosive.yml +++ b/Resources/Prototypes/_FTL/Entities/Objects/Weapons/Guns/Projectiles/explosive.yml @@ -1,88 +1,142 @@ # missiles - type: entity - id: BulletSDM - name: sdm + id: BaseBulletMissile + abstract: true parent: BaseBulletTrigger noSpawn: true components: - - type: Sprite - sprite: _FTL/Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi - layers: - - state: sdm - - type: ExplodeOnTrigger - - type: Explosive - explosionType: Default - totalIntensity: 1000.0 - intensitySlope: 20 # around 50 tiles - maxIntensity: 400 - maxTileBreak: 1 - - type: PointLight - radius: 3.5 - color: orange - energy: 0.5 + - type: Sprite + sprite: _FTL/Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi + - type: ExplodeOnTrigger + - type: Explosive + explosionType: Default + - type: PointLight + radius: 3.5 + color: orange + energy: 0.5 + - type: HeatSeeking + rotationSpeed: 5 + - type: NpcFactionMember + factions: + - Missile + - type: MobState + - type: Fixtures + fixtures: + projectile: + shape: + !type:PhysShapeCircle + radius: .5 + mask: + - MobMask + layer: + - MobLayer + fly-by: + shape: + !type:PhysShapeCircle + radius: 10 # TODO: missile flyby sound + layer: + - Impassable + - MidImpassable + - HighImpassable + - LowImpassable + hard: false + - type: Damageable + damageContainer: Inorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: entity - id: BulletSAM - name: sam - parent: BaseBulletTrigger + id: BulletTomahawk + name: tomahawk + parent: BaseBulletMissile + noSpawn: true + components: + - type: Sprite + layers: + - state: sdm + - type: Explosive + explosionType: DemolitionCharge + totalIntensity: 500.0 + intensitySlope: 50 # around 50 tiles + maxIntensity: 100 + maxTileBreak: 1 + - type: HeatSeeking + rotationSpeed: 10 + acceleration: 50 + - type: Damageable + damageContainer: Inorganic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + +- type: entity + id: BulletWolf + name: wolf + parent: BaseBulletMissile noSpawn: true components: - type: Sprite - sprite: _FTL/Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi layers: - state: sam - - type: ExplodeOnTrigger - type: Explosive explosionType: Default - totalIntensity: 3000.0 # large - intensitySlope: 1 - maxIntensity: 500 + totalIntensity: 25000.0 # large + intensitySlope: 500 + maxIntensity: 100 maxTileBreak: 1 - - type: PointLight - radius: 3.5 - color: orange - energy: 0.5 + - type: HeatSeeking + acceleration: 75 + rotationSpeed: 15 + seekRange: 150 - type: entity - id: BulletTAD - name: tad - parent: BaseBulletTrigger + id: BulletGR + name: golden-rod + parent: BaseBulletMissile noSpawn: true components: - type: Sprite - sprite: _FTL/Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi layers: - state: sam - - type: ExplodeOnTrigger - type: Explosive - explosionType: Default + explosionType: DemolitionCharge totalIntensity: 300000.0 # large - intensitySlope: 5 + intensitySlope: 500 maxIntensity: 500 maxTileBreak: 1 - - type: PointLight - radius: 3.5 - color: orange - energy: 0.5 + - type: HeatSeeking + acceleration: 175 + seekRange: 1500 + rotationSpeed: 25 - type: entity id: BulletTND name: tnd - parent: BaseBulletTrigger + parent: BaseBulletMissile noSpawn: true components: - type: Sprite - sprite: _FTL/Objects/Weapons/Guns/Ammunition/Explosives/explosives.rsi layers: - state: sam - - type: ExplodeOnTrigger - type: Explosive explosionType: Default totalIntensity: 500000.0 # large intensitySlope: 1 maxIntensity: 500 maxTileBreak: 1 - - type: PointLight - radius: 3.5 - color: orange - energy: 0.5 + - type: HeatSeeking + acceleration: 155 + seekRange: 5000 + lockedIn: true + rotationSpeed: 40 diff --git a/Resources/Prototypes/_FTL/NPCs/root.yml b/Resources/Prototypes/_FTL/NPCs/root.yml index db2ff6ceed..84d84e2082 100644 --- a/Resources/Prototypes/_FTL/NPCs/root.yml +++ b/Resources/Prototypes/_FTL/NPCs/root.yml @@ -5,3 +5,31 @@ - tasks: - !type:HTNCompoundTask task: IdleCompound + +- type: htnCompound + id: PointDefenseCompound + branches: + - tasks: + - !type:HTNPrimitiveTask + operator: !type:UtilityOperator + proto: NearbyGunTargetsInorganic + + - !type:HTNPrimitiveTask + preconditions: + - !type:KeyExistsPrecondition + key: Target +# - !type:TargetInRangePrecondition +# targetKey: Target +# # TODO: Non-scuffed +# rangeKey: RangedRange + - !type:TargetInLOSPrecondition + targetKey: Target + rangeKey: RangedRange + operator: !type:GunOperator + targetKey: Target + requireLOS: true + services: + - !type:UtilityService + id: RangedService + proto: NearbyGunTargetsInorganic + key: Target diff --git a/Resources/Prototypes/_FTL/NPCs/utility_queries.yml b/Resources/Prototypes/_FTL/NPCs/utility_queries.yml new file mode 100644 index 0000000000..e9372a2976 --- /dev/null +++ b/Resources/Prototypes/_FTL/NPCs/utility_queries.yml @@ -0,0 +1,12 @@ +- type: utilityQuery + id: NearbyGunTargetsInorganic + query: + - !type:NearbyHostilesQuery + considerations: + - !type:TargetDistanceCon + curve: !type:PresetCurve + preset: TargetDistance + - !type:TargetAccessibleCon + curve: !type:BoolCurve + - !type:TargetInLOSOrCurrentCon + curve: !type:BoolCurve diff --git a/Resources/Prototypes/_FTL/ai_factions.yml b/Resources/Prototypes/_FTL/ai_factions.yml index 6472dbd6ff..e96671bcbb 100644 --- a/Resources/Prototypes/_FTL/ai_factions.yml +++ b/Resources/Prototypes/_FTL/ai_factions.yml @@ -1,5 +1,16 @@ +- type: npcFaction + id: Missile + hostile: + - PointDefenseGun # this will never be used but why not ykwim + +- type: npcFaction + id: PointDefenseGun + hostile: + - Missile + - NanoTrasen + # !!!!!!HEY HEY READ THIS HEY HEY!!!!!! -# USE THESE FOR SHIPS ONLY I BEG +# USE THE FOLLOWING FOR SHIPS ONLY I BEG # THANK YOU!!!!!!!!!! - type: npcFaction diff --git a/Resources/migration.yml b/Resources/migration.yml index f23461afcf..d301dd3bd6 100644 --- a/Resources/migration.yml +++ b/Resources/migration.yml @@ -351,7 +351,7 @@ AirlockServiceCaptainLocked: AirlockCaptainLocked # ekrixi ChemMasterMachineCircuitboard: null -GenericMissile: MissileSDM +GenericMissile: MissileTomahawk ShieldGenerator: null WeaponSiloFedSpawner: null @@ -383,3 +383,7 @@ SwarmMarkTwoMissileSilo: null HoleMarkTwoMissileSilo: null PunchMarkTwoMissileSilo: null MachineSleeperCryopod: CryogenicSleepUnitSpawner + +MissileSDM: MissileTomahawk +MissileSAM: MissileWolf +MissileTAD: MissileGR