Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using Robust.Client.Graphics;
using Robust.Shared.Graphics.RSI;

namespace Content.Client._NF.Vehicles;
namespace Content.Client._NF.Vehicle.EntitySystems;

// Rewritten from Goobstation's VehicleSystem.
public sealed class VehicleSystem : SharedVehicleSystem
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using Content.Shared._NF.Vehicle.EntitySystems;

namespace Content.Client._NF.Vehicle.EntitySystems;

public sealed class VehicleUpgradeSystem : SharedVehicleUpgradeSystem
{ }
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using Content.Shared.Tag;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; // Frontier: upgradeable machine parts

namespace Content.Server.Construction.Components
{
Expand Down
444 changes: 222 additions & 222 deletions Content.Server/_NF/Construction/Components/PartExchangerSystem.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using Content.Shared.Construction.Components;
using Content.Shared.Construction.Prototypes;
using Content.Shared.Examine;
using Content.Shared.Stacks;
using Content.Shared.Verbs;
using Robust.Shared.Utility;

Expand Down Expand Up @@ -107,8 +106,13 @@ public Dictionary<string, float> GetPartsRatings(List<MachinePartState> partStat

public void RefreshParts(EntityUid uid, MachineComponent component)
{
var parts = GetAllParts(component);
EntityManager.EventBus.RaiseLocalEvent(uid, new RefreshPartsEvent
RefreshParts((uid, component));
}

public void RefreshParts(Entity<MachineComponent> machine)
{
var parts = GetAllParts(machine.Comp);
EntityManager.EventBus.RaiseLocalEvent(machine, new RefreshPartsEvent
{
Parts = parts,
PartRatings = GetPartsRatings(parts),
Expand Down
110 changes: 110 additions & 0 deletions Content.Server/_NF/Vehicle/EntitySystems/VehicleUpgradeSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using Content.Server.Construction;
using Content.Shared._NF.Vehicle.Components;
using Content.Shared._NF.Vehicle.EntitySystems;
using Content.Shared.Construction.Prototypes;
using Content.Shared.Movement.Systems;
using Robust.Server.Containers;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;

namespace Content.Server._NF.Vehicle.EntitySystems;

public sealed class VehicleUpgradeSystem : SharedVehicleUpgradeSystem
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly ContainerSystem _container = default!;
[Dependency] private readonly ConstructionSystem _construction = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<VehicleUpgradeComponent, ComponentStartup>(OnVehicleStartup);
SubscribeLocalEvent<VehicleUpgradeComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovementSpeedModifiers);
}

private void OnVehicleStartup(Entity<VehicleUpgradeComponent> ent, ref ComponentStartup args)
{
var partContainer = _container.EnsureContainer<Container>(ent, VehicleUpgradeComponent.PartContainerName);
if (partContainer.ContainedEntities.Count > 0)
// Already initialized, don't add double the parts.
return;

ent.Comp.PartContainer = partContainer;

var xform = Transform(ent);
foreach (var (part, amount) in ent.Comp.Requirements)
{
var partProto = _prototypeManager.Index(part);
for (var i = 0; i < amount; i++)
{
var p = EntityManager.SpawnEntity(partProto.StockPartPrototype, xform.Coordinates);

if (!_container.Insert(p, partContainer))
throw new Exception($"Couldn't insert machine part of type {part} to vehicle with prototype {partProto.StockPartPrototype.ToString() ?? "N/A"}!");
}
}
}

private void OnRefreshMovementSpeedModifiers(Entity<VehicleUpgradeComponent> ent, ref RefreshMovementSpeedModifiersEvent args)
{
args.ModifySpeed(ent.Comp.CurrentSpeedModifier);
}

public void UpdateParts(Entity<VehicleUpgradeComponent> ent)
{
// First, let's find the tier for each relevant machine part type.
var ratingByPartType = new Dictionary<ProtoId<MachinePartPrototype>, float>();
foreach (var (partType, count) in ent.Comp.Requirements)
{
var totalRating = 0;
foreach (var x in ent.Comp.PartContainer.ContainedEntities)
{
if (!_construction.GetMachinePartState(x, out var machinePart) ||
machinePart.Part.PartType != partType)
// Weird but okay
continue;

var stackCount = machinePart.Stack?.Count ?? 1;
totalRating += machinePart.Part.Rating * stackCount;
}

var averageRating = (float)totalRating / count;
ratingByPartType.Add(partType, averageRating);
}

// Second, calculate the upgrade multiplier per available upgrade.
foreach (var (target, upgrade) in ent.Comp.AvailableUpgrades)
{
var partRating = ratingByPartType.GetValueOrDefault(upgrade.PartType);
var multiplier = GetUpgradeMultiplier(partRating, upgrade);

switch (target)
{
case UpgradableVehicleProperty.Speed:
ent.Comp.CurrentSpeedModifier = multiplier;
break;
default:
throw new Exception($"Upgradable vehicle property not handled: {target}");
}
}

// Lastly, housekeeping to update dependent systems.
Dirty(ent);
_movementSpeedModifier.RefreshMovementSpeedModifiers(ent);
}

private float GetUpgradeMultiplier(float partRating, VehicleUpgrade upgrade)
{
var tier = (int)partRating;
if (tier >= upgrade.UpgradePerTier.Count - 1)
{
// We've reached max tier for this vehicle, no need to interpolate.
return upgrade.UpgradePerTier[^1];
}
// Note: partRating < tier + 1, hence partRating - tier is in [0, 1).
// If partRating is an integer, partRating - tier = 0 and we use the lower tier (fine).
return MathHelper.Lerp(upgrade.UpgradePerTier[tier], upgrade.UpgradePerTier[tier + 1], partRating - tier);
}
}
62 changes: 62 additions & 0 deletions Content.Shared/_NF/Vehicle/Components/VehicleUpgradeComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using Content.Shared.Construction.Prototypes;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;

namespace Content.Shared._NF.Vehicle.Components;

[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class VehicleUpgradeComponent : Component
{
public const string PartContainerName = "machine_parts";

/// <summary>
/// Contains the vehicle's machine parts.
/// </summary>
[ViewVariables]
public Container PartContainer = default!;

/// <summary>
/// Machine parts needed to upgrade this vehicle.
/// </summary>
[DataField(required: true)]
public Dictionary<ProtoId<MachinePartPrototype>, int> Requirements = default!;

/// <summary>
/// Available upgrades for this vehicle.
/// Note: Only one machine part per upgradable property. Can't have speed depend on
/// both capacitors and manipulators, for example.
/// </summary>
[DataField(required: true)]
public Dictionary<UpgradableVehicleProperty, VehicleUpgrade> AvailableUpgrades = default!;

/// <summary>
/// Current modifier applied to the vehicle's speed.
/// </summary>
[ViewVariables]
[AutoNetworkedField]
public float CurrentSpeedModifier = 1.0f;
}

[DataDefinition]
public partial struct VehicleUpgrade
{
/// <summary>
/// The machine part used for this upgrade.
/// </summary>
[DataField(required: true)]
public ProtoId<MachinePartPrototype> PartType;

/// <summary>
/// The upgrade multiplier per tier. These are factors applied to the base
/// value of the target property. This list should normally contain five
/// values (none [unused], basic, advanced, super, bluespace).
/// </summary>
[DataField(required: true)]
public List<float> UpgradePerTier;
}

public enum UpgradableVehicleProperty : byte
{
Speed,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using Content.Shared._NF.Vehicle.Components;
using Content.Shared.Examine;
using Content.Shared.Verbs;
using Robust.Shared.Utility;

namespace Content.Shared._NF.Vehicle.EntitySystems;

public abstract class SharedVehicleUpgradeSystem : EntitySystem
{
[Dependency] private readonly ExamineSystemShared _examine = default!;

private const string UpgradeIconPath = "/Textures/Interface/VerbIcons/pickup.svg.192dpi.png";

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<VehicleUpgradeComponent, GetVerbsEvent<ExamineVerb>>(OnVehicleVerbExamine);
}

private void OnVehicleVerbExamine(Entity<VehicleUpgradeComponent> ent, ref GetVerbsEvent<ExamineVerb> args)
{
if (!args.CanInteract || !args.CanAccess)
return;

var markup = new FormattedMessage();
GetSpeedModifierExamine(markup, ent);

_examine.AddDetailedExamineVerb(args, ent.Comp, markup,
Loc.GetString("vehicle-verb-examinable-upgrades-text"),
UpgradeIconPath,
Loc.GetString("vehicle-verb-examinable-upgrades-message"));
}

private void GetSpeedModifierExamine(FormattedMessage msg, Entity<VehicleUpgradeComponent> ent)
{
var percent = Math.Round(100 * MathF.Abs(ent.Comp.CurrentSpeedModifier - 1), 2);
var locId = ent.Comp.CurrentSpeedModifier switch
{
< 1 => "vehicle-upgrade-speed-decreased",
1 or float.NaN => "vehicle-upgrade-speed-not-upgraded",
> 1 => "vehicle-upgrade-speed-increased",
};
msg.AddMarkupOrThrow(Loc.GetString(locId, ("percent", percent)));
}
}
17 changes: 17 additions & 0 deletions Resources/Locale/en-US/vehicle/vehicle.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,20 @@ vehicle-use-key = You use {THE($keys)} to start {THE($vehicle)}.
vehicle-cannot-pull = You need to stop pulling {THE($object)} before you can ride {THE($vehicle)}.

vehicle-slot-component-slot-name-keys = Keys

# Frontier
vehicle-verb-upgrade = Upgrade
# Frontier
vehicle-verb-downgrade = Downgrade

# Frontier
vehicle-verb-examinable-upgrades-text = Vehicle Upgrades
# Frontier
vehicle-verb-examinable-upgrades-message = Examine the vehicle upgrades

# Frontier
vehicle-upgrade-speed-increased = [color=yellow]Speed[/color] increased by {$percent}%.
# Frontier
vehicle-upgrade-speed-decreased = [color=yellow]Speed[/color] reduced by {percent}%.
# Frontier
vehicle-upgrade-speed-not-upgraded = [color=yellow]Speed[/color] not upgraded.
Original file line number Diff line number Diff line change
Expand Up @@ -300,3 +300,16 @@
slots: {} # Frontier: no keys required
- type: Strap # Frontier
unbuckleOnInteractHand: False # Frontier
# Frontier: upgradable hoverchair
- type: ContainerContainer
containers:
machine_parts: !type:Container
ents: [] # initialized on startup
- type: VehicleUpgrade
requirements:
Capacitor: 8
availableUpgrades:
Speed:
partType: Capacitor
upgradePerTier: [0.75, 1, 1.25, 1.5, 1.75]
# End Frontier
23 changes: 23 additions & 0 deletions Resources/Prototypes/_NF/Entities/Objects/Vehicles/vehicles.yml
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,17 @@
- type: PointLight
radius: 7
energy: 3
- type: ContainerContainer
containers:
machine_parts: !type:Container
- type: VehicleUpgrade
requirements:
Capacitor: 8
availableUpgrades:
Speed:
partType: Capacitor
# slightly smaller step from super to BS, still a noticeable speedup
upgradePerTier: [0.75, 1, 1.35, 1.70, 2]

- type: entity
parent: NFVehicleHoverbike
Expand Down Expand Up @@ -523,6 +534,18 @@
collection: NFVehicleHorn # resetting from the motorbike
params:
variation: 0.125
- type: ContainerContainer
containers:
storagebase: !type:Container
machine_parts: !type:Container
- type: VehicleUpgrade
requirements:
Capacitor: 8
availableUpgrades:
Speed:
partType: Capacitor
# slightly smaller step from super to BS, still a noticeable speedup
upgradePerTier: [0.75, 1, 1.35, 1.70, 2]

- type: entity
parent: VehicleHoverbikeMailcarrier
Expand Down
Loading