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
|
- type: entity
|
||||||
parent: BaseTreeConifer
|
parent: BaseTreeConifer
|
||||||
id: FloraTreeChristmas02
|
id: FloraTreeChristmas02
|
||||||
|
suffix: PresentsGiver
|
||||||
name: christmas tree
|
name: christmas tree
|
||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
@@ -346,6 +347,19 @@
|
|||||||
density: 4500
|
density: 4500
|
||||||
layer:
|
layer:
|
||||||
- WallLayer
|
- 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
|
- type: entity
|
||||||
parent: BaseTreeConifer
|
parent: BaseTreeConifer
|
||||||
|
|||||||
@@ -1,32 +1,53 @@
|
|||||||
- type: entity
|
- type: entity
|
||||||
id: Present
|
id: PresentBase
|
||||||
parent: BaseStorageItem
|
|
||||||
name: Present
|
name: Present
|
||||||
suffix: Empty
|
|
||||||
description: A little box with incredible surprises inside.
|
description: A little box with incredible surprises inside.
|
||||||
|
abstract: true
|
||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Objects/Decoration/present.rsi
|
sprite: Objects/Decoration/present.rsi
|
||||||
netsync: false
|
netsync: false
|
||||||
layers:
|
layers:
|
||||||
- state: present
|
- state: present
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: Present
|
||||||
|
parent: [PresentBase, BaseStorageItem]
|
||||||
|
suffix: Empty
|
||||||
|
components:
|
||||||
- type: Item
|
- type: Item
|
||||||
size: 30
|
size: 30
|
||||||
- type: Storage
|
- type: Storage
|
||||||
capacity: 30
|
capacity: 30
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: PresentRandom
|
id: PresentRandomUnsafe
|
||||||
parent: BaseItem
|
parent: [PresentBase, BaseItem]
|
||||||
name: Present
|
suffix: Filled Unsafe
|
||||||
suffix: Filled Random
|
components:
|
||||||
description: A little box with incredible surprises inside.
|
- 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:
|
components:
|
||||||
- type: Sprite
|
|
||||||
sprite: Objects/Decoration/present.rsi
|
|
||||||
netsync: false
|
|
||||||
layers:
|
|
||||||
- state: present
|
|
||||||
- type: SpawnItemsOnUse
|
- type: SpawnItemsOnUse
|
||||||
items:
|
items:
|
||||||
- id: PresentTrash
|
- id: PresentTrash
|
||||||
|
|||||||
Reference in New Issue
Block a user