Skip to content
Draft
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 @@ -61,4 +61,5 @@ public enum MartialArtsForms
KungFuDragon,
Ninjutsu,
HellRip,
BigBosCloseQuartersCombat, // Pirate Changes Власне бойове мистецтво
}
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,11 @@ private void OnComboAttackPerformed(Entity<MartialArtsKnowledgeComponent> ent, r
case MartialArtsForms.Capoeira:
OnCapoeiraAttackPerformed(ent, ref args);
break;
// Pirate Changes Start Here Власне Бойове Мистецтво
case MartialArtsForms.BigBosCloseQuartersCombat:
OnCQCAttackPerformed(ent, ref args);
break;
// Pirate Changes End Here Власне Бойове Мистецтво
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using Content.Pirate.Shared._JustDecor.MartialArts.Components;
using Content.Server.NPC.HTN;
using Robust.Shared.GameObjects;
using Robust.Shared.Timing;
using Robust.Shared.Prototypes;
using System.Collections.Generic;
using Content.Shared.Stunnable;
using Content.Shared.NPC.Systems;

namespace Content.Pirate.Server._JustDecor.MartialArts.Systems;

public sealed class BigBosCQCSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly SharedStunSystem _stun = default!;
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;

public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<NpcEliminatedComponent, ComponentStartup>(OnEliminatedStartup);
SubscribeLocalEvent<NpcEliminatedComponent, ComponentShutdown>(OnEliminatedShutdown);
}

public override void Update(float frameTime)
{
base.Update(frameTime);

var query = EntityQueryEnumerator<NpcEliminatedComponent>();
while (query.MoveNext(out var uid, out var component))
{
if (TerminatingOrDeleted(uid))
continue;

if (_timing.CurTime - component.StartTime > component.Duration)
{
RemCompDeferred<NpcEliminatedComponent>(uid);
}
}
}

private void OnEliminatedStartup(EntityUid uid, NpcEliminatedComponent component, ComponentStartup args)
{
component.StartTime = _timing.CurTime;

// Збиває ціль з ніг, щоб вона лежала до відновлення
_stun.TryKnockdown(uid, component.Duration, true);

if (TryComp<HTNComponent>(uid, out var htn))
{
component.OriginalHTN = htn.RootTask.Task;
RemCompDeferred<HTNComponent>(uid);
}

if (TryComp<Content.Shared.NPC.Components.NpcFactionMemberComponent>(uid, out var faction))
{
component.OriginalFactions = new HashSet<string>();
foreach (var f in faction.Factions)
{
component.OriginalFactions.Add(f);
}
RemCompDeferred<Content.Shared.NPC.Components.NpcFactionMemberComponent>(uid);
}
}

