diff --git a/Content.Server/_Sunrise/Smile/SmileSlimeSystem.cs b/Content.Server/_Sunrise/Smile/SmileSlimeSystem.cs new file mode 100644 index 00000000000..037de447185 --- /dev/null +++ b/Content.Server/_Sunrise/Smile/SmileSlimeSystem.cs @@ -0,0 +1,134 @@ +using Content.Shared._Sunrise.Smile; +using Content.Shared.Damage; +using Content.Shared.Humanoid; +using Content.Shared.Interaction; +using Content.Shared.Mobs.Systems; +using Content.Shared.Popups; +using Content.Shared.Verbs; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Network; +using Robust.Shared.Physics.Events; +using Robust.Shared.Random; +using Robust.Shared.Timing; +using Content.Shared._Sunrise.Smile; +using Content.Shared.Actions; +using Content.Shared.Body.Components; +using Content.Shared.DoAfter; +using Content.Shared.Movement.Systems; +using Content.Shared.Zombies; + +namespace Content.Server._Sunrise.Smile; + +public sealed class SmileSlimeSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly MobStateSystem _mobStateSystem = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly INetManager _netMan = default!; + [Dependency] private readonly EntityManager _entMan = default!; + [Dependency] private readonly EntityLookupSystem _entityLookup = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly DamageableSystem _damageableSystem = default!; + + private TimeSpan _nextTick = TimeSpan.Zero; + private readonly TimeSpan _refreshCooldown = TimeSpan.FromSeconds(5); + + public override void Initialize() + { + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnLoveAction); + SubscribeLocalEvent(OnCompShutdown); + SubscribeLocalEvent(OnDoAfter); + SubscribeLocalEvent(OnZombified); + } + + private void OnMapInit(EntityUid uid, SmileSlimeComponent comp, MapInitEvent args) + { + _actions.AddAction(uid, ref comp.ActionEntity, comp.Action); + } + + private void OnCompShutdown(EntityUid uid, SmileSlimeComponent comp, ComponentShutdown args) + { + _actions.RemoveAction(uid, comp.ActionEntity); + } + + private void OnZombified(EntityUid uid, SmileSlimeComponent comp, EntityZombifiedEvent args) + { + _actions.RemoveAction(uid, comp.ActionEntity); + } + + private void OnLoveAction(EntityUid smileUid, SmileSlimeComponent comp, SmileLoveActionEvent args) + { + var doAfter = new DoAfterArgs(EntityManager, + smileUid, + comp.ActionTime, + new SmileLoveDoAfterEvent(), + smileUid, + args.Target) + { + BreakOnMove = true, + BlockDuplicate = true, + BreakOnDamage = true, + CancelDuplicate = true, + }; + + if (!TryComp(args.Target, out var targetXform)) + return; + + _audio.PlayPvs(comp.SoundSpecifier, targetXform.Coordinates); + _entMan.SpawnEntity("EffectHearts", targetXform.Coordinates); + _popupSystem.PopupEntity( + Loc.GetString(comp.AffectionPopupText, + ("slime", MetaData(smileUid).EntityName), + ("target", MetaData(args.Target).EntityName)), + args.Target, + PopupType.Medium); + _doAfterSystem.TryStartDoAfter(doAfter); + + args.Handled = true; + } + + private void OnDoAfter(EntityUid uid, SmileSlimeComponent comp, SmileLoveDoAfterEvent args) + { + if (args.Cancelled || args.Handled) + return; + + if (args.Target == null) + return; + + if (!TryComp(args.Target.Value, out var targetXform)) + return; + + _entMan.SpawnEntity("EffectHearts", targetXform.Coordinates); + _audio.PlayPvs(comp.SoundSpecifier, targetXform.Coordinates); + _damageableSystem.TryChangeDamage(args.Target, comp.DamageSpecifier, true, false); + args.Handled = true; + } + + public override void Update(float delay) + { + if (_nextTick > _gameTiming.CurTime) + return; + + _nextTick += _refreshCooldown; + + var query = AllEntityQuery(); + while (query.MoveNext(out var slimeUid, out var smileSlimeComponent)) + { + if (HasComp(slimeUid)) + continue; + foreach (var entity in _entityLookup.GetEntitiesInRange(slimeUid, 2f)) + { + if (HasComp(entity)) + continue; + if (!TryComp(entity, out _)) + continue; + _popupSystem.PopupEntity(Loc.GetString(_random.Pick(smileSlimeComponent.Messages)), entity, entity); + } + } + } +} diff --git a/Content.Shared/_Sunrise/Smile/SmileEvents.cs b/Content.Shared/_Sunrise/Smile/SmileEvents.cs new file mode 100644 index 00000000000..e0303ead5b1 --- /dev/null +++ b/Content.Shared/_Sunrise/Smile/SmileEvents.cs @@ -0,0 +1,10 @@ +using Content.Shared.Actions; +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared._Sunrise.Smile; + +public sealed partial class SmileLoveActionEvent : EntityTargetActionEvent; + +[Serializable, NetSerializable] +public sealed partial class SmileLoveDoAfterEvent : SimpleDoAfterEvent; diff --git a/Content.Shared/_Sunrise/Smile/SmileSlimeComponent.cs b/Content.Shared/_Sunrise/Smile/SmileSlimeComponent.cs new file mode 100644 index 00000000000..a9fac4d3220 --- /dev/null +++ b/Content.Shared/_Sunrise/Smile/SmileSlimeComponent.cs @@ -0,0 +1,58 @@ +using Content.Shared.Damage; +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; + +namespace Content.Shared._Sunrise.Smile; + +[RegisterComponent, AutoGenerateComponentState] +public sealed partial class SmileSlimeComponent : Component +{ + /// + /// Действие обнимашек + /// + [DataField(required: true)] + [ViewVariables(VVAccess.ReadWrite)] + [AutoNetworkedField] + public EntProtoId Action; + + [AutoNetworkedField] + [DataField("actionEntity")] + public EntityUid? ActionEntity; + + /// + /// Сколько восстанавливают обнимашки + /// + [DataField] + public DamageSpecifier DamageSpecifier; + + /// + /// Звук обнимашек + /// + [DataField] + public SoundSpecifier SoundSpecifier; + + /// + /// Тест, который будет появляться над целью обнимашек + /// + [DataField] + public string AffectionPopupText = "smile-affection-popup"; + + /// + /// Сущность, которая спавнится при обнимашках + /// + [DataField] + public string EffectPrototype = "EffectHearts"; + + /// + /// Сколько длятся обнимашки + /// + [DataField] + public TimeSpan ActionTime = TimeSpan.FromSeconds(3); + + /// + /// Какие сообщения пишутся при приближении к смайлу + /// + [DataField("messages")] + [ViewVariables(VVAccess.ReadWrite)] + public List Messages = default!; +} diff --git a/Resources/Locale/ru-RU/_sunrise/smile/smile.ftl b/Resources/Locale/ru-RU/_sunrise/smile/smile.ftl new file mode 100644 index 00000000000..851ba951111 --- /dev/null +++ b/Resources/Locale/ru-RU/_sunrise/smile/smile.ftl @@ -0,0 +1 @@ +smile-affection-popup = { $slime } затягивает в обнимашки { $target } diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml b/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml index 209d84fa18f..51966f51e44 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml @@ -730,8 +730,8 @@ - type: MeleeWeapon damage: types: - Blunt: 1 - Caustic: 1 + Blunt: 3 # Sunrise-edit + Caustic: 3 # Sunrise-edit - type: MultiHandedItem - type: Item size: Huge @@ -749,6 +749,41 @@ gender: female - type: TTS voice: Neco + # Sunrise-start + - type: InteractionPopup + interactDelay: 0.5 + interactSuccessSpawn: EffectHearts + interactSuccessSound: /Audio/Animals/cat_meow.ogg + successChance: 1 + - type: Puller + needsHands: false + - type: SmileSlime + action: SmileAffection + soundSpecifier: /Audio/Animals/cat_meow.ogg + actionTime: 5 + affectionPopupText: "smile-affection-popup" + damageSpecifier: + groups: + Brute: -5 + Burn: -5 + types: + Bloodloss: -5 + messages: + - goodfeeling-artifact-1 + - goodfeeling-artifact-2 + - goodfeeling-artifact-3 + - goodfeeling-artifact-4 + - goodfeeling-artifact-5 + - goodfeeling-artifact-6 + - goodfeeling-artifact-7 + - goodfeeling-artifact-8 + - goodfeeling-artifact-9 + - goodfeeling-artifact-10 + - goodfeeling-artifact-11 + - goodfeeling-artifact-12 + - goodfeeling-artifact-13 + - goodfeeling-artifact-14 + # Sunrise-end - type: entity name: Pun Pun diff --git a/Resources/Prototypes/_Sunrise/Actions/smile_actions.yml b/Resources/Prototypes/_Sunrise/Actions/smile_actions.yml new file mode 100644 index 00000000000..93a9c058a5c --- /dev/null +++ b/Resources/Prototypes/_Sunrise/Actions/smile_actions.yml @@ -0,0 +1,18 @@ +- type: entity + id: SmileAffection + name: Обнимашки + description: Смайл обнимается с целью, восстанавливая ей некоторое количество здоровья. + noSpawn: true + components: + - type: EntityTargetAction + range: 3 + targetingIndicator: true + useDelay: 30 + icon: Effects/hearts.rsi/hearts.png + whitelist: + components: + - Body + canTargetSelf: false + interactOnMiss: false + ignoreContainer: true + event: !type:SmileLoveActionEvent