Merge branch 'master' into ed-25-08-2025-upstream-sync
# Conflicts: # Resources/Prototypes/_CP14/ModularCraft/Blade/axe.yml # Resources/Prototypes/_CP14/ModularCraft/Blade/base.yml # Resources/Prototypes/_CP14/ModularCraft/Blade/dagger.yml # Resources/Prototypes/_CP14/ModularCraft/Blade/hammer.yml # Resources/Prototypes/_CP14/ModularCraft/Blade/hoe.yml # Resources/Prototypes/_CP14/ModularCraft/Blade/mace.yml # Resources/Prototypes/_CP14/ModularCraft/Blade/mop.yml # Resources/Prototypes/_CP14/ModularCraft/Blade/pickaxe.yml # Resources/Prototypes/_CP14/ModularCraft/Blade/shovel.yml # Resources/Prototypes/_CP14/ModularCraft/Blade/sickle.yml # Resources/Prototypes/_CP14/ModularCraft/Blade/skimitar.yml # Resources/Prototypes/_CP14/ModularCraft/Blade/spear.yml # Resources/Prototypes/_CP14/ModularCraft/Blade/sword.yml # Resources/Prototypes/_CP14/ModularCraft/Garde/guildmaster.yml # Resources/Prototypes/_CP14/ModularCraft/Garde/sharp.yml
This commit is contained in:
@@ -66,13 +66,19 @@ public sealed class MeleeHitEvent : HandledEntityEventArgs
|
||||
/// </remarks>
|
||||
public bool IsHit = true;
|
||||
|
||||
public MeleeHitEvent(List<EntityUid> hitEntities, EntityUid user, EntityUid weapon, DamageSpecifier baseDamage, Vector2? direction)
|
||||
/// <summary>
|
||||
/// CP14 Heavy attack flag.
|
||||
/// </summary>
|
||||
public bool CP14Heavy;
|
||||
|
||||
public MeleeHitEvent(List<EntityUid> hitEntities, EntityUid user, EntityUid weapon, DamageSpecifier baseDamage, Vector2? direction, bool heavy = false)
|
||||
{
|
||||
HitEntities = hitEntities;
|
||||
User = user;
|
||||
Weapon = weapon;
|
||||
BaseDamage = baseDamage;
|
||||
Direction = direction;
|
||||
CP14Heavy = heavy; //CP14
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ public sealed partial class MeleeWeaponComponent : Component
|
||||
/// Multiplies damage by this amount for single-target attacks.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public FixedPoint2 ClickDamageModifier = FixedPoint2.New(1);
|
||||
public FixedPoint2 ClickDamageModifier = FixedPoint2.New(1.3); //CP14 default bonus damage
|
||||
|
||||
// TODO: Temporarily 1.5 until interactionoutline is adjusted to use melee, then probably drop to 1.2
|
||||
/// <summary>
|
||||
@@ -131,7 +131,7 @@ public sealed partial class MeleeWeaponComponent : Component
|
||||
/// CrystallEdge Melee upgrade. how far away from the player the animation should be played.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float CPAnimationOffset = -1f;
|
||||
public float CPAnimationOffset = 1f;
|
||||
|
||||
// Sounds
|
||||
|
||||
|
||||
@@ -498,7 +498,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
LogImpact.Low,
|
||||
$"{ToPrettyString(user):actor} melee attacked (light) using {ToPrettyString(meleeUid):tool} and missed");
|
||||
}
|
||||
var missEvent = new MeleeHitEvent(new List<EntityUid>(), user, meleeUid, damage, null);
|
||||
var missEvent = new MeleeHitEvent(new List<EntityUid>(), user, meleeUid, damage, null, heavy: false);
|
||||
RaiseLocalEvent(meleeUid, missEvent);
|
||||
_meleeSound.PlaySwingSound(user, meleeUid, component);
|
||||
return;
|
||||
@@ -507,7 +507,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
// Sawmill.Debug($"Melee damage is {damage.Total} out of {component.Damage.Total}");
|
||||
|
||||
// Raise event before doing damage so we can cancel damage if the event is handled
|
||||
var hitEvent = new MeleeHitEvent(new List<EntityUid> { target.Value }, user, meleeUid, damage, null);
|
||||
var hitEvent = new MeleeHitEvent(new List<EntityUid> { target.Value }, user, meleeUid, damage, null, heavy: false);
|
||||
RaiseLocalEvent(meleeUid, hitEvent);
|
||||
|
||||
if (hitEvent.Handled)
|
||||
@@ -600,7 +600,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
LogImpact.Low,
|
||||
$"{ToPrettyString(user):actor} melee attacked (heavy) using {ToPrettyString(meleeUid):tool} and missed");
|
||||
}
|
||||
var missEvent = new MeleeHitEvent(new List<EntityUid>(), user, meleeUid, damage, direction);
|
||||
var missEvent = new MeleeHitEvent(new List<EntityUid>(), user, meleeUid, damage, direction, heavy: true);
|
||||
RaiseLocalEvent(meleeUid, missEvent);
|
||||
|
||||
// immediate audio feedback
|
||||
@@ -649,7 +649,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
||||
// Sawmill.Debug($"Melee damage is {damage.Total} out of {component.Damage.Total}");
|
||||
|
||||
// Raise event before doing damage so we can cancel damage if the event is handled
|
||||
var hitEvent = new MeleeHitEvent(targets, user, meleeUid, damage, direction);
|
||||
var hitEvent = new MeleeHitEvent(targets, user, meleeUid, damage, direction, heavy: true);
|
||||
RaiseLocalEvent(meleeUid, hitEvent);
|
||||
|
||||
if (hitEvent.Handled)
|
||||
|
||||
7
Content.Shared/_CP14/Eye/CP14ToggleableEyeSystem.cs
Normal file
7
Content.Shared/_CP14/Eye/CP14ToggleableEyeSystem.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using Content.Shared.Actions;
|
||||
|
||||
namespace Content.Shared._CP14.Eye;
|
||||
|
||||
public sealed partial class CP14EyeOffsetToggleActionEvent : InstantActionEvent
|
||||
{
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.IdentityManagement.Components;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Serialization;
|
||||
@@ -15,7 +15,7 @@ public abstract class CP14SharedIdentityRecognitionSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
[Dependency] private readonly SharedIdentitySystem _identity = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -68,11 +68,17 @@ public abstract class CP14SharedIdentityRecognitionSystem : EntitySystem
|
||||
Priority = 2,
|
||||
Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/sentient.svg.192dpi.png")),
|
||||
Text = Loc.GetString("cp14-remember-name-verb"),
|
||||
Disabled = seeAttemptEv.Cancelled,
|
||||
Act = () =>
|
||||
{
|
||||
_uiSystem.SetUiState(_args.User, CP14RememberNameUiKey.Key, new CP14RememberNameUiState(GetNetEntity(ent)));
|
||||
_uiSystem.TryToggleUi(_args.User, CP14RememberNameUiKey.Key, actor.PlayerSession);
|
||||
if (seeAttemptEv.Cancelled)
|
||||
{
|
||||
_popup.PopupClient(Loc.GetString("cp14-remember-fail-mask"), _args.Target, _args.User);
|
||||
}
|
||||
else
|
||||
{
|
||||
_uiSystem.SetUiState(_args.User, CP14RememberNameUiKey.Key, new CP14RememberNameUiState(GetNetEntity(ent)));
|
||||
_uiSystem.TryToggleUi(_args.User, CP14RememberNameUiKey.Key, actor.PlayerSession);
|
||||
}
|
||||
},
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
|
||||
27
Content.Shared/_CP14/MagicSpell/Spells/CP14SpellKnockdown.cs
Normal file
27
Content.Shared/_CP14/MagicSpell/Spells/CP14SpellKnockdown.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Content.Shared.Stunnable;
|
||||
|
||||
namespace Content.Shared._CP14.MagicSpell.Spells;
|
||||
|
||||
public sealed partial class CP14SpellKnockdown : CP14SpellEffect
|
||||
{
|
||||
[DataField]
|
||||
public float ThrowPower = 10f;
|
||||
|
||||
[DataField]
|
||||
public TimeSpan Time = TimeSpan.FromSeconds(1f);
|
||||
|
||||
[DataField]
|
||||
public bool DropItems = false;
|
||||
|
||||
public override void Effect(EntityManager entManager, CP14SpellEffectBaseArgs args)
|
||||
{
|
||||
if (args.Target is null || args.User is null)
|
||||
return;
|
||||
|
||||
var targetEntity = args.Target.Value;
|
||||
|
||||
var stun = entManager.System<SharedStunSystem>();
|
||||
|
||||
stun.TryKnockdown(args.Target.Value, Time, true, true, DropItems);
|
||||
}
|
||||
}
|
||||
@@ -63,5 +63,29 @@ public sealed partial class CP14SpellPointerToVampireClan : CP14SpellEffect
|
||||
|
||||
transform.SetWorldRotation(pointer, angle + Angle.FromDegrees(90));
|
||||
}
|
||||
|
||||
var heartsInRange = lookup.GetEntitiesInRange<CP14VampireClanHeartComponent>(originEntPosition, SearchRange);
|
||||
foreach (var heart in heartsInRange)
|
||||
{
|
||||
if (!Inversed)
|
||||
{
|
||||
if (heart.Comp.Faction != vampireComponent.Faction)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (heart.Comp.Faction == vampireComponent.Faction)
|
||||
continue;
|
||||
}
|
||||
|
||||
var targetPosition = transform.GetWorldPosition(heart);
|
||||
|
||||
//Calculate the rotation
|
||||
Angle angle = new(targetPosition - originPosition);
|
||||
|
||||
var pointer = entManager.Spawn(PointerEntity, new MapCoordinates(originPosition, transform.GetMapId(originEntPosition)));
|
||||
|
||||
transform.SetWorldRotation(pointer, angle + Angle.FromDegrees(90));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,8 +44,7 @@ public sealed partial class CP14SpellTeleportToVampireSingleton : CP14SpellEffec
|
||||
if (singleton.Key != indexedVampireFaction.SingletonTeleportKey)
|
||||
continue;
|
||||
|
||||
var randomOffset = new Vector2(random.Next(-1, 1), random.Next(-1, 1));
|
||||
var second = entManager.SpawnAtPosition(PortalProto, xform.Coordinates.Offset(randomOffset));
|
||||
var second = entManager.SpawnAtPosition(PortalProto, xform.Coordinates);
|
||||
|
||||
linkSys.TryLink(first, second, true);
|
||||
return;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using Content.Shared.Damage;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MeleeWeapon.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Adds bonus damage to weapons if targets are at a certain distance from the attacker.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class CP14BonusDistanceMeleeDamageComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public DamageSpecifier BonusDamage = new();
|
||||
|
||||
[DataField]
|
||||
public float MinDistance = 1f;
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/_CP14/Effects/critical.ogg")
|
||||
{
|
||||
Params = AudioParams.Default.WithVariation(0.125f),
|
||||
};
|
||||
|
||||
[DataField]
|
||||
public EntProtoId VFX = "CP14MeleeCritEffect";
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
using Content.Shared.Damage;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared._CP14.MeleeWeapon.Components;
|
||||
|
||||
/// <summary>
|
||||
/// After several wide attacks, a light attack deals additional damage.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class CP14ComboBonusMeleeDamageComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public DamageSpecifier BonusDamage = new();
|
||||
|
||||
[DataField]
|
||||
public int HeavyAttackNeed = 2;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public int CurrentHeavyAttacks = 0;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public HashSet<EntityUid> HitEntities = new();
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/_CP14/Effects/critical_sword.ogg")
|
||||
{
|
||||
Params = AudioParams.Default.WithVariation(0.125f),
|
||||
};
|
||||
|
||||
[DataField]
|
||||
public EntProtoId VFX = "CP14MeleeCritEffect";
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._CP14.MeleeWeapon.Components;
|
||||
|
||||
/// <summary>
|
||||
/// After several wide attacks, a light attack deals additional damage.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class CP14LightMeleeKnockdownComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public float ThrowDistance = 0.5f;
|
||||
|
||||
[DataField]
|
||||
public TimeSpan KnockdownTime = TimeSpan.FromSeconds(0.25f);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Shared._CP14.MeleeWeapon.Components;
|
||||
|
||||
/// <summary>
|
||||
/// allows this item to be knocked out of your hands by a successful parry
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class CP14MeleeParriableComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public TimeSpan LastMeleeHit = TimeSpan.Zero;
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier ParrySound = new SoundPathSpecifier("/Audio/_CP14/Effects/parry1.ogg", AudioParams.Default.WithVariation(0.2f));
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Content.Shared._CP14.MeleeWeapon.Components;
|
||||
|
||||
/// <summary>
|
||||
/// attacks with this item may knock CP14ParriableComponent items out of your hand on a hit
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class CP14MeleeParryComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public TimeSpan ParryWindow = TimeSpan.FromSeconds(1f);
|
||||
|
||||
[DataField]
|
||||
public float ParryPower = 1f;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared._CP14.MeleeWeapon.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Using this weapon damages the wearer's stamina.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class CP14MeleeWeaponStaminaCostComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public float Stamina = 10f;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Content.Shared._CP14.MeleeWeapon.Components;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
|
||||
namespace Content.Shared._CP14.MeleeWeapon.EntitySystems;
|
||||
|
||||
public sealed class CP14MeleeSelfDamageSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<CP14MeleeSelfDamageComponent, MeleeHitEvent>(OnMeleeHit);
|
||||
}
|
||||
|
||||
private void OnMeleeHit(Entity<CP14MeleeSelfDamageComponent> ent, ref MeleeHitEvent args)
|
||||
{
|
||||
if (!args.IsHit)
|
||||
return;
|
||||
if (args.HitEntities.Count == 0)
|
||||
return;
|
||||
_damageable.TryChangeDamage(ent, ent.Comp.DamageToSelf);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared._CP14.MeleeWeapon.Components;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Stunnable;
|
||||
using Content.Shared.Throwing;
|
||||
using Content.Shared.Weapons.Melee.Events;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared._CP14.MeleeWeapon.EntitySystems;
|
||||
|
||||
public sealed class CP14MeleeWeaponSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SharedStunSystem _stun = default!;
|
||||
[Dependency] private readonly ThrowingSystem _throw = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly SharedStaminaSystem _stamina = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<CP14MeleeSelfDamageComponent, MeleeHitEvent>(OnMeleeHit);
|
||||
SubscribeLocalEvent<CP14BonusDistanceMeleeDamageComponent, MeleeHitEvent>(OnDistanceBonusDamage);
|
||||
SubscribeLocalEvent<CP14ComboBonusMeleeDamageComponent, MeleeHitEvent>(OnComboBonusDamage);
|
||||
SubscribeLocalEvent<CP14LightMeleeKnockdownComponent, MeleeHitEvent>(OnKnockdownAttack);
|
||||
SubscribeLocalEvent<CP14MeleeParryComponent, MeleeHitEvent>(OnMeleeParryHit);
|
||||
SubscribeLocalEvent<CP14MeleeParriableComponent, AttemptMeleeEvent>(OnMeleeParriableHitAttmpt);
|
||||
SubscribeLocalEvent<CP14MeleeWeaponStaminaCostComponent, MeleeHitEvent>(OnMeleeStaminaCost);
|
||||
}
|
||||
|
||||
private void OnMeleeStaminaCost(Entity<CP14MeleeWeaponStaminaCostComponent> ent, ref MeleeHitEvent args)
|
||||
{
|
||||
_stamina.TakeStaminaDamage(args.User, ent.Comp.Stamina);
|
||||
}
|
||||
|
||||
private void OnMeleeParryHit(Entity<CP14MeleeParryComponent> ent, ref MeleeHitEvent args)
|
||||
{
|
||||
if (args.HitEntities.Count != 1)
|
||||
return;
|
||||
|
||||
var target = args.HitEntities[0];
|
||||
|
||||
var activeTargetHand = _hands.GetActiveHand(target);
|
||||
|
||||
var heldItem = _hands.GetHeldItem(target, activeTargetHand);
|
||||
if (heldItem is null)
|
||||
return;
|
||||
|
||||
if (!TryComp<CP14MeleeParriableComponent>(heldItem, out var meleeParriable))
|
||||
return;
|
||||
|
||||
if (_timing.CurTime > meleeParriable.LastMeleeHit + ent.Comp.ParryWindow)
|
||||
return;
|
||||
|
||||
_hands.TryDrop(target, heldItem.Value);
|
||||
_throw.TryThrow(heldItem.Value, _random.NextAngle().ToWorldVec(), ent.Comp.ParryPower, target);
|
||||
_popup.PopupPredicted( Loc.GetString("cp14-successful-parry"), args.User, args.User);
|
||||
_audio.PlayPredicted(meleeParriable.ParrySound, heldItem.Value, args.User);
|
||||
}
|
||||
|
||||
private void OnMeleeParriableHitAttmpt(Entity<CP14MeleeParriableComponent> ent, ref AttemptMeleeEvent args)
|
||||
{
|
||||
ent.Comp.LastMeleeHit = _timing.CurTime;
|
||||
}
|
||||
|
||||
private void OnKnockdownAttack(Entity<CP14LightMeleeKnockdownComponent> ent, ref MeleeHitEvent args)
|
||||
{
|
||||
if (args.CP14Heavy)
|
||||
return;
|
||||
|
||||
foreach (var hit in args.HitEntities)
|
||||
{
|
||||
_stun.TryKnockdown(hit, ent.Comp.KnockdownTime, true, drop: false);
|
||||
|
||||
// Vector from splitter to item
|
||||
var direction = Transform(hit).Coordinates.Position - Transform(args.User).Coordinates.Position;
|
||||
if (direction != Vector2.Zero)
|
||||
{
|
||||
var dir = direction.Normalized() * ent.Comp.ThrowDistance;
|
||||
_throw.TryThrow(hit, dir, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnComboBonusDamage(Entity<CP14ComboBonusMeleeDamageComponent> ent, ref MeleeHitEvent args)
|
||||
{
|
||||
// Resets combo state
|
||||
void Reset()
|
||||
{
|
||||
ent.Comp.HitEntities.Clear();
|
||||
ent.Comp.CurrentHeavyAttacks = 0;
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
// No hits this swing → reset
|
||||
if (args.HitEntities.Count == 0)
|
||||
{
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
var comp = ent.Comp;
|
||||
|
||||
// Not enough heavy attacks accumulated yet
|
||||
if (comp.CurrentHeavyAttacks < comp.HeavyAttackNeed)
|
||||
{
|
||||
// Light attack before threshold → reset combo
|
||||
if (!args.CP14Heavy)
|
||||
{
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// Heavy attack: track overlapping targets across swings
|
||||
if (comp.HitEntities.Count == 0)
|
||||
{
|
||||
// First heavy: initialize the set with current hits
|
||||
comp.HitEntities.UnionWith(args.HitEntities);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subsequent heavy: keep only targets hit every time
|
||||
comp.HitEntities.IntersectWith(args.HitEntities);
|
||||
|
||||
// Diverged to different targets → reset
|
||||
if (comp.HitEntities.Count == 0)
|
||||
{
|
||||
Reset();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
comp.CurrentHeavyAttacks++;
|
||||
Dirty(ent);
|
||||
return;
|
||||
}
|
||||
|
||||
// Light attack after enough heavies → check if it hits any tracked target
|
||||
if (comp.HitEntities.Overlaps(args.HitEntities) && !args.CP14Heavy)
|
||||
{
|
||||
if (_timing.IsFirstTimePredicted)
|
||||
{
|
||||
_audio.PlayPredicted(comp.Sound, ent, args.User);
|
||||
args.BonusDamage += comp.BonusDamage;
|
||||
|
||||
// Visual feedback on every hit entity this swing
|
||||
foreach (var hit in args.HitEntities)
|
||||
{
|
||||
PredictedSpawnAtPosition(comp.VFX, Transform(hit).Coordinates);
|
||||
}
|
||||
}
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDistanceBonusDamage(Entity<CP14BonusDistanceMeleeDamageComponent> ent, ref MeleeHitEvent args)
|
||||
{
|
||||
var critical = true;
|
||||
|
||||
if (args.HitEntities.Count == 0)
|
||||
return;
|
||||
|
||||
var userPos = _transform.GetWorldPosition(args.User);
|
||||
//Crit only if all targets are at distance
|
||||
foreach (var hit in args.HitEntities)
|
||||
{
|
||||
var targetPos = _transform.GetWorldPosition(hit);
|
||||
|
||||
var distance = (userPos - targetPos).Length();
|
||||
if (distance < ent.Comp.MinDistance)
|
||||
{
|
||||
critical = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!critical)
|
||||
return;
|
||||
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
_audio.PlayPredicted(ent.Comp.Sound, ent, args.User);
|
||||
args.BonusDamage += ent.Comp.BonusDamage;
|
||||
|
||||
//Visual effect!
|
||||
foreach (var hit in args.HitEntities)
|
||||
{
|
||||
PredictedSpawnAtPosition(ent.Comp.VFX, Transform(hit).Coordinates);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMeleeHit(Entity<CP14MeleeSelfDamageComponent> ent, ref MeleeHitEvent args)
|
||||
{
|
||||
if (!args.IsHit)
|
||||
return;
|
||||
if (args.HitEntities.Count == 0)
|
||||
return;
|
||||
_damageable.TryChangeDamage(ent, ent.Comp.DamageToSelf);
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ public abstract partial class CP14SharedTradingPlatformSystem : EntitySystem
|
||||
var repComp = EnsureComp<CP14TradingReputationComponent>(args.User);
|
||||
repComp.Reputation.TryAdd(ent.Comp.Faction, 0);
|
||||
_audio.PlayLocal(new SoundCollectionSpecifier("CP14CoinImpact"), args.User, args.User);
|
||||
_popup.PopupPredicted(Loc.GetString("cp14-trading-contract-use", ("name", Loc.GetString(indexedFaction.Name))), args.User, args.User);
|
||||
_popup.PopupClient(Loc.GetString("cp14-trading-contract-use", ("name", Loc.GetString(indexedFaction.Name))), args.User, args.User);
|
||||
|
||||
if (_net.IsServer)
|
||||
QueueDel(ent);
|
||||
|
||||
@@ -32,7 +32,7 @@ public sealed partial class CP14VampireClanHeartComponent : Component
|
||||
public FixedPoint2 Level4 = 21f;
|
||||
|
||||
[DataField]
|
||||
public FixedPoint2 EssenceRegenPerLevel = 0.1f;
|
||||
public FixedPoint2 EssenceRegen = 0.2f;
|
||||
|
||||
[DataField]
|
||||
public TimeSpan RegenFrequency = TimeSpan.FromMinutes(1);
|
||||
|
||||
Reference in New Issue
Block a user