Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Headcrabs #190

Merged
merged 38 commits into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
1d16ddf
[Feature] Headcrabs
PuroSlavKing Dec 23, 2024
6897547
huh
PuroSlavKing Dec 24, 2024
b9cb952
guidebook
PuroSlavKing Dec 24, 2024
6ef2678
ftl work
PuroSlavKing Dec 24, 2024
50e0a8c
fix rsi
PuroSlavKing Dec 24, 2024
ef5c81d
fixes
PuroSlavKing Dec 24, 2024
74433ae
remove SVO
PuroSlavKing Dec 24, 2024
9ec3408
event announcement
PuroSlavKing Dec 24, 2024
fb48ed1
Update Content.Server/_White/Headcrab/HeadcrabComponent.cs
PuroSlavKing Dec 26, 2024
9e2e03b
Update Content.Server/_White/Headcrab/HeadcrabComponent.cs
PuroSlavKing Dec 26, 2024
23a2a3c
Update Content.Server/_White/Headcrab/HeadcrabComponent.cs
PuroSlavKing Dec 26, 2024
93dc9bb
Update Content.Server/_White/Headcrab/HeadcrabComponent.cs
PuroSlavKing Dec 26, 2024
af74dbc
Update Content.Server/_White/Headcrab/HeadcrabComponent.cs
PuroSlavKing Dec 26, 2024
c8a708f
Update Content.Server/_White/Headcrab/HeadcrabComponent.cs
PuroSlavKing Dec 26, 2024
5df19a6
Update Content.Server/_White/Headcrab/HeadcrabSystem.cs
PuroSlavKing Dec 26, 2024
92ea589
Update Content.Server/_White/Headcrab/HeadcrabSystem.cs
PuroSlavKing Dec 26, 2024
eb7063e
Update Content.Server/_White/Headcrab/HeadcrabComponent.cs
PuroSlavKing Dec 26, 2024
5b904f9
Update Content.Server/_White/Headcrab/HeadcrabSystem.cs
PuroSlavKing Dec 26, 2024
6287c35
Update Content.Server/_White/Headcrab/HeadcrabSystem.cs
PuroSlavKing Dec 26, 2024
7ed51e3
Update Content.Server/_White/Headcrab/HeadcrabSystem.cs
PuroSlavKing Dec 26, 2024
1d811f6
Update Content.Server/_White/Headcrab/HeadcrabSystem.cs
PuroSlavKing Dec 26, 2024
26b73b2
Update Content.Server/_White/Headcrab/HeadcrabSystem.cs
PuroSlavKing Dec 26, 2024
144fd11
Update Content.Server/_White/Headcrab/HeadcrabSystem.cs
PuroSlavKing Dec 26, 2024
31b7935
Update Content.Server/_White/Headcrab/HeadcrabSystem.cs
PuroSlavKing Dec 26, 2024
b1bf8bd
ok
PuroSlavKing Dec 26, 2024
d9c8d7b
fixiki
PuroSlavKing Dec 26, 2024
d054e4a
edit action
PuroSlavKing Dec 26, 2024
af4a2af
aghost safe
PuroSlavKing Dec 26, 2024
0d6f7ae
better action
PuroSlavKing Dec 26, 2024
36daeff
remove duplicate
PuroSlavKing Dec 26, 2024
9161ac3
Update Content.Server/_White/Headcrab/HeadcrabSystem.cs
PuroSlavKing Dec 26, 2024
55b0b49
Update Content.Server/_White/Headcrab/HeadcrabSystem.cs
PuroSlavKing Dec 26, 2024
84104ee
Update Content.Server/_White/Headcrab/HeadcrabSystem.cs
PuroSlavKing Dec 26, 2024
83924e6
Update Content.Server/_White/Headcrab/HeadcrabSystem.cs
PuroSlavKing Dec 26, 2024
2a8bc4f
Update Content.Server/_White/Headcrab/HeadcrabSystem.cs
PuroSlavKing Dec 26, 2024
6f82c7e
Update Content.Server/_White/Headcrab/HeadcrabSystem.cs
PuroSlavKing Dec 26, 2024
ceadd97
Update Content.Server/_White/Headcrab/HeadcrabSystem.cs
PuroSlavKing Dec 26, 2024
c0dab27
Update Content.Server/_White/Headcrab/HeadcrabSystem.cs
PuroSlavKing Dec 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions Content.Server/_White/Headcrab/HeadcrabComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Content.Shared.Damage;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;

namespace Content.Server._White.Headcrab;

