PKA Modkits + Rebalance (#31247)

This commit is contained in:
Nemanja
2025-04-25 14:18:23 -04:00
committed by GitHub
parent 9430a0f835
commit f900d9f8b2
24 changed files with 455 additions and 4 deletions

View File

@@ -0,0 +1,24 @@
using Content.Shared.Tag;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared.Weapons.Ranged.Upgrades.Components;
/// <summary>
/// Used to denote compatibility with <see cref="UpgradeableGunComponent"/>. Does not contain explicit behavior.
/// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(GunUpgradeSystem))]
public sealed partial class GunUpgradeComponent : Component
{
/// <summary>
/// Tags used to ensure mutually exclusive upgrades and duplicates are not stacked.
/// </summary>
[DataField]
public List<ProtoId<TagPrototype>> Tags = new();
/// <summary>
/// Markup added to the gun on examine to display the upgrades.
/// </summary>
[DataField]
public LocId ExamineText;
}

View File

@@ -0,0 +1,17 @@
using Content.Shared.Damage;
using Robust.Shared.GameStates;
namespace Content.Shared.Weapons.Ranged.Upgrades.Components;
/// <summary>
/// A <see cref="GunUpgradeComponent"/> for increasing the damage of a gun's projectile.
/// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(GunUpgradeSystem))]
public sealed partial class GunUpgradeDamageComponent : Component
{
/// <summary>
/// Additional damage added onto the projectile's base damage.
/// </summary>
[DataField]
public DamageSpecifier Damage = new();
}

View File

@@ -0,0 +1,16 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Weapons.Ranged.Upgrades.Components;
/// <summary>
/// A <see cref="GunUpgradeComponent"/> for increasing the firerate of a gun.
/// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(GunUpgradeSystem))]
public sealed partial class GunUpgradeFireRateComponent : Component
{
/// <summary>
/// Multiplier for the speed of a gun's fire rate.
/// </summary>
[DataField]
public float Coefficient = 1;
}

View File

@@ -0,0 +1,16 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Weapons.Ranged.Upgrades.Components;
/// <summary>
/// A <see cref="GunUpgradeComponent"/> for increasing the speed of a gun's projectile.
/// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(GunUpgradeSystem))]
public sealed partial class GunUpgradeSpeedComponent : Component
{
/// <summary>
/// Multiplier for the speed of a gun's projectile.
/// </summary>
[DataField]
public float Coefficient = 1;
}

View File

@@ -0,0 +1,36 @@
using Content.Shared.Whitelist;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
namespace Content.Shared.Weapons.Ranged.Upgrades.Components;
/// <summary>
/// Component that stores and manages <see cref="GunUpgradeComponent"/> that modify a given weapon.
/// </summary>
[RegisterComponent, NetworkedComponent, Access(typeof(GunUpgradeSystem))]
public sealed partial class UpgradeableGunComponent : Component
{
/// <summary>
/// ID of container that holds upgrades.
/// </summary>
[DataField]
public string UpgradesContainerId = "upgrades";
/// <summary>
/// Whitelist which denotes the types of upgrades that can be added.
/// </summary>
[DataField]
public EntityWhitelist Whitelist = new();
/// <summary>
/// Sound played when upgrade is inserted.
/// </summary>
[DataField]
public SoundSpecifier? InsertSound = new SoundPathSpecifier("/Audio/Effects/thunk.ogg");
/// <summary>
/// The maximum amount of upgrades this gun can hold.
/// </summary>
[DataField]
public int MaxUpgradeCount = 2;
}

View File

@@ -0,0 +1,145 @@
using System.Linq;
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Content.Shared.Examine;
using Content.Shared.Interaction;
using Content.Shared.Popups;
using Content.Shared.Projectiles;
using Content.Shared.Tag;
using Content.Shared.Weapons.Ranged.Events;
using Content.Shared.Weapons.Ranged.Systems;
using Content.Shared.Weapons.Ranged.Upgrades.Components;
using Content.Shared.Whitelist;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
namespace Content.Shared.Weapons.Ranged.Upgrades;
public sealed class GunUpgradeSystem : EntitySystem
{
[Dependency] private readonly ISharedAdminLogManager _adminLog = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly SharedGunSystem _gun = default!;
[Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<UpgradeableGunComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<UpgradeableGunComponent, AfterInteractUsingEvent>(OnAfterInteractUsing);
SubscribeLocalEvent<UpgradeableGunComponent, ExaminedEvent>(OnExamine);
SubscribeLocalEvent<UpgradeableGunComponent, GunRefreshModifiersEvent>(RelayEvent);
SubscribeLocalEvent<UpgradeableGunComponent, GunShotEvent>(RelayEvent);
SubscribeLocalEvent<GunUpgradeFireRateComponent, GunRefreshModifiersEvent>(OnFireRateRefresh);
SubscribeLocalEvent<GunUpgradeSpeedComponent, GunRefreshModifiersEvent>(OnSpeedRefresh);
SubscribeLocalEvent<GunUpgradeDamageComponent, GunShotEvent>(OnDamageGunShot);
}
private void RelayEvent<T>(Entity<UpgradeableGunComponent> ent, ref T args) where T : notnull
{
foreach (var upgrade in GetCurrentUpgrades(ent))
{
RaiseLocalEvent(upgrade, ref args);
}
}
private void OnExamine(Entity<UpgradeableGunComponent> ent, ref ExaminedEvent args)
{
using (args.PushGroup(nameof(UpgradeableGunComponent)))
{
foreach (var upgrade in GetCurrentUpgrades(ent))
{
args.PushMarkup(Loc.GetString(upgrade.Comp.ExamineText));
}
}
}
private void OnInit(Entity<UpgradeableGunComponent> ent, ref ComponentInit args)
{
_container.EnsureContainer<Container>(ent, ent.Comp.UpgradesContainerId);
}
private void OnAfterInteractUsing(Entity<UpgradeableGunComponent> ent, ref AfterInteractUsingEvent args)
{
if (args.Handled || !args.CanReach || !TryComp<GunUpgradeComponent>(args.Used, out var upgradeComponent))
return;
if (GetCurrentUpgrades(ent).Count >= ent.Comp.MaxUpgradeCount)
{
_popup.PopupPredicted(Loc.GetString("upgradeable-gun-popup-upgrade-limit"), ent, args.User);
return;
}
if (_entityWhitelist.IsWhitelistFail(ent.Comp.Whitelist, args.Used))
return;
if (GetCurrentUpgradeTags(ent).ToHashSet().IsSupersetOf(upgradeComponent.Tags))
{
_popup.PopupPredicted(Loc.GetString("upgradeable-gun-popup-already-present"), ent, args.User);
return;
}
_audio.PlayPredicted(ent.Comp.InsertSound, ent, args.User);
_popup.PopupClient(Loc.GetString("gun-upgrade-popup-insert", ("upgrade", args.Used),("gun", ent.Owner)), args.User);
_gun.RefreshModifiers(ent.Owner);
args.Handled = _container.Insert(args.Used, _container.GetContainer(ent, ent.Comp.UpgradesContainerId));
_adminLog.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(args.Used):player} inserted gun upgrade {ToPrettyString(args.Used)} into {ToPrettyString(ent.Owner)}.");
}
private void OnFireRateRefresh(Entity<GunUpgradeFireRateComponent> ent, ref GunRefreshModifiersEvent args)
{
args.FireRate *= ent.Comp.Coefficient;
}
private void OnSpeedRefresh(Entity<GunUpgradeSpeedComponent> ent, ref GunRefreshModifiersEvent args)
{
args.ProjectileSpeed *= ent.Comp.Coefficient;
}
private void OnDamageGunShot(Entity<GunUpgradeDamageComponent> ent, ref GunShotEvent args)
{
foreach (var (ammo, _) in args.Ammo)
{
if (TryComp<ProjectileComponent>(ammo, out var proj))
proj.Damage += ent.Comp.Damage;
}
}
/// <summary>
/// Gets the entities inside the gun's upgrade container.
/// </summary>
public HashSet<Entity<GunUpgradeComponent>> GetCurrentUpgrades(Entity<UpgradeableGunComponent> ent)
{
if (!_container.TryGetContainer(ent, ent.Comp.UpgradesContainerId, out var container))
return new HashSet<Entity<GunUpgradeComponent>>();
var upgrades = new HashSet<Entity<GunUpgradeComponent>>();
foreach (var contained in container.ContainedEntities)
{
if (TryComp<GunUpgradeComponent>(contained, out var upgradeComp))
upgrades.Add((contained, upgradeComp));
}
return upgrades;
}
/// <summary>
/// Gets the tags of the upgrades currently applied.
/// </summary>
public IEnumerable<ProtoId<TagPrototype>> GetCurrentUpgradeTags(Entity<UpgradeableGunComponent> ent)
{
foreach (var upgrade in GetCurrentUpgrades(ent))
{
foreach (var tag in upgrade.Comp.Tags)
{
yield return tag;
}
}
}
}

