using System.Threading; using Content.Server._NF.Trade; using Content.Server.Cargo.Systems; using Content.Server.GameTicking; using Content.Shared._NF.Trade; using Content.Shared.Cargo; using Content.Shared.Examine; using Content.Shared.Labels.EntitySystems; using Content.Shared.Throwing; using Timer = Robust.Shared.Timing.Timer; namespace Content.Server._NF.Cargo.Systems; public sealed partial class NFCargoSystem { [Dependency] private LabelSystem _label = default!; private readonly List _destinations = new(); private void InitializeTradeCrates() { SubscribeLocalEvent(OnTradeCrateGetPriceEvent); SubscribeLocalEvent(OnTradeCrateInit); SubscribeLocalEvent(OnTradeCrateRemove); SubscribeLocalEvent(OnTradeCrateExamined); SubscribeLocalEvent(OnTradeCrateThrow); SubscribeLocalEvent(OnDestinationInit); SubscribeLocalEvent(OnDestinationRemove); } private void OnTradeCrateGetPriceEvent(Entity ent, ref PriceCalculationEvent ev) { var owningStation = _station.GetOwningStation(ent); var atDestination = ent.Comp.DestinationStation != EntityUid.Invalid && owningStation == ent.Comp.DestinationStation || HasComp(owningStation); ev.Price = atDestination ? ent.Comp.ValueAtDestination : ent.Comp.ValueElsewhere; if (ent.Comp.ExpressDeliveryTime != null) { if (_timing.CurTime <= ent.Comp.ExpressDeliveryTime && atDestination) ev.Price += ent.Comp.ExpressOnTimeBonus; else if (_timing.CurTime > ent.Comp.ExpressDeliveryTime) ev.Price -= ent.Comp.ExpressLatePenalty; } ev.Price = double.Max(0.0, ev.Price); // Ensure non-negative values. } private void OnTradeCrateInit(Entity ent, ref ComponentInit ev) { // If there are no available destinations, tough luck. if (_destinations.Count > 0) { var randomIndex = _random.Next(_destinations.Count); // Better have more than one destination. if (_station.GetOwningStation(ent) == _destinations[randomIndex]) { randomIndex = (randomIndex + 1 + _random.Next(_destinations.Count - 1)) % _destinations.Count; } var destination = _destinations[randomIndex]; ent.Comp.DestinationStation = destination; if (TryComp(destination, out var destComp)) _appearance.SetData(ent, TradeCrateVisuals.DestinationIcon, destComp.DestinationProto.Id); if (TryComp(destination, out MetaDataComponent? metadata)) _label.Label(ent, metadata.EntityName); } if (ent.Comp.ExpressDeliveryDuration > TimeSpan.Zero) { ent.Comp.ExpressDeliveryTime = _timing.CurTime + ent.Comp.ExpressDeliveryDuration; _appearance.SetData(ent, TradeCrateVisuals.IsPriority, true); ent.Comp.ExpressCancelToken = new CancellationTokenSource(); Timer.Spawn((int)ent.Comp.ExpressDeliveryDuration.TotalMilliseconds, () => DisableTradeCratePriority(ent), ent.Comp.ExpressCancelToken.Token); } } private void OnTradeCrateRemove(Entity ent, ref ComponentRemove ev) { ent.Comp.ExpressCancelToken?.Cancel(); } // TODO: move to shared, share delivery time? private void OnTradeCrateExamined(Entity ent, ref ExaminedEvent ev) { if (!TryComp(ent.Comp.DestinationStation, out MetaDataComponent? metadata)) return; using (ev.PushGroup(nameof(TradeCrateComponent))) { ev.PushMarkup(Loc.GetString("trade-crate-destination-station", ("destination", metadata.EntityName))); if (ent.Comp.ExpressDeliveryTime == null) return; ev.PushMarkup(ent.Comp.ExpressDeliveryTime >= _timing.CurTime ? Loc.GetString("trade-crate-priority-active") : Loc.GetString("trade-crate-priority-inactive")); var timeLeft = ent.Comp.ExpressDeliveryTime.Value - _timing.CurTime; var timeLeftSeconds = timeLeft.TotalSeconds; if (timeLeftSeconds > 1) ev.PushMarkup(Loc.GetString("trade-crate-priority-time", ("time", timeLeft.ToString(@"hh\:mm\:ss")))); else if (timeLeftSeconds >= 0) ev.PushMarkup(Loc.GetString("trade-crate-priority-time-now")); else ev.PushMarkup(Loc.GetString("trade-crate-priority-past-due", ("time", timeLeft.ToString(@"hh\:mm\:ss")))); } } private void OnTradeCrateThrow(Entity ent, ref ThrowItemAttemptEvent ev) { // Borgs can pick these up, don't let them be thrown. ev.Cancelled = true; } private void DisableTradeCratePriority(EntityUid uid) { _appearance.SetData(uid, TradeCrateVisuals.IsPriorityInactive, true); } private void OnDestinationInit(Entity ent, ref ComponentInit ev) { if (!_destinations.Contains(ent)) _destinations.Add(ent); } private void OnDestinationRemove(Entity ent, ref ComponentRemove ev) { _destinations.Remove(ent); } private void CleanupTradeCrateDestinations() { _destinations.Clear(); } }