[Access(typeof(HeadcrabSystem))]
[RegisterComponent]
public sealed partial class HeadcrabComponent : Component
{
/// <summary>
/// WorldTargetAction
/// </summary>
[DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string JumpAction = "ActionHeadcrabJump";

[DataField]
public float ParalyzeTime = 3f;

[DataField]
public int ChancePounce = 33;

[DataField(required: true)]
public DamageSpecifier Damage = default!;

public EntityUid EquippedOn;

[ViewVariables]
public float Accumulator = 0;

[DataField]
public float DamageFrequency = 5;

[DataField]
public SoundSpecifier? JumpSound = new SoundPathSpecifier("/Audio/_White/Misc/Headcrab/headcrab_jump.ogg");

}
216 changes: 216 additions & 0 deletions Content.Server/_White/Headcrab/HeadcrabSystem.cs
PuroSlavKing marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
using System;
using System.Linq;
using Content.Server.Actions;
using Content.Server.NPC.Components;
using Content.Server.Nutrition.Components;
using Content.Server.Nutrition.EntitySystems;
using Content.Server.Popups;
using Content.Server.NPC.Systems;
using Content.Server._White.Headcrab;
using Content.Server.Zombies;
using Content.Shared.Zombies;
using Content.Shared.CombatMode;
using Content.Shared.Ghost;
using Content.Shared.Damage;
using Content.Shared.CombatMode.Pacification;
using Content.Shared.Hands;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Humanoid;
using Content.Shared.Nutrition.Components;
using Content.Shared._White.Headcrab;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Systems;
using Content.Shared.Mobs.Components;
using Content.Shared.Popups;
using Content.Shared.Stunnable;
using Content.Shared.Throwing;
using Content.Shared.Weapons.Melee.Events;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Player;
using Robust.Shared.Random;
PuroSlavKing marked this conversation as resolved.
Show resolved Hide resolved

namespace Content.Server._White.Headcrab;

public sealed partial class HeadcrabSystem : EntitySystem
{
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly SharedCombatModeSystem _combat = default!;
[Dependency] private readonly ThrowingSystem _throwing = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly ActionsSystem _action = default!;
[Dependency] private readonly IRobustRandom _random = default!;

public override void Initialize()
{
SubscribeLocalEvent<HeadcrabComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<HeadcrabComponent, MeleeHitEvent>(OnMeleeHit);
SubscribeLocalEvent<HeadcrabComponent, GotEquippedEvent>(OnGotEquipped);
PuroSlavKing marked this conversation as resolved.
Show resolved Hide resolved
SubscribeLocalEvent<HeadcrabComponent, GotUnequippedEvent>(OnGotUnequipped);
SubscribeLocalEvent<HeadcrabComponent, GotEquippedHandEvent>(OnGotEquippedHand);
SubscribeLocalEvent<HeadcrabComponent, BeingUnequippedAttemptEvent>(OnUnequipAttempt);
SubscribeLocalEvent<HeadcrabComponent, JumpActionEvent>(OnJump);
}

private void OnStartup(EntityUid uid, HeadcrabComponent component, ComponentStartup args)
{
_action.AddAction(uid, component.JumpAction);
}

private void OnGotEquipped(EntityUid uid, HeadcrabComponent component, GotEquippedEvent args)
PuroSlavKing marked this conversation as resolved.
Show resolved Hide resolved
{
if (args.Slot != "mask")
return;

component.EquippedOn = args.Equipee;
EnsureComp<PacifiedComponent>(uid);
PuroSlavKing marked this conversation as resolved.
Show resolved Hide resolved
}

private void OnUnequipAttempt(EntityUid uid, HeadcrabComponent component, BeingUnequippedAttemptEvent args)
{
if (args.Slot != "mask" ||
component.EquippedOn != args.Unequipee ||
HasComp<ZombieComponent>(args.Unequipee) ||
_mobState.IsDead(uid))
return;

_popup.PopupEntity(Loc.GetString("headcrab-try-unequip"),
args.Unequipee, args.Unequipee, PopupType.Large);
args.Cancel();
}

private void OnGotEquippedHand(EntityUid uid, HeadcrabComponent component, GotEquippedHandEvent args)
{
if (_mobState.IsDead(uid) || HasComp<ZombieComponent>(args.User))
return;

if (_mobState.IsDead(uid) ||
HasComp<ZombieComponent>(args.User) ||
HasComp<GhostComponent>(args.User))
return;

_handsSystem.TryDrop(args.User, uid, checkActionBlocker: false);
_damageableSystem.TryChangeDamage(args.User, component.Damage);
_popup.PopupEntity(Loc.GetString("headcrab-entity-bite"),
args.User, args.User);
}

private void OnGotUnequipped(EntityUid uid, HeadcrabComponent component, GotUnequippedEvent args)
{
if (args.Slot != "mask")
return;

component.EquippedOn = EntityUid.Invalid;
RemCompDeferred<PacifiedComponent>(uid);
var combatMode = EnsureComp<CombatModeComponent>(uid);
_combat.SetInCombatMode(uid, true, combatMode);
EnsureComp<NPCMeleeCombatComponent>(uid);
PuroSlavKing marked this conversation as resolved.
Show resolved Hide resolved
}

private void OnMeleeHit(EntityUid uid, HeadcrabComponent component, MeleeHitEvent args)
{
if (!args.HitEntities.Any())
return;

foreach (var entity in args.HitEntities)
{
if (!HasComp<HumanoidAppearanceComponent>(entity))
return;

if (TryComp(entity, out MobStateComponent? mobState))
{
if (mobState.CurrentState is not MobState.Alive)
{
return;
}
}
Spatison marked this conversation as resolved.
Show resolved Hide resolved

_inventory.TryGetSlotEntity(entity, "head", out var headItem);
if (HasComp<IngestionBlockerComponent>(headItem))
return;

var shouldEquip = _random.Next(1, 101) <= component.ChancePounce;
if (!shouldEquip)
return;

var equipped = _inventory.TryEquip(entity, uid, "mask", true);
if (!equipped)
return;
PuroSlavKing marked this conversation as resolved.
Show resolved Hide resolved

if (_mobState.IsDead(uid) ||
HasComp<ZombieComponent>(entity) ||
!HasComp<HumanoidAppearanceComponent>(entity) ||
!_mobState.IsAlive(entity))
return;

_inventory.TryGetSlotEntity(entity, "head", out var headItem2);
if (HasComp<IngestionBlockerComponent>(headItem2) ||
!_inventory.TryEquip(entity, uid, "mask", true))
return;

component.EquippedOn = entity;

EnsureComp<PacifiedComponent>(uid);
_stunSystem.TryParalyze(entity, TimeSpan.FromSeconds(component.ParalyzeTime), true);
_damageableSystem.TryChangeDamage(entity, component.Damage, origin: entity);
break;
}
PuroSlavKing marked this conversation as resolved.
Show resolved Hide resolved
}

private void OnJump(EntityUid uid, HeadcrabComponent component, JumpActionEvent args)
{
if (args.Handled || component.EquippedOn is { Valid: true })
return;

args.Handled = true;
var xform = Transform(uid);
var mapCoords = _transform.ToMapCoordinates(args.Target);
var direction = mapCoords.Position - _transform.GetMapCoordinates(xform).Position;

_throwing.TryThrow(uid, direction, 7F, uid, 10F);
if (component.JumpSound != null)
_audioSystem.PlayPvs(component.JumpSound, uid, component.JumpSound.Params);
}

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

var query = EntityQueryEnumerator<HeadcrabComponent>();
while (query.MoveNext(out var uid, out var comp))
{
comp.Accumulator += frameTime;

if (comp.Accumulator <= comp.DamageFrequency)
continue;

comp.Accumulator = 0;

if (comp.EquippedOn is not { Valid: true } targetId ||
HasComp<ZombieComponent>(comp.EquippedOn) ||
_mobState.IsDead(uid))
continue;

if (!_mobState.IsAlive(targetId))
{
_inventory.TryUnequip(targetId, "mask", true, true);
comp.EquippedOn = EntityUid.Invalid;
return;
PuroSlavKing marked this conversation as resolved.
Show resolved Hide resolved
}

_damageableSystem.TryChangeDamage(targetId, comp.Damage);
_popup.PopupEntity(Loc.GetString("headcrab-eat-entity-face"),
targetId, targetId, PopupType.LargeCaution);
_popup.PopupEntity(Loc.GetString("headcrab-eat-other-entity-face",
("entity", targetId)), targetId, Filter.PvsExcept(targetId), true);
}
Spatison marked this conversation as resolved.
Show resolved Hide resolved
}
}
PuroSlavKing marked this conversation as resolved.
Show resolved Hide resolved
10 changes: 10 additions & 0 deletions Content.Shared/_White/Headcrab/SharedHeadcrab.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Content.Shared.Actions;
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;