View File

@@ -35,6 +35,7 @@ research-technology-wave-particle-harnessing = Wave Particle Harnessing
research-technology-advanced-riot-control = Advanced Riot Control
research-technology-portable-microfusion-weaponry = Portable Microfusion Weaponry
research-technology-experimental-battery-ammo = Experimental Battery Ammo
research-technology-kinetic-modifications = Kinetic Modifications
research-technology-basic-shuttle-armament = Shuttle Basic Armament
research-technology-advanced-shuttle-weapon = Advanced Shuttle Weapons
research-technology-thermal-weaponry = Thermal Weaponry

View File

@@ -0,0 +1,7 @@
upgradeable-gun-popup-already-present = Upgrade already installed!
upgradeable-gun-popup-upgrade-limit = Max upgrades reached!
gun-upgrade-popup-insert = Inserted {THE($upgrade)} into {THE($gun)}!
gun-upgrade-examine-text-damage = This has upgraded [color=#ec9b2d][bold]damage.[/bold][/color]
gun-upgrade-examine-text-range = This has upgraded [color=#2decec][bold]range.[/bold][/color]
gun-upgrade-examine-text-reload = This has upgraded [color=#bbf134][bold]fire rate.[/bold][/color]

View File

@@ -256,6 +256,12 @@
children:
- id: JetpackBlueFilled
- id: JetpackBlackFilled
- !type:GroupSelector
children:
- id: PKAUpgradeDamage
- id: PKAUpgradeRange
- id: PKAUpgradeFireRate
- type: entityTable
id: RandomGeneratorTable

View File

@@ -0,0 +1,80 @@
- type: entity
id: BasePKAUpgrade
parent: BaseItem
name: PKA modkit
description: A modkit for a proto-kinetic accelerator.
abstract: true
components:
- type: Sprite
sprite: Objects/Tools/upgrade.rsi
- type: Item
size: Small
- type: GunUpgrade
- type: StaticPrice
price: 750
- type: Tag
tags:
- PKAUpgrade
- type: entity
id: PKAUpgradeDamage
parent: BasePKAUpgrade
name: PKA modkit (damage)
components:
- type: Sprite
layers:
- state: base
- state: overlay-1
color: "#ec9b2d"
- state: overlay-2
color: "#a71010"
- state: overlay-3
color: "#eb4c13"
- type: GunUpgrade
tags: [ GunUpgradeDamage ]
examineText: gun-upgrade-examine-text-damage
- type: GunUpgradeDamage
damage:
types:
Blunt: 10
Structural: 15
- type: entity
id: PKAUpgradeRange
parent: BasePKAUpgrade
name: PKA modkit (range)
components:
- type: Sprite
layers:
- state: base
- state: overlay-1
color: "#2decec"
- state: overlay-2
color: "#1012a7"
- state: overlay-3
color: "#1373eb"
- type: GunUpgrade
tags: [ GunUpgradeRange ]
examineText: gun-upgrade-examine-text-range
- type: GunUpgradeSpeed
coefficient: 1.5
- type: entity
id: PKAUpgradeFireRate
parent: BasePKAUpgrade
name: PKA modkit (fire rate)
components:
- type: Sprite
layers:
- state: base
- state: overlay-1
color: "#bbf134"
- state: overlay-2
color: "#07901b"
- state: overlay-3
color: "#9bf134"
- type: GunUpgrade
tags: [ GunUpgradeReloadSpeed ]
examineText: gun-upgrade-examine-text-reload
- type: GunUpgradeFireRate
coefficient: 1.5

View File

@@ -7,7 +7,9 @@
sprite: Objects/Weapons/Guns/Basic/kinetic_accelerator.rsi
- type: Item
sprite: Objects/Weapons/Guns/Basic/kinetic_accelerator.rsi
size: Normal
size: Large
shape:
- 0,0,2,1
- type: GunWieldBonus
minAngle: -43
maxAngle: -43

View File

@@ -13,3 +13,10 @@
map: [ "empty-icon" ]
# todo: add itemcomponent with inhandVisuals states using unused texture and animation assets in kinetic_accelerator.rsi
# todo: add clothingcomponent with clothingVisuals states using unused texture and animations assets in kinetic_accelerator.rsi
- type: UpgradeableGun
whitelist:
tags:
- PKAUpgrade
- type: ContainerContainer
containers:
upgrades: !type:Container

View File

@@ -457,11 +457,11 @@
impactEffect: BulletImpactEffectKinetic
damage:
types:
Blunt: 25
Structural: 30
Blunt: 15
Structural: 15
# Short lifespan
- type: TimedDespawn
lifetime: 0.4
lifetime: 0.170 # ~4 tiles of range.
- type: GatheringProjectile
- type: entity

View File

@@ -53,6 +53,9 @@
- WeaponPistolCHIMP
- WeaponForceGun
- WeaponProtoKineticAccelerator
- PKAUpgradeDamage
- PKAUpgradeRange
- PKAUpgradeFireRate
- WeaponTetherGun
- WeaponGauntletGorilla

View File

@@ -59,3 +59,6 @@
id: SalvageSecurityWeapons
recipes:
- WeaponProtoKineticAccelerator
- PKAUpgradeDamage
- PKAUpgradeRange
- PKAUpgradeFireRate

View File

@@ -68,3 +68,37 @@
Plastic: 200
Silver: 200
Diamond: 100
- type: latheRecipe
id: PKAUpgradeDamage
result: PKAUpgradeDamage
categories:
- Weapons
completetime: 5
materials:
Steel: 1500
Gold: 500
Silver: 500
- type: latheRecipe
id: PKAUpgradeRange
result: PKAUpgradeRange
categories:
- Weapons
completetime: 5
materials:
Steel: 1500
Gold: 500
Silver: 500
- type: latheRecipe
id: PKAUpgradeFireRate
result: PKAUpgradeFireRate
categories:
- Weapons
completetime: 5
materials:
Steel: 1500
Gold: 500
Silver: 500

View File

@@ -155,6 +155,20 @@
recipeUnlocks:
- WeaponXrayCannon
- type: technology
id: KineticModifications
name: research-technology-kinetic-modifications
icon:
sprite: Objects/Tools/upgrade.rsi
state: display
discipline: Arsenal
tier: 2
cost: 7500
recipeUnlocks:
- PKAUpgradeDamage
- PKAUpgradeRange
- PKAUpgradeFireRate
- type: technology
id: BasicShuttleArmament
name: research-technology-basic-shuttle-armament

View File

@@ -617,6 +617,15 @@
- type: Tag
id: GuideEmbeded
- type: Tag
id: GunUpgradeDamage
- type: Tag
id: GunUpgradeRange
- type: Tag
id: GunUpgradeReloadSpeed
- type: Tag
id: Hamster
@@ -981,6 +990,9 @@
- type: Tag
id: Pizza
- type: Tag
id: PKAUpgrade
- type: Tag
id: PlantAnalyzer

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

View File

@@ -0,0 +1,28 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from https://github.com/tgstation/tgstation at commit 6665eec76c98a4f3f89bebcd10b34b47dcc0b8ae.",
"size":
{
"x": 32,
"y": 32
},
"states":
[
{
"name": "base"
},
{
"name": "overlay-1"
},
{
"name": "overlay-2"
},
{
"name": "overlay-3"
},
{
"name": "display"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 B