Christmas trees now grant gifts. (#13118)
* Christmas trees now grant gifts. * docs * add holiday requirements, fix sound. * doc * remove redundant stuff * rename GiftPackin to RandomGift. * a word * Update Content.Server/Holiday/Christmas/LimitedItemGiverSystem.cs Co-authored-by: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Co-authored-by: moonheart08 <moonheart08@users.noreply.github.com> Co-authored-by: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
using Content.Shared.Storage;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.Holiday.Christmas;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for granting items to lucky souls, exactly once.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(LimitedItemGiverSystem))]
|
||||
public sealed class LimitedItemGiverComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Santa knows who you are behind the screen, only one gift per player per round!
|
||||
/// </summary>
|
||||
public readonly HashSet<NetUserId> GrantedPlayers = new();
|
||||
|
||||
/// <summary>
|
||||
/// Selects what entities can be given out by the giver.
|
||||
/// </summary>
|
||||
[DataField("spawnEntries", required: true)]
|
||||
public List<EntitySpawnEntry> SpawnEntries = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The (localized) message shown upon receiving something.
|
||||
/// </summary>
|
||||
[DataField("receivedPopup", required: true)]
|
||||
public string ReceivedPopup = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The (localized) message shown upon being denied.
|
||||
/// </summary>
|
||||
[DataField("deniedPopup", required: true)]
|
||||
public string DeniedPopup = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The holiday required for this giver to work, if any.
|
||||
/// </summary>
|
||||
[DataField("requiredHoliday", customTypeSerializer: typeof(PrototypeIdSerializer<HolidayPrototype>)), ViewVariables(VVAccess.ReadWrite)]
|
||||
public string? RequiredHoliday = null;
|
||||
}
|
||||
50
Content.Server/Holiday/Christmas/LimitedItemGiverSystem.cs
Normal file
50
Content.Server/Holiday/Christmas/LimitedItemGiverSystem.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using Content.Server.Hands.Systems;
|
||||
using Content.Server.Mind.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Storage;
|
||||
using Robust.Server.GameObjects;
|
||||
|
||||
namespace Content.Server.Holiday.Christmas;
|
||||
|
||||
/// <summary>
|
||||
/// This handles handing out items from item givers.
|
||||
/// </summary>
|
||||
public sealed class LimitedItemGiverSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly HandsSystem _hands = default!;
|
||||
[Dependency] private readonly HolidaySystem _holiday = default!;
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<LimitedItemGiverComponent, InteractHandEvent>(OnInteractHand);
|
||||
}
|
||||
|
||||
private void OnInteractHand(EntityUid uid, LimitedItemGiverComponent component, InteractHandEvent args)
|
||||
{
|
||||
if (!TryComp<ActorComponent>(args.User, out var actor))
|
||||
return;
|
||||
|
||||
if (component.GrantedPlayers.Contains(actor.PlayerSession.UserId) || (component.RequiredHoliday is not null && !_holiday.IsCurrentlyHoliday(component.RequiredHoliday)))
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString(component.DeniedPopup), uid, args.User);
|
||||
return;
|
||||
}
|
||||
|
||||
var toGive = EntitySpawnCollection.GetSpawns(component.SpawnEntries);
|
||||
var coords = Transform(args.User).Coordinates;
|
||||
|
||||
foreach (var item in toGive)
|
||||
{
|
||||
if (item is null)
|
||||
continue;
|
||||
|
||||
var spawned = Spawn(item, coords);
|
||||
_hands.PickupOrDrop(args.User, spawned);
|
||||
}
|
||||
|
||||
component.GrantedPlayers.Add(actor.PlayerSession.UserId);
|
||||
_popup.PopupEntity(Loc.GetString(component.ReceivedPopup), uid, args.User);
|
||||
}
|
||||
}
|
||||
43
Content.Server/Holiday/Christmas/RandomGiftComponent.cs
Normal file
43
Content.Server/Holiday/Christmas/RandomGiftComponent.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.Holiday.Christmas;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for gifts with COMPLETELY random things.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(RandomGiftSystem))]
|
||||
public sealed class RandomGiftComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The wrapper entity to spawn when unwrapping the gift.
|
||||
/// </summary>
|
||||
[DataField("wrapper", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string? Wrapper;
|
||||
|
||||
/// <summary>
|
||||
/// A sound to play when the items are spawned. For example, gift boxes being unwrapped.
|
||||
/// </summary>
|
||||
[DataField("sound", required: true)]
|
||||
public SoundSpecifier? Sound;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the gift should be limited only to actual items.
|
||||
/// </summary>
|
||||
[DataField("insaneMode", required: true), ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool InsaneMode;
|
||||
|
||||
/// <summary>
|
||||
/// What entities are allowed to examine this gift to see its contents.
|
||||
/// </summary>
|
||||
[DataField("contentsViewers", required: true)]
|
||||
public EntityWhitelist ContentsViewers = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The currently selected entity to give out. Used so contents viewers can see inside.
|
||||
/// </summary>
|
||||
[DataField("selectedEntity"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public string? SelectedEntity;
|
||||
}
|
||||
102
Content.Server/Holiday/Christmas/RandomGiftSystem.cs
Normal file
102
Content.Server/Holiday/Christmas/RandomGiftSystem.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using Content.Server.Hands.Systems;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Item;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Holiday.Christmas;
|
||||
|
||||
/// <summary>
|
||||
/// This handles granting players their gift.
|
||||
/// </summary>
|
||||
public sealed class RandomGiftSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AudioSystem _audio = default!;
|
||||
[Dependency] private readonly HandsSystem _hands = default!;
|
||||
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
private readonly List<string> _possibleGiftsSafe = new();
|
||||
private readonly List<string> _possibleGiftsUnsafe = new();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
_prototype.PrototypesReloaded += OnPrototypesReloaded;
|
||||
SubscribeLocalEvent<RandomGiftComponent, MapInitEvent>(OnGiftMapInit);
|
||||
SubscribeLocalEvent<RandomGiftComponent, UseInHandEvent>(OnUseInHand);
|
||||
SubscribeLocalEvent<RandomGiftComponent, ExaminedEvent>(OnExamined);
|
||||
BuildIndex();
|
||||
}
|
||||
|
||||
private void OnExamined(EntityUid uid, RandomGiftComponent component, ExaminedEvent args)
|
||||
{
|
||||
if (!component.ContentsViewers.IsValid(args.Examiner, EntityManager) || component.SelectedEntity is null)
|
||||
return;
|
||||
|
||||
var name = _prototype.Index<EntityPrototype>(component.SelectedEntity).Name;
|
||||
args.Message.PushNewline();
|
||||
args.Message.AddText(Loc.GetString("gift-packin-contains", ("name", name)));
|
||||
}
|
||||
|
||||
private void OnUseInHand(EntityUid uid, RandomGiftComponent component, UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (component.SelectedEntity is null)
|
||||
return;
|
||||
|
||||
var coords = Transform(args.User).Coordinates;
|
||||
var handsEnt = Spawn(component.SelectedEntity, coords);
|
||||
EnsureComp<ItemComponent>(handsEnt); // For insane mode.
|
||||
if (component.Wrapper is not null)
|
||||
Spawn(component.Wrapper, coords);
|
||||
|
||||
args.Handled = true;
|
||||
_audio.PlayPvs(component.Sound, args.User);
|
||||
Del(uid);
|
||||
_hands.PickupOrDrop(args.User, handsEnt);
|
||||
|
||||
}
|
||||
|
||||
private void OnGiftMapInit(EntityUid uid, RandomGiftComponent component, MapInitEvent args)
|
||||
{
|
||||
if (component.InsaneMode)
|
||||
component.SelectedEntity = _random.Pick(_possibleGiftsUnsafe);
|
||||
else
|
||||
component.SelectedEntity = _random.Pick(_possibleGiftsSafe);
|
||||
}
|
||||
|
||||
private void OnPrototypesReloaded(PrototypesReloadedEventArgs obj)
|
||||
{
|
||||
BuildIndex();
|
||||
}
|
||||
|
||||
private void BuildIndex()
|
||||
{
|
||||
_possibleGiftsSafe.Clear();
|
||||
_possibleGiftsUnsafe.Clear();
|
||||
var itemCompName = _componentFactory.GetComponentName(typeof(ItemComponent));
|
||||
var mapGridCompName = _componentFactory.GetComponentName(typeof(MapGridComponent));
|
||||
var physicsCompName = _componentFactory.GetComponentName(typeof(PhysicsComponent));
|
||||
|
||||
foreach (var proto in _prototype.EnumeratePrototypes<EntityPrototype>())
|
||||
{
|
||||
if (proto.Abstract || proto.NoSpawn || proto.Components.ContainsKey(mapGridCompName) || !proto.Components.ContainsKey(physicsCompName))
|
||||
continue;
|
||||
|
||||
_possibleGiftsUnsafe.Add(proto.ID);
|
||||
|
||||
if (!proto.Components.ContainsKey(itemCompName))
|
||||
continue;
|
||||
|
||||
_possibleGiftsSafe.Add(proto.ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
10
Content.Server/Holiday/Christmas/SantaComponent.cs
Normal file
10
Content.Server/Holiday/Christmas/SantaComponent.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Content.Server.Holiday.Christmas;
|
||||
|
||||
/// <summary>
|
||||
/// This is used as a marker component, allows them to see gift contents.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class SantaComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
3
Resources/Locale/en-US/holiday/gifts.ftl
Normal file
3
Resources/Locale/en-US/holiday/gifts.ftl
Normal file
@@ -0,0 +1,3 @@
|
||||
gift-packin-contains = This present appears to contain {INDEFINITE($name)} {$name}.
|
||||
christmas-tree-got-gift = After a bit of digging, you find a present with your name on it!
|
||||
christmas-tree-no-gift = There isn't a gift under the tree for you...
|
||||
@@ -334,6 +334,7 @@
|
||||
- type: entity
|
||||
parent: BaseTreeConifer
|
||||
id: FloraTreeChristmas02
|
||||
suffix: PresentsGiver
|
||||
name: christmas tree
|
||||
components:
|
||||
- type: Sprite
|
||||
@@ -346,6 +347,19 @@
|
||||
density: 4500
|
||||
layer:
|
||||
- WallLayer
|
||||
- type: LimitedItemGiver
|
||||
spawnEntries:
|
||||
- id: PresentRandom
|
||||
orGroup: present
|
||||
- id: PresentRandomUnsafe
|
||||
prob: 0.5
|
||||
orGroup: present
|
||||
- id: PresentRandomInsane
|
||||
prob: 0.2
|
||||
orGroup: present
|
||||
receivedPopup: christmas-tree-got-gift
|
||||
deniedPopup: christmas-tree-no-gift
|
||||
requiredHoliday: FestiveSeason
|
||||
|
||||
- type: entity
|
||||
parent: BaseTreeConifer
|
||||
|
||||
@@ -1,32 +1,53 @@
|
||||
- type: entity
|
||||
id: Present
|
||||
parent: BaseStorageItem
|
||||
id: PresentBase
|
||||
name: Present
|
||||
suffix: Empty
|
||||
description: A little box with incredible surprises inside.
|
||||
abstract: true
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Decoration/present.rsi
|
||||
netsync: false
|
||||
layers:
|
||||
- state: present
|
||||
|
||||
- type: entity
|
||||
id: Present
|
||||
parent: [PresentBase, BaseStorageItem]
|
||||
suffix: Empty
|
||||
components:
|
||||
- type: Item
|
||||
size: 30
|
||||
- type: Storage
|
||||
capacity: 30
|
||||
|
||||
- type: entity
|
||||
id: PresentRandom
|
||||
parent: BaseItem
|
||||
name: Present
|
||||
suffix: Filled Random
|
||||
description: A little box with incredible surprises inside.
|
||||
id: PresentRandomUnsafe
|
||||
parent: [PresentBase, BaseItem]
|
||||
suffix: Filled Unsafe
|
||||
components:
|
||||
- type: RandomGift
|
||||
wrapper: PresentTrash
|
||||
sound:
|
||||
path: /Audio/Effects/unwrap.ogg
|
||||
insaneMode: false
|
||||
contentsViewers:
|
||||
components:
|
||||
- Ghost
|
||||
- Santa
|
||||
|
||||
- type: entity
|
||||
id: PresentRandomInsane
|
||||
parent: PresentRandomUnsafe
|
||||
suffix: Filled Insane
|
||||
components:
|
||||
- type: RandomGift
|
||||
insaneMode: true
|
||||
|
||||
- type: entity
|
||||
id: PresentRandom
|
||||
parent: [PresentBase, BaseItem]
|
||||
suffix: Filled Safe
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Objects/Decoration/present.rsi
|
||||
netsync: false
|
||||
layers:
|
||||
- state: present
|
||||
- type: SpawnItemsOnUse
|
||||
items:
|
||||
- id: PresentTrash
|
||||
|
||||
Reference in New Issue
Block a user