Deathgasp + last words / succumbing / fake deathgasp as crit actions (#18993)

This commit is contained in:
Kara
2023-08-11 22:56:34 -07:00
committed by GitHub
parent eff36d2fe9
commit 7b51cebfea
20 changed files with 459 additions and 35 deletions

View File

@@ -55,11 +55,18 @@ public partial class ChatSystem
/// <param name="hideLog">Whether or not this message should appear in the adminlog window</param> /// <param name="hideLog">Whether or not this message should appear in the adminlog window</param>
/// <param name="range">Conceptual range of transmission, if it shows in the chat window, if it shows to far-away ghosts or ghosts at all...</param> /// <param name="range">Conceptual range of transmission, if it shows in the chat window, if it shows to far-away ghosts or ghosts at all...</param>
/// <param name="nameOverride">The name to use for the speaking entity. Usually this should just be modified via <see cref="TransformSpeakerNameEvent"/>. If this is set, the event will not get raised.</param> /// <param name="nameOverride">The name to use for the speaking entity. Usually this should just be modified via <see cref="TransformSpeakerNameEvent"/>. If this is set, the event will not get raised.</param>
public void TryEmoteWithChat(EntityUid source, string emoteId, ChatTransmitRange range = ChatTransmitRange.Normal, bool hideLog = false, string? nameOverride = null) public void TryEmoteWithChat(
EntityUid source,
string emoteId,
ChatTransmitRange range = ChatTransmitRange.Normal,
bool hideLog = false,
string? nameOverride = null,
bool ignoreActionBlocker = false
)
{ {
if (!_prototypeManager.TryIndex<EmotePrototype>(emoteId, out var proto)) if (!_prototypeManager.TryIndex<EmotePrototype>(emoteId, out var proto))
return; return;
TryEmoteWithChat(source, proto, range, hideLog, nameOverride); TryEmoteWithChat(source, proto, range, hideLog, nameOverride, ignoreActionBlocker);
} }
/// <summary> /// <summary>
@@ -71,35 +78,44 @@ public partial class ChatSystem
/// <param name="hideChat">Whether or not this message should appear in the chat window</param> /// <param name="hideChat">Whether or not this message should appear in the chat window</param>
/// <param name="range">Conceptual range of transmission, if it shows in the chat window, if it shows to far-away ghosts or ghosts at all...</param> /// <param name="range">Conceptual range of transmission, if it shows in the chat window, if it shows to far-away ghosts or ghosts at all...</param>
/// <param name="nameOverride">The name to use for the speaking entity. Usually this should just be modified via <see cref="TransformSpeakerNameEvent"/>. If this is set, the event will not get raised.</param> /// <param name="nameOverride">The name to use for the speaking entity. Usually this should just be modified via <see cref="TransformSpeakerNameEvent"/>. If this is set, the event will not get raised.</param>
public void TryEmoteWithChat(EntityUid source, EmotePrototype emote, ChatTransmitRange range = ChatTransmitRange.Normal, bool hideLog = false, string? nameOverride = null) public void TryEmoteWithChat(
EntityUid source,
EmotePrototype emote,
ChatTransmitRange range = ChatTransmitRange.Normal,
bool hideLog = false,
string? nameOverride = null,
bool ignoreActionBlocker = false
)
{ {
// check if proto has valid message for chat // check if proto has valid message for chat
if (emote.ChatMessages.Count != 0) if (emote.ChatMessages.Count != 0)
{ {
var action = _random.Pick(emote.ChatMessages); // not all emotes are loc'd, but for the ones that are we pass in entity
SendEntityEmote(source, action, range, nameOverride, false, hideLog); var action = Loc.GetString(_random.Pick(emote.ChatMessages), ("entity", source));
SendEntityEmote(source, action, range, nameOverride, false, hideLog, ignoreActionBlocker);
} }
// do the rest of emote event logic here // do the rest of emote event logic here
TryEmoteWithoutChat(source, emote); TryEmoteWithoutChat(source, emote, ignoreActionBlocker);
} }
/// <summary> /// <summary>
/// Makes selected entity to emote using <see cref="EmotePrototype"/> without sending any messages to chat. /// Makes selected entity to emote using <see cref="EmotePrototype"/> without sending any messages to chat.
/// </summary> /// </summary>
public void TryEmoteWithoutChat(EntityUid uid, string emoteId) public void TryEmoteWithoutChat(EntityUid uid, string emoteId, bool ignoreActionBlocker = false)
{ {
if (!_prototypeManager.TryIndex<EmotePrototype>(emoteId, out var proto)) if (!_prototypeManager.TryIndex<EmotePrototype>(emoteId, out var proto))
return; return;
TryEmoteWithoutChat(uid, proto);
TryEmoteWithoutChat(uid, proto, ignoreActionBlocker);
} }
/// <summary> /// <summary>
/// Makes selected entity to emote using <see cref="EmotePrototype"/> without sending any messages to chat. /// Makes selected entity to emote using <see cref="EmotePrototype"/> without sending any messages to chat.
/// </summary> /// </summary>
public void TryEmoteWithoutChat(EntityUid uid, EmotePrototype proto) public void TryEmoteWithoutChat(EntityUid uid, EmotePrototype proto, bool ignoreActionBlocker = false)
{ {
if (!_actionBlocker.CanEmote(uid)) if (!_actionBlocker.CanEmote(uid) && !ignoreActionBlocker)
return; return;
InvokeEmoteEvent(uid, proto); InvokeEmoteEvent(uid, proto);

View File

@@ -137,10 +137,17 @@ public sealed partial class ChatSystem : SharedChatSystem
/// <param name="shell"></param> /// <param name="shell"></param>
/// <param name="player">The player doing the speaking</param> /// <param name="player">The player doing the speaking</param>
/// <param name="nameOverride">The name to use for the speaking entity. Usually this should just be modified via <see cref="TransformSpeakerNameEvent"/>. If this is set, the event will not get raised.</param> /// <param name="nameOverride">The name to use for the speaking entity. Usually this should just be modified via <see cref="TransformSpeakerNameEvent"/>. If this is set, the event will not get raised.</param>
public void TrySendInGameICMessage(EntityUid source, string message, InGameICChatType desiredType, bool hideChat, bool hideLog = false, public void TrySendInGameICMessage(
IConsoleShell? shell = null, IPlayerSession? player = null, string? nameOverride = null, bool checkRadioPrefix = true) EntityUid source,
string message,
InGameICChatType desiredType,
bool hideChat, bool hideLog = false,
IConsoleShell? shell = null,
IPlayerSession? player = null, string? nameOverride = null,
bool checkRadioPrefix = true,
bool ignoreActionBlocker = false)
{ {
TrySendInGameICMessage(source, message, desiredType, hideChat ? ChatTransmitRange.HideChat : ChatTransmitRange.Normal, hideLog, shell, player, nameOverride, checkRadioPrefix); TrySendInGameICMessage(source, message, desiredType, hideChat ? ChatTransmitRange.HideChat : ChatTransmitRange.Normal, hideLog, shell, player, nameOverride, checkRadioPrefix, ignoreActionBlocker);
} }
/// <summary> /// <summary>
@@ -153,8 +160,19 @@ public sealed partial class ChatSystem : SharedChatSystem
/// <param name="shell"></param> /// <param name="shell"></param>
/// <param name="player">The player doing the speaking</param> /// <param name="player">The player doing the speaking</param>
/// <param name="nameOverride">The name to use for the speaking entity. Usually this should just be modified via <see cref="TransformSpeakerNameEvent"/>. If this is set, the event will not get raised.</param> /// <param name="nameOverride">The name to use for the speaking entity. Usually this should just be modified via <see cref="TransformSpeakerNameEvent"/>. If this is set, the event will not get raised.</param>
public void TrySendInGameICMessage(EntityUid source, string message, InGameICChatType desiredType, ChatTransmitRange range, bool hideLog = false, /// <param name="ignoreActionBlocker">If set to true, action blocker will not be considered for whether an entity can send this message.</param>
IConsoleShell? shell = null, IPlayerSession? player = null, string? nameOverride = null, bool checkRadioPrefix = true) public void TrySendInGameICMessage(
EntityUid source,
string message,
InGameICChatType desiredType,
ChatTransmitRange range,
bool hideLog = false,
IConsoleShell? shell = null,
IPlayerSession? player = null,
string? nameOverride = null,
bool checkRadioPrefix = true,
bool ignoreActionBlocker = false
)
{ {
if (HasComp<GhostComponent>(source)) if (HasComp<GhostComponent>(source))
{ {
@@ -187,7 +205,7 @@ public sealed partial class ChatSystem : SharedChatSystem
// Was there an emote in the message? If so, send it. // Was there an emote in the message? If so, send it.
if (player != null && emoteStr != message && emoteStr != null) if (player != null && emoteStr != message && emoteStr != null)
{ {
SendEntityEmote(source, emoteStr, range, nameOverride); SendEntityEmote(source, emoteStr, range, nameOverride, ignoreActionBlocker);
} }
// This can happen if the entire string is sanitized out. // This can happen if the entire string is sanitized out.
@@ -199,7 +217,7 @@ public sealed partial class ChatSystem : SharedChatSystem
{ {
if (TryProccessRadioMessage(source, message, out var modMessage, out var channel)) if (TryProccessRadioMessage(source, message, out var modMessage, out var channel))
{ {
SendEntityWhisper(source, modMessage, range, channel, nameOverride); SendEntityWhisper(source, modMessage, range, channel, nameOverride, ignoreActionBlocker);
return; return;
} }
} }
@@ -208,19 +226,25 @@ public sealed partial class ChatSystem : SharedChatSystem
switch (desiredType) switch (desiredType)
{ {
case InGameICChatType.Speak: case InGameICChatType.Speak:
SendEntitySpeak(source, message, range, nameOverride, hideLog); SendEntitySpeak(source, message, range, nameOverride, hideLog, ignoreActionBlocker);
break; break;
case InGameICChatType.Whisper: case InGameICChatType.Whisper:
SendEntityWhisper(source, message, range, null, nameOverride, hideLog); SendEntityWhisper(source, message, range, null, nameOverride, hideLog, ignoreActionBlocker);
break; break;
case InGameICChatType.Emote: case InGameICChatType.Emote:
SendEntityEmote(source, message, range, nameOverride, hideLog); SendEntityEmote(source, message, range, nameOverride, hideLog, ignoreActionBlocker);
break; break;
} }
} }
public void TrySendInGameOOCMessage(EntityUid source, string message, InGameOOCChatType type, bool hideChat, public void TrySendInGameOOCMessage(
IConsoleShell? shell = null, IPlayerSession? player = null) EntityUid source,
string message,
InGameOOCChatType type,
bool hideChat,
IConsoleShell? shell = null,
IPlayerSession? player = null
)
{ {
if (!CanSendInGame(message, shell, player)) if (!CanSendInGame(message, shell, player))
return; return;
@@ -262,8 +286,13 @@ public sealed partial class ChatSystem : SharedChatSystem
/// <param name="sender">The sender (Communications Console in Communications Console Announcement)</param> /// <param name="sender">The sender (Communications Console in Communications Console Announcement)</param>
/// <param name="playSound">Play the announcement sound</param> /// <param name="playSound">Play the announcement sound</param>
/// <param name="colorOverride">Optional color for the announcement message</param> /// <param name="colorOverride">Optional color for the announcement message</param>
public void DispatchGlobalAnnouncement(string message, string sender = "Central Command", public void DispatchGlobalAnnouncement(
bool playSound = true, SoundSpecifier? announcementSound = null, Color? colorOverride = null) string message,
string sender = "Central Command",
bool playSound = true,
SoundSpecifier? announcementSound = null,
Color? colorOverride = null
)
{ {
var wrappedMessage = Loc.GetString("chat-manager-sender-announcement-wrap-message", ("sender", sender), ("message", FormattedMessage.EscapeText(message))); var wrappedMessage = Loc.GetString("chat-manager-sender-announcement-wrap-message", ("sender", sender), ("message", FormattedMessage.EscapeText(message)));
_chatManager.ChatMessageToAll(ChatChannel.Radio, message, wrappedMessage, default, false, true, colorOverride); _chatManager.ChatMessageToAll(ChatChannel.Radio, message, wrappedMessage, default, false, true, colorOverride);
@@ -282,8 +311,13 @@ public sealed partial class ChatSystem : SharedChatSystem
/// <param name="sender">The sender (Communications Console in Communications Console Announcement)</param> /// <param name="sender">The sender (Communications Console in Communications Console Announcement)</param>
/// <param name="playDefaultSound">Play the announcement sound</param> /// <param name="playDefaultSound">Play the announcement sound</param>
/// <param name="colorOverride">Optional color for the announcement message</param> /// <param name="colorOverride">Optional color for the announcement message</param>
public void DispatchStationAnnouncement(EntityUid source, string message, string sender = "Central Command", public void DispatchStationAnnouncement(
bool playDefaultSound = true, SoundSpecifier? announcementSound = null, Color? colorOverride = null) EntityUid source,
string message,
string sender = "Central Command",
bool playDefaultSound = true,
SoundSpecifier? announcementSound = null,
Color? colorOverride = null)
{ {
var wrappedMessage = Loc.GetString("chat-manager-sender-announcement-wrap-message", ("sender", sender), ("message", FormattedMessage.EscapeText(message))); var wrappedMessage = Loc.GetString("chat-manager-sender-announcement-wrap-message", ("sender", sender), ("message", FormattedMessage.EscapeText(message)));
var station = _stationSystem.GetOwningStation(source); var station = _stationSystem.GetOwningStation(source);
@@ -312,9 +346,16 @@ public sealed partial class ChatSystem : SharedChatSystem
#region Private API #region Private API
private void SendEntitySpeak(EntityUid source, string originalMessage, ChatTransmitRange range, string? nameOverride, bool hideLog = false) private void SendEntitySpeak(
EntityUid source,
string originalMessage,
ChatTransmitRange range,
string? nameOverride,
bool hideLog = false,
bool ignoreActionBlocker = false
)
{ {
if (!_actionBlocker.CanSpeak(source)) if (!_actionBlocker.CanSpeak(source) && !ignoreActionBlocker)
return; return;
var message = TransformSpeech(source, originalMessage); var message = TransformSpeech(source, originalMessage);
@@ -366,9 +407,17 @@ public sealed partial class ChatSystem : SharedChatSystem
} }
} }
private void SendEntityWhisper(EntityUid source, string originalMessage, ChatTransmitRange range, RadioChannelPrototype? channel, string? nameOverride, bool hideLog = false) private void SendEntityWhisper(
EntityUid source,
string originalMessage,
ChatTransmitRange range,
RadioChannelPrototype? channel,
string? nameOverride,
bool hideLog = false,
bool ignoreActionBlocker = false
)
{ {
if (!_actionBlocker.CanSpeak(source)) if (!_actionBlocker.CanSpeak(source) && !ignoreActionBlocker)
return; return;
var message = TransformSpeech(source, originalMessage); var message = TransformSpeech(source, originalMessage);
@@ -449,16 +498,27 @@ public sealed partial class ChatSystem : SharedChatSystem
} }
} }
private void SendEntityEmote(EntityUid source, string action, ChatTransmitRange range, string? nameOverride, bool hideLog = false, bool checkEmote = true) private void SendEntityEmote(
EntityUid source,
string action,
ChatTransmitRange range,
string? nameOverride,
bool hideLog = false,
bool checkEmote = true,
bool ignoreActionBlocker = false
)
{ {
if (!_actionBlocker.CanEmote(source)) return; if (!_actionBlocker.CanEmote(source) && !ignoreActionBlocker)
return;
// get the entity's apparent name (if no override provided). // get the entity's apparent name (if no override provided).
string name = FormattedMessage.EscapeText(nameOverride ?? Identity.Name(source, EntityManager)); var ent = Identity.Entity(source, EntityManager);
string name = FormattedMessage.EscapeText(nameOverride ?? Name(ent));
// Emotes use Identity.Name, since it doesn't actually involve your voice at all. // Emotes use Identity.Name, since it doesn't actually involve your voice at all.
var wrappedMessage = Loc.GetString("chat-manager-entity-me-wrap-message", var wrappedMessage = Loc.GetString("chat-manager-entity-me-wrap-message",
("entityName", name), ("entityName", name),
("entity", ent),
("message", FormattedMessage.EscapeText(action))); ("message", FormattedMessage.EscapeText(action)));
if (checkEmote) if (checkEmote)

View File

@@ -0,0 +1,98 @@
using Content.Server.Administration;
using Content.Server.Chat.Systems;
using Content.Server.GameTicking;
using Content.Server.Mind.Components;
using Content.Shared.Actions;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Robust.Server.Console;
using Robust.Server.GameObjects;
using System;
namespace Content.Server.Mobs;
/// <summary>
/// Handles performing crit-specific actions.
/// </summary>
public sealed class CritMobActionsSystem : EntitySystem
{
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly DeathgaspSystem _deathgasp = default!;
[Dependency] private readonly IServerConsoleHost _host = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly QuickDialogSystem _quickDialog = default!;
private const int MaxLastWordsLength = 30;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<MobStateActionsComponent, CritSuccumbEvent>(OnSuccumb);
SubscribeLocalEvent<MobStateActionsComponent, CritFakeDeathEvent>(OnFakeDeath);
SubscribeLocalEvent<MobStateActionsComponent, CritLastWordsEvent>(OnLastWords);
}
private void OnSuccumb(EntityUid uid, MobStateActionsComponent component, CritSuccumbEvent args)
{
if (!TryComp<ActorComponent>(uid, out var actor) || !_mobState.IsCritical(uid))
return;
_host.ExecuteCommand(actor.PlayerSession, "ghost");
args.Handled = true;
}
private void OnFakeDeath(EntityUid uid, MobStateActionsComponent component, CritFakeDeathEvent args)
{
if (!_mobState.IsCritical(uid))
return;
args.Handled = _deathgasp.Deathgasp(uid);
}
private void OnLastWords(EntityUid uid, MobStateActionsComponent component, CritLastWordsEvent args)
{
if (!TryComp<ActorComponent>(uid, out var actor))
return;
_quickDialog.OpenDialog(actor.PlayerSession, Loc.GetString("action-name-crit-last-words"), "",
(string lastWords) =>
{
if (actor.PlayerSession.AttachedEntity != uid
|| !_mobState.IsCritical(uid))
return;
if (lastWords.Length > MaxLastWordsLength)
{
lastWords = lastWords.Substring(0, MaxLastWordsLength);
}
lastWords += "...";
_chat.TrySendInGameICMessage(uid, lastWords, InGameICChatType.Whisper, ChatTransmitRange.Normal, ignoreActionBlocker: true);
_host.ExecuteCommand(actor.PlayerSession, "ghost");
});
args.Handled = true;
}
}
/// <summary>
/// Only applies to mobs in crit capable of ghosting/succumbing
/// </summary>
public sealed class CritSuccumbEvent : InstantActionEvent
{
}
/// <summary>
/// Only applies/has functionality to mobs in crit that have <see cref="DeathgaspComponent"/>
/// </summary>
public sealed class CritFakeDeathEvent : InstantActionEvent
{
}
/// <summary>
/// Only applies to mobs capable of speaking, as a last resort in crit
/// </summary>
public sealed class CritLastWordsEvent : InstantActionEvent
{
}

View File

@@ -0,0 +1,18 @@
using Content.Shared.Chat.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Mobs;
/// <summary>
/// Mobs with this component will emote a deathgasp when they die.
/// </summary>
/// <see cref="DeathgaspSystem"/>
[RegisterComponent]
public sealed class DeathgaspComponent : Component
{
/// <summary>
/// The emote prototype to use.
/// </summary>
[DataField("prototype", customTypeSerializer:typeof(PrototypeIdSerializer<EmotePrototype>))]
public string Prototype = "DefaultDeathgasp";
}

View File

@@ -0,0 +1,40 @@
using Content.Server.Chat.Systems;
using Content.Shared.Mobs;
using Robust.Shared.Prototypes;
namespace Content.Server.Mobs;
/// <see cref="DeathgaspComponent"/>
public sealed class DeathgaspSystem: EntitySystem
{
[Dependency] private readonly ChatSystem _chat = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DeathgaspComponent, MobStateChangedEvent>(OnMobStateChanged);
}
private void OnMobStateChanged(EntityUid uid, DeathgaspComponent component, MobStateChangedEvent args)
{
// don't deathgasp if they arent going straight from crit to dead
if (args.NewMobState != MobState.Dead || args.OldMobState != MobState.Critical)
return;
Deathgasp(uid, component);
}
/// <summary>
/// Causes an entity to perform their deathgasp emote, if they have one.
/// </summary>
public bool Deathgasp(EntityUid uid, DeathgaspComponent? component = null)
{
if (!Resolve(uid, ref component, false))
return false;
_chat.TryEmoteWithChat(uid, component.Prototype, ignoreActionBlocker: true);
return true;
}
}

View File

@@ -0,0 +1,27 @@
using Content.Shared.Mobs.Systems;
namespace Content.Shared.Mobs.Components;
/// <summary>
/// Used for specifying actions that should be automatically added/removed on mob state transitions
/// </summary>
/// <remarks>
/// Mostly for crit-specific actions.
/// </remarks>
/// <see cref="MobStateActionsSystem"/>
[RegisterComponent]
public sealed class MobStateActionsComponent : Component
{
/// <summary>
/// Specifies a list of actions that should be available if a mob is in a given state.
/// </summary>
/// <example>
/// actions:
/// Critical:
/// - CritSuccumb
/// Alive:
/// - AnimalLayEgg
/// </example>
[DataField("actions")]
public Dictionary<MobState, List<string>> Actions = new();
}

View File

@@ -0,0 +1,54 @@
using Content.Shared.Actions;
using Content.Shared.Actions.ActionTypes;
using Content.Shared.Mobs.Components;
using Robust.Shared.Prototypes;
namespace Content.Shared.Mobs.Systems;
/// <summary>
/// Adds and removes defined actions when a mob's <see cref="MobState"/> changes.
/// </summary>
public sealed class MobStateActionsSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly SharedActionsSystem _actions = default!;
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<MobStateActionsComponent, MobStateChangedEvent>(OnMobStateChanged);
}
private void OnMobStateChanged(EntityUid uid, MobStateActionsComponent component, MobStateChangedEvent args)
{
if (!TryComp<ActionsComponent>(uid, out var action))
return;
foreach (var (state, acts) in component.Actions)
{
if (state != args.NewMobState && state != args.OldMobState)
continue;
foreach (var item in acts)
{
if (!_proto.TryIndex<InstantActionPrototype>(item, out var proto))
continue;
var instance = new InstantAction(proto);
if (state == args.OldMobState)
{
// Don't remove actions that would be getting readded anyway
if (component.Actions.TryGetValue(args.NewMobState, out var value)
&& value.Contains(item))
continue;
_actions.RemoveAction(uid, instance, action);
}
else if (state == args.NewMobState)
{
_actions.AddAction(uid, instance, null, action);
}
}
}
}
}

View File

@@ -0,0 +1,9 @@
action-name-crit-succumb = Succumb
action-description-crit-succumb = Accept your fate.
action-name-crit-fake-death = Fake Death
action-description-crit-fake-death = Pretend to take your final breath while staying alive.
action-name-crit-last-words = Say Last Words
action-description-crit-last-words = Whisper your last words to anyone nearby, and then succumb to your fate. You only have 30 characters to work with.

View File

@@ -11,25 +11,36 @@ chat-manager-crit-looc-chat-enabled-message = Crit players can now use LOOC.
chat-manager-crit-looc-chat-disabled-message = Crit players can no longer use LOOC. chat-manager-crit-looc-chat-disabled-message = Crit players can no longer use LOOC.
chat-manager-admin-ooc-chat-enabled-message = Admin OOC chat has been enabled. chat-manager-admin-ooc-chat-enabled-message = Admin OOC chat has been enabled.
chat-manager-admin-ooc-chat-disabled-message = Admin OOC chat has been disabled. chat-manager-admin-ooc-chat-disabled-message = Admin OOC chat has been disabled.
chat-manager-max-message-length-exceeded-message = Your message exceeded {$limit} character limit chat-manager-max-message-length-exceeded-message = Your message exceeded {$limit} character limit
chat-manager-no-headset-on-message = You don't have a headset on! chat-manager-no-headset-on-message = You don't have a headset on!
chat-manager-no-radio-key = No radio key specified! chat-manager-no-radio-key = No radio key specified!
chat-manager-no-such-channel = There is no channel with key '{$key}'! chat-manager-no-such-channel = There is no channel with key '{$key}'!
chat-manager-whisper-headset-on-message = You can't whisper on the radio! chat-manager-whisper-headset-on-message = You can't whisper on the radio!
chat-manager-server-wrap-message = [bold]{$message}[/bold] chat-manager-server-wrap-message = [bold]{$message}[/bold]
chat-manager-sender-announcement-wrap-message = [font size=14][bold]{$sender} Announcement:[/font][font size=12] chat-manager-sender-announcement-wrap-message = [font size=14][bold]{$sender} Announcement:[/font][font size=12]
{$message}[/bold][/font] {$message}[/bold][/font]
chat-manager-entity-say-wrap-message = [bold]{$entityName}[/bold] says, "{$message}" chat-manager-entity-say-wrap-message = [bold]{$entityName}[/bold] says, "{$message}"
chat-manager-entity-whisper-wrap-message = [font size=11][italic]{$entityName} whispers, "{$message}"[/italic][/font] chat-manager-entity-whisper-wrap-message = [font size=11][italic]{$entityName} whispers, "{$message}"[/italic][/font]
chat-manager-entity-whisper-unknown-wrap-message = [font size=11][italic]Someone whispers, "{$message}"[/italic][/font] chat-manager-entity-whisper-unknown-wrap-message = [font size=11][italic]Someone whispers, "{$message}"[/italic][/font]
chat-manager-entity-me-wrap-message = [italic]{$entityName} {$message}[/italic]
# THE() is not used here because the entity and its name can technically be disconnected if a nameOverride is passed...
chat-manager-entity-me-wrap-message = [italic]{ PROPER($entity) ->
*[false] the {$entityName} {$message}[/italic]
[true] {$entityName} {$message}[/italic]
}
chat-manager-entity-looc-wrap-message = LOOC: [bold]{$entityName}:[/bold] {$message} chat-manager-entity-looc-wrap-message = LOOC: [bold]{$entityName}:[/bold] {$message}
chat-manager-send-ooc-wrap-message = OOC: [bold]{$playerName}:[/bold] {$message} chat-manager-send-ooc-wrap-message = OOC: [bold]{$playerName}:[/bold] {$message}
chat-manager-send-ooc-patron-wrap-message = OOC: [bold][color={$patronColor}]{$playerName}[/color]:[/bold] {$message} chat-manager-send-ooc-patron-wrap-message = OOC: [bold][color={$patronColor}]{$playerName}[/color]:[/bold] {$message}
chat-manager-send-dead-chat-wrap-message = {$deadChannelName}: [bold]{$playerName}:[/bold] {$message} chat-manager-send-dead-chat-wrap-message = {$deadChannelName}: [bold]{$playerName}:[/bold] {$message}
chat-manager-send-admin-dead-chat-wrap-message = {$adminChannelName}: [bold]({$userName}):[/bold] {$message} chat-manager-send-admin-dead-chat-wrap-message = {$adminChannelName}: [bold]({$userName}):[/bold] {$message}
chat-manager-send-admin-chat-wrap-message = {$adminChannelName}: [bold]{$playerName}:[/bold] {$message} chat-manager-send-admin-chat-wrap-message = {$adminChannelName}: [bold]{$playerName}:[/bold] {$message}
chat-manager-send-admin-announcement-wrap-message = [bold]{$adminChannelName}: {$message}[/bold] chat-manager-send-admin-announcement-wrap-message = [bold]{$adminChannelName}: {$message}[/bold]
chat-manager-send-hook-ooc-wrap-message = OOC: [bold](D){$senderName}:[/bold] {$message} chat-manager-send-hook-ooc-wrap-message = OOC: [bold](D){$senderName}:[/bold] {$message}
chat-manager-dead-channel-name = DEAD chat-manager-dead-channel-name = DEAD

View File

@@ -0,0 +1 @@
emote-deathgasp = seizes up and falls limp, {POSS-ADJ($entity)} eyes dead and lifeless...

View File

@@ -0,0 +1,34 @@
# Actions added to mobs in crit.
- type: instantAction
id: CritSuccumb
name: action-name-crit-succumb
description: action-description-crit-succumb
itemIconStyle: NoItem
checkCanInteract: false
icon:
sprite: Mobs/Ghosts/ghost_human.rsi
state: icon
serverEvent: !type:CritSuccumbEvent
- type: instantAction
id: CritFakeDeath
name: action-name-crit-fake-death
description: action-description-crit-fake-death
itemIconStyle: NoItem
checkCanInteract: false
icon:
sprite: Interface/Actions/actions_crit.rsi
state: fakedeath
serverEvent: !type:CritFakeDeathEvent
useDelay: 30
- type: instantAction
id: CritLastWords
name: action-name-crit-last-words
description: action-description-crit-last-words
itemIconStyle: NoItem
checkCanInteract: false
icon:
sprite: Interface/Actions/actions_crit.rsi
state: lastwords
serverEvent: !type:CritLastWordsEvent

View File

@@ -933,6 +933,13 @@
layer: layer:
- SmallMobLayer - SmallMobLayer
- type: MobState - type: MobState
- type: Deathgasp
- type: MobStateActions
actions:
Critical:
- CritSuccumb
- CritFakeDeath
- CritLastWords
- type: MobThresholds - type: MobThresholds
thresholds: thresholds:
0: Alive 0: Alive
@@ -2233,6 +2240,13 @@
layer: layer:
- SmallMobLayer - SmallMobLayer
- type: MobState - type: MobState
- type: Deathgasp
- type: MobStateActions
actions:
Critical:
- CritSuccumb
- CritFakeDeath
- CritLastWords
- type: MobThresholds - type: MobThresholds
thresholds: thresholds:
0: Alive 0: Alive

View File

@@ -91,6 +91,13 @@
types: types:
Heat : 0.1 #per second, scales with temperature & other constants Heat : 0.1 #per second, scales with temperature & other constants
- type: MobState - type: MobState
- type: Deathgasp
- type: MobStateActions
actions:
Critical:
- CritSuccumb
- CritFakeDeath
- CritLastWords
- type: MobThresholds - type: MobThresholds
thresholds: thresholds:
0: Alive 0: Alive

View File

@@ -72,6 +72,11 @@
layer: layer:
- FlyingMobLayer - FlyingMobLayer
- type: MobState - type: MobState
- type: MobStateActions
actions:
Critical:
- CritSuccumb
- CritLastWords
- type: MobThresholds - type: MobThresholds
thresholds: thresholds:
0: Alive 0: Alive

View File

@@ -198,6 +198,13 @@
thermalRegulationTemperatureThreshold: 25 thermalRegulationTemperatureThreshold: 25
- type: Internals - type: Internals
- type: MobState - type: MobState
- type: Deathgasp
- type: MobStateActions
actions:
Critical:
- CritSuccumb
- CritFakeDeath
- CritLastWords
- type: MobThresholds - type: MobThresholds
thresholds: thresholds:
0: Alive 0: Alive

View File

@@ -124,7 +124,7 @@
- chittered - chittered
- chittered. - chittered.
- chittered! - chittered!
- type: emote - type: emote
id: Squeak id: Squeak
category: Vocal category: Vocal
@@ -184,3 +184,8 @@
- salutes. - salutes.
- salutes! - salutes!
- type: emote
id: DefaultDeathgasp
chatMessages: ["emote-deathgasp"]
chatTriggers:
- deathgasp

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 B

View File

@@ -0,0 +1,17 @@
{
"version": 1,
"size": {
"x": 32,
"y": 32
},
"license": "CC-BY-SA-3.0",
"copyright": "Created by mirrorcult for SS14, derivative of other licensed sprites in the repo",
"states": [
{
"name": "lastwords"
},
{
"name": "fakedeath"
}
]
}

View File

@@ -57,6 +57,7 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GD/@EntryIndexedValue">GD</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GD/@EntryIndexedValue">GD</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GL/@EntryIndexedValue">GL</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GL/@EntryIndexedValue">GL</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HW/@EntryIndexedValue">HW</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HW/@EntryIndexedValue">HW</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IC/@EntryIndexedValue">IC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IL/@EntryIndexedValue">IL</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IL/@EntryIndexedValue">IL</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IP/@EntryIndexedValue">IP</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IP/@EntryIndexedValue">IP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=KHR/@EntryIndexedValue">KHR</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=KHR/@EntryIndexedValue">KHR</s:String>