Upstream 08.03-09.03 (#299)

* Grabbing Fixes / Table Slam (#1889)

# Description
Ports several fixes + Tabling from
[/Goob-Station#1922](https://github.com/Goob-Station/Goob-Station/pull/1922)
Tabling is pretty much 1:1 with how it is from SS13

## This shit is so code
Required before I can port [Martial
Arts](https://github.com/Goob-Station/Goob-Station/pull/1868)

# TODO
* [ ] Await merge
* [ ] Gaming

# Media

![CQC](https://github.com/user-attachments/assets/dc202ce1-ec97-4448-b8bc-71b9a44a608f)

# Changelog
🆑 Eagle

* add: Table slamming. Harm a table when you have someone harm choked,
see what happens.
* tweak: Grab throw damage to other entities is now based on the thrown
entities kinetic energy. No more mouse wrecking balls.
* tweak: You can now escape from a soft grab by just walking away.
* tweak: You can no longer grab someone else while your being grabbed.
* tweak: Mass now effects grab release attempts.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **New Features**
- Introduced a table slam mechanic that brings dynamic combat
interactions. Characters and objects can now be “tabled” with associated
damage, stamina effects, and paralysis chance.
- Added new interactive states for pullable entities, enriching
environmental and combat engagements.

- **Gameplay Improvements**
- Refined pulling and throwing mechanics to enhance collision handling
and damage calculations, resulting in more impactful throw actions and
balanced kinetic responses.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

(cherry picked from commit 71147f8295c5c817b56d52c5d2a38acced2f14b9)

* Automatic Changelog Update (#1889)

(cherry picked from commit 434ce42a8a0739ff0873c4c02bfe83ed39c857e9)

* Fix UI Crap (#1888)

I have no idea if this fixes the issues, and I have not checked if it
does. But this is the only thing we're missing that wasn't related to
other unrelated stuff.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Bug Fixes**
- Improved the initialization process for several in-game user
interfaces, ensuring that all essential functionalities load
consistently when accessed.
- **New Features**
- Enhanced the voice mask configuration panel to automatically present
available speech verb options, streamlining the setup process for users.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Milon <milonpl.git@proton.me>
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
(cherry picked from commit e2fbebba312a01d9fb92eaac64190df503607f6b)

* Shuttle Spawner Airlocks (#1890)

# Description

This PR adds a variety of "Shuttle Spawning Airlocks" for certain ships
in this game that mappers might wish to use. The most important of which
are airlocks that cause a Cargo Shuttle and a Pathfinder to spawn
already docked to the station. The fact that nobody did this before was
fucking astounding to me.

# Changelog

🆑
- add: Added a variety of "Shuttle Spawning Airlocks" for mappers to
use, which can make it so that shuttles like the Cargo Shuttle,
Pathfinder, etc. Spawn already docked to the station.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced a refined docking and spawning system for shuttle
operations, incorporating multiple shuttle types including cargo, dart,
infiltrator, pathfinder, and SANDropship.
- Added dedicated deployment entities to manage shuttle instantiation
effectively.
- Rolled out a new tagging framework to enhance the categorization and
identification of dockable vehicles.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

(cherry picked from commit 2f9239f6b0918fbdca1f0c48a06c3b3b76a11ab8)

* Automatic Changelog Update (#1890)

(cherry picked from commit 39eb098ebe3fcf7f283f46fadfc76545c20e667c)

* Update Credits (#1854)

This is an automated Pull Request. This PR updates the GitHub
contributors in the credits section.

Co-authored-by: SimpleStation Changelogs <SimpleStation14@users.noreply.github.com>
(cherry picked from commit ad2ebc04093388d29db758fd7e632744e4d728d8)

* Remove Outdated Description From Flash, Add One to the New Disabler A… (#1887)

<!--
This is a semi-strict format, you can add/remove sections as needed but
the order/format should be kept the same
Remove these comments before submitting
-->

# Description

<!--
Explain this PR in as much detail as applicable

Some example prompts to consider:
How might this affect the game? The codebase?
What might be some alternatives to this?
How/Who does this benefit/hurt [the game/codebase]?
-->

Accidentally left an old ExtendDescription on a flash which is no longer
accurate, and added extra descriptions to the rev manifesto and civilian
disabler while I was at it.

---

# Changelog

<!--
You can add an author after the `🆑` to change the name that appears
in the changelog (ex: `🆑 Death`)
Leaving it blank will default to your GitHub display name
This includes all available types for the changelog
-->

🆑
- add: Added extra descriptions to the revolutionary manifesto and the
civilian disabler
- fix: Fixed extenddescription on flash

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Introduced extended, context-sensitive in-game descriptions for the
revolutionary manifesto, offering nuanced details about its use across
various roles.
- Added enhanced descriptive information for the civilian disabler
weapon, clarifying its legal ownership and accessory considerations.

- **Chores**
- Streamlined the flash item display by removing redundant extended
descriptions to improve clarity.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Timfa <timfalken@hotmail.com>
(cherry picked from commit 3e3bee060cb7eee98ae3fde7c4f7b819f16bf840)

* Automatic Changelog Update (#1887)

(cherry picked from commit 4af6dc83bd2ebded1421dadea70b6a9586776fda)

* NewMats Lathe Recipe Changes (#1873)

# Description

<!--
Explain this PR in as much detail as applicable

Some example prompts to consider:
How might this affect the game? The codebase?
What might be some alternatives to this?
How/Who does this benefit/hurt [the game/codebase]?
-->

Removes wait times from Copper, Lead, and Aluminum and reduces the wait
time for Tungsten to 0.13 (4 seconds over 30 ingots, too rare for it to
have no completiontime but too much completiontime for just one ingot).
It seems pretty ridiculous to make the former 3 recipes have a wait time
of TWO SECONDS PER INDIVIDUAL INGOT (which presents a lot of problems in
practice for salvagers) in comparison to the standard ore recipes which
have 0 wait times at all.

if the wait times are intentional then I would like you to consider
reducing them to something similar to what I did with the Tungsten- with
X seconds over 30 (or any other standard amount) ingots

---

# Changelog

🆑
- tweak: the Ore Processors now process Lead, Copper, and Aluminum
instantly, and is much faster at processing Tungsten

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Chores**
- Adjusted lathe production times for several recipes, resulting in more
accurate processing durations.
- The tungsten-based recipe now completes significantly faster (0.13
seconds versus 4 seconds).
- Three metal-based recipes have been updated to finish in shorter
durations (0.01 to 0.016 seconds).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

(cherry picked from commit d295e2535f9aa21497460279d0bfe108920c445b)

* Automatic Changelog Update (#1873)

(cherry picked from commit 8deed0c3c9d16ff0cdb956fc0ee457a5bf14f5ed)

* Revert "[Add] High-Risk Loadout Item For Warden: Power Gloves (#252)"

This reverts commit 1f936feaa8.

* Grab Intent Part 2: Martial Arts (#1891)

# Description
Finally, after 9 years in development, CQC is here.
Traitors can buy a CQC manual in the uplink, giving them access to
unarmed combos, and instant hardgrabs.
Traitors can also buy a Sleeping Carp Scroll, giving them 3 different
unarmed combos, and the ability to deflect all incoming projectiles, at
the cost of no longer being able to use ranged weapons.
The Chef can use Close-Quarters-Cooking while in the kitchen. Tiders
beware.
Security officers also have access to a Corporate Judo Belt as an
alternative to the stun baton.
The Warden starts with Krav Maga gloves in his locker, with 3 different
attacks.

## This code is, not shit perhaps?
Ports martial arts from
[/Goob-Station#1868](https://github.com/Goob-Station/Goob-Station/pull/1868)
All seems pretty well written, shouldn't be hard to add new ones in the
future.

There also exists a version of the CQC manual for the BSO. Might add to
the BSO locker if requested.

# TODO
* [x] Await reviews
* [x] Pain

# Media
Judo

https://github.com/user-attachments/assets/b0aa4d24-f5cd-478e-8358-a095d46a4572
CQC
https://youtu.be/c0EJfbwqil8
Sleeping Carp

https://github.com/user-attachments/assets/a16ec334-9f9a-4820-b4f1-32a0cc598c67

https://github.com/user-attachments/assets/3e2bfc95-7c92-46f6-9b7c-b1e6596540c7

# Changelog
🆑 Eagle

* add: Added Corporate Judo, CQC, Sleeping Carp, and Krav Maga martial
arts with unique abilities.
* add: The Chef has been given Close Quarters Cooking in the Kitchen and
Bar. Tiders beware.

---------

Signed-off-by: Eagle-0 <114363363+Eagle-0@users.noreply.github.com>

(cherry picked from commit 68872f85c8b2227e871667caed2289042edd0d7b)

* Automatic Changelog Update (#1891)

(cherry picked from commit 9e3ad56873aedb7a7d0fff6037f9aaf0026897c0)

* Bug Fix: Fix Roboticist Airlock Sprite Error (#1899)

<!--
This is a semi-strict format, you can add/remove sections as needed but
the order/format should be kept the same
Remove these comments before submitting
-->

# Description

Description.

A fix for [this
issue](https://github.com/Simple-Station/Einstein-Engines/issues/1872)

Fixed bug of the painted roboticist airlock displaying the windowed
counterpart instead of the standard one.

# TODO

<!--
A list of everything you have to do before this PR is "complete"
You probably won't have to complete everything before merging but it's
good to leave future references
-->

- [ ] Task
- [x] Completed Task

---

<!--
This is default collapsed, readers click to expand it and see all your
media
The PR media section can get very large at times, so this is a good way
to keep it clean
The title is written using HTML tags
The title must be within the <summary> tags or you won't see it
-->

<details><summary><h1>Media</h1></summary>

https://github.com/user-attachments/assets/fbe4c85f-c876-4e29-9c8d-cf95314e737f

</details>

---

# Changelog

<!--
You can add an author after the `🆑` to change the name that appears
in the changelog (ex: `🆑 Death`)
Leaving it blank will default to your GitHub display name
This includes all available types for the changelog
-->

🆑
- fix: Fixed bug of the painted roboticist airlock displaying the
windowed counterpart instead of the standard one.

(cherry picked from commit a3b823b0059a67767f0adf27ec65151d2f73a1fd)

* Automatic Changelog Update (#1899)

(cherry picked from commit ca839d18fbcdb85d4d1e60f2acad68fda02e1634)

* Fixes SM Being Started on Round Start (#1901)

# Description

To stop the SM from getting activated without something being thrown
into the SM or by having emitters hit the SM.

---

# TODO

- [x] Fix the SM by starting on its own.
---

# Changelog

🆑
- fix: SM no longer starts on round start.
- fix: SM will no longer delam from spacing unless it's activated.

---------

Co-authored-by: Nathaniel Adams <60526456+Nathaniel-Adams@users.noreply.github.com>
(cherry picked from commit 234ac6119f999ff2bfaabee6b93b5fa75c61c0fa)

* Automatic Changelog Update (#1901)

(cherry picked from commit 3a0c67ba9c6aa8341e9bfd529bb58818164e20c8)

* Tc rebalance

---------

Co-authored-by: Eagle-0 <114363363+Eagle-0@users.noreply.github.com>
Co-authored-by: SimpleStation Changelogs <SimpleStation14@users.noreply.github.com>
Co-authored-by: VMSolidus <evilexecutive@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Timfa <timfalken@hotmail.com>
Co-authored-by: RadsammyT <32146976+RadsammyT@users.noreply.github.com>
Co-authored-by: Paulo Artur Pinheiro Viana Villaça <112904295+AlgumCorrupto@users.noreply.github.com>
Co-authored-by: Solaris <60526456+SolarisBirb@users.noreply.github.com>
This commit is contained in:
Spatison
2025-03-09 15:01:34 +03:00
committed by GitHub
parent d46ece0beb
commit 5d347ebb94
100 changed files with 2846 additions and 483 deletions

View File

@@ -11,6 +11,8 @@ public sealed class AtmosAlertsComputerBoundUserInterface : BoundUserInterface
protected override void Open()
{
base.Open();
_menu = new AtmosAlertsComputerWindow(this, Owner);
_menu.OpenCentered();
_menu.OnClose += Close;

View File

@@ -41,6 +41,8 @@ namespace Content.Client.Instruments.UI
protected override void Open()
{
base.Open();
_instrumentMenu = this.CreateWindow<InstrumentMenu>();
_instrumentMenu.Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName;

View File

@@ -20,6 +20,8 @@ public sealed class NewsWriterBoundUserInterface : BoundUserInterface
protected override void Open()
{
base.Open();
_menu = this.CreateWindow<NewsWriterMenu>();
_menu.ArticleEditorPanel.PublishButtonPressed += OnPublishButtonPressed;

View File

@@ -14,6 +14,8 @@ public sealed class CrewMonitoringBoundUserInterface : BoundUserInterface
protected override void Open()
{
base.Open();
EntityUid? gridUid = null;
var stationName = string.Empty;

View File

@@ -18,6 +18,8 @@ namespace Content.Client.Nuke
protected override void Open()
{
base.Open();
_menu = this.CreateWindow<NukeMenu>();
_menu.OnKeypadButtonPressed += i =>

View File

@@ -12,6 +12,8 @@ public sealed class PowerMonitoringConsoleBoundUserInterface : BoundUserInterfac
protected override void Open()
{
base.Open();
_menu = this.CreateWindow<PowerMonitoringWindow>();
_menu.SetEntity(Owner);
_menu.SendPowerMonitoringConsoleMessageAction += SendPowerMonitoringConsoleMessage;

View File

@@ -27,6 +27,8 @@ public sealed class StoreBoundUserInterface : BoundUserInterface
protected override void Open()
{
base.Open();
_menu = this.CreateWindow<StoreMenu>();
if (EntMan.TryGetComponent<StoreComponent>(Owner, out var store))
_menu.Title = Loc.GetString(store.Name);

View File

@@ -21,6 +21,8 @@ public sealed class SurveillanceCameraSetupBoundUi : BoundUserInterface
protected override void Open()
{
base.Open();
_window = new();
if (_type == SurveillanceCameraSetupUiKey.Router)

View File

@@ -31,6 +31,8 @@ public sealed partial class VoiceMaskNameChangeWindow : FancyWindow
OnVerbChange?.Invoke((string?) args.Button.GetItemMetadata(args.Id));
SpeechVerbSelector.SelectId(args.Id);
};
AddVerbs();
}
public void ReloadVerbs(IPrototypeManager proto)

View File

@@ -11,6 +11,8 @@ public sealed class SupermatterConsoleBoundUserInterface : BoundUserInterface
protected override void Open()
{
base.Open();
_menu = new SupermatterConsoleWindow(this, Owner);
_menu.OpenCentered();
_menu.OnClose += Close;

View File

@@ -0,0 +1,10 @@
using Content.Shared._Goobstation.MartialArts;
namespace Content.Client._Goobstation.MartialArts;
/// <summary>
/// This handles...
/// </summary>
public sealed class MartialArtsSystem : SharedMartialArtsSystem
{
}

View File

@@ -5,6 +5,7 @@ using Content.Server.Chat.Systems;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.EntityEffects.EffectConditions;
using Content.Server.EntityEffects.Effects;
using Content.Shared._Goobstation.MartialArts.Components; // Goobstation - Martial Arts
using Content.Server.Popups;
using Content.Shared.Alert;
using Content.Shared.Atmos;
@@ -61,7 +62,11 @@ public sealed class RespiratorSystem : EntitySystem
{
if(respirator.Saturation < respirator.SuffocationThreshold)
return false;
return !TryComp<PullableComponent>(uid, out var pullable) || pullable.GrabStage != GrabStage.Suffocate;
if (TryComp<PullableComponent>(uid, out var pullable)
&& pullable.GrabStage == GrabStage.Suffocate)
return false;
return !HasComp<KravMagaBlockedBreathingComponent>(uid);
}
// Goobstation end
private void OnMapInit(Entity<RespiratorComponent> ent, ref MapInitEvent args)

View File

@@ -2,6 +2,7 @@ using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.Power.Events;
using Content.Server.Stunnable.Components;
using Content.Shared._Goobstation.MartialArts;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Damage.Events;
using Content.Shared.Examine;

View File

@@ -78,6 +78,9 @@ public sealed partial class SupermatterSystem : EntitySystem
foreach (var sm in EntityManager.EntityQuery<SupermatterComponent>())
{
if (!sm.Activated)
continue;
var uid = sm.Owner;
AnnounceCoreDamage(uid, sm);
}
@@ -99,7 +102,10 @@ public sealed partial class SupermatterSystem : EntitySystem
public void OnSupermatterUpdated(EntityUid uid, SupermatterComponent sm, AtmosDeviceUpdateEvent args)
{
ProcessAtmos(uid, sm, args.dt);
HandleDamage(uid, sm);
// This prevents SM from delamming due to spacing without activating the SM.
if (sm.Activated)
HandleDamage(uid, sm);
if (sm.Damage >= sm.DamageDelaminationPoint || sm.Delamming)
HandleDelamination(uid, sm);

View File

@@ -0,0 +1,26 @@
using Content.Server.Chat.Systems;
using Content.Shared.Chat;
using Content.Shared._Goobstation.MartialArts;
using Content.Shared._Goobstation.MartialArts.Components;
using Content.Shared._Goobstation.MartialArts.Events;
namespace Content.Server._Goobstation.MartialArts;
/// <summary>
/// Just handles carp sayings for now.
/// </summary>
public sealed class MartialArtsSystem : SharedMartialArtsSystem
{
[Dependency] private readonly ChatSystem _chat = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CanPerformComboComponent, SleepingCarpSaying>(OnSleepingCarpSaying);
}
private void OnSleepingCarpSaying(Entity<CanPerformComboComponent> ent, ref SleepingCarpSaying args)
{
_chat.TrySendInGameICMessage(ent, Loc.GetString(args.Saying), InGameICChatType.Speak, false);
}
}

View File

@@ -1,4 +1,6 @@
using System.Linq;
using Content.Shared._Goobstation.MartialArts;
using Content.Shared._Goobstation.MartialArts.Components;
using Content.Shared.Administration.Logs;
using Content.Shared.Alert;
using Content.Shared.CombatMode;
@@ -152,6 +154,13 @@ public sealed partial class StaminaSystem : EntitySystem
return;
}
// Goobstation - Martial Arts
if (TryComp<MartialArtsKnowledgeComponent>(args.User, out var knowledgeComp)
&& TryComp<MartialArtBlockedComponent>(args.Weapon, out var blockedComp)
&& knowledgeComp.MartialArtsForm == blockedComp.Form)
return;
// Goobstation
var ev = new StaminaDamageOnHitAttemptEvent();
RaiseLocalEvent(uid, ref ev);
if (ev.Cancelled)

View File

@@ -1,3 +1,4 @@
using Content.Shared._Goobstation.TableSlam; // Goobstation - Table SLam
using Content.Shared.Alert;
using Content.Shared.Movement.Pulling.Systems; // Goobstation
using Robust.Shared.GameStates;
@@ -9,7 +10,7 @@ namespace Content.Shared.Movement.Pulling.Components;
/// Specifies an entity as being pullable by an entity with <see cref="PullerComponent"/>
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
[Access(typeof(Systems.PullingSystem))]
[Access(typeof(Systems.PullingSystem), typeof(TableSlamSystem))]
public sealed partial class PullableComponent : Component
{
/// <summary>
@@ -57,6 +58,30 @@ public sealed partial class PullableComponent : Component
[AutoNetworkedField]
public TimeSpan NextEscapeAttempt = TimeSpan.Zero;
/// <summary>
/// If this pullable being tabled.
/// </summary>
[DataField, AutoNetworkedField]
public bool BeingTabled = false;
/// <summary>
/// Constant for tabling throw math
/// </summary>
[DataField]
public float BasedTabledForceSpeed = 5f;
/// <summary>
/// Stamina damage. taken on tabled
/// </summary>
[DataField]
public float TabledStaminaDamage = 40f;
/// <summary>
/// Damage taken on being tabled.
/// </summary>
[DataField]
public float TabledDamage = 5f;
// Goobstation end
/// <summary>

View File

@@ -1,4 +1,5 @@
using Content.Shared.Alert;
using Content.Shared._Goobstation.TableSlam; // Goobstation - Table Slam
using Content.Shared.Alert;
using Content.Shared.Movement.Pulling.Systems;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
@@ -10,8 +11,8 @@ namespace Content.Shared.Movement.Pulling.Components;
/// <summary>
/// Specifies an entity as being able to pull another entity with <see cref="PullableComponent"/>
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
[Access(typeof(PullingSystem))]
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
[Access(typeof(PullingSystem), typeof(TableSlamSystem))] // Goobstation - Table Slam
public sealed partial class PullerComponent : Component
{
/// <summary>
@@ -106,7 +107,7 @@ public sealed partial class PullerComponent : Component
public float SuffocateGrabStaminaDamage = 10f;
[DataField]
public float GrabThrowDamageModifier = 1f;
public float GrabThrowDamageModifier = 2f;
[ViewVariables]
public List<EntityUid> GrabVirtualItems = new();

View File

@@ -0,0 +1,14 @@
using Content.Shared.Movement.Components;
using Content.Shared.Movement.Pulling.Systems;
namespace Content.Shared.Movement.Pulling.Events;
public sealed class CheckGrabOverridesEvent : EntityEventArgs
{
public CheckGrabOverridesEvent(GrabStage stage)
{
Stage = stage;
}
public GrabStage Stage { get; set; }
}

View File

@@ -1,12 +1,13 @@
using Content.Shared._Goobstation.MartialArts.Events; // Goobstation - Martial Arts
using Content.Shared.Contests; // Goobstation - Grab Intent
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Numerics; // Goobstation - Grab Intent
using Content.Shared._White.Grab; // Goobstation
using Content.Shared.ActionBlocker;
using Content.Shared.Administration.Logs;
using Content.Shared.Alert;
using Content.Shared.Buckle.Components;
using Content.Shared.CombatMode; // Goobstation
using Content.Shared.Cuffs; // Goobstation
using Content.Shared.Cuffs.Components; // Goobstation
using Content.Shared.Damage; // Goobstation
using Content.Shared.Damage.Systems; // Goobstation
@@ -77,11 +78,11 @@ public sealed class PullingSystem : EntitySystem
[Dependency] private readonly SharedColorFlashEffectSystem _color = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly INetManager _netManager = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SharedVirtualItemSystem _virtualSystem = default!;
[Dependency] private readonly GrabThrownSystem _grabThrown = default!;
[Dependency] private readonly SharedCombatModeSystem _combatMode = default!;
[Dependency] private readonly ThrowingSystem _throwing = default!;
[Dependency] private readonly ContestsSystem _contests = default!; // Goobstation - Grab Intent
public override void Initialize()
{
@@ -106,7 +107,6 @@ public sealed class PullingSystem : EntitySystem
SubscribeLocalEvent<PullerComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovespeed);
SubscribeLocalEvent<PullerComponent, DropHandItemsEvent>(OnDropHandItems);
SubscribeLocalEvent<PullerComponent, VirtualItemThrownEvent>(OnVirtualItemThrown); // Goobstation - Grab Intent
SubscribeLocalEvent<PullerComponent, VirtualItemDropAttemptEvent>(OnVirtualItemDropAttempt); // Goobstation - Grab Intent
SubscribeLocalEvent<PullerComponent, AddCuffDoAfterEvent>(OnAddCuffDoAfterEvent); // Goobstation - Grab Intent
SubscribeLocalEvent<PullableComponent, StrappedEvent>(OnBuckled);
@@ -117,7 +117,6 @@ public sealed class PullingSystem : EntitySystem
.Bind(ContentKeyFunctions.ReleasePulledObject, InputCmdHandler.FromDelegate(OnReleasePulledObject, handle: false))
.Register<PullingSystem>();
}
// Goobstation - Grab Intent
private void OnAddCuffDoAfterEvent(Entity<PullerComponent> ent, ref AddCuffDoAfterEvent args)
{
@@ -129,7 +128,7 @@ public sealed class PullingSystem : EntitySystem
&& ent.Comp.Pulling != null)
{
if(_netManager.IsServer)
StopPulling((EntityUid) ent.Comp.Pulling, comp);
StopPulling(ent.Comp.Pulling.Value, comp);
}
}
// Goobstation
@@ -281,89 +280,62 @@ public sealed class PullingSystem : EntitySystem
component.NextPushTargetChange += args.PausedTime;
}
// Goobstation - Grab Intent
private void OnVirtualItemDropAttempt(EntityUid uid, PullerComponent component, VirtualItemDropAttemptEvent args)
{
if (component.Pulling == null)
return;
if (component.Pulling != args.BlockingEntity)
return;
if (_timing.CurTime < component.NextStageChange)
{
args.Cancel(); // VirtualItem is NOT being deleted
return;
}
if (!args.Throw)
{
if (component.GrabStage > GrabStage.No
&& TryComp(args.BlockingEntity, out PullableComponent? comp))
{
TryLowerGrabStage(component.Pulling.Value, uid);
args.Cancel(); // VirtualItem is NOT being deleted
}
}
else
{
if (component.GrabStage <= GrabStage.Soft)
{
TryLowerGrabStage(component.Pulling.Value, uid);
args.Cancel(); // VirtualItem is NOT being deleted
}
}
}
// Goobstation
private void OnVirtualItemDeleted(EntityUid uid, PullerComponent component, VirtualItemDeletedEvent args)
// Goobstation - Grab Intent Refactor
private void OnVirtualItemDeleted(Entity<PullerComponent> ent, ref VirtualItemDeletedEvent args)
{
// If client deletes the virtual hand then stop the pull.
if (component.Pulling == null)
if (ent.Comp.Pulling == null)
return;
if (component.Pulling != args.BlockingEntity)
return;
if (TryComp(args.BlockingEntity, out PullableComponent? comp)) // Goobstation
{
TryLowerGrabStage(component.Pulling.Value, uid);// Goobstation
}
}
// Goobstation - Grab Intent
private void OnVirtualItemThrown(EntityUid uid, PullerComponent component, VirtualItemThrownEvent args)
{
if (!TryComp(uid, out PhysicsComponent? throwerPhysics)
|| !TryComp(args.BlockingEntity, out PhysicsComponent? throweePhysics)
|| component.Pulling == null
|| component.Pulling != args.BlockingEntity)
if (ent.Comp.Pulling != args.BlockingEntity)
return;
if (TryComp(args.BlockingEntity, out PullableComponent? comp))
{
if (_combatMode.IsInCombatMode(uid) &&
!HasComp<GrabThrownComponent>(args.BlockingEntity) &&
component.GrabStage > GrabStage.Soft)
{
var distanceToCursor = args.Direction.Length();
var direction = args.Direction.Normalized() * MathF.Min(distanceToCursor, component.ThrowingDistance);
var damage = new DamageSpecifier();
damage.DamageDict.Add("Blunt", 5);
TryStopPull(args.BlockingEntity, comp, uid, true);
_grabThrown.Throw(args.BlockingEntity, uid, direction,
component.GrabThrownSpeed,
component.StaminaDamageOnThrown,
damage * component.GrabThrowDamageModifier,
damage * component.GrabThrowDamageModifier); // Throwing the grabbed person
_throwing.TryThrow(uid, -direction * throwerPhysics.InvMass); // Throws back the grabber
_audio.PlayPvs(new SoundPathSpecifier("/Audio/Effects/thudswoosh.ogg"), uid);
component.NextStageChange.Add(TimeSpan.FromSeconds(2f)); // To avoid grab and throw spamming
}
TryStopPull(ent.Comp.Pulling.Value, comp, ent);
}
foreach (var item in ent.Comp.GrabVirtualItems)
{
if(TryComp<VirtualItemComponent>(ent, out var virtualItemComponent))
_virtualSystem.DeleteVirtualItem((item,virtualItemComponent), ent);
}
ent.Comp.GrabVirtualItems.Clear();
}
// Goobstation - Grab Intent Refactor
// Goobstation - Grab Intent
private void OnVirtualItemThrown(EntityUid uid, PullerComponent component, VirtualItemThrownEvent args)
{
if (!TryComp<PhysicsComponent>(uid, out var throwerPhysics)
|| component.Pulling == null
|| component.Pulling != args.BlockingEntity)
return;
if (!TryComp(args.BlockingEntity, out PullableComponent? comp))
return;
if (!_combatMode.IsInCombatMode(uid)
|| HasComp<GrabThrownComponent>(args.BlockingEntity)
|| component.GrabStage <= GrabStage.Soft)
return;
var distanceToCursor = args.Direction.Length();
var direction = args.Direction.Normalized() * MathF.Min(distanceToCursor, component.ThrowingDistance);
var damage = new DamageSpecifier();
damage.DamageDict.Add("Blunt", 5);
TryStopPull(args.BlockingEntity, comp, uid, true);
_grabThrown.Throw(args.BlockingEntity,
uid,
direction,
component.GrabThrownSpeed,
component.StaminaDamageOnThrown,
damage * component.GrabThrowDamageModifier); // Throwing the grabbed person
_throwing.TryThrow(uid, -direction * throwerPhysics.InvMass); // Throws back the grabber
_audio.PlayPvs(new SoundPathSpecifier("/Audio/Effects/thudswoosh.ogg"), uid);
component.NextStageChange.Add(TimeSpan.FromSeconds(4f)); // To avoid grab and throw spamming
}
// Goobstation
@@ -419,12 +391,12 @@ public sealed class PullingSystem : EntitySystem
args.ModifySpeed(walkMod, sprintMod);
break;
case GrabStage.Soft:
var softSpeedMod = component.SoftGrabSpeedModifier;
args.ModifySpeed(walkMod * softSpeedMod, sprintMod * softSpeedMod);
var softGrabSpeedMod = component.SoftGrabSpeedModifier;
args.ModifySpeed(walkMod * softGrabSpeedMod, sprintMod * softGrabSpeedMod);
break;
case GrabStage.Hard:
var hardSpeedMod = component.HardGrabSpeedModifier;
args.ModifySpeed(walkMod * hardSpeedMod, sprintMod * hardSpeedMod);
var hardGrabSpeedModifier = component.HardGrabSpeedModifier;
args.ModifySpeed(walkMod * hardGrabSpeedModifier, sprintMod * hardGrabSpeedModifier);
break;
case GrabStage.Suffocate:
var chokeSpeedMod = component.ChokeGrabSpeedModifier;
@@ -443,12 +415,12 @@ public sealed class PullingSystem : EntitySystem
args.ModifySpeed(component.WalkSpeedModifier, component.SprintSpeedModifier);
break;
case GrabStage.Soft:
var softSpeedMod = component.SoftGrabSpeedModifier;
args.ModifySpeed(component.WalkSpeedModifier * softSpeedMod, component.SprintSpeedModifier * softSpeedMod);
var softGrabSpeedMod = component.SoftGrabSpeedModifier;
args.ModifySpeed(component.WalkSpeedModifier * softGrabSpeedMod, component.SprintSpeedModifier * softGrabSpeedMod);
break;
case GrabStage.Hard:
var hardSpeedMod = component.HardGrabSpeedModifier;
args.ModifySpeed(component.WalkSpeedModifier * hardSpeedMod, component.SprintSpeedModifier * hardSpeedMod);
var hardGrabSpeedModifier = component.HardGrabSpeedModifier;
args.ModifySpeed(component.WalkSpeedModifier * hardGrabSpeedModifier, component.SprintSpeedModifier * hardGrabSpeedModifier);
break;
case GrabStage.Suffocate:
var chokeSpeedMod = component.ChokeGrabSpeedModifier;
@@ -460,19 +432,24 @@ public sealed class PullingSystem : EntitySystem
}
}
private void OnPullableMoveInput(EntityUid uid, PullableComponent component, ref MoveInputEvent args)
// Goobstation - Grab Intent
private void OnPullableMoveInput(Entity<PullableComponent> ent, ref MoveInputEvent args)
{
// If someone moves then break their pulling.
if (!component.BeingPulled)
if (!ent.Comp.BeingPulled)
return;
var entity = args.Entity;
if (ent.Comp.GrabStage == GrabStage.Soft)
TryStopPull(ent, ent, ent);
if (!_blocker.CanMove(entity))
return;
TryStopPull(uid, component, user: uid);
TryStopPull(ent, ent, user: ent);
}
// Goobstation
private void OnPullableCollisionChange(EntityUid uid, PullableComponent component, ref CollisionChangeEvent args)
{
@@ -899,6 +876,10 @@ public sealed class PullingSystem : EntitySystem
if (!Resolve(puller.Owner, ref puller.Comp))
return false;
// prevent you from grabbing someone else while being grabbed
if (TryComp<PullableComponent>(puller.Owner, out var pullerAsPullable) && pullerAsPullable.Puller != null)
return false;
if (pullable.Comp.Puller != puller.Owner ||
puller.Comp.Pulling != pullable.Owner)
return false;
@@ -919,7 +900,6 @@ public sealed class PullingSystem : EntitySystem
if (!_combatMode.IsInCombatMode(puller.Owner))
return false;
// It's blocking stage update, maybe better UX?
if (puller.Comp.GrabStage == GrabStage.Suffocate)
{
@@ -940,6 +920,9 @@ public sealed class PullingSystem : EntitySystem
};
var newStage = puller.Comp.GrabStage + nextStageAddition;
var ev = new CheckGrabOverridesEvent(newStage); // guh
RaiseLocalEvent(puller, ev);
newStage = ev.Stage;
if (!TrySetGrabStages((puller.Owner, puller.Comp), (pullable.Owner, pullable.Comp), newStage))
return false;
@@ -970,7 +953,8 @@ public sealed class PullingSystem : EntitySystem
_ => throw new ArgumentOutOfRangeException()
};
pullable.Comp.GrabEscapeChance = puller.Comp.EscapeChances[stage];
var massModifier = _contests.MassContest(puller, pullable);
pullable.Comp.GrabEscapeChance = Math.Clamp(puller.Comp.EscapeChances[stage] / massModifier, 0f, 1f);
_alertsSystem.ShowAlert(puller, puller.Comp.PullingAlert, puller.Comp.PullingAlertSeverity[stage]);
_alertsSystem.ShowAlert(pullable, pullable.Comp.PulledAlert, pullable.Comp.PulledAlertAlertSeverity[stage]);
@@ -988,6 +972,9 @@ public sealed class PullingSystem : EntitySystem
_audio.PlayPvs(new SoundPathSpecifier("/Audio/Effects/thudswoosh.ogg"), pullable);
var comboEv = new ComboAttackPerformedEvent(puller.Owner, pullable.Owner, puller.Owner, ComboAttackType.Grab);
RaiseLocalEvent(puller.Owner, comboEv);
Dirty(pullable);
Dirty(puller);
@@ -1003,51 +990,50 @@ public sealed class PullingSystem : EntitySystem
if (puller.Comp.GrabVirtualItemStageCount.TryGetValue(puller.Comp.GrabStage, out var count))
newVirtualItemsCount += count;
if (virtualItemsCount != newVirtualItemsCount)
if (virtualItemsCount == newVirtualItemsCount)
return true;
var delta = newVirtualItemsCount - virtualItemsCount;
// Adding new virtual items
if (delta > 0)
{
var delta = newVirtualItemsCount - virtualItemsCount;
// Adding new virtual items
if (delta > 0)
for (var i = 0; i < delta; i++)
{
for (var i = 0; i < delta; i++)
var emptyHand = _handsSystem.TryGetEmptyHand(puller, out _);
if (!emptyHand)
{
var emptyHand = _handsSystem.TryGetEmptyHand(puller, out _);
if (!emptyHand)
{
if (_netManager.IsServer)
_popup.PopupEntity(Loc.GetString("popup-grab-need-hand"), puller, puller, PopupType.Medium);
if (_netManager.IsServer)
_popup.PopupEntity(Loc.GetString("popup-grab-need-hand"), puller, puller, PopupType.Medium);
return false;
}
if (!_virtualSystem.TrySpawnVirtualItemInHand(pullable, puller.Owner, out var item, true))
{
// I'm lazy write client code
if (_netManager.IsServer)
_popup.PopupEntity(Loc.GetString("popup-grab-need-hand"), puller, puller, PopupType.Medium);
return false;
}
puller.Comp.GrabVirtualItems.Add(item.Value);
return false;
}
}
if (delta < 0)
{
for (var i = 0; i < Math.Abs(delta); i++)
if (!_virtualSystem.TrySpawnVirtualItemInHand(pullable, puller.Owner, out var item, true))
{
if (i >= puller.Comp.GrabVirtualItems.Count)
break;
// I'm lazy write client code
if (_netManager.IsServer)
_popup.PopupEntity(Loc.GetString("popup-grab-need-hand"), puller, puller, PopupType.Medium);
var item = puller.Comp.GrabVirtualItems[i];
puller.Comp.GrabVirtualItems.Remove(item);
QueueDel(item);
return false;
}
puller.Comp.GrabVirtualItems.Add(item.Value);
}
}
if (delta >= 0)
return true;
for (var i = 0; i < Math.Abs(delta); i++)
{
if (i >= puller.Comp.GrabVirtualItems.Count)
break;
var item = puller.Comp.GrabVirtualItems[i];
puller.Comp.GrabVirtualItems.Remove(item);
if(TryComp<VirtualItemComponent>(item, out var virtualItemComponent))
_virtualSystem.DeleteVirtualItem((item,virtualItemComponent), puller);
}
return true;
}
@@ -1067,7 +1053,7 @@ public sealed class PullingSystem : EntitySystem
if (_random.Prob(pullable.Comp.GrabEscapeChance))
return true;
pullable.Comp.NextEscapeAttempt = _timing.CurTime.Add(TimeSpan.FromSeconds(1));
pullable.Comp.NextEscapeAttempt = _timing.CurTime.Add(TimeSpan.FromSeconds(3));
Dirty(pullable.Owner, pullable.Comp);
return false;
}
@@ -1115,6 +1101,7 @@ public sealed class PullingSystem : EntitySystem
pullable.Comp.NextEscapeAttempt = _timing.CurTime.Add(TimeSpan.FromSeconds(1f));
Dirty(pullable);
Dirty(puller);
if (!ignoreCombatMode && _combatMode.IsInCombatMode(puller.Owner))
{
@@ -1148,6 +1135,4 @@ public enum GrabStageDirection
Decrease,
}
// Goobstation - Grab Intent
// Goobstation

View File

@@ -155,6 +155,7 @@ public sealed class ThrowingSystem : EntitySystem
{
Thrower = user,
Animate = animated,
};
// if not given, get the default friction value for distance calculation

View File

@@ -1,6 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
using Content.Shared._Goobstation.MartialArts.Events; // Goobstation - Martial Arts
using Content.Shared._White;
using Content.Shared.ActionBlocker;
using Content.Shared.Administration.Logs;
@@ -524,6 +525,8 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
var modifiedDamage = DamageSpecifier.ApplyModifierSets(damage + hitEvent.BonusDamage + attackedEvent.BonusDamage, hitEvent.ModifiersList);
var damageResult = Damageable.TryChangeDamage(target, modifiedDamage, origin:user, ignoreResistances: resistanceBypass, partMultiplier: component.ClickPartDamageMultiplier);
var comboEv = new ComboAttackPerformedEvent(user, target.Value, meleeUid, ComboAttackType.Harm);
RaiseLocalEvent(user, comboEv);
if (damageResult is {Empty: false})
{
@@ -691,6 +694,9 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
var damageResult = Damageable.TryChangeDamage(entity, modifiedDamage, origin: user, partMultiplier: component.HeavyPartDamageMultiplier);
var comboEv = new ComboAttackPerformedEvent(user, entity, meleeUid, ComboAttackType.HarmLight);
RaiseLocalEvent(user, comboEv);
if (damageResult != null && damageResult.GetTotal() > FixedPoint2.Zero)
{
// If the target has stamina and is taking blunt damage, they should also take stamina damage based on their blunt to stamina factor
@@ -809,6 +815,9 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
return false;
}
var comboEv = new ComboAttackPerformedEvent(user, target.Value, meleeUid, ComboAttackType.Disarm);
RaiseLocalEvent(user, comboEv);
if (!TryComp<CombatModeComponent>(user, out var combatMode) ||
combatMode.CanDisarm == false) // WWDP
{

View File

@@ -19,7 +19,7 @@ public sealed partial class SupermatterComponent : Component
/// The SM will only cycle if activated.
/// </summary>
[DataField]
public bool Activated = true;
public bool Activated = false;
/// <summary>
/// The current status of the singularity, used for alert sounds and the monitoring console

View File

@@ -0,0 +1,70 @@
using Content.Shared._Goobstation.MartialArts.Components;
using Content.Shared._Goobstation.MartialArts.Events;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared._Goobstation.MartialArts;
[Prototype("combo")]
[Serializable, NetSerializable, DataDefinition]
public sealed partial class ComboPrototype : IPrototype
{
[IdDataField] public string ID { get; private set; } = default!;
[DataField(required: true)]
public MartialArtsForms MartialArtsForm;
[DataField("attacks", required: true)]
public List<ComboAttackType> AttackTypes = new();
//[DataField("weapon")] // Will be done later
//public string? WeaponProtoId;
[DataField("event", required: true)]
public object? ResultEvent;
/// <summary>
/// How much extra damage should this move do on perform?
/// </summary>
[DataField]
public int ExtraDamage;
/// <summary>
/// Stun time in seconds
/// </summary>
[DataField]
public int ParalyzeTime;
/// <summary>
/// How much stamina damage should this move do on perform.
/// </summary>
[DataField]
public float StaminaDamage;
/// <summary>
/// Blunt, Slash, etc.
/// </summary>
[DataField]
public string DamageType = "Blunt";
/// <summary>
/// How fast people are thrown on combo
/// </summary>
[DataField]
public float ThrownSpeed = 7f;
/// <summary>
/// Name of the move
/// </summary>
[DataField(required: true)]
public string Name = string.Empty;
}
[Prototype("comboList")]
public sealed partial class ComboListPrototype : IPrototype
{
[IdDataField] public string ID { get; private init; } = default!;
[DataField( required: true)]
public List<ProtoId<ComboPrototype>> Combos = new();
}

View File

@@ -0,0 +1,30 @@
using Content.Shared._Goobstation.MartialArts.Events;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared._Goobstation.MartialArts.Components;
[RegisterComponent]
[NetworkedComponent]
public sealed partial class CanPerformComboComponent : Component
{
[DataField]
public EntityUid? CurrentTarget;
[DataField]
public ProtoId<ComboPrototype> BeingPerformed;
[DataField]
public List<ComboAttackType> LastAttacks = new();
[DataField]
public List<ComboPrototype> AllowedCombos = new();
[DataField]
public List<ProtoId<ComboPrototype>> RoundstartCombos = new();
[DataField]
public TimeSpan ResetTime = TimeSpan.Zero;
[DataField]
public int ConsecutiveGnashes = 0;
}

View File

@@ -0,0 +1,12 @@
using Content.Shared.Movement.Pulling.Systems;
namespace Content.Shared._Goobstation.MartialArts.Components;
/// <summary>
/// Base component for martial arts that override the normal grab stages.
/// Allows martial arts to start at more advanced grab stages like Hard grabs.
/// </summary>
public abstract partial class GrabStagesOverrideComponent : Component
{
public GrabStage StartingStage = GrabStage.Hard;
}

View File

@@ -0,0 +1,44 @@
using Robust.Shared.GameStates;
namespace Content.Shared._Goobstation.MartialArts.Components;
public abstract partial class GrantMartialArtKnowledgeComponent : Component
{
[DataField]
public bool Used;
[DataField]
public virtual MartialArtsForms MartialArtsForm { get; set; } = MartialArtsForms.CloseQuartersCombat;
}
[RegisterComponent]
public sealed partial class GrantCqcComponent : GrantMartialArtKnowledgeComponent
{
[DataField]
public bool IsBlocked;
}
[RegisterComponent]
public sealed partial class GrantCorporateJudoComponent : GrantMartialArtKnowledgeComponent
{
[DataField]
public override MartialArtsForms MartialArtsForm { get; set; } = MartialArtsForms.CorporateJudo;
}
[RegisterComponent]
public sealed partial class GrantSleepingCarpComponent : GrantMartialArtKnowledgeComponent
{
[DataField]
public override MartialArtsForms MartialArtsForm { get; set; } = MartialArtsForms.SleepingCarp;
[DataField]
public int Stage = 1;
public TimeSpan UseAgainTime = TimeSpan.Zero;
[DataField]
public int MaxUseDelay = 90;
[DataField]
public int MinUseDelay = 30;
}

View File

@@ -0,0 +1,78 @@
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared._Goobstation.MartialArts.Components;
/// <summary>
/// This is used for...
/// </summary>
[RegisterComponent]
public sealed partial class KravMagaActionComponent : Component
{
[DataField]
public KravMagaMoves Configuration;
[DataField]
public string Name;
[DataField]
public float StaminaDamage;
[DataField]
public int EffectTime;
}
[RegisterComponent]
public sealed partial class KravMagaComponent : GrabStagesOverrideComponent
{
[DataField]
public KravMagaMoves? SelectedMove;
[DataField]
public KravMagaActionComponent? SelectedMoveComp;
public readonly List<EntProtoId> BaseKravMagaMoves = new()
{
"ActionLegSweep",
"ActionNeckChop",
"ActionLungPunch",
};
public readonly List<EntityUid> KravMagaMoveEntities = new()
{
};
[DataField]
public int BaseDamage = 5;
[DataField]
public int DownedDamageModifier = 2;
}
/// <summary>
/// Tracks when an entity is silenced through Krav Maga techniques.
/// Prevents the affected entity from using voice-activated abilities or speaking.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class KravMagaSilencedComponent : Component
{
[DataField]
public TimeSpan SilencedTime = TimeSpan.Zero;
}
/// <summary>
/// Tracks when an entity's breathing is blocked through Krav Maga techniques.
/// May cause suffocation damage over time when integrated with respiration systems.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class KravMagaBlockedBreathingComponent : Component
{
[DataField]
public TimeSpan BlockedTime = TimeSpan.Zero;
}
public enum KravMagaMoves
{
LegSweep,
NeckChop,
LungPunch,
}

View File

@@ -0,0 +1,61 @@
using Content.Shared.FixedPoint;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared._Goobstation.MartialArts.Components;
[RegisterComponent]
public sealed partial class MartialArtBlockedComponent : Component
{
[DataField]
public MartialArtsForms Form;
}
[RegisterComponent]
[NetworkedComponent]
[AutoGenerateComponentState]
public sealed partial class MartialArtsKnowledgeComponent : GrabStagesOverrideComponent
{
[DataField]
[AutoNetworkedField]
public MartialArtsForms MartialArtsForm = MartialArtsForms.CloseQuartersCombat;
[DataField]
[AutoNetworkedField]
public int MinRandomDamageModifier;
[DataField]
[AutoNetworkedField]
public int MaxRandomDamageModifier = 5;
[DataField]
[AutoNetworkedField]
public FixedPoint2 BaseDamageModifier;
[DataField]
[AutoNetworkedField]
public bool RandomDamageModifier;
[DataField]
[AutoNetworkedField]
public ProtoId<ComboListPrototype> RoundstartCombos = "CQCMoves";
[DataField]
[AutoNetworkedField]
public bool Blocked = false;
[DataField]
[AutoNetworkedField]
public List<LocId> RandomSayings = [];
[DataField]
[AutoNetworkedField]
public List<LocId> RandomSayingsDowned = [];
}
public enum MartialArtsForms
{
CorporateJudo,
CloseQuartersCombat,
SleepingCarp,
}

View File

@@ -0,0 +1,18 @@
using Robust.Shared.Serialization;
namespace Content.Shared._Goobstation.MartialArts.Events;
[Serializable, NetSerializable, DataDefinition]
public sealed partial class CqcSlamPerformedEvent : EntityEventArgs;
[Serializable, NetSerializable, DataDefinition]
public sealed partial class CqcKickPerformedEvent : EntityEventArgs;
[Serializable, NetSerializable, DataDefinition]
public sealed partial class CqcRestrainPerformedEvent : EntityEventArgs;
[Serializable, NetSerializable, DataDefinition]
public sealed partial class CqcPressurePerformedEvent : EntityEventArgs;
[Serializable, NetSerializable, DataDefinition]
public sealed partial class CqcConsecutivePerformedEvent : EntityEventArgs;

View File

@@ -0,0 +1,30 @@
using Robust.Shared.Serialization;
namespace Content.Shared._Goobstation.MartialArts.Events;
/// <summary>
/// Raised when a martial arts combo attack is performed. Contains information about
/// the performer, target, weapon used, and the type of combo attack.
/// </summary>
public sealed class ComboAttackPerformedEvent(
EntityUid performer,
EntityUid target,
EntityUid weapon,
ComboAttackType type)
: CancellableEntityEventArgs
{
public EntityUid Performer { get; } = performer;
public EntityUid Target { get; } = target;
public EntityUid Weapon { get; } = weapon;
public ComboAttackType Type { get; } = type;
}
[Serializable,NetSerializable]
public enum ComboAttackType : byte
{
Harm,
HarmLight,
Disarm,
Grab,
Hug,
}

View File

@@ -0,0 +1,10 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared._Goobstation.MartialArts.Events;
[Serializable,NetSerializable]
public sealed class ComboBeingPerformedEvent(ProtoId<ComboPrototype> protoId) : EntityEventArgs
{
public ProtoId<ComboPrototype> ProtoId = protoId;
}

View File

@@ -0,0 +1,16 @@
using Robust.Shared.Serialization;
namespace Content.Shared._Goobstation.MartialArts.Events;
[Serializable, NetSerializable, DataDefinition]
public sealed partial class JudoThrowPerformedEvent : EntityEventArgs;
[Serializable, NetSerializable, DataDefinition]
public sealed partial class JudoEyePokePerformedEvent : EntityEventArgs;
[Serializable, NetSerializable, DataDefinition]
public sealed partial class JudoArmbarPerformedEvent : EntityEventArgs;
[Serializable, NetSerializable, DataDefinition]
public sealed partial class JudoGoldenBlastPerformedEvent : EntityEventArgs;

View File

@@ -0,0 +1,10 @@
using Content.Shared.Actions;
namespace Content.Shared._Goobstation.MartialArts.Events;
/// <summary>
/// This handles selecting your krav maga action
/// </summary>
public sealed partial class KravMagaActionEvent : InstantActionEvent
{
}

View File

@@ -0,0 +1,18 @@
using Robust.Shared.Serialization;
namespace Content.Shared._Goobstation.MartialArts.Events;
[Serializable, NetSerializable, DataDefinition]
public sealed partial class SleepingCarpGnashingTeethPerformedEvent : EntityEventArgs;
[Serializable, NetSerializable, DataDefinition]
public sealed partial class SleepingCarpKneeHaulPerformedEvent : EntityEventArgs;
[Serializable, NetSerializable, DataDefinition]
public sealed partial class SleepingCarpCrashingWavesPerformedEvent : EntityEventArgs;
[Serializable,NetSerializable]
public sealed class SleepingCarpSaying(LocId saying) : EntityEventArgs
{
public LocId Saying = saying;
};

View File

@@ -0,0 +1,36 @@
using Content.Shared._Goobstation.MartialArts.Components;
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
namespace Content.Shared._Goobstation.MartialArts;
[Prototype("martialArt")]
public sealed class MartialArtPrototype : IPrototype
{
[IdDataField]
public string ID { get; private init; } = default!;
[DataField]
public MartialArtsForms MartialArtsForm = MartialArtsForms.CloseQuartersCombat;
[DataField]
public int MinRandomDamageModifier;
[DataField]
public int MaxRandomDamageModifier = 5;
[DataField]
public FixedPoint2 BaseDamageModifier;
[DataField]
public bool RandomDamageModifier;
[DataField]
public ProtoId<ComboListPrototype> RoundstartCombos = "CQCMoves";
[DataField]
public List<LocId> RandomSayings = [];
[DataField]
public List<LocId> RandomSayingsDowned = [];
}

View File

@@ -0,0 +1,186 @@
using Content.Shared._Goobstation.MartialArts.Components;
using Content.Shared._Goobstation.MartialArts.Events;
using Content.Shared._Shitmed.Targeting;
using Content.Shared.Bed.Sleep;
using Content.Shared.Damage.Components;
using Content.Shared.Examine;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction.Events;
using Content.Shared.Mobs.Components;
using Content.Shared.Movement.Pulling.Components;
using Robust.Shared.Audio;
namespace Content.Shared._Goobstation.MartialArts;
public partial class SharedMartialArtsSystem
{
private void InitializeCqc()
{
SubscribeLocalEvent<CanPerformComboComponent, CqcSlamPerformedEvent>(OnCQCSlam);
SubscribeLocalEvent<CanPerformComboComponent, CqcKickPerformedEvent>(OnCQCKick);
SubscribeLocalEvent<CanPerformComboComponent, CqcRestrainPerformedEvent>(OnCQCRestrain);
SubscribeLocalEvent<CanPerformComboComponent, CqcPressurePerformedEvent>(OnCQCPressure);
SubscribeLocalEvent<CanPerformComboComponent, CqcConsecutivePerformedEvent>(OnCQCConsecutive);
SubscribeLocalEvent<MartialArtsKnowledgeComponent, ComboAttackPerformedEvent>(OnCQCAttackPerformed);
SubscribeLocalEvent<GrantCqcComponent, UseInHandEvent>(OnGrantCQCUse);
SubscribeLocalEvent<GrantCqcComponent, MapInitEvent>(OnMapInitEvent);
SubscribeLocalEvent<GrantCqcComponent, ExaminedEvent>(OnGrantCQCExamine);
}
#region Generic Methods
private void OnMapInitEvent(Entity<GrantCqcComponent> ent, ref MapInitEvent args)
{
if (!HasComp<MobStateComponent>(ent))
return;
if (!TryGrant(ent.Comp, ent))
return;
if (TryComp<MartialArtsKnowledgeComponent>(ent, out var knowledge))
knowledge.Blocked = true;
}
private void OnGrantCQCUse(Entity<GrantCqcComponent> ent, ref UseInHandEvent args)
{
if (!_netManager.IsServer)
return;
if (ent.Comp.Used)
{
_popupSystem.PopupEntity(Loc.GetString("cqc-fail-used", ("manual", Identity.Entity(ent, EntityManager))),
args.User,
args.User);
return;
}
if (!TryGrant(ent.Comp, args.User))
return;
_popupSystem.PopupEntity(Loc.GetString("cqc-success-learned"), args.User, args.User);
ent.Comp.Used = true;
}
private void OnGrantCQCExamine(Entity<GrantCqcComponent> ent, ref ExaminedEvent args)
{
if (ent.Comp.Used)
args.PushMarkup(Loc.GetString("cqc-manual-used", ("manual", Identity.Entity(ent, EntityManager))));
}
private void OnCQCAttackPerformed(Entity<MartialArtsKnowledgeComponent> ent, ref ComboAttackPerformedEvent args)
{
if (!TryComp<MartialArtsKnowledgeComponent>(ent, out var knowledgeComponent))
return;
if (knowledgeComponent.MartialArtsForm != MartialArtsForms.CloseQuartersCombat)
return;
if(knowledgeComponent.Blocked)
return;
switch (args.Type)
{
case ComboAttackType.Disarm:
_stamina.TakeStaminaDamage(args.Target, 25f);
break;
case ComboAttackType.Harm:
if (!TryComp<RequireProjectileTargetComponent>(ent, out var standing)
|| !standing.Active)
return;
_stun.TryKnockdown(args.Target, TimeSpan.FromSeconds(5), true);
_standingState.Stand(ent);
break;
}
}
#endregion
#region Combo Methods
private void OnCQCSlam(Entity<CanPerformComboComponent> ent, ref CqcSlamPerformedEvent args)
{
if (!_proto.TryIndex(ent.Comp.BeingPerformed, out var proto)
|| !TryUseMartialArt(ent, proto.MartialArtsForm, out var target, out var downed)
|| downed)
return;
DoDamage(ent, target, proto.DamageType, proto.ExtraDamage, out _);
_stun.TryKnockdown(target, TimeSpan.FromSeconds(proto.ParalyzeTime), true);
if (TryComp<PullableComponent>(target, out var pullable))
_pulling.TryStopPull(target, pullable, ent, true);
_audio.PlayPvs(new SoundPathSpecifier("/Audio/Weapons/genhit3.ogg"), target);
ComboPopup(ent, target, proto.Name);
}
private void OnCQCKick(Entity<CanPerformComboComponent> ent, ref CqcKickPerformedEvent args)
{
if (!_proto.TryIndex(ent.Comp.BeingPerformed, out var proto)
|| !TryUseMartialArt(ent, proto.MartialArtsForm, out var target, out var downed)
|| !downed)
return;
if (TryComp<StaminaComponent>(target, out var stamina) && stamina.Critical)
{
_status.TryAddStatusEffect<ForcedSleepingComponent>(target, "ForcedSleep", TimeSpan.FromSeconds(10), true);
}
DoDamage(ent, target, proto.DamageType, proto.ExtraDamage, out var damage, TargetBodyPart.Head);
_stamina.TakeStaminaDamage(target, proto.StaminaDamage, source: ent);
var mapPos = _transform.GetMapCoordinates(ent).Position;
var hitPos = _transform.GetMapCoordinates(target).Position;
var dir = hitPos - mapPos;
dir *= 1f / dir.Length();
if (TryComp<PullableComponent>(target, out var pullable))
_pulling.TryStopPull(target, pullable, ent, true);
_grabThrowing.Throw(target, ent, dir, proto.ThrownSpeed, proto.StaminaDamage / 2, damage);
_audio.PlayPvs(new SoundPathSpecifier("/Audio/Weapons/genhit2.ogg"), target);
ComboPopup(ent, target, proto.Name);
}
private void OnCQCRestrain(Entity<CanPerformComboComponent> ent, ref CqcRestrainPerformedEvent args)
{
if (!_proto.TryIndex(ent.Comp.BeingPerformed, out var proto)
|| !TryUseMartialArt(ent, proto.MartialArtsForm, out var target, out _))
return;
_stun.TryKnockdown(target, TimeSpan.FromSeconds(proto.ParalyzeTime), true);
_stamina.TakeStaminaDamage(target, proto.StaminaDamage, source: ent);
}
private void OnCQCPressure(Entity<CanPerformComboComponent> ent, ref CqcPressurePerformedEvent args)
{
if (!_proto.TryIndex(ent.Comp.BeingPerformed, out var proto)
|| !TryUseMartialArt(ent, proto.MartialArtsForm, out var target, out _))
return;
_stamina.TakeStaminaDamage(target, proto.StaminaDamage, source: ent);
if (!_hands.TryGetActiveItem(target, out var activeItem))
return;
if(!_hands.TryDrop(target, activeItem.Value))
return;
if (!_hands.TryGetEmptyHand(target, out var emptyHand))
return;
if(!_hands.TryPickupAnyHand(ent, activeItem.Value))
return;
_hands.SetActiveHand(ent, emptyHand);
ComboPopup(ent, target, proto.Name);
}
private void OnCQCConsecutive(Entity<CanPerformComboComponent> ent, ref CqcConsecutivePerformedEvent args)
{
if (!_proto.TryIndex(ent.Comp.BeingPerformed, out var proto)
|| !TryUseMartialArt(ent, proto.MartialArtsForm, out var target, out _))
return;
DoDamage(ent, target, proto.DamageType, proto.ExtraDamage, out _);
_stamina.TakeStaminaDamage(target, proto.StaminaDamage, source: ent);
_audio.PlayPvs(new SoundPathSpecifier("/Audio/Weapons/genhit1.ogg"), target);
ComboPopup(ent, target, proto.Name);
}
#endregion
}

View File

@@ -0,0 +1,78 @@
using System.Linq;
using Content.Shared._Goobstation.MartialArts.Components;
using Content.Shared._Goobstation.MartialArts.Events;
using Content.Shared.Mobs.Components;
using Robust.Shared.Prototypes;
namespace Content.Shared._Goobstation.MartialArts;
/// <summary>
/// This handles determining if a combo was performed.
/// </summary>
public partial class SharedMartialArtsSystem
{
private void InitializeCanPerformCombo()
{
SubscribeLocalEvent<CanPerformComboComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<CanPerformComboComponent, ComboAttackPerformedEvent>(OnAttackPerformed);
SubscribeLocalEvent<CanPerformComboComponent, ComboBeingPerformedEvent>(OnComboBeingPerformed);
}
private void OnMapInit(EntityUid uid, CanPerformComboComponent component, MapInitEvent args)
{
foreach (var item in component.RoundstartCombos)
{
component.AllowedCombos.Add(_proto.Index(item));
}
}
private void OnAttackPerformed(EntityUid uid, CanPerformComboComponent component, ComboAttackPerformedEvent args)
{
if (!HasComp<MobStateComponent>(args.Target))
return;
if (component.CurrentTarget != null && args.Target != component.CurrentTarget.Value)
{
component.LastAttacks.Clear();
}
if (args.Weapon != uid)
{
component.LastAttacks.Clear();
return;
}
component.CurrentTarget = args.Target;
component.ResetTime = _timing.CurTime + TimeSpan.FromSeconds(4);
component.LastAttacks.Add(args.Type);
CheckCombo(uid, component);
}
private void CheckCombo(EntityUid uid, CanPerformComboComponent comp)
{
foreach (var proto in comp.AllowedCombos)
{
var sum = comp.LastAttacks.Count - proto.AttackTypes.Count;
if (sum < 0)
continue;
var list = comp.LastAttacks.GetRange(sum, proto.AttackTypes.Count).AsEnumerable();
var attackList = proto.AttackTypes.AsEnumerable();
if (!list.SequenceEqual(attackList) || proto.ResultEvent == null)
continue;
var beingPerformedEv = new ComboBeingPerformedEvent(proto.ID);
var ev = proto.ResultEvent;
RaiseLocalEvent(uid, beingPerformedEv);
RaiseLocalEvent(uid, ev);
comp.LastAttacks.Clear();
break;
}
}
private void OnComboBeingPerformed(Entity<CanPerformComboComponent> ent, ref ComboBeingPerformedEvent args)
{
ent.Comp.BeingPerformed = args.ProtoId;
Dirty(ent, ent.Comp);
}
}

View File

@@ -0,0 +1,134 @@
using Content.Shared._Goobstation.MartialArts.Components;
using Content.Shared._Goobstation.MartialArts.Events;
using Content.Shared.Clothing;
using Content.Shared.Damage;
using Content.Shared.Eye.Blinding.Components;
using Content.Shared.Movement.Pulling.Components;
using Content.Shared.StatusEffect;
using Robust.Shared.Audio;
namespace Content.Shared._Goobstation.MartialArts;
public partial class SharedMartialArtsSystem
{
private void InitializeCorporateJudo()
{
SubscribeLocalEvent<CanPerformComboComponent, JudoThrowPerformedEvent>(OnJudoThrow);
SubscribeLocalEvent<CanPerformComboComponent, JudoEyePokePerformedEvent>(OnJudoEyepoke);
SubscribeLocalEvent<CanPerformComboComponent, JudoArmbarPerformedEvent>(OnJudoArmbar);
SubscribeLocalEvent<GrantCorporateJudoComponent, ClothingGotEquippedEvent>(OnGrantCorporateJudo);
SubscribeLocalEvent<GrantCorporateJudoComponent, ClothingGotUnequippedEvent>(OnRemoveCorporateJudo);
//SubscribeLocalEvent<CanPerformComboComponent, JudoGoldenBlastPerformedEvent>(OnJudoGoldenBlast); -- rework
// Wheel throw
// Discombobulate
}
#region Generic Methods
private void OnGrantCorporateJudo(Entity<GrantCorporateJudoComponent> ent, ref ClothingGotEquippedEvent args)
{
if (!_netManager.IsServer)
return;
var user = args.Wearer;
TryGrant(ent.Comp, user);
}
private void OnRemoveCorporateJudo(Entity<GrantCorporateJudoComponent> ent, ref ClothingGotUnequippedEvent args)
{
var user = args.Wearer;
if (!TryComp<MartialArtsKnowledgeComponent>(user, out var martialArtsKnowledge))
return;
if (martialArtsKnowledge.MartialArtsForm != MartialArtsForms.CorporateJudo)
return;
RemComp<MartialArtsKnowledgeComponent>(user);
RemComp<CanPerformComboComponent>(user);
}
#endregion
#region Combo Methods
private void OnJudoThrow(Entity<CanPerformComboComponent> ent, ref JudoThrowPerformedEvent args)
{
if (!_proto.TryIndex(ent.Comp.BeingPerformed, out var proto)
|| !TryUseMartialArt(ent, proto.MartialArtsForm, out var target, out var downed)
|| downed)
return;
_stun.TryKnockdown(target, TimeSpan.FromSeconds(proto.ParalyzeTime), false);
_stamina.TakeStaminaDamage(target, proto.StaminaDamage);
if (TryComp<PullableComponent>(target, out var pullable))
_pulling.TryStopPull(target, pullable, ent, true);
_audio.PlayPvs(new SoundPathSpecifier("/Audio/Weapons/genhit3.ogg"), target);
ComboPopup(ent, target, proto.Name);
}
private void OnJudoEyepoke(Entity<CanPerformComboComponent> ent, ref JudoEyePokePerformedEvent args)
{
if (!_proto.TryIndex(ent.Comp.BeingPerformed, out var proto)
|| !TryUseMartialArt(ent, proto.MartialArtsForm, out var target, out _))
return;
if (!TryComp(target, out StatusEffectsComponent? status))
return;
_status.TryAddStatusEffect<TemporaryBlindnessComponent>(target,
"TemporaryBlindness",
TimeSpan.FromSeconds(2),
true,
status);
_status.TryAddStatusEffect<BlurryVisionComponent>(target,
"BlurryVision",
TimeSpan.FromSeconds(5),
false,
status);
DoDamage(ent, target, proto.DamageType, proto.ExtraDamage, out _);
_audio.PlayPvs(new SoundPathSpecifier("/Audio/Weapons/genhit3.ogg"), target);
ComboPopup(ent, target, proto.Name);
}
private void OnJudoArmbar(Entity<CanPerformComboComponent> ent, ref JudoArmbarPerformedEvent args)
{
if (!_proto.TryIndex(ent.Comp.BeingPerformed, out var proto)
|| !TryUseMartialArt(ent, proto.MartialArtsForm, out var target, out var downed))
return;
switch (downed)
{
case false:
var item = _hands.GetActiveItem(target);
if (item != null)
_hands.TryDrop(target, item.Value);
break;
case true:
_stamina.TakeStaminaDamage(target, proto.StaminaDamage);
_stun.TryKnockdown(target, TimeSpan.FromSeconds(proto.ParalyzeTime), false);
break;
}
_audio.PlayPvs(new SoundPathSpecifier("/Audio/Weapons/genhit3.ogg"), target);
ComboPopup(ent, target, proto.Name);
}
/* Pending Implement
private void OnJudoGoldenBlast(Entity<CanPerformComboComponent> ent, ref JudoGoldenBlastPerformedEvent args)
{
if (!TryUseMartialArt(ent, MartialArtsForms.CorporateJudo, out var target, out var downed))
return;
if (downed)
return;
_stun.TryParalyze(target, TimeSpan.FromSeconds(30), false);
if (TryComp<PullableComponent>(target, out var pullable))
_pulling.TryStopPull(target, pullable, ent, true);
_audio.PlayPvs(new SoundPathSpecifier("/Audio/Weapons/genhit3.ogg"), target);
}
*/
#endregion
}

View File

@@ -0,0 +1,109 @@
using Content.Shared._Goobstation.MartialArts.Components;
using Content.Shared._Goobstation.MartialArts.Events;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Mobs.Components;
using Content.Shared.Weapons.Melee.Events;
namespace Content.Shared._Goobstation.MartialArts;
/// <summary>
/// This handles...
/// </summary>
public abstract partial class SharedMartialArtsSystem
{
private void InitializeKravMaga()
{
SubscribeLocalEvent<KravMagaComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<KravMagaComponent, KravMagaActionEvent>(OnKravMagaAction);
SubscribeLocalEvent<KravMagaComponent, MeleeHitEvent>(OnMeleeHitEvent);
SubscribeLocalEvent<KravMagaComponent, ComponentShutdown>(OnKravMagaShutdown);
}
private void OnMeleeHitEvent(Entity<KravMagaComponent> ent, ref MeleeHitEvent args)
{
if (args.HitEntities.Count <= 0)
return;
foreach (var hitEntity in args.HitEntities)
{
if (!HasComp<MobStateComponent>(hitEntity))
continue;
if (!TryComp<RequireProjectileTargetComponent>(hitEntity, out var isDowned))
continue;
DoKravMaga(ent, hitEntity, isDowned);
}
}
private void DoKravMaga(Entity<KravMagaComponent> ent, EntityUid hitEntity, RequireProjectileTargetComponent requireProjectileTargetComponent)
{
if (ent.Comp.SelectedMoveComp == null)
return;
var moveComp = ent.Comp.SelectedMoveComp;
switch (ent.Comp.SelectedMove)
{
case KravMagaMoves.LegSweep:
if(_netManager.IsClient)
return;
_stun.TryParalyze(hitEntity, TimeSpan.FromSeconds(4), true);
break;
case KravMagaMoves.NeckChop:
var comp = EnsureComp<KravMagaSilencedComponent>(hitEntity);
comp.SilencedTime = _timing.CurTime + TimeSpan.FromSeconds(moveComp.EffectTime);
break;
case KravMagaMoves.LungPunch:
_stamina.TakeStaminaDamage(hitEntity, moveComp.StaminaDamage);
var blockedBreathingComponent = EnsureComp<KravMagaBlockedBreathingComponent>(hitEntity);
blockedBreathingComponent.BlockedTime = _timing.CurTime + TimeSpan.FromSeconds(moveComp.EffectTime);
break;
case null:
var damage = ent.Comp.BaseDamage;
if (requireProjectileTargetComponent.Active)
damage *= ent.Comp.DownedDamageModifier;
DoDamage(ent.Owner, hitEntity, "Blunt", damage, out _);
break;
default:
throw new ArgumentOutOfRangeException();
}
ent.Comp.SelectedMove = null;
ent.Comp.SelectedMoveComp = null;
}
private void OnKravMagaAction(Entity<KravMagaComponent> ent, ref KravMagaActionEvent args)
{
var actionEnt = args.Action.Owner;
if (!TryComp<KravMagaActionComponent>(actionEnt, out var kravActionComp))
return;
_popupSystem.PopupClient(Loc.GetString("krav-maga-ready", ("action", kravActionComp.Name)), ent, ent);
ent.Comp.SelectedMove = kravActionComp.Configuration;
ent.Comp.SelectedMoveComp = kravActionComp;
}
private void OnMapInit(Entity<KravMagaComponent> ent, ref MapInitEvent args)
{
if (HasComp<MartialArtsKnowledgeComponent>(ent))
return;
foreach (var actionId in ent.Comp.BaseKravMagaMoves)
{
var actions = _actions.AddAction(ent, actionId);
if (actions != null)
ent.Comp.KravMagaMoveEntities.Add(actions.Value);
}
}
private void OnKravMagaShutdown(Entity<KravMagaComponent> ent, ref ComponentShutdown args)
{
if (!TryComp<KravMagaComponent>(ent, out var kravMaga))
return;
foreach (var action in ent.Comp.KravMagaMoveEntities)
{
_actions.RemoveAction(action);
}
}
}

View File

@@ -0,0 +1,155 @@
using System.Linq;
using Content.Shared._Goobstation.MartialArts.Components;
using Content.Shared._Goobstation.MartialArts.Events;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Interaction.Events;
using Content.Shared.Movement.Pulling.Components;
using Content.Shared.Popups;
using Content.Shared.Weapons.Reflect;
using Robust.Shared.Audio;
namespace Content.Shared._Goobstation.MartialArts;
public partial class SharedMartialArtsSystem
{
private void InitializeSleepingCarp()
{
SubscribeLocalEvent<CanPerformComboComponent, SleepingCarpGnashingTeethPerformedEvent>(OnSleepingCarpGnashing);
SubscribeLocalEvent<CanPerformComboComponent, SleepingCarpKneeHaulPerformedEvent>(OnSleepingCarpKneeHaul);
SubscribeLocalEvent<CanPerformComboComponent, SleepingCarpCrashingWavesPerformedEvent>(OnSleepingCarpCrashingWaves);
SubscribeLocalEvent<GrantSleepingCarpComponent, UseInHandEvent>(OnGrantSleepingCarp);
}
#region Generic Methods
private void OnGrantSleepingCarp(Entity<GrantSleepingCarpComponent> ent, ref UseInHandEvent args)
{
if (!_netManager.IsServer)
return;
if (ent.Comp.Used)
return;
if (ent.Comp.UseAgainTime == TimeSpan.Zero)
{
CarpScrollDelay(ent, args.User);
return;
}
if (_timing.CurTime < ent.Comp.UseAgainTime)
{
_popupSystem.PopupEntity(
Loc.GetString("carp-scroll-waiting"),
ent,
args.User,
PopupType.MediumCaution);
return;
}
switch (ent.Comp.Stage)
{
case < 3:
CarpScrollDelay(ent, args.User);
break;
case >= 3:
if (!TryGrant(ent.Comp, args.User))
return;
var userReflect = EnsureComp<ReflectComponent>(args.User);
userReflect.ReflectProb = 1;
userReflect.Spread = 60;
ent.Comp.Used = true;
_popupSystem.PopupEntity(
Loc.GetString("carp-scroll-complete"),
ent,
args.User,
PopupType.LargeCaution);
return;
}
}
private void CarpScrollDelay(Entity<GrantSleepingCarpComponent> ent, EntityUid user)
{
var time = _random.Next(ent.Comp.MinUseDelay, ent.Comp.MaxUseDelay);
ent.Comp.UseAgainTime = _timing.CurTime + TimeSpan.FromSeconds(time);
ent.Comp.Stage++;
_popupSystem.PopupEntity(
Loc.GetString("carp-scroll-advance"),
ent,
user,
PopupType.Medium);
}
#endregion
#region Combo Methods
private void OnSleepingCarpGnashing(Entity<CanPerformComboComponent> ent,
ref SleepingCarpGnashingTeethPerformedEvent args)
{
if (!_proto.TryIndex(ent.Comp.BeingPerformed, out var proto)
|| !TryUseMartialArt(ent, proto.MartialArtsForm, out var target, out _))
return;
if (!TryComp<MartialArtsKnowledgeComponent>(ent.Owner, out var knowledgeComponent))
return;
DoDamage(ent, target, proto.DamageType, proto.ExtraDamage + ent.Comp.ConsecutiveGnashes * 5, out _);
ent.Comp.ConsecutiveGnashes++;
_audio.PlayPvs(new SoundPathSpecifier("/Audio/Weapons/genhit1.ogg"), target);
if (TryComp<RequireProjectileTargetComponent>(target, out var standing)
&& !standing.Active)
{
var saying =
knowledgeComponent.RandomSayings.ElementAt(
_random.Next(knowledgeComponent.RandomSayings.Count));
var ev = new SleepingCarpSaying(saying);
RaiseLocalEvent(ent, ev);
}
else
{
var saying =
knowledgeComponent.RandomSayingsDowned.ElementAt(
_random.Next(knowledgeComponent.RandomSayingsDowned.Count));
var ev = new SleepingCarpSaying(saying);
RaiseLocalEvent(ent, ev);
}
}
private void OnSleepingCarpKneeHaul(Entity<CanPerformComboComponent> ent,
ref SleepingCarpKneeHaulPerformedEvent args)
{
if (!_proto.TryIndex(ent.Comp.BeingPerformed, out var proto)
|| !TryUseMartialArt(ent, proto.MartialArtsForm, out var target, out var downed)
|| downed)
return;
DoDamage(ent, target, proto.DamageType, proto.ExtraDamage, out _);
_stamina.TakeStaminaDamage(target, proto.StaminaDamage);
_stun.TryParalyze(target, TimeSpan.FromSeconds(proto.ParalyzeTime), true);
if (TryComp<PullableComponent>(target, out var pullable))
_pulling.TryStopPull(target, pullable, ent, true);
_audio.PlayPvs(new SoundPathSpecifier("/Audio/Weapons/genhit3.ogg"), target);
ComboPopup(ent, target, proto.Name);
}
private void OnSleepingCarpCrashingWaves(Entity<CanPerformComboComponent> ent,
ref SleepingCarpCrashingWavesPerformedEvent args)
{
if (!_proto.TryIndex(ent.Comp.BeingPerformed, out var proto)
|| !TryUseMartialArt(ent, proto.MartialArtsForm, out var target, out var downed)
|| downed)
return;
DoDamage(ent, target, proto.DamageType, proto.ExtraDamage, out var damage);
var mapPos = _transform.GetMapCoordinates(ent).Position;
var hitPos = _transform.GetMapCoordinates(target).Position;
var dir = hitPos - mapPos;
if (TryComp<PullableComponent>(target, out var pullable))
_pulling.TryStopPull(target, pullable, ent, true);
_grabThrowing.Throw(target, ent, dir, proto.ThrownSpeed, proto.StaminaDamage / 2, damage);
_audio.PlayPvs(new SoundPathSpecifier("/Audio/Weapons/genhit2.ogg"), target);
ComboPopup(ent, target, proto.Name);
}
#endregion
}

View File

@@ -0,0 +1,287 @@
using Content.Shared._Goobstation.MartialArts.Components;
using Content.Shared._Shitmed.Targeting;
using Content.Shared._White.Grab;
using Content.Shared.Actions;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.IdentityManagement;
using Content.Shared.Movement.Pulling.Events;
using Content.Shared.Movement.Pulling.Systems;
using Content.Shared.Popups;
using Content.Shared.Speech;
using Content.Shared.Standing;
using Content.Shared.StatusEffect;
using Content.Shared.Stunnable;
using Content.Shared.Weapons.Melee;
using Content.Shared.Weapons.Melee.Events;
using Content.Shared.Weapons.Ranged.Events;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Shared._Goobstation.MartialArts;
/// <summary>
/// Handles most of Martial Arts Systems.
/// </summary>
public abstract partial class SharedMartialArtsSystem : EntitySystem
{
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly PullingSystem _pulling = default!;
[Dependency] private readonly StatusEffectsSystem _status = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly StaminaSystem _stamina = default!;
[Dependency] private readonly GrabThrownSystem _grabThrowing = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SharedStunSystem _stun = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;
[Dependency] private readonly SharedActionsSystem _actions = default!;
[Dependency] private readonly INetManager _netManager = default!;
[Dependency] private readonly StandingStateSystem _standingState = default!;
public override void Initialize()
{
base.Initialize();
InitializeKravMaga();
InitializeSleepingCarp();
InitializeCqc();
InitializeCorporateJudo();
InitializeCanPerformCombo();
SubscribeLocalEvent<MartialArtsKnowledgeComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<MartialArtsKnowledgeComponent, CheckGrabOverridesEvent>(CheckGrabStageOverride);
SubscribeLocalEvent<MartialArtsKnowledgeComponent, MeleeHitEvent>(OnMeleeHit);
SubscribeLocalEvent<MartialArtsKnowledgeComponent, ShotAttemptedEvent>(OnShotAttempt);
SubscribeLocalEvent<KravMagaSilencedComponent, SpeakAttemptEvent>(OnSilencedSpeakAttempt);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<CanPerformComboComponent>();
while (query.MoveNext(out _, out var comp))
{
if (_timing.CurTime < comp.ResetTime || comp.LastAttacks.Count <= 0)
continue;
comp.LastAttacks.Clear();
comp.ConsecutiveGnashes = 0;
}
var kravSilencedQuery = EntityQueryEnumerator<KravMagaSilencedComponent>();
while (kravSilencedQuery.MoveNext(out var ent, out var comp))
{
if (_timing.CurTime < comp.SilencedTime)
continue;
RemComp<KravMagaSilencedComponent>(ent);
}
var kravBlockedQuery = EntityQueryEnumerator<KravMagaBlockedBreathingComponent>();
while (kravBlockedQuery.MoveNext(out var ent, out var comp))
{
if (_timing.CurTime < comp.BlockedTime)
continue;
RemComp<KravMagaBlockedBreathingComponent>(ent);
}
}
#region Event Methods
private void OnMeleeHit(Entity<MartialArtsKnowledgeComponent> ent, ref MeleeHitEvent args)
{
if (args.Handled)
return;
if (!ent.Comp.RandomDamageModifier)
return;
var randomDamage = _random.Next(ent.Comp.MinRandomDamageModifier, ent.Comp.MaxRandomDamageModifier);
var bonusDamageSpec = new DamageSpecifier();
bonusDamageSpec.DamageDict.Add("Blunt", randomDamage);
args.BonusDamage += bonusDamageSpec;
}
private void OnShutdown(Entity<MartialArtsKnowledgeComponent> ent, ref ComponentShutdown args)
{
if(TryComp<CanPerformComboComponent>(ent, out var comboComponent))
comboComponent.AllowedCombos.Clear();
}
private void CheckGrabStageOverride<T>(EntityUid uid, T component, CheckGrabOverridesEvent args)
where T : GrabStagesOverrideComponent
{
if (args.Stage == GrabStage.Soft)
args.Stage = component.StartingStage;
}
private void OnSilencedSpeakAttempt(Entity<KravMagaSilencedComponent> ent, ref SpeakAttemptEvent args)
{
_popupSystem.PopupEntity(Loc.GetString("popup-grabbed-cant-speak"),
ent,
ent); // You cant speak while someone is choking you
args.Cancel();
}
private void OnShotAttempt(Entity<MartialArtsKnowledgeComponent> ent, ref ShotAttemptedEvent args)
{
if (ent.Comp.MartialArtsForm != MartialArtsForms.SleepingCarp)
return;
_popupSystem.PopupClient(Loc.GetString("gun-disabled"), ent, ent);
args.Cancel();
}
private void ComboPopup(EntityUid user, EntityUid target, string comboName)
{
if (!_netManager.IsServer)
return;
var userName = Identity.Entity(user, EntityManager);
var targetName = Identity.Entity(target, EntityManager);
_popupSystem.PopupEntity(Loc.GetString("martial-arts-action-sender",
("name", targetName),
("move", comboName)),
user,
user);
_popupSystem.PopupEntity(Loc.GetString("martial-arts-action-reciever",
("name", userName),
("move", comboName)),
target,
target);
}
#endregion
#region Helper Methods
private bool TryGrant(GrantMartialArtKnowledgeComponent comp, EntityUid user)
{
if (!_netManager.IsServer || MetaData(user).EntityLifeStage >= EntityLifeStage.Terminating)
return false;
if (HasComp<KravMagaComponent>(user))
{
_popupSystem.PopupEntity(Loc.GetString("cqc-fail-knowanother"), user, user);
return false;
}
if (!HasComp<CanPerformComboComponent>(user))
{
var canPerformComboComponent = EnsureComp<CanPerformComboComponent>(user);
var martialArtsKnowledgeComponent = EnsureComp<MartialArtsKnowledgeComponent>(user);
LoadPrototype(user, martialArtsKnowledgeComponent, comp.MartialArtsForm);
martialArtsKnowledgeComponent.Blocked = false;
if (TryComp<MeleeWeaponComponent>(user, out var meleeWeaponComponent))
{
var newDamage = new DamageSpecifier();
newDamage.DamageDict.Add("Blunt", martialArtsKnowledgeComponent.BaseDamageModifier);
meleeWeaponComponent.Damage += newDamage;
}
Dirty(user, canPerformComboComponent);
return true;
}
if (!TryComp<MartialArtsKnowledgeComponent>(user, out var cqc))
{
_popupSystem.PopupEntity(Loc.GetString("cqc-fail-knowanother"), user, user);
return false;
}
if (cqc.Blocked && comp.MartialArtsForm == MartialArtsForms.CloseQuartersCombat)
{
_popupSystem.PopupEntity(Loc.GetString("cqc-success-unblocked"), user, user);
cqc.Blocked = false;
comp.Used = true;
return false;
}
_popupSystem.PopupEntity(Loc.GetString("cqc-fail-already"), user, user);
return false;
}
private void LoadCombos(ProtoId<ComboListPrototype> list, CanPerformComboComponent combo)
{
combo.AllowedCombos.Clear();
if (!_proto.TryIndex(list, out var comboListPrototype))
return;
foreach (var item in comboListPrototype.Combos)
{
combo.AllowedCombos.Add(_proto.Index(item));
}
}
private void LoadPrototype(EntityUid uid, MartialArtsKnowledgeComponent component, MartialArtsForms name)
{
// just know i hate this and i probably could grab info from the prototype directly from protomanager i was being stupid
// when i originally wrote the code for this clearly.
var combo = EnsureComp<CanPerformComboComponent>(uid);
if (!_proto.TryIndex<MartialArtPrototype>(name.ToString(), out var martialArtsPrototype))
return;
component.MartialArtsForm = martialArtsPrototype.MartialArtsForm;
component.RoundstartCombos = martialArtsPrototype.RoundstartCombos;
component.MinRandomDamageModifier = martialArtsPrototype.MinRandomDamageModifier;
component.MaxRandomDamageModifier = martialArtsPrototype.MaxRandomDamageModifier;
component.RandomDamageModifier = martialArtsPrototype.RandomDamageModifier;
component.RandomSayings = martialArtsPrototype.RandomSayings;
component.RandomSayingsDowned = martialArtsPrototype.RandomSayingsDowned;
LoadCombos(martialArtsPrototype.RoundstartCombos, combo);
}
private bool TryUseMartialArt(Entity<CanPerformComboComponent> ent,
MartialArtsForms form,
out EntityUid target,
out bool downed)
{
target = EntityUid.Invalid;
downed = false;
if (ent.Comp.CurrentTarget == null)
return false;
if (!TryComp<MartialArtsKnowledgeComponent>(ent, out var knowledgeComponent))
return false;
if (!TryComp<RequireProjectileTargetComponent>(ent.Comp.CurrentTarget, out var isDowned))
return false;
downed = isDowned.Active;
target = ent.Comp.CurrentTarget.Value;
if (knowledgeComponent.MartialArtsForm == form && !knowledgeComponent.Blocked)
{
return true;
}
foreach (var entInRange in _lookup.GetEntitiesInRange(ent, 8f))
{
if (!TryPrototype(entInRange, out var proto) || proto.ID != "DefaultStationBeaconKitchen" || !knowledgeComponent.Blocked)
continue;
return true;
}
return false;
}
private void DoDamage(EntityUid ent,
EntityUid target,
string damageType,
int damageAmount,
out DamageSpecifier damage,
TargetBodyPart? targetBodyPart = null)
{
damage = new DamageSpecifier();
if(!TryComp<TargetingComponent>(ent, out var targetingComponent))
return;
damage.DamageDict.Add(damageType, damageAmount);
_damageable.TryChangeDamage(target, damage, origin: ent, targetPart: targetBodyPart ?? targetingComponent.Target);
}
#endregion
}

View File

@@ -0,0 +1,16 @@
using Robust.Shared.GameStates;
namespace Content.Shared._Goobstation.TableSlam;
/// <summary>
/// This is used for...
/// </summary>
[RegisterComponent]
public sealed partial class PostTabledComponent : Component
{
[DataField]
public TimeSpan PostTabledShovableTime = TimeSpan.Zero;
[DataField]
public float ParalyzeChance = 0.35f;
}

View File

@@ -0,0 +1,157 @@
using System.Linq;
using Content.Shared.Contests;
using Content.Shared._Shitmed.Targeting;
using Content.Shared.Actions.Events;
using Content.Shared.Climbing.Components;
using Content.Shared.CombatMode;
using Content.Shared.Coordinates;
using Content.Shared.Damage;
using Content.Shared.Damage.Events;
using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.Interaction;
using Content.Shared.Movement.Pulling.Components;
using Content.Shared.Movement.Pulling.Systems;
using Content.Shared.Standing;
using Content.Shared.StatusEffect;
using Content.Shared.Stunnable;
using Content.Shared.Throwing;
using Content.Shared.Weapons.Melee;
using Content.Shared.Weapons.Melee.Events;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Events;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Shared._Goobstation.TableSlam;
/// <summary>
/// This handles...
/// </summary>
public sealed class TableSlamSystem : EntitySystem
{
[Dependency] private readonly PullingSystem _pullingSystem = default!;
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
[Dependency] private readonly StandingStateSystem _standing = default!;
[Dependency] private readonly ThrowingSystem _throwingSystem = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly StaminaSystem _staminaSystem = default!;
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly ContestsSystem _contestsSystem = default!;
[Dependency] private readonly IRobustRandom _random = default!;
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<PullerComponent, MeleeHitEvent>(OnMeleeHit);
SubscribeLocalEvent<TableableComponent, StartCollideEvent>(OnStartCollide);
SubscribeLocalEvent<PostTabledComponent, DisarmAttemptEvent>(OnDisarmAttemptEvent);
}
private void OnDisarmAttemptEvent(Entity<PostTabledComponent> ent, ref DisarmAttemptEvent args)
{
if(!_random.Prob(ent.Comp.ParalyzeChance))
return;
_stunSystem.TryParalyze(ent, TimeSpan.FromSeconds(3), false);
RemComp<PostTabledComponent>(ent);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var tabledQuery = EntityQueryEnumerator<PostTabledComponent>();
while (tabledQuery.MoveNext(out var uid, out var comp))
{
if (_gameTiming.CurTime >= comp.PostTabledShovableTime)
RemComp<PostTabledComponent>(uid);
}
}
private void OnMeleeHit(Entity<PullerComponent> ent, ref MeleeHitEvent args)
{
if (ent.Comp.GrabStage < GrabStage.Suffocate
|| ent.Comp.Pulling == null)
return;
if(!TryComp<PullableComponent>(ent.Comp.Pulling, out var pullableComponent))
return;
if (args.Direction != null)
return;
if (args.HitEntities.Count is > 1 or 0)
return;
var target = args.HitEntities.ElementAt(0);
if (!HasComp<BonkableComponent>(target)) // checks if its a table.
return;
var massContest = _contestsSystem.MassContest(ent, ent.Comp.Pulling.Value);
var attemptChance = Math.Clamp(1 * massContest, 0, 1);
var attemptRoundedToNearestQuarter = Math.Round(attemptChance * 4, MidpointRounding.ToEven) / 4;
if(_random.Prob((float) attemptRoundedToNearestQuarter)) // base chance to table slam someone is 1 if your mass ratio is less than 1 then your going to have a harder time slamming somebody.
TryTableSlam((ent.Comp.Pulling.Value, pullableComponent), ent, target);
}
public void TryTableSlam(Entity<PullableComponent> ent, Entity<PullerComponent> pullerEnt, EntityUid tableUid)
{
if(!_transformSystem.InRange(ent.Owner.ToCoordinates(), tableUid.ToCoordinates(), 2f ))
return;
_standing.Down(ent);
_pullingSystem.TryStopPull(ent, ent.Comp, pullerEnt, ignoreGrab: true);
_throwingSystem.TryThrow(ent, tableUid.ToCoordinates() , ent.Comp.BasedTabledForceSpeed, animated: false, doSpin: false);
pullerEnt.Comp.NextStageChange = _gameTiming.CurTime.Add(TimeSpan.FromSeconds(3)); // prevent table slamming spam
if (TryComp<TableableComponent>(ent, out var tableableComp)) // Checks that the entity being tabled has the TableableComponent
{
tableableComp.BeingTabled = true;
}
else
{
tableableComp = EnsureComp<TableableComponent>(ent); // Adds the Tableable Component if it doesn't exist
tableableComp.BeingTabled = true; // This shit is so code :skull:
}
}
private void OnStartCollide(Entity<TableableComponent> ent, ref StartCollideEvent args)
{
if(!ent.Comp.BeingTabled)
return;
if (!HasComp<BonkableComponent>(args.OtherEntity))
return;
var modifierOnGlassBreak = 1;
if (TryComp<GlassTableComponent>(args.OtherEntity, out var glassTableComponent))
{
_damageableSystem.TryChangeDamage(args.OtherEntity, glassTableComponent.TableDamage, origin: ent, targetPart: TargetBodyPart.Torso);
_damageableSystem.TryChangeDamage(args.OtherEntity, glassTableComponent.ClimberDamage, origin: ent);
modifierOnGlassBreak = 2;
}
else
{
_damageableSystem.TryChangeDamage(ent,
new DamageSpecifier()
{
DamageDict = new Dictionary<string, FixedPoint2> { { "Blunt", ent.Comp.TabledDamage } },
},
targetPart: TargetBodyPart.Torso);
_damageableSystem.TryChangeDamage(ent,
new DamageSpecifier()
{
DamageDict = new Dictionary<string, FixedPoint2> { { "Blunt", ent.Comp.TabledDamage } },
});
}
_staminaSystem.TakeStaminaDamage(ent, ent.Comp.TabledStaminaDamage);
_stunSystem.TryKnockdown(ent, TimeSpan.FromSeconds(3 * modifierOnGlassBreak), false);
var postTabledComponent = EnsureComp<PostTabledComponent>(ent);
postTabledComponent.PostTabledShovableTime = _gameTiming.CurTime.Add(TimeSpan.FromSeconds(3));
ent.Comp.BeingTabled = false;
//_audioSystem.PlayPvs("/Audio/Effects/thudswoosh.ogg", uid);
}
}

View File

@@ -0,0 +1,32 @@
using Robust.Shared.GameStates;
namespace Content.Shared._Goobstation.TableSlam;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class TableableComponent : Component
{
/// <summary>
/// If this pullable being tabled.
/// </summary>
[DataField, AutoNetworkedField]
public bool BeingTabled = false;
/// <summary>
/// Constant for tabling throw math
/// </summary>
[DataField]
public float BasedTabledForceSpeed = 5f;
/// <summary>
/// Stamina damage. taken on tabled
/// </summary>
[DataField]
public float TabledStaminaDamage = 40f;
/// <summary>
/// Damage taken on being tabled.
/// </summary>
[DataField]
public float TabledDamage = 5f;
// Goobstation end
}

View File

@@ -8,8 +8,6 @@ public sealed partial class GrabThrownComponent : Component
{
public DamageSpecifier? DamageOnCollide;
public DamageSpecifier? WallDamageOnCollide;
public float? StaminaDamageOnCollide;
public List<EntityUid> IgnoreEntity = new();

View File

@@ -8,6 +8,7 @@ using Robust.Shared.Player;
using System.Numerics;
using Content.Shared._White;
using Content.Shared.Standing;
using Robust.Shared.Physics.Components;
namespace Content.Shared._White.Grab;
@@ -33,33 +34,39 @@ public sealed class GrabThrownSystem : EntitySystem
if (_netMan.IsClient) // To avoid effect spam
return;
if (!HasComp<ThrownItemComponent>(ent.Owner))
if (!HasComp<ThrownItemComponent>(ent))
{
RemComp<GrabThrownComponent>(ent.Owner);
RemComp<GrabThrownComponent>(ent);
return;
}
if (ent.Comp.IgnoreEntity.Contains(args.OtherEntity))
return;
if (!HasComp<DamageableComponent>(ent.Owner))
RemComp<GrabThrownComponent>(ent.Owner);
if (!HasComp<DamageableComponent>(ent))
RemComp<GrabThrownComponent>(ent);
if(!TryComp<PhysicsComponent>(ent, out var physicsComponent))
return;
ent.Comp.IgnoreEntity.Add(args.OtherEntity);
var speed = args.OurBody.LinearVelocity.Length();
var velocity = args.OurBody.LinearVelocity.Length();
var velocitySquared = args.OurBody.LinearVelocity.LengthSquared();
var mass = physicsComponent.Mass;
var kineticEnergy = 0.5f * mass * velocitySquared;
if (ent.Comp.StaminaDamageOnCollide != null)
_stamina.TakeStaminaDamage(ent.Owner, ent.Comp.StaminaDamageOnCollide.Value);
_stamina.TakeStaminaDamage(ent, ent.Comp.StaminaDamageOnCollide.Value);
var damageScale = speed;
if (ent.Comp.WallDamageOnCollide != null)
_damageable.TryChangeDamage(args.OtherEntity, ent.Comp.WallDamageOnCollide * damageScale);
var kineticEnergyDamage = new DamageSpecifier();
kineticEnergyDamage.DamageDict.Add("Blunt", 1);
kineticEnergyDamage *= Math.Floor(kineticEnergy / 100) / 2 + 3;
_damageable.TryChangeDamage(args.OtherEntity, kineticEnergyDamage);
_layingDown.TryLieDown(args.OtherEntity, behavior: DropHeldItemsBehavior.AlwaysDrop);
_color.RaiseEffect(Color.Red, new List<EntityUid>() { ent.Owner }, Filter.Pvs(ent.Owner, entityManager: EntityManager));
_color.RaiseEffect(Color.Red, new List<EntityUid>() { ent }, Filter.Pvs(ent, entityManager: EntityManager));
}
private void OnStopThrow(EntityUid uid, GrabThrownComponent comp, StopThrowEvent args)
@@ -77,26 +84,23 @@ public sealed class GrabThrownSystem : EntitySystem
/// <param name="uid">Entity to throw</param>
/// <param name="thrower">Entity that throws</param>
/// <param name="vector">Direction</param>
/// <param name="grabThrownSpeed">How fast you fly when thrown</param>
/// <param name="staminaDamage">Stamina damage on collide</param>
/// <param name="damageToUid">Damage to entity on collide</param>
/// <param name="damageToWall">Damage to wall or anything that was hit by entity</param>
public void Throw(
EntityUid uid,
EntityUid thrower,
Vector2 vector,
float grabThrownSpeed,
float? staminaDamage = null,
DamageSpecifier? damageToUid = null,
DamageSpecifier? damageToWall = null)
DamageSpecifier? damageToUid = null)
{
_layingDown.TryLieDown(uid, behavior: DropHeldItemsBehavior.AlwaysDrop);
var comp = EnsureComp<GrabThrownComponent>(uid);
comp.StaminaDamageOnCollide = staminaDamage;
comp.DamageOnCollide = damageToUid;
comp.WallDamageOnCollide = damageToWall;
comp.IgnoreEntity.Add(thrower);
comp.DamageOnCollide = damageToUid;
_layingDown.TryLieDown(uid, behavior: DropHeldItemsBehavior.AlwaysDrop);
_throwing.TryThrow(uid, vector, grabThrownSpeed, animated: false);
}
}

View File

@@ -12402,3 +12402,83 @@ Entries:
id: 6911
time: '2025-03-07T22:45:53.0000000+00:00'
url: https://github.com/Simple-Station/Einstein-Engines/pull/1885
- author: Eagle
changes:
- type: Add
message: >-
Table slamming. Harm a table when you have someone harm choked, see what
happens.
- type: Tweak
message: >-
Grab throw damage to other entities is now based on the thrown entities
kinetic energy. No more mouse wrecking balls.
- type: Tweak
message: You can now escape from a soft grab by just walking away.
- type: Tweak
message: You can no longer grab someone else while your being grabbed.
- type: Tweak
message: Mass now effects grab release attempts.
id: 6912
time: '2025-03-08T19:49:02.0000000+00:00'
url: https://github.com/Simple-Station/Einstein-Engines/pull/1889
- author: VMSolidus
changes:
- type: Add
message: >-
Added a variety of "Shuttle Spawning Airlocks" for mappers to use, which
can make it so that shuttles like the Cargo Shuttle, Pathfinder, etc.
Spawn already docked to the station.
id: 6913
time: '2025-03-08T20:11:39.0000000+00:00'
url: https://github.com/Simple-Station/Einstein-Engines/pull/1890
- author: Timfa2112
changes:
- type: Add
message: >-
Added extra descriptions to the revolutionary manifesto and the civilian
disabler
- type: Fix
message: Fixed extenddescription on flash
id: 6914
time: '2025-03-08T21:08:39.0000000+00:00'
url: https://github.com/Simple-Station/Einstein-Engines/pull/1887
- author: RadsammyT
changes:
- type: Tweak
message: >-
the Ore Processors now process Lead, Copper, and Aluminum instantly, and
is much faster at processing Tungsten
id: 6915
time: '2025-03-08T21:11:30.0000000+00:00'
url: https://github.com/Simple-Station/Einstein-Engines/pull/1873
- author: Eagle
changes:
- type: Add
message: >-
Added Corporate Judo, CQC, Sleeping Carp, and Krav Maga martial arts
with unique abilities.
- type: Add
message: >-
The Chef has been given Close Quarters Cooking in the Kitchen and Bar.
Tiders beware.
id: 6916
time: '2025-03-09T00:31:51.0000000+00:00'
url: https://github.com/Simple-Station/Einstein-Engines/pull/1891
- author: AlgumCorrupto
changes:
- type: Fix
message: >-
Fixed bug of the painted roboticist airlock displaying the windowed
counterpart instead of the standard one.
id: 6917
time: '2025-03-09T03:19:17.0000000+00:00'
url: https://github.com/Simple-Station/Einstein-Engines/pull/1899
- author: SolarisBirb
changes:
- type: Fix
message: SM no longer starts on round start.
- type: Fix
message: SM will no longer delam from spacing unless it's activated.
id: 6918
time: '2025-03-09T04:45:00.0000000+00:00'
url: https://github.com/Simple-Station/Einstein-Engines/pull/1901

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,44 @@
cqc-fail-used = {CAPITALIZE(THE($manual))} is already used.
cqc-fail-notself = You can't teach anyone with {CAPITALIZE(THE($manual))}.
cqc-fail-knowanother = You already know another martial art.
cqc-fail-already = You already know everything about a Martial art.
cqc-success-unblocked = Your CQC skills are not bound to kitchen anymore.
cqc-success-learned = You have learned CQC.
cqc-manual-used = [color=white]{CAPITALIZE(THE($manual))} looks to be already used.[/color]
ent-CQCManual = CQC manual
.desc = Looks like a usual book, but contains some secrets inside.
carp-scroll-waiting = The journey of a thousand miles begins with one step, and the path of wisdom is traveled slowly, one lesson at a time.
carp-scroll-advance = You have taken one step closer to becoming a master of the Way of the Sleeping Carp.
carp-scroll-complete = You are now a master of the Way of the Sleeping Carp.
carp-saying-huah = HUAH!
carv-saying-hya = HYA!
carp-saying-choo = CHOO!
carp-saying-wuo = WUO!
carp-saying-kya = KYA!
carp-saying-huh = HUH!
carp-saying-hiyoh = HIYOH!
carp-saying-strike = CARP STRIKE!
carp-saying-bite = CARP BITE!
carp-saying-banzai = BANZAIII!
carp-saying-kiya = KIYAAAA!
carp-saying-omae = OMAE WA MOU SHINDEIRU!
carp-saying-see = YOU CAN'T SEE ME!
carp-saying-time = MY TIME IS NOW!!
carp-saying-cowabunga = COWABUNGA!
krav-maga-ready = You ready a {$action}
martial-arts-action-sender = You hit {$name} with {$move}
martial-arts-action-receiver = {$name} hit you with {$move}
uplink-cqc-name = CQC Manual
uplink-cqc-desc = A manual that teaches a single user tactical Close-Quarters Combat before self-destructing. Does not restrict weapon usage. Your disarms will inflict some stamina damage while making people temporarily slur their words, your grabs will immobilize for a second making it easier to reinforce them, your harm attacks will deal moderate stamina damage, and you will get a set of combos that allow you to efficiently knockdown, disarm or stun your opponent. You can find more information on CQC and its combos here.
uplink-sleeping-carp-name = Martial Arts Scroll
uplink-sleeping-carp-desc = This scroll contains the secrets of an ancient martial arts technique. You will master unarmed combat, deflecting ranged weapon fire. Learning this art means you will also refuse to use dishonorable ranged weaponry. Unable to be understood by vampire and changeling agents.

View File

@@ -1 +0,0 @@
steal-target-power-gloves = power gloves

View File

@@ -0,0 +1,4 @@
revolutionarymanifesto-extenddescription-headrevolutionary = I can use this item to convert others to my cause.
revolutionarymanifesto-extenddescription-revolutionary = My revolutionary leader can use this item to convert others to our cause.
revolutionarymanifesto-extenddescription-security = Despite its dangerous ideology, this book is not illegal to possess.
revolutionarymanifesto-extenddescription-mindshield = An affront to all values a righteous Nanotrasen employee should stand for!

View File

@@ -0,0 +1 @@
civiliandisabler-extenddescription-security = This weapon is legal to own and carry without a license on NanoTrasen stations, but may be considered as an accessory equivalent to a weapon if used in a crime.

View File

@@ -1,3 +0,0 @@
ent-ClothingHandsGlovesPowergloveReal = силовые перчатки
.desc = Помни основы CQC.
.suffix = { "Смотритель" }

View File

@@ -1 +0,0 @@
steal-target-power-gloves = силовые перчатки

View File

@@ -1,7 +1,6 @@
loadout-description-LoadoutSecurityUniformJumpskirtSenior = A skirt fit for the best of the best.
loadout-description-LoadoutSecurityUniformJumpsuitSenior = A suit fit for the best of the best.
loadout-description-LoadoutSecurityShoesJackboots = A really nice, heavy, pair of black boots.
# Equipment
loadout-name-LoadoutMagazinePistolSpare = pistol magazine (.35 auto, spare)
loadout-name-LoadoutSpeedLoaderMagnumSpare = speed loader (.45 magnum, spare)

View File

@@ -5,31 +5,31 @@
components:
- type: StorageFill
contents:
- id: ClothingHandsGlovesPowergloveReal # WWDP, traitor steal objective, to do replace with krav-magas
- id: FlashlightSeclite
- id: WeaponDisabler
prob: 0.3
- id: ClothingBeltSecurityFilled
- id: Flash
- id: ClothingEyesGlassesSunglasses
- id: ClothingHeadsetAltSecurity
# - id: ClothingHandsGlovesCombat # WWDP, replaced by powergloves
- id: ClothingShoesBootsJack
- id: ClothingOuterCoatWarden
- id: ClothingOuterWinterWarden
- id: RubberStampWarden
- id: DoorRemoteArmory
- id: ClothingOuterHardsuitWarden # WWDP edit
- id: OxygenTankFilled # WWDP edit
- id: HoloprojectorSecurity
- id: ClothingEyesHudSecurity
- id: BoxPDAPrisoner # Delta-V
- id: ClothingShoesBootsWinterWarden #Delta V: Add departmental winter boots
- id: BoxEncryptionKeyPrisoner #Delta-V
- id: BoxPrisonerHeadset
- id: LunchboxSecurityFilledRandom # Delta-v Lunchboxes!
prob: 0.3
- id: BoxBodyBagPrisoner # WD
- id: FlashlightSeclite
- id: WeaponDisabler
prob: 0.3
- id: ClothingBeltSecurityFilled
- id: ClothingHandsGlovesKravMaga # Goobstation - Martial Arts
- id: Flash
- id: ClothingEyesGlassesSunglasses
- id: ClothingHeadsetAltSecurity
- id: ClothingHandsGlovesCombat
- id: ClothingShoesBootsJack
- id: ClothingOuterCoatWarden
- id: ClothingOuterWinterWarden
- id: RubberStampWarden
- id: DoorRemoteArmory
- id: ClothingOuterHardsuitWarden # WWDP edit
- id: OxygenTankFilled # WWDP edit
- id: HoloprojectorSecurity
- id: ClothingEyesHudSecurity
- id: BoxPDAPrisoner # Delta-V
- id: ClothingShoesBootsWinterWarden #Delta V: Add departmental winter boots
- id: BoxEncryptionKeyPrisoner #Delta-V
- id: BoxPrisonerHeadset
- id: LunchboxSecurityFilledRandom # Delta-v Lunchboxes!
prob: 0.3
- id: BoxBodyBagPrisoner # WD
- type: entity
id: LockerWardenFilled
@@ -38,29 +38,29 @@
components:
- type: StorageFill
contents:
- id: ClothingHandsGlovesPowergloveReal # WWDP, traitor steal objective, to do replace with krav-magas
- id: FlashlightSeclite
- id: WeaponDisabler
prob: 0.3
- id: ClothingBeltSecurityFilled
- id: Flash
- id: ClothingEyesGlassesSunglasses
- id: ClothingHeadsetAltSecurity
# - id: ClothingHandsGlovesCombat # WWDP, replaced by powergloves
- id: ClothingShoesBootsJack
- id: ClothingOuterCoatWarden
- id: ClothingOuterWinterWarden
- id: RubberStampWarden
- id: DoorRemoteArmory
- id: HoloprojectorSecurity
- id: ClothingEyesHudSecurity
- id: BoxPDAPrisoner # Delta-V
- id: ClothingShoesBootsWinterWarden #Delta V: Add departmental winter boots
- id: BoxEncryptionKeyPrisoner #Delta-V
- id: BoxPrisonerHeadset
- id: LunchboxSecurityFilledRandom # Delta-v Lunchboxes!
prob: 0.3
- id: BoxBodyBagPrisoner # WD
- id: FlashlightSeclite
- id: WeaponDisabler
prob: 0.3
- id: ClothingBeltSecurityFilled
- id: ClothingHandsGlovesKravMaga # Goobstation - Martial Arts
- id: Flash
- id: ClothingEyesGlassesSunglasses
- id: ClothingHeadsetAltSecurity
- id: ClothingHandsGlovesCombat
- id: ClothingShoesBootsJack
- id: ClothingOuterCoatWarden
- id: ClothingOuterWinterWarden
- id: RubberStampWarden
- id: DoorRemoteArmory
- id: HoloprojectorSecurity
- id: ClothingEyesHudSecurity
- id: BoxPDAPrisoner # Delta-V
- id: ClothingShoesBootsWinterWarden #Delta V: Add departmental winter boots
- id: BoxEncryptionKeyPrisoner #Delta-V
- id: BoxPrisonerHeadset
- id: LunchboxSecurityFilledRandom # Delta-v Lunchboxes!
prob: 0.3
- id: BoxBodyBagPrisoner # WD
- type: entity
id: LockerSecurityFilled
@@ -69,21 +69,24 @@
components:
- type: StorageFill
contents:
- id: FlashlightSeclite
- id: ClothingUniformJumpsuitSec # WD - Less random bloat
- id: ClothingHeadHelmetBasic
# - id: ClothingHeadHelmetInsulated # Nyanotrasen - Insulative headgear # WWDP - all sec helmets are insulated now
- id: ClothingOuterArmorBasic # WD - plate carrier and stabproof vest replaced back to regular bodyarmor
- id: ClothingBeltSecurityFilled
- id: ClothingEyesGlassesSecurity # WD - SecHuds
- id: ClothingHeadsetSecurity
- id: ClothingHandsGlovesColorBlack
- id: ClothingShoesBootsJack
- id: WeaponDisabler # WD
- id: WeaponMeleeNeedle
prob: 0.1
- id: LunchboxSecurityFilledRandom # Delta-v Lunchboxes!
prob: 0.3
- id: FlashlightSeclite
- id: ClothingUniformJumpsuitSec # WD - Less random bloat
- id: ClothingHeadHelmetBasic
# - id: ClothingHeadHelmetInsulated # Nyanotrasen - Insulative headgear # WWDP - all sec helmets are insulated now
- id: ClothingOuterArmorBasic # WD - plate carrier and stabproof vest replaced back to regular bodyarmor
- id: ClothingBeltSecurityFilled
- id: ClothingBeltCorporateJudo # Goobstation - Martial Arts
- id: Flash
prob: 0.5
- id: ClothingEyesGlassesSecurity # WD - SecHuds
- id: ClothingHeadsetSecurity
- id: ClothingHandsGlovesColorBlack
- id: ClothingShoesBootsJack
- id: WeaponDisabler # WD
- id: WeaponMeleeNeedle
prob: 0.1
- id: LunchboxSecurityFilledRandom # Delta-v Lunchboxes!
prob: 0.3
- type: entity
id: LockerBrigmedicFilled
@@ -92,37 +95,37 @@
components:
- type: StorageFill
contents:
- id: ClothingEyesGlassesCorpsman # DeltaV - Corpsman Glasses
- id: WeaponDisabler
- id: TrackingImplanter
amount: 2
## - id: ClothingOuterHardsuitCombatCorpsman # DeltaV - ClothingOuterHardsuitBrigmedic replaced in favour of corpsman's combat hardsuit; removing from standard filled locker to place in hardsuit filled locker.
- id: BoxSterileMask
- id: ClothingHeadHatBeretCorpsman # DeltaV - ClothingHeadHatBeretBrigmedic replaced in favour of corpsman beret.
## - id: ClothingOuterCoatAMG # DeltaV - removed until I can resprite it or replace it.
- id: ClothingUniformJumpsuitBrigmedic
- id: ClothingUniformJumpskirtBrigmedic
## - id: ClothingUniformJumpskirtOfLife # DeltaV - nah
## prob: 0.1
- id: HandheldGPSBasic # Delta V - added it for tracking the implant tracker pop up.
- id: MedkitFilled
- id: MedkitCombatFilled
prob: 0.6
- id: MedkitAdvancedFilled
prob: 0.4
- id: MedkitOxygenFilled
prob: 0.3
- id: MedkitBruteFilled
prob: 0.3
- id: MedkitToxinFilled
prob: 0.3
- id: MedkitBurnFilled
prob: 0.7
## - id: ClothingNeckCloakMoth #bzzz Moth-pocalypse # DeltaV - why
## prob: 0.15
- id: LunchboxSecurityFilledRandom # Delta-v Lunchboxes!
prob: 0.3
- id: BoxBodyBagPrisoner # WD
- id: ClothingEyesGlassesCorpsman # DeltaV - Corpsman Glasses
- id: WeaponDisabler
- id: TrackingImplanter
amount: 2
## - id: ClothingOuterHardsuitCombatCorpsman # DeltaV - ClothingOuterHardsuitBrigmedic replaced in favour of corpsman's combat hardsuit; removing from standard filled locker to place in hardsuit filled locker.
- id: BoxSterileMask
- id: ClothingHeadHatBeretCorpsman # DeltaV - ClothingHeadHatBeretBrigmedic replaced in favour of corpsman beret.
## - id: ClothingOuterCoatAMG # DeltaV - removed until I can resprite it or replace it.
- id: ClothingUniformJumpsuitBrigmedic
- id: ClothingUniformJumpskirtBrigmedic
## - id: ClothingUniformJumpskirtOfLife # DeltaV - nah
## prob: 0.1
- id: HandheldGPSBasic # Delta V - added it for tracking the implant tracker pop up.
- id: MedkitFilled
- id: MedkitCombatFilled
prob: 0.6
- id: MedkitAdvancedFilled
prob: 0.4
- id: MedkitOxygenFilled
prob: 0.3
- id: MedkitBruteFilled
prob: 0.3
- id: MedkitToxinFilled
prob: 0.3
- id: MedkitBurnFilled
prob: 0.7
## - id: ClothingNeckCloakMoth #bzzz Moth-pocalypse # DeltaV - why
## prob: 0.15
- id: LunchboxSecurityFilledRandom # Delta-v Lunchboxes!
prob: 0.3
- id: BoxBodyBagPrisoner # WD
# DeltaV - adding corpsman locker w/ hardsuit
@@ -133,31 +136,31 @@
components:
- type: StorageFill
contents:
- id: ClothingEyesGlassesCorpsman # DeltaV - Corpsman Glasses
- id: WeaponDisabler
- id: TrackingImplanter
amount: 2
- id: ClothingOuterHardsuitBrigmedic # WWDP edit
- id: OxygenTankFilled # WWDP edit
- id: ClothingUniformJumpsuitBrigmedic
- id: ClothingUniformJumpskirtBrigmedic
- id: HandheldGPSBasic # Added GPS because I just think it should be there tbh.
- id: MedkitFilled
- id: MedkitCombatFilled
prob: 0.6
- id: MedkitAdvancedFilled
prob: 0.4
- id: MedkitOxygenFilled
prob: 0.3
- id: MedkitBruteFilled
prob: 0.3
- id: MedkitToxinFilled
prob: 0.3
- id: MedkitBurnFilled
prob: 0.7
- id: LunchboxSecurityFilledRandom # Delta-v Lunchboxes!
prob: 0.3
- id: BoxBodyBagPrisoner # WD
- id: ClothingEyesGlassesCorpsman # DeltaV - Corpsman Glasses
- id: WeaponDisabler
- id: TrackingImplanter
amount: 2
- id: ClothingOuterHardsuitBrigmedic # WWDP edit
- id: OxygenTankFilled # WWDP edit
- id: ClothingUniformJumpsuitBrigmedic
- id: ClothingUniformJumpskirtBrigmedic
- id: HandheldGPSBasic # Added GPS because I just think it should be there tbh.
- id: MedkitFilled
- id: MedkitCombatFilled
prob: 0.6
- id: MedkitAdvancedFilled
prob: 0.4
- id: MedkitOxygenFilled
prob: 0.3
- id: MedkitBruteFilled
prob: 0.3
- id: MedkitToxinFilled
prob: 0.3
- id: MedkitBurnFilled
prob: 0.7
- id: LunchboxSecurityFilledRandom # Delta-v Lunchboxes!
prob: 0.3
- id: BoxBodyBagPrisoner # WD
# DeltaV - end addition
@@ -168,23 +171,23 @@
components:
- type: StorageFill
contents:
- id: ClothingEyesHudSecurity
prob: 0.3
- id: ClothingHeadHatFedoraBrown
- id: ClothingNeckTieDet
- id: ClothingOuterVestDetective
- id: ClothingOuterCoatDetective
- id: FlashlightSeclite
- id: ForensicScanner
- id: LogProbeCartridge
- id: BoxForensicPad
- id: DrinkDetFlask
- id: ClothingHandsGlovesForensic
- id: RubberStampDetective
- id: HoloprojectorSecurity
- id: BoxEvidenceMarkers
- id: LunchboxSecurityFilledRandom # Delta-v Lunchboxes!
prob: 0.3
- id: ClothingEyesHudSecurity
prob: 0.3
- id: ClothingHeadHatFedoraBrown
- id: ClothingNeckTieDet
- id: ClothingOuterVestDetective
- id: ClothingOuterCoatDetective
- id: FlashlightSeclite
- id: ForensicScanner
- id: LogProbeCartridge
- id: BoxForensicPad
- id: DrinkDetFlask
- id: ClothingHandsGlovesForensic
- id: RubberStampDetective
- id: HoloprojectorSecurity
- id: BoxEvidenceMarkers
- id: LunchboxSecurityFilledRandom # Delta-v Lunchboxes!
prob: 0.3
- type: entity
id: ClosetBombFilled
@@ -193,15 +196,15 @@
components:
- type: StorageFill
contents:
- id: ClothingHeadHelmetBombSuit
- id: ClothingOuterSuitBomb
# NT is cheap, what can you do...
- id: Wirecutter
prob: 0.9
- id: Screwdriver
prob: 0.9
- id: Multitool
prob: 0.5
- id: ClothingHeadHelmetBombSuit
- id: ClothingOuterSuitBomb
# NT is cheap, what can you do...
- id: Wirecutter
prob: 0.9
- id: Screwdriver
prob: 0.9
- id: Multitool
prob: 0.5
- type: entity
parent: GunSafe

View File

@@ -22,6 +22,38 @@
sound: /Audio/SimpleStation14/Items/Handling/book_drop.ogg
- type: EmitSoundOnLand
sound: /Audio/SimpleStation14/Items/Handling/book_drop.ogg
- type: ExtendDescription
descriptionList:
- description: "revolutionarymanifesto-extenddescription-headrevolutionary"
fontSize: 12
color: "#880000"
requireDetailRange: false
requirements:
- !type:CharacterAntagonistRequirement
antagonists:
- HeadRev
- description: "revolutionarymanifesto-extenddescription-revolutionary"
fontSize: 12
color: "#880000"
requireDetailRange: false
requirements:
- !type:CharacterAntagonistRequirement
antagonists:
- Rev
- description: "revolutionarymanifesto-extenddescription-security"
fontSize: 12
color: "#ff0000"
requireDetailRange: true
requirements:
- !type:CharacterDepartmentRequirement
departments:
- Security
- description: "revolutionarymanifesto-extenddescription-mindshield"
fontSize: 12
color: "#11aaff"
requireDetailRange: true
requirements:
- !type:CharacterMindshieldRequirement
- type: GuideHelp
guides:
- Revolutionaries

View File

@@ -603,6 +603,16 @@
steps: 5
zeroVisible: true
- type: Appearance
- type: ExtendDescription
descriptionList:
- description: "civiliandisabler-extenddescription-security"
fontSize: 12
color: "#ff0000"
requireDetailRange: true
requirements:
- !type:CharacterDepartmentRequirement
departments:
- Security
- type: MeleeWeapon
damage:
types:

View File

@@ -13,7 +13,7 @@
tags:
- Stunbaton
- type: Stunbaton
energyPerUse: 50 # WD EDIT
energyPerUse: 50
- type: ItemToggle
predictable: false
soundActivate:
@@ -64,7 +64,7 @@
- type: Appearance
- type: GenericVisualizer
visuals:
enum.ToggleVisuals.Toggled:
enum.ToggleVisuals.Toggled:
enum.ToggleVisuals.Layer:
True: {state: stunbaton_on}
False: {state: stunbaton_off}
@@ -90,6 +90,8 @@
- type: GuideHelp
guides:
- Security
- type: MartialArtBlocked # Goobstation - Martial Arts
form: CorporateJudo # Goobstation - Martial Arts
- type: KnockdownOnHit # WD EDIT
- type: entity
@@ -139,67 +141,49 @@
id: Flash
description: An ultrabright flashbulb with a trigger, which causes the victim to be dazed and lose their eyesight for a moment. Useless when burnt out.
components:
- type: Sprite
sprite: Objects/Weapons/Melee/flash.rsi
layers:
- state: flash
map: [ "enum.FlashVisuals.BaseLayer" ]
- state: flashing
map: [ "enum.FlashVisuals.LightLayer" ]
visible: false
shader: unshaded
- type: Flash
- type: ExtendDescription
descriptionList:
- description: "flash-extenddescription-headrevolutionary"
fontSize: 12
color: "#880000"
requireDetailRange: false
requirements:
- !type:CharacterAntagonistRequirement
antagonists:
- HeadRev
- description: "flash-extenddescription-revolutionary"
fontSize: 12
color: "#880000"
requireDetailRange: false
requirements:
- !type:CharacterAntagonistRequirement
antagonists:
- Rev
- type: LimitedCharges
maxCharges: 5
charges: 5
- type: MeleeWeapon
canBeBlocked: false # WD EDIT
wideAnimationRotation: 180
damage:
types:
Blunt: 0 # melee weapon to allow flashing individual targets
angle: 10
- type: Item
size: Small
sprite: Objects/Weapons/Melee/flash.rsi
# - type: DynamicPrice
# price: 40
- type: ReverseEngineering # Nyano
recipes:
- Flash
- type: StaticPrice
price: 40
- type: Appearance
- type: GenericVisualizer
visuals:
enum.FlashVisuals.Burnt:
enum.FlashVisuals.BaseLayer:
True: {state: burnt}
enum.FlashVisuals.Flashing:
enum.FlashVisuals.LightLayer:
True: {visible: true}
False: {visible: false}
- type: GuideHelp
guides:
- Security
- type: Sprite
sprite: Objects/Weapons/Melee/flash.rsi
layers:
- state: flash
map: [ "enum.FlashVisuals.BaseLayer" ]
- state: flashing
map: [ "enum.FlashVisuals.LightLayer" ]
visible: false
shader: unshaded
- type: Flash
- type: LimitedCharges
maxCharges: 5
charges: 5
- type: MeleeWeapon
canBeBlocked: false # WD EDIT
wideAnimationRotation: 180
damage:
types:
Blunt: 0 # melee weapon to allow flashing individual targets
angle: 10
- type: Item
size: Small
sprite: Objects/Weapons/Melee/flash.rsi
# - type: DynamicPrice
# price: 40
- type: ReverseEngineering # Nyano
recipes:
- Flash
- type: StaticPrice
price: 40
- type: Appearance
- type: GenericVisualizer
visuals:
enum.FlashVisuals.Burnt:
enum.FlashVisuals.BaseLayer:
True: {state: burnt}
enum.FlashVisuals.Flashing:
enum.FlashVisuals.LightLayer:
True: {visible: true}
False: {visible: false}
- type: GuideHelp
guides:
- Security
- type: entity
name: flash
@@ -207,9 +191,9 @@
suffix: 2 charges
id: SciFlash
components:
- type: LimitedCharges
maxCharges: 2
charges: 2
- type: LimitedCharges
maxCharges: 2
charges: 2
- type: entity
name: portable flasher
@@ -217,44 +201,44 @@
id: PortableFlasher
description: An ultrabright flashbulb with a proximity trigger, useful for making an area security-only.
components:
- type: EmitSoundOnTrigger
sound:
path: /Audio/Weapons/flash.ogg
- type: FlashOnTrigger
range: 3
- type: TriggerOnProximity
enabled: true
cooldown: 5
shape:
!type:PhysShapeCircle
radius: 2
repeating: true
- type: Anchorable
- type: Sprite
sprite: Objects/Weapons/pflash.rsi
layers:
- state: "off"
map: ["enum.ProximityTriggerVisualLayers.Base"]
- type: InteractionOutline
- type: Physics
- type: Fixtures
fixtures:
fix1:
shape:
!type:PhysShapeAabb
bounds: "-0.15,-0.3,0.15,0.3"
mask:
- MachineMask
layer:
- MachineLayer
density: 380
- type: Appearance
- type: AnimationPlayer
- type: PointLight
energy: 2.0
radius: 0
softness: 0
enabled: true
- type: GuideHelp
guides:
- Security
- type: EmitSoundOnTrigger
sound:
path: /Audio/Weapons/flash.ogg
- type: FlashOnTrigger
range: 3
- type: TriggerOnProximity
enabled: true
cooldown: 5
shape:
!type:PhysShapeCircle
radius: 2
repeating: true
- type: Anchorable
- type: Sprite
sprite: Objects/Weapons/pflash.rsi
layers:
- state: "off"
map: ["enum.ProximityTriggerVisualLayers.Base"]
- type: InteractionOutline
- type: Physics
- type: Fixtures
fixtures:
fix1:
shape:
!type:PhysShapeAabb
bounds: "-0.15,-0.3,0.15,0.3"
mask:
- MachineMask
layer:
- MachineLayer
density: 380
- type: Appearance
- type: AnimationPlayer
- type: PointLight
energy: 2.0
radius: 0
softness: 0
enabled: true
- type: GuideHelp
guides:
- Security

View File

@@ -14,7 +14,7 @@
security: Structures/Doors/Airlocks/Standard/security.rsi
virology: Structures/Doors/Airlocks/Standard/virology.rsi
justice: DeltaV/Structures/Doors/Airlocks/Standard/justice.rsi # Delta V - Add Justice Dept
roboticist: DeltaV/Structures/Doors/Airlocks/Glass/roboticist.rsi #Added Roboticist Role
roboticist: DeltaV/Structures/Doors/Airlocks/Standard/roboticist.rsi # Added Roboticist Role
- type: AirlockGroup
id: Glass

View File

@@ -12,7 +12,6 @@
weights:
CaptainIDStealObjective: 1
CMOHyposprayStealObjective: 1
PowerGlovesStealObjective: 1 # WWDP warden's CQC gloves
CMOCrewMonitorStealObjective: 1
RDHardsuitStealObjective: 1
NukeDiskStealObjective: 1

View File

@@ -3,7 +3,7 @@
result: SheetSteel1
applyMaterialDiscount: false
miningPoints: 1
completetime: 0
completetime: 0.0083 # 0.25 per 30
materials:
RawIron: 100
Coal: 30
@@ -20,8 +20,8 @@
id: SheetGlass1
result: SheetGlass1
applyMaterialDiscount: false
miningPoints: 1
completetime: 0
miningPoints: 1 # 0.25 per 30
completetime: 0.0083
materials:
RawQuartz: 100
@@ -37,7 +37,7 @@
result: SheetRGlass1
applyMaterialDiscount: false
miningPoints: 1
completetime: 0
completetime: 0.025 # 0.75 per 30
materials:
Glass: 100
Steel: 50
@@ -55,7 +55,7 @@
- type: latheRecipe
id: SheetPGlass1
result: SheetPGlass1
completetime: 0
completetime: 0.0083 # 0.75 per 30
miningPoints: 16
materials:
RawQuartz: 100
@@ -72,7 +72,7 @@
- type: latheRecipe
id: SheetRPGlass1
result: SheetRPGlass1
completetime: 0
completetime: 0.025 # 0.75 per 30
miningPoints: 16
materials:
RawQuartz: 100
@@ -93,7 +93,7 @@
- type: latheRecipe
id: SheetPlasma1
result: SheetPlasma1
completetime: 0
completetime: 0.0116 # 0.35 per 30
miningPoints: 15
materials:
RawPlasma: 100
@@ -108,7 +108,7 @@
- type: latheRecipe
id: SheetPlasteel1
result: SheetPlasteel1
completetime: 0
completetime: 0.025 # 0.75 per 30
miningPoints: 17
materials:
RawPlasma: 100
@@ -134,7 +134,7 @@
- type: latheRecipe
id: SheetUGlass1
result: SheetUGlass1
completetime: 0
completetime: 0.0083 # 0.25 per 30
miningPoints: 31
materials:
RawUranium: 100
@@ -151,7 +151,7 @@
- type: latheRecipe
id: SheetRUGlass1
result: SheetRUGlass1
completetime: 0
completetime: 0.025 # 0.75 per 30
miningPoints: 31
materials:
RawUranium: 100
@@ -193,7 +193,7 @@
- type: latheRecipe
id: MaterialDiamond
result: MaterialDiamond1
completetime: 0
completetime: 0.026 # 0.8 over 30
miningPoints: 50
materials:
RawDiamond: 100
@@ -201,7 +201,7 @@
- type: latheRecipe
id: SheetUranium1
result: SheetUranium1
completetime: 0
completetime: 0.02 # 0.6 seconds for 30
miningPoints: 30
materials:
RawUranium: 100
@@ -209,7 +209,7 @@
- type: latheRecipe
id: IngotGold1
result: IngotGold1
completetime: 0
completetime: 0.016 # 0.5 over 30
miningPoints: 18
materials:
RawGold: 100
@@ -217,7 +217,7 @@
- type: latheRecipe
id: IngotSilver1
result: IngotSilver1
completetime: 0
completetime: 0.013 # 0.4 over 30
miningPoints: 16
materials:
RawSilver: 100
@@ -233,7 +233,7 @@
- type: latheRecipe
id: MaterialBananium1
result: MaterialBananium1
completetime: 0
completetime: 0.1 # 1 over 10
miningPoints: 60
materials:
RawBananium: 100
@@ -291,7 +291,7 @@
result: IngotTungsten1
applyMaterialDiscount: false
miningPoints: 5
completetime: 4
completetime: 0.13 # Or, 4 seconds over 30 ingots.
materials:
RawTungsten: 100
RawIron: 100

View File

@@ -21,6 +21,8 @@
- !type:AddComponentSpecial
components:
- type: ProfessionalChef #Nyano - End Summary.
- type: GrantCqc # Goobstation - Martial Arts
isBlocked: true # Goobstation - Martial Arts
- type: startingGear
id: ChefGear

View File

@@ -0,0 +1,81 @@
# Unlimited, these are targets for shuttles to dock to.
- type: entity
parent: AirlockGlassShuttle
id: AirlockExternalGlassShuttleCargoShuttle
suffix: External, Cargo, Glass, Docking
components:
- type: PriorityDock
tag: DockCargoShuttle
- type: entity
parent: AirlockGlassShuttle
id: AirlockExternalGlassShuttleDart
suffix: External, ERT, Glass, Docking
components:
- type: PriorityDock
tag: DockDart
- type: entity
parent: AirlockSyndicateGlass
id: AirlockSyndicateGlassShuttleInfiltrator
suffix: External, Infiltrator, Glass, Docking
components:
- type: PriorityDock
tag: DockInfiltrator
- type: entity
parent: AirlockGlassShuttle
id: AirlockExternalGlassShuttlePathfinder
suffix: External, Salvage, Glass, Docking
components:
- type: PriorityDock
tag: DockPathfinder
- type: entity
parent: AirlockSyndicateGlass
id: AirlockSyndicateGlassShuttleSANDropship
suffix: External, SAN Dropship, Glass, Docking
components:
- type: PriorityDock
tag: DockSANDropship
# 1 per map, these spawn a shuttle.
- type: entity
parent: AirlockExternalGlassShuttleCargoShuttle
id: AirlockExternalGlassShuttleCargoShuttleFilled
suffix: Cargo Shuttle Spawner, MAX ONE PER MAP
components:
- type: GridFill
path: /Maps/Shuttles/cargo.yml
- type: entity
parent: AirlockExternalGlassShuttleDart
id: AirlockExternalGlassShuttleDartFilled
suffix: ERT Ship Spawner, MAX ONE PER MAP
components:
- type: GridFill
path: /Maps/Shuttles/dart.yml
- type: entity
parent: AirlockSyndicateGlassShuttleInfiltrator
id: AirlockSyndicateGlassShuttleInfiltratorFilled
suffix: Infiltrator Ship Spawner, MAX ONE PER MAP
components:
- type: GridFill
path: /Maps/Shuttles/infiltrator.yml
- type: entity
parent: AirlockExternalGlassShuttlePathfinder
id: AirlockExternalGlassShuttlePathfinderFilled
suffix: Salvage Ship Spawner, MAX ONE PER MAP
components:
- type: GridFill
path: /Maps/Shuttles/pathfinder.yml
- type: entity
parent: AirlockSyndicateGlassShuttleSANDropship
id: AirlockSyndicateGlassShuttleSANDropshipFilled
suffix: SAN Dropship Spawner, MAX ONE PER MAP
components:
- type: GridFill
path: /Maps/Shuttles/ShuttleEvent/SANDropship.yml

View File

@@ -0,0 +1,14 @@
- type: Tag
id: DockCargoShuttle
- type: Tag
id: DockDart
- type: Tag
id: DockInfiltrator
- type: Tag
id: DockPathfinder
- type: Tag
id: DockSANDropship

View File

@@ -37,6 +37,39 @@
tags:
- NukeOpsUplink
# Martial Arts
- type: listing
id: UplinkCQCManual
name: uplink-cqc-name
description: uplink-cqc-desc
productEntity: CQCManual
# WD EDIT START
discountCategory: veryRareDiscounts
discountDownTo:
Telecrystal: 3
cost:
Telecrystal: 12
# WD EDIT END
categories:
- UplinkDeception
- type: listing
id: UplinkMysteriousScroll
name: uplink-sleeping-carp-name
description: uplink-sleeping-carp-desc
productEntity: SleepingCarpScroll
# WD EDIT START
discountCategory: veryRareDiscounts
discountDownTo:
Telecrystal: 3
cost:
Telecrystal: 14
# WD EDIT END
categories:
- UplinkWeaponry
# Misc
- type: listing
id: UplinkHardsuitCybersunStealth
name: uplink-hardsuit-cybersun-stealth-name

View File

@@ -0,0 +1,33 @@
- type: entity
parent: ClothingBeltSecurity
id: ClothingBeltCorporateJudo
name: Corporate Judo Belt
description: Teaches the wearer NT Corporate Judo.
components:
- type: Sprite
sprite: _Goobstation/Clothing/Belt/judobelt.rsi
- type: Clothing
sprite: _Goobstation/Clothing/Belt/judobelt.rsi
- type: GrantCorporateJudo
- type: Storage
whitelist:
tags:
- CigPack
- Taser
- SecBeltEquip
- Radio
- Sidearm
- MagazinePistol
- MagazineMagnum
- CombatKnife
- Truncheon
components:
- FlashOnTrigger
- SmokeOnTrigger
- Flash
- Handcuff
- BallisticAmmoProvider
- CartridgeAmmo
- DoorRemote
- Whistle
- BalloonPopper

View File

@@ -0,0 +1,16 @@
- type: entity
parent: ClothingHandsBase
id: ClothingHandsGlovesKravMaga
name: Krav Maga gloves
description: These gloves can teach you to perform Krav Maga using nanochips for as long as you're wearing them.
components:
- type: Sprite
sprite: _Goobstation/Clothing/Hands/Gloves/kravgloves.rsi
- type: Item
- type: Clothing
- type: Fiber
fiberMaterial: fibers-leather
fiberColor: fibers-red
- type: ClothingGrantComponent
component:
- type: KravMaga

View File

@@ -0,0 +1,15 @@
- type: entity
parent: BaseItem
id: BSOManual
name: Nanotrasen CQC Manual
description: A small, blue manual. There are drawn instructions of tactical hand-to-hand combat.
components:
- type: Sprite
sprite: _Goobstation/Objects/Misc/cqc_manual.rsi
layers:
- state: icon-alt
- type: Item
size: Small
- type: StaticPrice
price: 3000
- type: GrantCqc

View File

@@ -0,0 +1,32 @@
- type: entity
parent: BaseItem
id: CQCManual
name: old manual
description: A small, black manual. There are drawn instructions of tactical hand-to-hand combat.
components:
- type: Sprite
sprite: _Goobstation/Objects/Misc/cqc_manual.rsi
layers:
- state: icon
- type: Item
size: Small
- type: StaticPrice
price: 3000
- type: GrantCqc
- type: entity
parent: BaseItem
id: SleepingCarpScroll
name: mysterious scroll
description: A scroll filled with strange markings. It seems to be drawings of some sort of martial art.
components:
- type: Sprite
sprite: _Goobstation/Wizard/Objects/scroll.rsi
layers:
- state: scroll2
- type: Item
sprite: _Goobstation/Wizard/Objects/scroll.rsi
size: Small
- type: StaticPrice
price: 3000
- type: GrantSleepingCarp

View File

@@ -0,0 +1,64 @@
- type: martialArt
id: SleepingCarp
martialArtsForm: SleepingCarp
roundstartCombos: SleepingCarpMoves
randomDamageModifier: True
baseDamageModifier: 5
randomSayings:
- carp-saying-huah
- carv-saying-hya
- carp-saying-choo
- carp-saying-wuo
- carp-saying-kya
- carp-saying-huh
- carp-saying-hiyoh
- carp-saying-strike
- carp-saying-bite
randomSayingsDowned:
- carp-saying-banzai
- carp-saying-kiya
- carp-saying-omae
- carp-saying-see
- carp-saying-time
- carp-saying-cowabunga
- type: comboList
id: SleepingCarpMoves
combos:
- SleepingCarpGnashingTeeth
- SleepingCarpKneeHaul
- SleepingCarpCrashingWaves
- type: combo
id: SleepingCarpGnashingTeeth
name: Gnashing Teeth
martialArtsForm: SleepingCarp
attacks:
- Harm
- Harm
event: !type:SleepingCarpGnashingTeethPerformedEvent
damageType: Slash
extraDamage: 20
- type: combo
id: SleepingCarpKneeHaul
name: Knee Haul
martialArtsForm: SleepingCarp
attacks:
- Harm
- Grab
event: !type:SleepingCarpKneeHaulPerformedEvent
extraDamage: 10
paralyzeTime: 6
staminaDamage: 60
- type: combo
id: SleepingCarpCrashingWaves
name: Crashing Waves
martialArtsForm: SleepingCarp
attacks:
- Harm
- Disarm
event: !type:SleepingCarpCrashingWavesPerformedEvent
staminaDamage: 25

View File

@@ -0,0 +1,69 @@
- type: martialArt
id: CloseQuartersCombat
martialArtsForm: CloseQuartersCombat
roundstartCombos: CQCMoves
- type: comboList
id: CQCMoves
combos:
- CQCConsecutive
- CQCPressure
- CQCRestrain
- CQCKick
- CQCSlam
- type: combo
id: CQCSlam
name: Slam
martialArtsForm: CloseQuartersCombat
attacks:
- Grab
- Harm
event: !type:CqcSlamPerformedEvent { }
extraDamage: 10 #
paralyzeTime: 12 # in seconds
- type: combo
id: CQCKick
name: Kick
martialArtsForm: CloseQuartersCombat
attacks:
- Harm
- Harm
event: !type:CqcKickPerformedEvent
extraDamage: 10
staminaDamage: 55
- type: combo
id: CQCRestrain
name: Restrain
martialArtsForm: CloseQuartersCombat
attacks:
- Grab
- Grab
event: !type:CqcRestrainPerformedEvent
paralyzeTime: 10
staminaDamage: 30
- type: combo
id: CQCPressure
name: Pressure
martialArtsForm: CloseQuartersCombat
attacks:
- Disarm
- Grab
event: !type:CqcPressurePerformedEvent
staminaDamage: 65
- type: combo
id: CQCConsecutive
name: Consecutive
martialArtsForm: CloseQuartersCombat
attacks:
- Disarm
- Disarm
- Harm
event: !type:CqcConsecutivePerformedEvent
extraDamage: 10
staminaDamage: 70

View File

@@ -0,0 +1,45 @@
- type: martialArt
id: CorporateJudo
martialArtsForm: CorporateJudo
roundstartCombos: CorporateJudoMoves
baseDamageModifier: 5
- type: comboList
id: CorporateJudoMoves
combos:
- JudoThrow
- JudoEyepoke
- JudoArmbar
- type: combo
id: JudoThrow
name: Judo Throw
martialArtsForm: CorporateJudo
attacks:
- Grab
- Disarm
event: !type:JudoThrowPerformedEvent
staminaDamage: 50
paralyzeTime: 7
- type: combo
id: JudoArmbar
name: Armbar
martialArtsForm: CorporateJudo
attacks:
- Disarm
- Disarm
- Grab
event: !type:JudoArmbarPerformedEvent
staminaDamage: 45
paralyzeTime: 5
- type: combo
id: JudoEyepoke
name: Eyepoke
martialArtsForm: CorporateJudo
attacks:
- Disarm
- Harm
event: !type:JudoEyePokePerformedEvent
extraDamage: 5

View File

@@ -0,0 +1,50 @@
- type: entity
id: ActionLegSweep
name: Leg Sweep
description: Sweeps the legs out from under a target to knock them down for a few seconds.
categories: [ HideSpawnMenu ]
components:
- type: InstantAction
icon:
sprite: _Goobstation/Actions/kravmaga.rsi
state: legsweep
useDelay: 3
event: !type:KravMagaActionEvent { }
- type: KravMagaAction
configuration: LegSweep
name: Leg Sweep
- type: entity
id: ActionNeckChop
name: Neck Chop
description: A hard swing with the side of your hand deals some damage and disables the target's ability to speak for twenty seconds
categories: [ HideSpawnMenu ]
components:
- type: InstantAction
icon:
sprite: _Goobstation/Actions/kravmaga.rsi
state: neckchop
useDelay: 3
event: !type:KravMagaActionEvent { }
- type: KravMagaAction
configuration: NeckChop
name: Neck Chop
effectTime: 20
- type: entity
id: ActionLungPunch
name: Lung Punch
description: Deals 30 stamina damage and prevents breathing for ten seconds. Four of those will incapacitate a target less-than-lethally, but beware of the loss of breath you cause.
categories: [ HideSpawnMenu ]
components:
- type: InstantAction
icon:
sprite: _Goobstation/Actions/kravmaga.rsi
state: lungpunch
useDelay: 3
event: !type:KravMagaActionEvent { }
- type: KravMagaAction
configuration: LungPunch
name: Lung Punch
staminaDamage: 40
effectTime: 10

View File

@@ -3,14 +3,14 @@
result: N14IngotLead1
miningPoints: 10
applyMaterialDiscount: false
completetime: 2
completetime: 0.01 # 0.3 per 30
materials:
RawLead: 100
- type: latheRecipe
id: IngotCopper1
result: IngotCopper1
completetime: 2
completetime: 0.016 # 0.5 per 30
miningPoints: 10
materials:
RawCopper: 100
@@ -19,7 +19,7 @@
- type: latheRecipe
id: N14IngotAluminium1
result: N14IngotAluminium1
completetime: 2
completetime: 0.016 # 0.5 per 30
miningPoints: 10
materials:
RawAluminium: 100

View File

@@ -19,31 +19,3 @@
sprite: _White/Clothing/Hands/sheriff_gloves.rsi
- type: Clothing
sprite: _White/Clothing/Hands/sheriff_gloves.rsi
- type: entity
parent: ClothingHandsGlovesPowerglove
id: ClothingHandsGlovesPowergloveReal # WWDP HIGH RISK WARDEN GLOVES (fake krav magas)
name: power gloves
suffix: Powered
description: Now I'm playin' with power! BAM!
components:
- type: Insulated
- type: MeleeWeapon
autoAttack: true
attackRate: 0.5
canHeavyAttack: false
maxTargets: 1
damage:
types:
Blunt: 5
soundHit:
collection: Punch
animation: WeaponArcFist
mustBeEquippedToUse: true
- type: StaminaDamageOnHit
damage: 25
- type: Tag
tags:
- HighRiskItem
- type: StealTarget
stealGroup: WardenPowerGloves

View File

@@ -1,6 +0,0 @@
- type: stealTargetGroup
id: WardenPowerGloves
name: steal-target-power-gloves
sprite:
sprite: Clothing/Hands/Gloves/powerglove.rsi
state: icon

View File

@@ -1,11 +0,0 @@
- type: entity
categories: [HideSpawnMenu]
parent: BaseTraitorStealObjective
id: PowerGlovesStealObjective
components:
- type: NotJobRequirement
job: Warden
- type: StealCondition
stealGroup: WardenPowerGloves
verifyMapExistence: true
owner: job-name-warden

View File

@@ -23,4 +23,4 @@
"directions": 4
}
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

View File

@@ -0,0 +1,20 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from Paradise at https://github.com/ParadiseSS13/Paradise/blob/a5726efe0c300662cb6f3a4a12d9151aa3eabe78/icons/mob/actions/actions.dmi",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "legsweep"
},
{
"name": "lungpunch"
},
{
"name": "neckchop"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 B

View File

@@ -0,0 +1,18 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from Paradise at https://github.com/ParadiseSS13/Paradise/blob/7da09f5e7230369ec48d9e5d31bdddb3f95a6d8f/icons/mob/clothing/belt.dmi, https://github.com/ParadiseSS13/Paradise/blob/7da09f5e7230369ec48d9e5d31bdddb3f95a6d8f/icons/obj/clothing/belts.dmi",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "equipped-BELT",
"directions": 4
},
{
"name": "icon"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

View File

@@ -0,0 +1,18 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from Paradise at https://github.com/ParadiseSS13/Paradise/blob/bd91a1b962fe9bd38e346e9fafd4ebb10784fcb3/icons/mob/clothing/hands.dmi and https://github.com/ParadiseSS13/Paradise/blob/bd91a1b962fe9bd38e346e9fafd4ebb10784fcb3/icons/obj/clothing/gloves.dmi and https://github.com/ParadiseSS13/Paradise/blob/HEAD/icons/mob/clothing/hands.dmi ",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon"
},
{
"name": "equipped-HAND",
"directions": 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

View File

@@ -0,0 +1,17 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Sprite taken at: https://github.com/tgstation/tgstation",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon"
},
{
"name": "icon-alt"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

View File

@@ -0,0 +1,29 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/80ab61c8c7741e1d95e5f168357a9e6e61b38f2c",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "scroll"
},
{
"name": "scroll2"
},
{
"name": "equipped-BELT",
"directions": 4
},
{
"name": "inhand-left",
"directions": 4
},
{
"name": "inhand-right",
"directions": 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B