diff --git a/Content.Client/ADT/Weapons/Medbeam/ADTMedbeamSystem.cs b/Content.Client/ADT/Weapons/Medbeam/ADTMedbeamSystem.cs new file mode 100644 index 00000000000..82bd9f1913d --- /dev/null +++ b/Content.Client/ADT/Weapons/Medbeam/ADTMedbeamSystem.cs @@ -0,0 +1,7 @@ +using Content.Shared.ADT.Weapons.Medbeam; + +namespace Content.Client.ADT.Weapons.Medbeam; + +public sealed class ADTMedbeamSystem : SharedADTMedbeamSystem +{ +} \ No newline at end of file diff --git a/Content.Server/ADT/Weapons/Medbeam/ADTMedbeamSystem.cs b/Content.Server/ADT/Weapons/Medbeam/ADTMedbeamSystem.cs new file mode 100644 index 00000000000..d1732d99fb9 --- /dev/null +++ b/Content.Server/ADT/Weapons/Medbeam/ADTMedbeamSystem.cs @@ -0,0 +1,263 @@ +using System.Numerics; +using System.Threading; +using Content.Server.Explosion.EntitySystems; +using Content.Server.Mech.Systems; +using Content.Shared.ADT.Weapons.Medbeam; +using Content.Shared.Body.Components; +using Content.Shared.Body.Systems; +using Content.Shared.Damage.Components; +using Content.Shared.Damage.Systems; +using Content.Shared.Examine; +using Content.Shared.FixedPoint; +using Content.Shared.Mobs.Systems; +using Content.Shared.Mech.Components; +using Robust.Shared.Map; +using Robust.Shared.Timing; +using RobustTimer = Robust.Shared.Timing.Timer; + +namespace Content.Server.ADT.Weapons.Medbeam; + +public sealed class ADTMedbeamSystem : SharedADTMedbeamSystem +{ + [Dependency] private readonly DamageableSystem _damage = default!; + [Dependency] private readonly ExamineSystemShared _examine = default!; + [Dependency] private readonly SharedBloodstreamSystem _blood = default!; + [Dependency] private readonly ExplosionSystem _explosion = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly MechSystem _mech = default!; + [Dependency] private readonly SharedTransformSystem _xform = default!; + + private readonly Dictionary _beamTokens = new(); + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnShutdown); + } + + public override void AttachBeam(Entity ent, EntityUid target) + { + base.AttachBeam(ent, target); + + if (_beamTokens.Remove(ent.Owner, out var old)) + old.Cancel(); + + var cts = new CancellationTokenSource(); + _beamTokens[ent.Owner] = cts; + RobustTimer.SpawnRepeating(TimeSpan.FromSeconds(ent.Comp.UpdateInterval), () => OnBeamTick(ent, cts), cts.Token); + } + + public override void DetachBeam(Entity ent) + { + if (_beamTokens.Remove(ent.Owner, out var cts)) + cts.Cancel(); + + base.DetachBeam(ent); + } + + private void OnShutdown(Entity ent, ref ComponentShutdown args) + { + if (_beamTokens.Remove(ent.Owner, out var cts)) + cts.Cancel(); + } + + private void OnBeamTick(Entity ent, CancellationTokenSource cts) + { + if (cts.IsCancellationRequested) + return; + + if (!Exists(ent.Owner) || ent.Comp.Target == null) + { + cts.Cancel(); + if (_beamTokens.TryGetValue(ent.Owner, out var current) && current == cts) + _beamTokens.Remove(ent.Owner); + return; + } + + TickBeam(ent); + } + + private void TickBeam(Entity ent) + { + var target = ent.Comp.Target; + if (!Exists(target)) + { + DetachBeam(ent); + return; + } + + if (GetHolder(ent) is not { } holder) + { + DetachBeam(ent); + return; + } + + var hasSomethingToHeal = HasSomethingToHeal(ent, target.Value); + + if (TryComp(holder, out var mech)) + { + if (mech.PilotSlot.ContainedEntity is not { } pilot || !_mobState.IsAlive(pilot)) + { + DetachBeam(ent); + return; + } + + if (hasSomethingToHeal) + { + var energyUsage = (FixedPoint2) (ent.Comp.EnergyUsage * ent.Comp.UpdateInterval); + if (mech.Energy < energyUsage) + { + DetachBeam(ent); + return; + } + + _mech.TryChangeEnergy(holder, -energyUsage, mech); + } + } + else if (ent.Comp.RequireMech || !_mobState.IsAlive(holder)) + { + DetachBeam(ent); + return; + } + + if (!_examine.InRangeUnOccluded(ent.Owner, target.Value, ent.Comp.MaxRange, + entity => entity == ent.Owner || entity == target.Value)) + { + DetachBeam(ent); + return; + } + + if (TryGetCrossing(ent, target.Value, out var otherGun, out var epicenter)) + { + ExplodeBeams(ent, (otherGun, Comp(otherGun)), epicenter); + return; + } + + if (!hasSomethingToHeal) + return; + + _damage.TryChangeDamage(target.Value, ent.Comp.Damage, origin: ent.Owner); + + if (HasComp(target.Value)) + { + if (ent.Comp.BloodRestore > 0) + _blood.TryRegenerateBlood(target.Value, (FixedPoint2) ent.Comp.BloodRestore); + + _blood.TryModifyBleedAmount(target.Value, -Comp(target.Value).BleedAmount); + } + } + + private bool HasSomethingToHeal(Entity ent, EntityUid target) + { + if (TryComp(target, out var damageable) && damageable.TotalDamage > 0) + return true; + + if (HasComp(target)) + { + if (Comp(target).BleedAmount > 0) + return true; + + if (ent.Comp.BloodRestore > 0 && _blood.GetBloodLevel(target) < 1f) + return true; + } + + return false; + } + + private bool TryGetCrossing(Entity ent, EntityUid target, out EntityUid otherGun, out MapCoordinates epicenter) + { + otherGun = default; + epicenter = default; + + var gunCoords = _xform.GetMapCoordinates(ent.Owner); + var targetPos = _xform.GetMapCoordinates(target).Position; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var other)) + { + if (uid == ent.Owner || other.Target == null) + continue; + + var otherCoords = _xform.GetMapCoordinates(uid); + var otherTargetCoords = _xform.GetMapCoordinates(other.Target.Value); + if (otherCoords.MapId != gunCoords.MapId || otherTargetCoords.MapId != gunCoords.MapId) + continue; + + if (!TrySegmentIntersect(gunCoords.Position, targetPos, otherCoords.Position, otherTargetCoords.Position, out var point)) + continue; + + otherGun = uid; + epicenter = new MapCoordinates(point, gunCoords.MapId); + return true; + } + + return false; + } + + private void ExplodeBeams(Entity a, Entity b, MapCoordinates epicenter) + { + a.Comp.Target = null; + a.Comp.Accumulator = 0; + b.Comp.Target = null; + b.Comp.Accumulator = 0; + Dirty(a.Owner, a.Comp); + Dirty(b.Owner, b.Comp); + + var explosionType = a.Comp.ExplosionType; + var totalIntensity = a.Comp.ExplosionTotalIntensity; + var slope = a.Comp.ExplosionIntensitySlope; + var maxTileIntensity = a.Comp.ExplosionMaxTileIntensity; + + _explosion.QueueExplosion(epicenter, explosionType, totalIntensity, slope, maxTileIntensity, cause: a.Owner); + _explosion.QueueExplosion(_xform.GetMapCoordinates(a.Owner), explosionType, totalIntensity, slope, maxTileIntensity, cause: a.Owner); + _explosion.QueueExplosion(_xform.GetMapCoordinates(b.Owner), explosionType, totalIntensity, slope, maxTileIntensity, cause: b.Owner); + + QueueDel(a.Owner); + QueueDel(b.Owner); + } + + private static bool TrySegmentIntersect(Vector2 a1, Vector2 a2, Vector2 b1, Vector2 b2, out Vector2 point) + { + point = default; + + var r = a2 - a1; + var s = b2 - b1; + var rxs = Cross(r, s); + var qmp = b1 - a1; + + if (MathHelper.CloseTo(rxs, 0)) + { + if (!MathHelper.CloseTo(Cross(qmp, r), 0)) + return false; + + var denom = Vector2.Dot(r, r); + if (MathHelper.CloseTo(denom, 0)) + return false; + + var t0 = Vector2.Dot(qmp, r) / denom; + var t1 = t0 + Vector2.Dot(s, r) / denom; + var minT = MathF.Min(t0, t1); + var maxT = MathF.Max(t0, t1); + + if (maxT < 0 || minT > 1) + return false; + + point = a1 + r * Math.Clamp((minT + maxT) / 2, 0, 1); + return true; + } + + var t = Cross(qmp, s) / rxs; + var u = Cross(qmp, r) / rxs; + + if (t < 0 || t > 1 || u < 0 || u > 1) + return false; + + point = a1 + r * t; + return true; + } + + private static float Cross(Vector2 a, Vector2 b) + { + return a.X * b.Y - a.Y * b.X; + } +} \ No newline at end of file diff --git a/Content.Shared/ADT/Mech/Systems/MechToolSystem.cs b/Content.Shared/ADT/Mech/Systems/MechToolSystem.cs index d26bd83c4cc..211e62ba8a9 100644 --- a/Content.Shared/ADT/Mech/Systems/MechToolSystem.cs +++ b/Content.Shared/ADT/Mech/Systems/MechToolSystem.cs @@ -1,4 +1,5 @@ using Content.Shared.ADT.Mech.Components; +using Content.Shared.ADT.Weapons.Medbeam; using Content.Shared.Interaction; using Content.Shared.Mech.Components; using Content.Shared.Mech.Equipment.Components; @@ -32,7 +33,10 @@ private void OnGetUsedEntity(EntityUid uid, MechComponent comp, ref GetUsedEntit if (comp.Energy <= 0) return; - if (comp.CurrentSelectedEquipment is not { } equipment || !HasComp(equipment)) + if (comp.CurrentSelectedEquipment is not { } equipment) + return; + + if (!HasComp(equipment) && !HasComp(equipment)) return; args.Used = equipment; diff --git a/Content.Shared/ADT/Weapons/Medbeam/ADTMedbeamComponent.cs b/Content.Shared/ADT/Weapons/Medbeam/ADTMedbeamComponent.cs new file mode 100644 index 00000000000..4759601f840 --- /dev/null +++ b/Content.Shared/ADT/Weapons/Medbeam/ADTMedbeamComponent.cs @@ -0,0 +1,86 @@ +using System.Numerics; +using Content.Shared.Damage; +using Content.Shared.Explosion; +using Content.Shared.Explosion.EntitySystems; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; + +namespace Content.Shared.ADT.Weapons.Medbeam; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ADTMedbeamComponent : Component +{ + /// + /// The entity currently being healed by the beam. + /// + [DataField, AutoNetworkedField] + public EntityUid? Target; + + /// + /// Whether the gun only works while installed inside a mech. + /// + [DataField] + public bool RequireMech; + + /// + /// How much mech energy the beam drains per second while healing. + /// + [DataField] + public float EnergyUsage; + + /// + /// How far the beam can stretch before it breaks. + /// + [DataField] + public float MaxRange = 8f; + + /// + /// How often the beam heals the target. + /// + [DataField] + public float UpdateInterval = 1f; + + [ViewVariables(VVAccess.ReadWrite)] + public float Accumulator; + + /// + /// Healing applied per tick. Negative damage heals. + /// + [DataField] + public DamageSpecifier Damage = new(); + + /// + /// How much blood is restored per tick. + /// + [DataField] + public float BloodRestore = 5f; + + /// + /// Explosion triggered when two beams cross. + /// + [DataField] + public ProtoId ExplosionType = SharedExplosionSystem.DefaultExplosionPrototypeId; + + [DataField] + public float ExplosionTotalIntensity = 100f; + + [DataField] + public float ExplosionIntensitySlope = 4f; + + [DataField] + public float ExplosionMaxTileIntensity = 10f; + + [DataField] + public SpriteSpecifier Beam = + new SpriteSpecifier.Rsi(new ResPath("/Textures/ADT/Misc/medbeam.rsi"), "medbeam"); + + [DataField] + public SpriteSpecifier? Start; + + [DataField] + public SpriteSpecifier? End; + + [DataField] + public Vector2 Scale = Vector2.One; +} \ No newline at end of file diff --git a/Content.Shared/ADT/Weapons/Medbeam/SharedADTMedbeamSystem.cs b/Content.Shared/ADT/Weapons/Medbeam/SharedADTMedbeamSystem.cs new file mode 100644 index 00000000000..3f922d6d657 --- /dev/null +++ b/Content.Shared/ADT/Weapons/Medbeam/SharedADTMedbeamSystem.cs @@ -0,0 +1,129 @@ +using Content.Shared.ADT.Heretic.Common; +using Content.Shared.Damage.Components; +using Content.Shared.Interaction; +using Content.Shared.Interaction.Events; +using Content.Shared.Mech.Components; +using Content.Shared.Mobs.Components; +using Content.Shared.Silicons.Borgs.Components; +using Robust.Shared.Containers; +using Robust.Shared.Timing; + +namespace Content.Shared.ADT.Weapons.Medbeam; + +public abstract partial class SharedADTMedbeamSystem : EntitySystem +{ + [Dependency] protected readonly SharedContainerSystem Containers = default!; + [Dependency] protected readonly IGameTiming Timing = default!; + + public const string BeamId = "medbeam"; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAfterInteract); + SubscribeLocalEvent(OnActivate); + SubscribeLocalEvent(OnDropped); + SubscribeLocalEvent(OnInserted); + } + + private void OnAfterInteract(Entity ent, ref AfterInteractEvent args) + { + if (args.Handled || args.Target == null) + return; + + if (ent.Comp.RequireMech) + { + if (GetHolder(ent) is not { } holder || !HasComp(holder)) + return; + } + + DetachBeam(ent); + + if (args.Target is not { } target || target == args.User || !IsValidTarget(target)) + { + args.Handled = true; + return; + } + + AttachBeam(ent, target); + args.Handled = true; + } + + protected virtual bool IsValidTarget(EntityUid target) + { + if (!HasComp(target)) + return false; + + if (!TryComp(target, out var damageable)) + return false; + + if (HasComp(target) || HasComp(target)) + return false; + + if (CompOrNull(target)?.DamageContainer == "BiologicalMetaphysical") + return false; + + return true; + } + + public virtual void AttachBeam(Entity ent, EntityUid target) + { + ent.Comp.Target = target; + Dirty(ent); + + var visuals = EnsureComp(ent.Owner); + visuals.Data[GetNetEntity(target)] = + new ComplexJointVisualsData(BeamId, ent.Comp.Beam, ent.Comp.Start, ent.Comp.End, Timing.CurTime) + { + Scale = ent.Comp.Scale, + }; + Dirty(ent.Owner, visuals); + } + + public virtual void DetachBeam(Entity ent) + { + if (ent.Comp.Target is not { } target) + return; + + ent.Comp.Target = null; + ent.Comp.Accumulator = 0; + Dirty(ent); + + if (TryComp(ent.Owner, out var visuals)) + { + visuals.Data.Remove(GetNetEntity(target)); + if (visuals.Data.Count == 0) + RemCompDeferred(ent.Owner, visuals); + else + Dirty(ent.Owner, visuals); + } + } + + protected EntityUid? GetHolder(Entity ent) + { + if (Containers.TryGetContainingContainer((ent.Owner, null), out var container)) + return container.Owner; + + return null; + } + + private void OnActivate(Entity ent, ref ActivateInWorldEvent args) + { + if (!args.Complex || args.Handled) + return; + + DetachBeam(ent); + args.Handled = true; + } + + private void OnDropped(Entity ent, ref DroppedEvent args) + { + DetachBeam(ent); + } + + private void OnInserted(Entity ent, ref EntGotInsertedIntoContainerMessage args) + { + DetachBeam(ent); + } +} \ No newline at end of file diff --git a/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs b/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs index 884cfde9571..dec4db0194d 100644 --- a/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs +++ b/Content.Shared/Body/Systems/SharedBloodstreamSystem.cs @@ -545,13 +545,13 @@ public void SpillAllSolutions(Entity ent) } // ADT-Tweak start - public void TryRegenerateBlood(Entity ent) + public void TryRegenerateBlood(Entity ent, FixedPoint2? amountToAdd = null) { if (!Resolve(ent, ref ent.Comp, logMissing: false) || !SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodSolutionName, ref ent.Comp.BloodSolution, out var bloodSolution)) return; - var amountToAdd = ent.Comp.BloodRefreshAmount; + amountToAdd ??= ent.Comp.BloodRefreshAmount; var currentVolume = bloodSolution.Volume; var referenceVolume = ent.Comp.BloodReferenceSolution.Volume; @@ -565,16 +565,16 @@ public void TryRegenerateBlood(Entity ent) foreach (var (referenceReagent, referenceQuantity) in ent.Comp.BloodReferenceSolution) { - var currentAmount = bloodSolution.GetTotalPrototypeQuantity(referenceReagent.Prototype); - var toAdd = FixedPoint2.Min(amountToAdd, availableSpace); + var share = (FixedPoint2) (referenceQuantity.Float() / referenceVolume.Float() * amountToAdd.Value.Float()); + var toAdd = FixedPoint2.Min(share, availableSpace); - if (toAdd > 0) - { - bloodSolution.AddReagent(referenceReagent, toAdd); - availableSpace -= toAdd; - if (availableSpace <= 0) - break; - } + if (toAdd <= 0) + continue; + + bloodSolution.AddReagent(referenceReagent, toAdd); + availableSpace -= toAdd; + if (availableSpace <= 0) + break; } } // ADT-Tweak end diff --git a/Resources/Locale/ru-RU/ADT/prototypes/Catalog/store/bobr.ftl b/Resources/Locale/ru-RU/ADT/prototypes/Catalog/store/bobr.ftl index 35adb5342d7..5fb7330792f 100644 --- a/Resources/Locale/ru-RU/ADT/prototypes/Catalog/store/bobr.ftl +++ b/Resources/Locale/ru-RU/ADT/prototypes/Catalog/store/bobr.ftl @@ -348,3 +348,6 @@ boobr-trash-bag-desc = Просто мешок для сбора мусора boobr-clean-bot-name = Чистобот boobr-clean-bot-desc = Ужас автоматизации теперь угрожает и космическим уборщикам. + +boobr-medbeam-name = Мед-ган ОБР +boobr-medbeam-desc = Лечащий луч, цепляющийся за цель. Модель из арсенала ОБР, лечит быстрее гражданской версии. Не скрещивайте лучи. diff --git a/Resources/Locale/ru-RU/ADT/prototypes/Catalog/store/uplink-catalog.ftl b/Resources/Locale/ru-RU/ADT/prototypes/Catalog/store/uplink-catalog.ftl index dd3f5ff1180..ba001d1232d 100644 --- a/Resources/Locale/ru-RU/ADT/prototypes/Catalog/store/uplink-catalog.ftl +++ b/Resources/Locale/ru-RU/ADT/prototypes/Catalog/store/uplink-catalog.ftl @@ -261,3 +261,6 @@ uplink-box-music-disks-desc = Тихо выполнять цели - не ваш uplink-code-speak-implanter-name = Имплантер Кодового языка uplink-code-speak-implanter-desc = Каждый уважающий себя ядерный оперативник знает этот язык. Имплантер замаскирован под обычный имплантер Общегалактического языка. + +uplink-medbeam-name = Мед-ган +uplink-medbeam-desc = Лечащий луч, цепляющийся за цель. Лечит быстрее гражданской версии, но скрещённые лучи взрываются вместе с обоими устройствами. diff --git a/Resources/Locale/ru-RU/ADT/prototypes/Entities/Objects/Weapons/Guns/Battery/medbeam_gun.ftl b/Resources/Locale/ru-RU/ADT/prototypes/Entities/Objects/Weapons/Guns/Battery/medbeam_gun.ftl new file mode 100644 index 00000000000..2ccb0ecb059 --- /dev/null +++ b/Resources/Locale/ru-RU/ADT/prototypes/Entities/Objects/Weapons/Guns/Battery/medbeam_gun.ftl @@ -0,0 +1,8 @@ +ent-ADTWeaponMedbeam = мед-ган + .desc = Лечащий луч, который цепляется за цель и лечит её, пока вы держите пушку. За стеной луч обрывается, а скрещённые лучи взрываются вместе с обоими устройствами. +ent-ADTWeaponMedbeamCivil = мед-ган гражданский + .desc = Гражданская версия медицинского лучемёта. Лечит заметно медленнее боевой модели, но и стоит недорого. +ent-ADTWeaponMedbeamSyndicate = мед-ган синдиката + .desc = Компактная модель медицинского лучемёта от синдиката. Лечит быстрее гражданской версии. +ent-ADTWeaponMedbeamERT = мед-ган ОБР + .desc = Медицинский лучемёт из арсенала ОБР. Лечит быстрее гражданской версии. \ No newline at end of file diff --git a/Resources/Locale/ru-RU/ADT/prototypes/Research/paranormal.ftl b/Resources/Locale/ru-RU/ADT/prototypes/Research/paranormal.ftl index c338a18313e..b86512d0554 100644 --- a/Resources/Locale/ru-RU/ADT/prototypes/Research/paranormal.ftl +++ b/Resources/Locale/ru-RU/ADT/prototypes/Research/paranormal.ftl @@ -5,3 +5,5 @@ research-technology-combat-pseudiscience = Боевая псевдонаука research-technology-protection-pseudiscience = Защитная псевдонаука research-technology-medical-cloning-adt = Устройство клонирования + +research-technology-medbeam = Медицинский регенератор diff --git a/Resources/Prototypes/ADT/Catalog/boobr_catalog.yml b/Resources/Prototypes/ADT/Catalog/boobr_catalog.yml index bd1efebf014..01573305eca 100644 --- a/Resources/Prototypes/ADT/Catalog/boobr_catalog.yml +++ b/Resources/Prototypes/ADT/Catalog/boobr_catalog.yml @@ -1336,6 +1336,17 @@ categories: - ADTUplinkERTMisc +- type: listing + id: ADTBoberMedbeam + name: boobr-medbeam-name + description: boobr-medbeam-desc + icon: { sprite: /Textures/ADT/Objects/Weapons/Guns/Battery/healgun.rsi, state: icon } + productEntity: ADTWeaponMedbeamERT + cost: + Productunit: 20 + categories: + - ADTUplinkERTMisc + #Аплинк ОБР - гранаты - type: listing id: ADTBoberc4 diff --git a/Resources/Prototypes/ADT/Catalog/uplink_catalog.yml b/Resources/Prototypes/ADT/Catalog/uplink_catalog.yml index 25ddbe4674f..1d48403560c 100644 --- a/Resources/Prototypes/ADT/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/ADT/Catalog/uplink_catalog.yml @@ -767,6 +767,17 @@ categories: - UplinkImplants +- type: listing + id: ADTUplinkMedbeam + name: uplink-medbeam-name + description: uplink-medbeam-desc + icon: { sprite: /Textures/ADT/Objects/Weapons/Guns/Battery/healgun.rsi, state: icon } + productEntity: ADTWeaponMedbeamSyndicate + cost: + Telecrystal: 20 + categories: + - UplinkChemicals + #UplinkAllies #UplinkPointless diff --git a/Resources/Prototypes/ADT/Entities/Objects/Specific/mechequipment.yml b/Resources/Prototypes/ADT/Entities/Objects/Specific/mechequipment.yml index 2dae0c96e06..f2d536dffd1 100644 --- a/Resources/Prototypes/ADT/Entities/Objects/Specific/mechequipment.yml +++ b/Resources/Prototypes/ADT/Entities/Objects/Specific/mechequipment.yml @@ -190,31 +190,32 @@ - type: entity name: medigun - parent: ADTBaseMechGunBattery + parent: BaseMechEquipment id: ADTMechGunMedigun description: medigun components: - type: Sprite sprite: Objects/Specific/Mech/mecha_equipment.rsi state: mecha_medigun - - type: Gun - selectedMode: FullAuto - availableModes: - - FullAuto - fireRate: 5 - soundGunshot: - path: /Audio/Weapons/Guns/Gunshots/taser2.ogg - - type: HitscanMechAmmoProvider - proto: ADTHealMechGun - fireCost: 40 - type: Tag tags: - - ADTMechEquipmentMed - - ADTArchimedesArmModule - - type: BatterySelfRecharger - autoRecharge: true - autoRechargeRate: 5 - - type: MechGun + - ADTMechEquipmentMed + - ADTArchimedesArmModule + - type: ADTMedbeam + bloodRestore: 2 + requireMech: true + energyUsage: 10 + damage: + types: + Blunt: -2 + Slash: -2 + Piercing: -2 + Heat: -2 + Caustic: -2 + Cold: -2 + Shock: -2 + Poison: -2 + Asphyxiation: -2 - type: entity name: бур diff --git a/Resources/Prototypes/ADT/Entities/Objects/Weapons/Guns/Battery/medbeam_gun.yml b/Resources/Prototypes/ADT/Entities/Objects/Weapons/Guns/Battery/medbeam_gun.yml new file mode 100644 index 00000000000..bff7c0001f1 --- /dev/null +++ b/Resources/Prototypes/ADT/Entities/Objects/Weapons/Guns/Battery/medbeam_gun.yml @@ -0,0 +1,98 @@ +- type: entity + parent: BaseItem + id: ADTWeaponMedbeam + components: + - type: Sprite + sprite: ADT/Objects/Weapons/Guns/Battery/healgun.rsi + layers: + - state: icon + - type: Item + sprite: ADT/Objects/Weapons/Guns/Battery/healgun.rsi + - type: Clothing + sprite: ADT/Objects/Weapons/Guns/Battery/healgun.rsi + quickEquip: false + slots: + - Belt + - suitStorage + - type: ADTMedbeam + damage: + types: + Blunt: -5 + Slash: -5 + Piercing: -5 + Heat: -5 + Caustic: -5 + Cold: -5 + Shock: -5 + Poison: -5 + Asphyxiation: -5 + explosionType: Default + explosionTotalIntensity: 100 + explosionIntensitySlope: 4 + explosionMaxTileIntensity: 10 + +- type: entity + parent: ADTWeaponMedbeam + id: ADTWeaponMedbeamCivil + components: + - type: ADTMedbeam + bloodRestore: 0.5 + damage: + types: + Blunt: -1.2 + Slash: -1.2 + Piercing: -1.2 + Heat: -1.2 + Caustic: -1.2 + Cold: -1.2 + Shock: -1.2 + Poison: -1.2 + Asphyxiation: -1.2 + explosionType: Default + explosionTotalIntensity: 50 + explosionIntensitySlope: 2 + explosionMaxTileIntensity: 5 + +- type: entity + parent: ADTWeaponMedbeam + id: ADTWeaponMedbeamSyndicate + components: + - type: ADTMedbeam + bloodRestore: 1 + damage: + types: + Blunt: -2.5 + Slash: -2.5 + Piercing: -2.5 + Heat: -2.5 + Caustic: -2.5 + Cold: -2.5 + Shock: -2.5 + Poison: -2.5 + Asphyxiation: -2.5 + explosionType: Default + explosionTotalIntensity: 100 + explosionIntensitySlope: 4 + explosionMaxTileIntensity: 10 + +- type: entity + parent: ADTWeaponMedbeam + id: ADTWeaponMedbeamERT + components: + - type: ADTMedbeam + bloodRestore: 1 + damage: + types: + Blunt: -2.5 + Slash: -2.5 + Piercing: -2.5 + Heat: -2.5 + Caustic: -2.5 + Cold: -2.5 + Shock: -2.5 + Poison: -2.5 + Asphyxiation: -2.5 + explosionType: Default + explosionTotalIntensity: 100 + explosionIntensitySlope: 4 + explosionMaxTileIntensity: 10 \ No newline at end of file diff --git a/Resources/Prototypes/ADT/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml b/Resources/Prototypes/ADT/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml index 3ea22453a1d..94468594077 100644 --- a/Resources/Prototypes/ADT/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml +++ b/Resources/Prototypes/ADT/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml @@ -51,32 +51,6 @@ types: Heat: 14 -- type: entity - parent: BasicHitscan - id: ADTHealMechGun - categories: [ HideSpawnMenu ] - components: - - type: HitscanBasicDamage - damage: - types: - Blunt: -2 - Slash: -2 - Piercing: -2 - Heat: -2 - Caustic: -2 - Cold: -2 - Shock: -2 - - type: HitscanBasicVisuals - muzzleFlash: - sprite: ADT/Objects/Weapons/Guns/Projectiles/medigun_projectiles.rsi - state: muzzle_heal - travelFlash: - sprite: ADT/Objects/Weapons/Guns/Projectiles/medigun_projectiles.rsi - state: beam_heal - impactFlash: - sprite: ADT/Objects/Weapons/Guns/Projectiles/medigun_projectiles.rsi - state: impact_heal - - type: entity parent: BasicHitscan id: RedLaserBlaster diff --git a/Resources/Prototypes/ADT/Recipes/Lathes/Packs/medical.yml b/Resources/Prototypes/ADT/Recipes/Lathes/Packs/medical.yml index be91ce64fc6..9507e6becc1 100644 --- a/Resources/Prototypes/ADT/Recipes/Lathes/Packs/medical.yml +++ b/Resources/Prototypes/ADT/Recipes/Lathes/Packs/medical.yml @@ -54,3 +54,8 @@ id: ADTThermoTech recipes: - ADTPatchThermo + +- type: latheRecipePack + id: ADTMedbeamPack + recipes: + - ADTWeaponMedbeamCivil \ No newline at end of file diff --git a/Resources/Prototypes/ADT/Recipes/Lathes/medical.yml b/Resources/Prototypes/ADT/Recipes/Lathes/medical.yml index 1052ab6a470..82fdc1b98d3 100644 --- a/Resources/Prototypes/ADT/Recipes/Lathes/medical.yml +++ b/Resources/Prototypes/ADT/Recipes/Lathes/medical.yml @@ -124,4 +124,17 @@ result: ADTMedicalSprayBottle completetime: 2 materials: - Plastic: 100 \ No newline at end of file + Plastic: 100 + +- type: latheRecipe + id: ADTWeaponMedbeamCivil + result: ADTWeaponMedbeamCivil + completetime: 3 + materials: + Steel: 500 + Plastic: 500 + Glass: 300 + Silver: 300 + Plasma: 300 + ADTBScrystal: 200 + Copper: 500 \ No newline at end of file diff --git a/Resources/Prototypes/ADT/Research/biochemical.yml b/Resources/Prototypes/ADT/Research/biochemical.yml index 3e3ae85b99d..63c53364ee4 100644 --- a/Resources/Prototypes/ADT/Research/biochemical.yml +++ b/Resources/Prototypes/ADT/Research/biochemical.yml @@ -64,6 +64,21 @@ requiredTech: - ADTThermoTech +- type: technology + id: ADTMedbeam + name: research-technology-medbeam + icon: + sprite: ADT/Objects/Weapons/Guns/Battery/healgun.rsi + state: icon + discipline: Biochemical + tier: 3 + cost: 12000 + recipeUnlocks: + - ADTWeaponMedbeamCivil + position: 0,-1 + requiredTech: + - ADTCloning + - type: technology id: ADTIndustrialMedicine name: research-technology-industrial-medicine diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 36f6f39c092..41e2fc0c6ac 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -615,6 +615,7 @@ - ADTAdvancedMedicalCarePack - ADTCryoTech - ADTThermoTech + - ADTMedbeamPack #ADT-Tweak End - type: Machine board: MedicalTechFabCircuitboard diff --git a/Resources/Textures/ADT/Misc/medbeam.rsi/medbeam.png b/Resources/Textures/ADT/Misc/medbeam.rsi/medbeam.png new file mode 100644 index 00000000000..d5a68d8078b Binary files /dev/null and b/Resources/Textures/ADT/Misc/medbeam.rsi/medbeam.png differ diff --git a/Resources/Textures/ADT/Misc/medbeam.rsi/meta.json b/Resources/Textures/ADT/Misc/medbeam.rsi/meta.json new file mode 100644 index 00000000000..ab6de44da20 --- /dev/null +++ b/Resources/Textures/ADT/Misc/medbeam.rsi/meta.json @@ -0,0 +1,34 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from Shiptest at https://github.com/shiptest-ss13/Shiptest/blob/0d5af35f03b00cd34a951b7aece4c658f6606065/icons/effects/beam.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "medbeam", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/ADT/Objects/Weapons/Guns/Battery/healgun.rsi/icon.png b/Resources/Textures/ADT/Objects/Weapons/Guns/Battery/healgun.rsi/icon.png new file mode 100644 index 00000000000..5fac73e2dc0 Binary files /dev/null and b/Resources/Textures/ADT/Objects/Weapons/Guns/Battery/healgun.rsi/icon.png differ diff --git a/Resources/Textures/ADT/Objects/Weapons/Guns/Battery/healgun.rsi/inhand-left.png b/Resources/Textures/ADT/Objects/Weapons/Guns/Battery/healgun.rsi/inhand-left.png new file mode 100644 index 00000000000..aadde5da238 Binary files /dev/null and b/Resources/Textures/ADT/Objects/Weapons/Guns/Battery/healgun.rsi/inhand-left.png differ diff --git a/Resources/Textures/ADT/Objects/Weapons/Guns/Battery/healgun.rsi/inhand-right.png b/Resources/Textures/ADT/Objects/Weapons/Guns/Battery/healgun.rsi/inhand-right.png new file mode 100644 index 00000000000..c98136f4bc6 Binary files /dev/null and b/Resources/Textures/ADT/Objects/Weapons/Guns/Battery/healgun.rsi/inhand-right.png differ diff --git a/Resources/Textures/ADT/Objects/Weapons/Guns/Battery/healgun.rsi/meta.json b/Resources/Textures/ADT/Objects/Weapons/Guns/Battery/healgun.rsi/meta.json new file mode 100644 index 00000000000..4333c6f8195 --- /dev/null +++ b/Resources/Textures/ADT/Objects/Weapons/Guns/Battery/healgun.rsi/meta.json @@ -0,0 +1,22 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from BlueMoon-Station at https://github.com/BlueMoon-Labs/BlueMoon-Station/blob/35a1723e98a60f375df590ca572cc90f1bb80bd5/icons/obj/chronos.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} \ No newline at end of file