private void OnEliminatedShutdown(EntityUid uid, NpcEliminatedComponent component, ComponentShutdown args)
{
if (TerminatingOrDeleted(uid)) return;

if (component.OriginalHTN != null)
{
var htn = EnsureComp<HTNComponent>(uid);
htn.RootTask = new Content.Server.NPC.HTN.HTNCompoundTask() { Task = component.OriginalHTN };
}

if (component.OriginalFactions != null)
{
foreach (var f in component.OriginalFactions)
{
_npcFaction.AddFaction(uid, f);
}
}

RemCompDeferred<Content.Shared.CombatMode.Pacification.PacifiedComponent>(uid);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
using Content.Goobstation.Common.MartialArts;
using Content.Goobstation.Shared.MartialArts.Components;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Timing;

namespace Content.Pirate.Shared._JustDecor.MartialArts.Components;

/// <summary>
/// Компонент для отримання знання BigBos CQC
/// </summary>
[RegisterComponent]
public sealed partial class GrantBigBosCQCComponent : GrantMartialArtKnowledgeComponent
{
[DataField]
public override MartialArtsForms MartialArtsForm { get; set; } = MartialArtsForms.BigBosCloseQuartersCombat;

public override LocId? LearnMessage { get; set; } = "bigbos-cqc-success-learned";
}


/// <summary>
/// Компонент для бафу кулдаунів BigBos CQC
/// </summary>
[RegisterComponent]
public sealed partial class BigBosCqcCooldownsComponent : Component
{
[DataField]
public Dictionary<string, TimeSpan> CooldownTimers = new();
}

/// <summary>
/// Компонент для бафу "знання" BigBos CQC
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class BigBosCqcKnowledgeComponent : Component
{
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
public int CombosPerformed = 0;

[DataField]
public TimeSpan LastComboTime;

[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
public bool CombatMode = false;

[DataField]
public TimeSpan CombatModeDuration = TimeSpan.FromSeconds(10);

[DataField]
public TimeSpan CombatModeEndTime;

[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
public int CurrentComboChain = 0;

[DataField]
public int MaxComboChain = 3;

[DataField]
public TimeSpan ComboChainTimeout = TimeSpan.FromSeconds(2);

[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
public float HasteMeter = 0f;

[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
public float MaxHasteBonus = 0.8f; // Max 60% speed bonus (was 40%)

[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
public float HasteDecayRate = 0.1f; // Decay per second (was 0.2)

[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
public float? OriginalAttackRate;
}

/// <summary>
/// Компонент для бафу Rush (збільшення швидкості)
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class BigBosCqcRushBuffComponent : Component
{
[DataField, AutoNetworkedField]
public TimeSpan EndTime;

[DataField, AutoNetworkedField]
public float SpeedMultiplier = 1.3f;
}

/// <summary>
/// Компонент для бафу (зменшення/відбиття шкоди)
/// </summary>
[RegisterComponent]
public sealed partial class BigBosCqcCounterBuffComponent : Component
{
[DataField]
public TimeSpan EndTime;

[DataField]
public float DamageReduction = 0.5f;

[DataField]
public float ReflectDamage = 0.2f;

[DataField]
public SoundSpecifier? CounterSound;
}

/// <summary>
/// Компонент для удушення
/// </summary>
[RegisterComponent]
public sealed partial class InChokeholdComponent : Component
{
[DataField]
public EntityUid Choker;

[DataField]
public float OxygenDamageRate = 2f;

[DataField]
public TimeSpan LastDamageTick;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;

namespace Content.Pirate.Shared._JustDecor.MartialArts.Components;

/// <summary>
/// Allows an NPC to be interrogated using the Big Boss CQC Interrogation combo.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class NpcInterrogatableComponent : Component
{
/// <summary>
/// The ID of the interrogation dataset prototype.
/// </summary>
[DataField]
public ProtoId<NpcInterrogationPrototype>? Prototype { get; set; }

/// <summary>
/// Has this NPC already been interrogated?
/// </summary>
[DataField]
public bool Interrogated { get; set; }

[ViewVariables]
public List<string>? PendingLinkedAnswers { get; set; }
}

/// <summary>
/// Prototypical data for NPC interrogation responses.
/// </summary>
[Prototype("npcInterrogation")]
[Serializable, NetSerializable]
public sealed class NpcInterrogationPrototype : IPrototype
{
[IdDataField]
public string ID { get; private set; } = default!;

/// <summary>
/// List of general answers when interrogated.
/// </summary>
[DataField]
public List<string> Answers { get; private set; } = new();

/// <summary>
/// List of general interrogation questions said by the interrogator.
/// </summary>
[DataField]
public List<string> Questions { get; private set; } = new();

/// <summary>
/// Linked question/answer sets.
/// </summary>
[DataField]
public List<NpcInterrogationLinkedEntry> Linked { get; private set; } = new();
}

/// <summary>
/// Component added to NPCs that have been "eliminated" (e.g. after interrogation).
/// This should prevent them from fighting back.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class NpcEliminatedComponent : Component
{
[DataField]
public string? OriginalHTN;

[DataField]
public HashSet<string>? OriginalFactions;

[DataField]
public TimeSpan StartTime;

[DataField]
public TimeSpan Duration = TimeSpan.FromSeconds(15);
}

[DataDefinition, Serializable]
public sealed partial class NpcInterrogationLinkedEntry
{
[DataField("question")]
public string Question { get; set; } = string.Empty;

[DataField("answers")]
public List<string> Answers { get; set; } = new();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;

namespace Content.Pirate.Shared._JustDecor.MartialArts.Events;

/// <summary>
/// Івенти для комбо
/// </summary>

public sealed partial class BigBosCqcTakedownPerformedEvent : EntityEventArgs { }

public sealed partial class BigBosCqcDisarmPerformedEvent : EntityEventArgs { }

public sealed partial class BigBosCqcThrowPerformedEvent : EntityEventArgs { }

public sealed partial class BigBosCqcChokePerformedEvent : EntityEventArgs { }

public sealed partial class BigBosCqcChainPerformedEvent : EntityEventArgs { }

public sealed partial class BigBosCqcCounterPerformedEvent : EntityEventArgs { }

public sealed partial class BigBosCqcInterrogationPerformedEvent : EntityEventArgs { }

public sealed partial class BigBosCqcStealthTakedownPerformedEvent : EntityEventArgs { }

public sealed partial class BigBosCqcRushPerformedEvent : EntityEventArgs { }

public sealed partial class BigBosCqcFinisherPerformedEvent : EntityEventArgs { }

// DoAfter івенти для подовжених комбошок
[Serializable, NetSerializable]
public sealed partial class BigBosCqcInterrogationDoAfterEvent : DoAfterEvent
{
public override DoAfterEvent Clone() => this;
}

[Serializable, NetSerializable]
public sealed partial class BigBosCqcChokeDoAfterEvent : DoAfterEvent
{
public override DoAfterEvent Clone() => this;
}
Loading