namespace Content.Shared._White.Headcrab;

public sealed partial class JumpActionEvent : WorldTargetActionEvent
{

}
Binary file not shown.
11 changes: 11 additions & 0 deletions Resources/Locale/en-US/_white/popups/headcrab.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
ghost-role-information-headcrab-name = Headcrab
ghost-role-information-headcrab-description = Ride the carrier, bite off its head and protect your nest.
headcrab-eat-entity-face = Headcrab is eating your head!
headcrab-eat-other-entity-face = Headcrab is eating { CAPITALIZE($entity) } head!
headcrab-hit-entity-head = You got { CAPITALIZE($entity) } head!
headcrab-entity-bite = Headcrab bit your hand!
headcrab-try-unequip = You cant take headcrab off your head.
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
station-event-stray-supply-pod-announcement = Stray capsule with goods was found on long-range scanners. The approximate landing location is unknown.
station-event-stray-supply-pod-syndicate-announcement = Stray capsule with goods was found on long-range scanners. The approximate landing location is unknown.
station-event-headcrab-invasion-announcement = Confirmed sightings of hostile wildlife on the station. Personnel are advised to arm themselves, barricade doors, and defend themselves if necessary. Security is advised to eradicate the threat as soon as possible.
5 changes: 5 additions & 0 deletions Resources/Locale/ru-RU/_white/guidbook/antagonist.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
antagonist-name-headcrabs = Хедкрабы
antagonist-name-headcrab = Хедкраб
antagonist-name-headcrab-fast = Быстрый хедкраб
antagonist-name-headcrab-poison = Ядовитый хедкраб
3 changes: 3 additions & 0 deletions Resources/Locale/ru-RU/_white/guidbook/main.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
guide-entry-wwdp = WWDP
guide-entry-antagonist = Антагонисты
11 changes: 11 additions & 0 deletions Resources/Locale/ru-RU/_white/popups/headcrab.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
ghost-role-information-headcrab-name = Хедкраб
ghost-role-information-headcrab-description = Оседлай носителя, откуси ему голову и защити своё гнездо.
headcrab-eat-entity-face = Хедкраб пожирает твою голову!
headcrab-eat-other-entity-face = Хедкраб ест голову { CAPITALIZE($entity) }!
headcrab-hit-entity-head = Вы ухватились за голову { CAPITALIZE($entity) }!
headcrab-entity-bite = Хедкраб укусил вас за руку!
headcrab-try-unequip = Вы не можете снять хедкраба со своей головы.
3 changes: 3 additions & 0 deletions Resources/Locale/ru-RU/_white/prototypes/actions/types.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ ent-ToggleNightVision = Переключить ночное зрение
ent-ToggleThermalVision = Переключить термальное зрение
.desc = Переключает термальное зрение.
ent-ActionHeadcrabJump = Прыгнуть
.desc = Сделать крутой прыжок.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ent-SpawnHeadcrabNest = спавнер гнездо хедкраба
ent-SpawnMobHeadcrab = спавнер хедкраб
ent-SpawnMobHeadcrabFast = спавнер быстрый хедкраб
ent-SpawnMobHeadcrabPoison = спавнер ядовитый хедкраб
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
ent-BaseMobHeadcrab = хедкраб
.desc = Похоже на что-то паразитирующее.
ent-MobHeadcrab = { ent-BaseMobHeadcrab }
.desc = { ent-BaseMobHeadcrab.desc }
ent-MobHeadcrabFast = быстрый хедкраб
.desc = { ent-BaseMobHeadcrab.desc }
ent-MobHeadcrabPoison = ядовитый хедкраб
.desc = { ent-BaseMobHeadcrab.desc }
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ent-HeadcrabNest = гнездо хедкрабов
.desc = Кажется оно шевелится.
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
station-event-stray-supply-pod-announcement = На сканерах дальнего действия обнаружена бродячая капсула с товарами. Примерное место приземления неизвестно.
station-event-stray-supply-pod-syndicate-announcement = На сканерах дальнего действия обнаружена бродячая капсула с товарами. Примерное место приземления неизвестно.
station-event-headcrab-invasion-announcement = Подтверждено наличие на станции враждебной фауны. Персоналу рекомендуется вооружиться, забаррикадировать двери и защищаться в случае необходимости. Службе безопасности следует как можно скорее ликвидировать угрозу.
19 changes: 18 additions & 1 deletion Resources/Prototypes/_White/Actions/types.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,21 @@
icon:
sprite: _White/Clothing/Eyes/Goggles/thermal.rsi
state: icon
event: !type:ToggleThermalVisionEvent
event: !type:ToggleThermalVisionEvent

- type: entity
id: ActionHeadcrabJump
name: Jump
description: Do super jump.
categories: [ HideSpawnMenu ]
components:
- type: WorldTargetAction
icon:
sprite: _White/Interface/Actions/headcrab.rsi
state: jump-off
iconOn:
sprite: _White/Interface/Actions/headcrab.rsi
state: jump-on
event: !type:JumpActionEvent
useDelay: 6
range: 160
Loading
Loading