Новый антагонист: Вампиры (#560)
* Initial Commit * Unfinished * Continuing, more abilities * Remove necromicon healing * oops * Continuing * Continuing * Checkpoint incase i need to undo something * Rewrite to use event per power, working passives * refactor fixes * so many fixes * some fixes * some fixes * new textures, status icon * vampire antag role, mind role * проверку на дауна вызывали?(если Null, у сервера ступор головного фатала) * fix 1 * fix 2 * fix fatal, fix build * breafing audio * fix icons style, store in action * clear store things, add gamerule, mid round game rule * some rework * clear any store systems * clear x2 * if you have 150 Blood Essence, you get mutation * upd * some changes * simple mutation menu * я вот сижу, в 12 ночи, пишу какую-то очередную хуйню, вместо приятного сна... * upd * normal mutations menu * actions translate * None type, base actions list * passive abilities * ability earning * fix * translate vampire preset * translate vampire mutations menu * mutations menu textures * disable some * ДРАНЫЙ БРИФ ФАТАЛКУ ПО ПРИКОЛУ БЕЕМ * game preset * mutation can infinity changed * fix * fully rework abilities update system * gargantua info * update ignore list * fix gargantua passives * vampire objectives * new icons * balance * fix * upd objectives * fix metabolizer type, balance 2 * fix metabolizer type 2 * fix syntax * clear translation * xd * update * round end info * fix * drain objective fix * fix * fix syntax * fix * clear --------- Co-authored-by: Rainfey <rainfey0+github@gmail.com>
42
Content.Client/Vampire/VampireMutationBoundUserInterface.cs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
using Content.Shared.Vampire;
|
||||
using Content.Shared.Vampire.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
namespace Content.Client.Vampire;
|
||||
[UsedImplicitly]
|
||||
public sealed class VampireMutationBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[ViewVariables]
|
||||
private VampireMutationMenu? _menu;
|
||||
public VampireMutationBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
_menu = new VampireMutationMenu();
|
||||
_menu.OnClose += Close;
|
||||
_menu.OnIdSelected += OnIdSelected;
|
||||
_menu.OpenCentered();
|
||||
}
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
if (state is not VampireMutationBoundUserInterfaceState st)
|
||||
return;
|
||||
_menu?.UpdateState(st.MutationList, st.SelectedMutation);
|
||||
}
|
||||
private void OnIdSelected(VampireMutationsType selectedId)
|
||||
{
|
||||
SendMessage(new VampireMutationPrototypeSelectedMessage(selectedId));
|
||||
}
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (disposing)
|
||||
{
|
||||
_menu?.Close();
|
||||
_menu = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Content.Client/Vampire/VampireMutationMenu.xaml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
Title="{Loc 'vampire-mutation-menu-ui-window-name'}"
|
||||
MinSize="250 300"
|
||||
SetSize="250 300">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<ScrollContainer VerticalExpand="True">
|
||||
<GridContainer Name="Grid" Columns="3" Margin="0 5" >
|
||||
</GridContainer>
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
92
Content.Client/Vampire/VampireMutationMenu.xaml.cs
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
using System.Numerics;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Vampire;
|
||||
using Content.Shared.Vampire.Components;
|
||||
using Content.Client.Resources;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Vampire;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class VampireMutationMenu : DefaultWindow
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
private IResourceCache _resourceCache;
|
||||
private readonly SpriteSystem _sprite;
|
||||
public event Action<VampireMutationsType>? OnIdSelected;
|
||||
|
||||
private HashSet<VampireMutationsType> _possibleMutations = new();
|
||||
private VampireMutationsType _selectedId;
|
||||
|
||||
public VampireMutationMenu()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
_sprite = _entityManager.System<SpriteSystem>();
|
||||
_resourceCache = IoCManager.Resolve<IResourceCache>();
|
||||
}
|
||||
|
||||
public void UpdateState(HashSet<VampireMutationsType> mutationList, VampireMutationsType selectedMutation)
|
||||
{
|
||||
_possibleMutations = mutationList;
|
||||
_selectedId = selectedMutation;
|
||||
UpdateGrid();
|
||||
}
|
||||
|
||||
private void UpdateGrid()
|
||||
{
|
||||
ClearGrid();
|
||||
|
||||
var group = new ButtonGroup();
|
||||
|
||||
foreach (var Mutation in _possibleMutations)
|
||||
{
|
||||
//if (!_prototypeManager.TryIndex("NormalBlobTile", out EntityPrototype? proto))
|
||||
// continue;
|
||||
|
||||
string texturePath = Mutation switch
|
||||
{
|
||||
VampireMutationsType.None => "/Textures/Interface/Actions/actions_vampire.rsi/deathsembrace.png",
|
||||
VampireMutationsType.Hemomancer => "/Textures/Interface/Actions/actions_vampire.rsi/hemomancer.png",
|
||||
VampireMutationsType.Umbrae => "/Textures/Interface/Actions/actions_vampire.rsi/umbrae.png",
|
||||
VampireMutationsType.Gargantua => "/Textures/Interface/Actions/actions_vampire.rsi/gargantua.png",
|
||||
VampireMutationsType.Dantalion => "/Textures/Interface/Actions/actions_vampire.rsi/dantalion.png",
|
||||
VampireMutationsType.Bestia => "/Textures/Interface/Actions/actions_vampire.rsi/bestia.png",
|
||||
_ => "/Textures/Interface/Actions/actions_vampire.rsi/deathsembrace.png"
|
||||
};
|
||||
|
||||
var button = new Button
|
||||
{
|
||||
MinSize = new Vector2(64, 64),
|
||||
HorizontalExpand = true,
|
||||
Group = group,
|
||||
StyleClasses = {StyleBase.ButtonSquare},
|
||||
ToggleMode = true,
|
||||
Pressed = _selectedId == Mutation,
|
||||
ToolTip = Loc.GetString($"vampire-mutation-{Mutation.ToString().ToLower()}-info"),
|
||||
TooltipDelay = 0.01f,
|
||||
};
|
||||
button.OnPressed += _ => OnIdSelected?.Invoke(Mutation);
|
||||
Grid.AddChild(button);
|
||||
|
||||
var texture = _resourceCache.GetTexture(texturePath);
|
||||
button.AddChild(new TextureRect
|
||||
{
|
||||
Stretch = TextureRect.StretchMode.KeepAspectCentered,
|
||||
Texture = texture,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearGrid()
|
||||
{
|
||||
Grid.RemoveAllChildren();
|
||||
}
|
||||
}
|
||||
|
|
@ -165,5 +165,19 @@ public sealed partial class AdminVerbSystem
|
|||
Message = Loc.GetString("admin-verb-make-changeling"),
|
||||
};
|
||||
args.Verbs.Add(ling);
|
||||
|
||||
Verb vampire = new()
|
||||
{
|
||||
Text = Loc.GetString("admin-verb-text-make-vampire"),
|
||||
Category = VerbCategory.Antag,
|
||||
Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Interface/Actions/actions_vampire.rsi"), "unholystrength"),
|
||||
Act = () =>
|
||||
{
|
||||
_antag.ForceMakeAntag<VampireRuleComponent>(targetPlayer, "Vampire");
|
||||
},
|
||||
Impact = LogImpact.High,
|
||||
Message = Loc.GetString("admin-verb-make-vampire"),
|
||||
};
|
||||
args.Verbs.Add(vampire);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,12 +13,16 @@ using Content.Shared.Inventory;
|
|||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Stunnable;
|
||||
using Content.Shared.Timing;
|
||||
using Content.Shared.Vampire.Components;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Bible
|
||||
{
|
||||
|
|
@ -34,12 +38,14 @@ namespace Content.Server.Bible
|
|||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly UseDelaySystem _delay = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly SharedStunSystem _stun = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<BibleComponent, AfterInteractEvent>(OnAfterInteract);
|
||||
SubscribeLocalEvent<BibleComponent, EntGotInsertedIntoContainerMessage>(OnInsertedContainer);
|
||||
SubscribeLocalEvent<SummonableComponent, GetVerbsEvent<AlternativeVerb>>(AddSummonVerb);
|
||||
SubscribeLocalEvent<SummonableComponent, GetItemActionsEvent>(GetSummonAction);
|
||||
SubscribeLocalEvent<SummonableComponent, SummonActionEvent>(OnSummon);
|
||||
|
|
@ -47,6 +53,20 @@ namespace Content.Server.Bible
|
|||
SubscribeLocalEvent<FamiliarComponent, GhostRoleSpawnerUsedEvent>(OnSpawned);
|
||||
}
|
||||
|
||||
private void OnInsertedContainer(EntityUid uid, BibleComponent component, EntGotInsertedIntoContainerMessage args)
|
||||
{
|
||||
//If an unholy creature picks up the bible, knock them down
|
||||
if (HasComp<UnholyComponent>(args.Container.Owner))
|
||||
{
|
||||
Timer.Spawn(500, () =>
|
||||
{
|
||||
_stun.TryParalyze(args.Container.Owner, TimeSpan.FromSeconds(10), true);
|
||||
_damageableSystem.TryChangeDamage(args.Container.Owner, component.DamageOnUnholyUse);
|
||||
_audio.PlayPvs(component.SizzleSoundPath, args.Container.Owner);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Queue<EntityUid> _addQueue = new();
|
||||
private readonly Queue<EntityUid> _remQueue = new();
|
||||
|
||||
|
|
@ -116,7 +136,21 @@ namespace Content.Server.Bible
|
|||
return;
|
||||
}
|
||||
|
||||
// This only has a chance to fail if the target is not wearing anything on their head and is not a familiar.
|
||||
//Damage unholy creatures
|
||||
if (HasComp<UnholyComponent>(args.Target))
|
||||
{
|
||||
_damageableSystem.TryChangeDamage(args.Target.Value, component.DamageUnholy, true, origin: uid);
|
||||
|
||||
var othersMessage = Loc.GetString(component.LocPrefix + "-damage-unholy-others", ("user", Identity.Entity(args.User, EntityManager)), ("target", Identity.Entity(args.Target.Value, EntityManager)), ("bible", uid));
|
||||
_popupSystem.PopupEntity(othersMessage, args.User, Filter.PvsExcept(args.User), true, PopupType.MediumCaution);
|
||||
|
||||
var selfMessage = Loc.GetString(component.LocPrefix + "-damage-unholy-self", ("target", Identity.Entity(args.Target.Value, EntityManager)), ("bible", uid));
|
||||
_popupSystem.PopupEntity(selfMessage, args.User, args.User, PopupType.LargeCaution);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// This only has a chance to fail if the target is not wearing anything on their head and is not a familiar..
|
||||
if (!_invSystem.TryGetSlotEntity(args.Target.Value, "head", out var _) && !HasComp<FamiliarComponent>(args.Target.Value))
|
||||
{
|
||||
if (_random.Prob(component.FailChance))
|
||||
|
|
|
|||
|
|
@ -27,6 +27,14 @@ namespace Content.Server.Bible.Components
|
|||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageSpecifier DamageOnUntrainedUse = default!;
|
||||
|
||||
[DataField("damageOnUnholyUse", required: true)]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageSpecifier DamageOnUnholyUse = default!;
|
||||
|
||||
[DataField("damageUnholy", required: true)]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageSpecifier DamageUnholy = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Chance the bible will fail to heal someone with no helmet
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using Content.Server.Body.Components;
|
|||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Body.Organ;
|
||||
using Content.Shared.Body.Prototypes;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
|
|
@ -230,6 +231,31 @@ namespace Content.Server.Body.Systems
|
|||
|
||||
_solutionContainerSystem.UpdateChemicals(soln.Value);
|
||||
}
|
||||
|
||||
public bool TryAddMetabolizerType(MetabolizerComponent component, string metabolizerType)
|
||||
{
|
||||
if (!_prototypeManager.HasIndex<MetabolizerTypePrototype>(metabolizerType))
|
||||
return false;
|
||||
|
||||
if (component.MetabolizerTypes == null)
|
||||
component.MetabolizerTypes = new();
|
||||
|
||||
return component.MetabolizerTypes.Add(metabolizerType);
|
||||
}
|
||||
|
||||
public bool TryRemoveMetabolizerType(MetabolizerComponent component, string metabolizerType)
|
||||
{
|
||||
if (component.MetabolizerTypes == null)
|
||||
return true;
|
||||
|
||||
return component.MetabolizerTypes.Remove(metabolizerType);
|
||||
}
|
||||
|
||||
public void ClearMetabolizerTypes(MetabolizerComponent component)
|
||||
{
|
||||
if (component.MetabolizerTypes != null)
|
||||
component.MetabolizerTypes.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO REFACTOR THIS
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using Content.Shared.Chemistry.EntitySystems;
|
|||
using Content.Shared.Body.Organ;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
|
|
@ -130,5 +131,10 @@ namespace Content.Server.Body.Systems
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetSpecialDigestible(StomachComponent component, EntityWhitelist? whitelist)
|
||||
{
|
||||
component.SpecialDigestible = whitelist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
using Content.Shared.NPC.Prototypes;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Vampire.Components;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules.Components;
|
||||
|
||||
[RegisterComponent, Access(typeof(VampireRuleSystem))]
|
||||
public sealed partial class VampireRuleComponent : Component
|
||||
{
|
||||
public readonly List<EntityUid> VampireMinds = new();
|
||||
|
||||
|
||||
public readonly List<ProtoId<EntityPrototype>> BaseObjectives = new()
|
||||
{
|
||||
"VampireKillRandomPersonObjective",
|
||||
"VampireDrainObjective"
|
||||
};
|
||||
|
||||
public readonly List<ProtoId<EntityPrototype>> EscapeObjectives = new()
|
||||
{
|
||||
"VampireSurviveObjective",
|
||||
"VampireEscapeObjective"
|
||||
};
|
||||
|
||||
public readonly List<ProtoId<EntityPrototype>> StealObjectives = new()
|
||||
{
|
||||
"CMOHyposprayVampireStealObjective",
|
||||
"RDHardsuitVampireStealObjective",
|
||||
"EnergyShotgunVampireStealObjective",
|
||||
"MagbootsVampireStealObjective",
|
||||
"ClipboardVampireStealObjective",
|
||||
"CaptainIDVampireStealObjective",
|
||||
"CaptainJetpackVampireStealObjective",
|
||||
"CaptainGunVampireStealObjective"
|
||||
};
|
||||
}
|
||||
159
Content.Server/GameTicking/Rules/VampireRuleSystem.cs
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
using Content.Server.Antag;
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Objectives;
|
||||
using Content.Server.Roles;
|
||||
using Content.Server.Vampire;
|
||||
using Content.Shared.Vampire.Components;
|
||||
using Content.Shared.NPC.Prototypes;
|
||||
using Content.Shared.NPC.Systems;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Store;
|
||||
using Content.Shared.Store.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System.Text;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules;
|
||||
|
||||
public sealed partial class VampireRuleSystem : GameRuleSystem<VampireRuleComponent>
|
||||
{
|
||||
[Dependency] private readonly MindSystem _mind = default!;
|
||||
[Dependency] private readonly AntagSelectionSystem _antag = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _role = default!;
|
||||
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
|
||||
[Dependency] private readonly ObjectivesSystem _objective = default!;
|
||||
[Dependency] private readonly VampireSystem _vampire = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||
|
||||
public readonly SoundSpecifier BriefingSound = new SoundPathSpecifier("/Audio/Ambience/Antag/vampire_start.ogg");
|
||||
|
||||
public readonly ProtoId<AntagPrototype> VampirePrototypeId = "Vampire";
|
||||
|
||||
// public readonly ProtoId<NpcFactionPrototype> ChangelingFactionId = "Changeling";
|
||||
|
||||
// public readonly ProtoId<NpcFactionPrototype> NanotrasenFactionId = "NanoTrasen";
|
||||
|
||||
public readonly ProtoId<CurrencyPrototype> Currency = "BloodEssence";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<VampireRuleComponent, GetBriefingEvent>(OnGetBriefing);
|
||||
|
||||
SubscribeLocalEvent<VampireRuleComponent, AfterAntagEntitySelectedEvent>(OnSelectAntag);
|
||||
//SubscribeLocalEvent<VampireRuleComponent, ObjectivesTextPrependEvent>(OnTextPrepend);
|
||||
}
|
||||
|
||||
private void OnSelectAntag(EntityUid mindId, VampireRuleComponent comp, ref AfterAntagEntitySelectedEvent args)
|
||||
{
|
||||
var ent = args.EntityUid;
|
||||
_antag.SendBriefing(ent, MakeBriefing(ent), Color.Yellow, BriefingSound);
|
||||
MakeVampire(ent, comp);
|
||||
}
|
||||
public bool MakeVampire(EntityUid target, VampireRuleComponent rule)
|
||||
{
|
||||
if (!_mind.TryGetMind(target, out var mindId, out var mind))
|
||||
return false;
|
||||
|
||||
// briefing
|
||||
if (TryComp<MetaDataComponent>(target, out var metaData))
|
||||
{
|
||||
var briefing = Loc.GetString("vampire-role-greeting", ("name", metaData?.EntityName ?? "Unknown"));
|
||||
var briefingShort = Loc.GetString("vampire-role-greeting-short", ("name", metaData?.EntityName ?? "Unknown"));
|
||||
|
||||
_role.MindHasRole<VampireRoleComponent>(mindId, out var vampireRole);
|
||||
_role.MindHasRole<RoleBriefingComponent>(mindId, out var briefingComp);
|
||||
if (vampireRole is not null && briefingComp is null)
|
||||
{
|
||||
AddComp<RoleBriefingComponent>(vampireRole.Value.Owner);
|
||||
Comp<RoleBriefingComponent>(vampireRole.Value.Owner).Briefing = briefing;
|
||||
}
|
||||
}
|
||||
// vampire stuff
|
||||
// _npcFaction.RemoveFaction(target, NanotrasenFactionId, false);
|
||||
// _npcFaction.AddFaction(target, ChangelingFactionId);
|
||||
|
||||
// make sure it's initial chems are set to max
|
||||
var vampireComponent = EnsureComp<VampireComponent>(target);
|
||||
var interfaceComponent = EnsureComp<UserInterfaceComponent>(target);
|
||||
|
||||
if (HasComp<UserInterfaceComponent>(target))
|
||||
_uiSystem.SetUiState(target, VampireMutationUiKey.Key, new VampireMutationBoundUserInterfaceState(vampireComponent.VampireMutations, vampireComponent.CurrentMutation));
|
||||
|
||||
vampireComponent.Balance = new() { { VampireComponent.CurrencyProto, 0 } };
|
||||
|
||||
rule.VampireMinds.Add(mindId);
|
||||
|
||||
if (HasComp<VampireComponent>(target))
|
||||
_vampire.AddStartingAbilities(target);
|
||||
|
||||
Random random = new Random();
|
||||
|
||||
foreach (var objective in rule.BaseObjectives)
|
||||
_mind.TryAddObjective(mindId, mind, objective);
|
||||
|
||||
if (rule.EscapeObjectives.Count > 0)
|
||||
{
|
||||
var randomEscapeObjective = rule.EscapeObjectives[random.Next(rule.EscapeObjectives.Count)];
|
||||
_mind.TryAddObjective(mindId, mind, randomEscapeObjective);
|
||||
}
|
||||
|
||||
if (rule.StealObjectives.Count > 0)
|
||||
{
|
||||
var randomEscapeObjective = rule.StealObjectives[random.Next(rule.StealObjectives.Count)];
|
||||
_mind.TryAddObjective(mindId, mind, randomEscapeObjective);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnGetBriefing(Entity<VampireRuleComponent> role, ref GetBriefingEvent args)
|
||||
{
|
||||
var ent = args.Mind.Comp.OwnedEntity;
|
||||
|
||||
if (ent is null)
|
||||
return;
|
||||
args.Append(MakeBriefing(ent.Value));
|
||||
}
|
||||
|
||||
private string MakeBriefing(EntityUid ent)
|
||||
{
|
||||
if (TryComp<MetaDataComponent>(ent, out var metaData))
|
||||
{
|
||||
var briefing = Loc.GetString("vampire-role-greeting", ("name", metaData?.EntityName ?? "Unknown"));
|
||||
|
||||
return briefing;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
private void OnTextPrepend(EntityUid uid, VampireRuleComponent comp, ref ObjectivesTextPrependEvent args)
|
||||
{
|
||||
var mostDrainedName = string.Empty;
|
||||
var mostDrained = 0f;
|
||||
|
||||
foreach (var vamp in EntityQuery<VampireComponent>())
|
||||
{
|
||||
if (!_mind.TryGetMind(vamp.Owner, out var mindId, out var mind))
|
||||
continue;
|
||||
|
||||
if (!TryComp<MetaDataComponent>(vamp.Owner, out var metaData))
|
||||
continue;
|
||||
|
||||
if (vamp.TotalBloodDrank > mostDrained)
|
||||
{
|
||||
mostDrained = vamp.TotalBloodDrank;
|
||||
mostDrainedName = _objective.GetTitle((mindId, mind), metaData.EntityName);
|
||||
}
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendLine(Loc.GetString($"roundend-prepend-vampire-drained{(!string.IsNullOrWhiteSpace(mostDrainedName) ? "-named" : "")}", ("name", mostDrainedName), ("number", mostDrained)));
|
||||
|
||||
args.Text = sb.ToString();
|
||||
}
|
||||
}
|
||||
11
Content.Server/Objectives/Components/BloodDrainCondition.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
using Content.Server.Vampire;
|
||||
using Content.Server.Objectives.Systems;
|
||||
|
||||
namespace Content.Server.Objectives.Components;
|
||||
|
||||
[RegisterComponent, Access(typeof(VampireSystem))]
|
||||
public sealed partial class BloodDrainConditionComponent : Component
|
||||
{
|
||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public float BloodDranked = 0f;
|
||||
}
|
||||
8
Content.Server/Roles/VampireRoleComponent.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
using Content.Shared.Roles;
|
||||
|
||||
namespace Content.Server.Roles;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class VampireRoleComponent : BaseMindRoleComponent
|
||||
{
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
using Content.Server.Store.Systems;
|
||||
using Content.Shared.Store;
|
||||
using Content.Shared.Store.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
|
@ -15,6 +16,7 @@ public sealed partial class BuyBeforeCondition : ListingCondition
|
|||
/// <summary>
|
||||
/// Listing(s) that if bought, block this purchase, if any.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public HashSet<ProtoId<ListingPrototype>>? Blacklist;
|
||||
|
||||
public override bool Condition(ListingConditionArgs args)
|
||||
|
|
|
|||
|
|
@ -161,8 +161,8 @@ public sealed partial class StoreSystem
|
|||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (!IsOnStartingMap(uid, component))
|
||||
component.RefundAllowed = false;
|
||||
|
||||
|
|
@ -180,7 +180,8 @@ public sealed partial class StoreSystem
|
|||
RaiseLocalEvent(buyer, ref ev);
|
||||
// Sunrise-End
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//spawn entity
|
||||
if (listing.ProductEntity != null)
|
||||
{
|
||||
|
|
@ -257,6 +258,7 @@ public sealed partial class StoreSystem
|
|||
|
||||
if (upgradeActionId != null)
|
||||
HandleRefundComp(uid, component, upgradeActionId.Value);
|
||||
|
||||
}
|
||||
|
||||
if (listing.ProductEvent != null)
|
||||
|
|
@ -267,6 +269,7 @@ public sealed partial class StoreSystem
|
|||
RaiseLocalEvent(buyer, listing.ProductEvent);
|
||||
}
|
||||
|
||||
|
||||
//log dat shit.
|
||||
_admin.Add(LogType.StorePurchase,
|
||||
LogImpact.Low,
|
||||
|
|
|
|||
762
Content.Server/Vampire/VampireSystem.Abilities.cs
Normal file
|
|
@ -0,0 +1,762 @@
|
|||
using Content.Server.Bible.Components;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Flash;
|
||||
using Content.Server.Flash.Components;
|
||||
using Content.Server.Speech.Components;
|
||||
using Content.Server.Storage.Components;
|
||||
using Content.Server.Store.Components;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Server.Objectives.Components;
|
||||
using Content.Shared.Bed.Sleep;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Chat.Prototypes;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Cuffs.Components;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Flash;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Prying.Components;
|
||||
using Content.Shared.Stealth.Components;
|
||||
using Content.Shared.Store.Events;
|
||||
using Content.Shared.Store.Components;
|
||||
using Content.Shared.Stunnable;
|
||||
using Content.Shared.Vampire;
|
||||
using Content.Shared.Vampire.Components;
|
||||
using Content.Shared.Weapons.Melee;
|
||||
using FastAccessors;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Collections.Frozen;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Content.Server.Vampire;
|
||||
|
||||
public sealed partial class VampireSystem
|
||||
{
|
||||
private FrozenDictionary<string, VampirePowerProtype> _powerCache = default!;
|
||||
private FrozenDictionary<string, VampirePassiveProtype> _passiveCache = default!;
|
||||
|
||||
private void InitializePowers()
|
||||
{
|
||||
_powerCache = BuildPowerCache();
|
||||
_passiveCache = BuildPassiveCache();
|
||||
|
||||
//Abilities
|
||||
SubscribeLocalEvent<VampireComponent, VampireOpenMutationsMenu>(OnVampireOpenMutationsMenu);
|
||||
SubscribeLocalEvent<VampireComponent, VampireToggleFangsEvent>(OnVampireToggleFangs);
|
||||
SubscribeLocalEvent<VampireComponent, VampireGlareEvent>(OnVampireGlare);
|
||||
SubscribeLocalEvent<VampireComponent, VampireScreechEvent>(OnVampireScreech);
|
||||
SubscribeLocalEvent<VampireComponent, VampirePolymorphEvent>(OnVampirePolymorph);
|
||||
SubscribeLocalEvent<VampireComponent, VampireHypnotiseEvent>(OnVampireHypnotise);
|
||||
SubscribeLocalEvent<VampireComponent, VampireBloodStealEvent>(OnVampireBloodSteal);
|
||||
SubscribeLocalEvent<VampireComponent, VampireCloakOfDarknessEvent>(OnVampireCloakOfDarkness);
|
||||
|
||||
//Hypnotise
|
||||
SubscribeLocalEvent<VampireComponent, VampireHypnotiseDoAfterEvent>(HypnotiseDoAfter);
|
||||
|
||||
//Drink Blood
|
||||
SubscribeLocalEvent<VampireComponent, BeforeInteractHandEvent>(OnInteractHandEvent);
|
||||
SubscribeLocalEvent<VampireComponent, VampireDrinkBloodDoAfterEvent>(DrinkDoAfter);
|
||||
|
||||
//Deaths embrace
|
||||
SubscribeLocalEvent<VampireDeathsEmbraceComponent, EntGotInsertedIntoContainerMessage>(OnInsertedIntoContainer);
|
||||
SubscribeLocalEvent<VampireDeathsEmbraceComponent, EntGotRemovedFromContainerMessage>(OnRemovedFromContainer);
|
||||
SubscribeLocalEvent<VampireDeathsEmbraceComponent, MobStateChangedEvent>(OnVampireStateChanged);
|
||||
|
||||
}
|
||||
|
||||
#region Ability Entry Points
|
||||
private void OnVampireOpenMutationsMenu(EntityUid uid, VampireComponent component, VampireOpenMutationsMenu ev)
|
||||
{
|
||||
TryOpenUi(uid, ev.Performer, component);
|
||||
ev.Handled = true;
|
||||
}
|
||||
private void OnVampireToggleFangs(EntityUid entity, VampireComponent component, VampireToggleFangsEvent ev)
|
||||
{
|
||||
if (!TryGetPowerDefinition(ev.DefinitionName, out var def))
|
||||
return;
|
||||
|
||||
var vampire = new Entity<VampireComponent>(entity, component);
|
||||
|
||||
if (!IsAbilityUsable(vampire, def))
|
||||
return;
|
||||
|
||||
var actionEntity = GetPowerEntity(vampire, def.ID);
|
||||
if (actionEntity == null)
|
||||
return;
|
||||
|
||||
var toggled = ToggleFangs(vampire);
|
||||
|
||||
_action.SetToggled(actionEntity, toggled);
|
||||
|
||||
ev.Handled = true;
|
||||
}
|
||||
private void OnVampireGlare(EntityUid entity, VampireComponent component, VampireGlareEvent ev)
|
||||
{
|
||||
if (!TryGetPowerDefinition(ev.DefinitionName, out var def))
|
||||
return;
|
||||
|
||||
var vampire = new Entity<VampireComponent>(entity, component);
|
||||
|
||||
if (!IsAbilityUsable(vampire, def))
|
||||
return;
|
||||
|
||||
Glare(vampire, ev.Target, def.Duration, def.Damage);
|
||||
|
||||
ev.Handled = true;
|
||||
}
|
||||
private void OnVampireScreech(EntityUid entity, VampireComponent component, VampireScreechEvent ev)
|
||||
{
|
||||
if (!TryGetPowerDefinition(ev.DefinitionName, out var def))
|
||||
return;
|
||||
|
||||
var vampire = new Entity<VampireComponent>(entity, component);
|
||||
|
||||
if (!IsAbilityUsable(vampire, def))
|
||||
return;
|
||||
|
||||
Screech(vampire, def.Duration, def.Damage);
|
||||
|
||||
ev.Handled = true;
|
||||
}
|
||||
private void OnVampirePolymorph(EntityUid entity, VampireComponent component, VampirePolymorphEvent ev)
|
||||
{
|
||||
if (!TryGetPowerDefinition(ev.DefinitionName, out var def))
|
||||
return;
|
||||
|
||||
var vampire = new Entity<VampireComponent>(entity, component);
|
||||
|
||||
if (!IsAbilityUsable(vampire, def))
|
||||
return;
|
||||
|
||||
PolymorphSelf(vampire, def.PolymorphTarget);
|
||||
|
||||
ev.Handled = true;
|
||||
}
|
||||
private void OnVampireHypnotise(EntityUid entity, VampireComponent component, VampireHypnotiseEvent ev)
|
||||
{
|
||||
if (!TryGetPowerDefinition(ev.DefinitionName, out var def))
|
||||
return;
|
||||
|
||||
var vampire = new Entity<VampireComponent>(entity, component);
|
||||
|
||||
if (!IsAbilityUsable(vampire, def))
|
||||
return;
|
||||
|
||||
ev.Handled = TryHypnotise(vampire, ev.Target, def.Duration, def.DoAfterDelay);
|
||||
}
|
||||
private void OnVampireBloodSteal(EntityUid entity, VampireComponent component, VampireBloodStealEvent ev)
|
||||
{
|
||||
if (!TryGetPowerDefinition(ev.DefinitionName, out var def))
|
||||
return;
|
||||
|
||||
var vampire = new Entity<VampireComponent>(entity, component);
|
||||
|
||||
if (!IsAbilityUsable(vampire, def))
|
||||
return;
|
||||
|
||||
BloodSteal(vampire);
|
||||
|
||||
ev.Handled = true;
|
||||
}
|
||||
private void OnVampireCloakOfDarkness(EntityUid entity, VampireComponent component, VampireCloakOfDarknessEvent ev)
|
||||
{
|
||||
if (!TryGetPowerDefinition(ev.DefinitionName, out var def))
|
||||
return;
|
||||
|
||||
var vampire = new Entity<VampireComponent>(entity, component);
|
||||
|
||||
if (!IsAbilityUsable(vampire, def))
|
||||
return;
|
||||
|
||||
var actionEntity = GetPowerEntity(vampire.Comp, def.ID);
|
||||
if (actionEntity == null)
|
||||
return;
|
||||
|
||||
var toggled = CloakOfDarkness(vampire, def.Upkeep, 0.75f, -0.5f);
|
||||
|
||||
_action.SetToggled(actionEntity, toggled);
|
||||
|
||||
ev.Handled = true;
|
||||
}
|
||||
private void OnInteractHandEvent(EntityUid uid, VampireComponent component, BeforeInteractHandEvent args)
|
||||
{
|
||||
if (!HasComp<HumanoidAppearanceComponent>(args.Target))
|
||||
return;
|
||||
|
||||
if (args.Target == uid)
|
||||
return;
|
||||
|
||||
if (!TryGetPowerDefinition(VampireComponent.DrinkBloodPrototype, out var def))
|
||||
return;
|
||||
|
||||
var vampire = new Entity<VampireComponent>(uid, component);
|
||||
|
||||
args.Handled = TryDrink(vampire, args.Target, def.DoAfterDelay!.Value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
private bool TryGetPowerDefinition(string name, [NotNullWhen(true)] out VampirePowerProtype? definition)
|
||||
=> _powerCache.TryGetValue(name, out definition);
|
||||
|
||||
private bool IsAbilityUsable(Entity<VampireComponent> vampire, VampirePowerProtype def)
|
||||
{
|
||||
if (!IsPowerUnlocked(vampire, def.ID))
|
||||
return false;
|
||||
|
||||
//Block if we are cuffed
|
||||
if (!def.UsableWhileCuffed && TryComp<CuffableComponent>(vampire, out var cuffable) && !cuffable.CanStillInteract)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("vampire-cuffed"), vampire, vampire, PopupType.MediumCaution);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Block if we are stunned
|
||||
if (!def.UsableWhileStunned && HasComp<StunnedComponent>(vampire))
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("vampire-stunned"), vampire, vampire, PopupType.MediumCaution);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Block if we are muzzled - so far only one item does this?
|
||||
if (!def.UsableWhileMuffled && TryComp<ReplacementAccentComponent>(vampire, out var accent) && accent.Accent.Equals("mumble"))
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("vampire-muffled"), vampire, vampire, PopupType.MediumCaution);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Block if we dont have enough essence
|
||||
if (def.ActivationCost > 0 && !SubtractBloodEssence(vampire, def.ActivationCost))
|
||||
{
|
||||
_popup.PopupClient(Loc.GetString("vampire-not-enough-blood"), vampire, vampire, PopupType.MediumCaution);
|
||||
return false;
|
||||
}
|
||||
|
||||
//Check if we are near an anchored prayable entity - ie the chapel
|
||||
if (IsNearPrayable(vampire))
|
||||
{
|
||||
//Warning about holy power
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#region Passive Powers
|
||||
private void UnnaturalStrength(Entity<VampireComponent> vampire)
|
||||
{
|
||||
var damage = new DamageSpecifier();
|
||||
damage.DamageDict.Add("Slash", 15);
|
||||
|
||||
var meleeComp = EnsureComp<MeleeWeaponComponent>(vampire);
|
||||
meleeComp.Damage += damage;
|
||||
}
|
||||
private void SupernaturalStrength(Entity<VampireComponent> vampire)
|
||||
{
|
||||
var pryComp = EnsureComp<PryingComponent>(vampire);
|
||||
pryComp.Force = true;
|
||||
pryComp.PryPowered = true;
|
||||
|
||||
var damage = new DamageSpecifier();
|
||||
damage.DamageDict.Add("Slash", 15);
|
||||
|
||||
var meleeComp = EnsureComp<MeleeWeaponComponent>(vampire);
|
||||
meleeComp.Damage += damage;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Other Powers
|
||||
private void Screech(Entity<VampireComponent> vampire, TimeSpan? duration, DamageSpecifier? damage = null)
|
||||
{
|
||||
foreach (var entity in _entityLookup.GetEntitiesInRange(vampire, 3, LookupFlags.Approximate | LookupFlags.Dynamic | LookupFlags.Static | LookupFlags.Sundries))
|
||||
{
|
||||
if (HasComp<BibleUserComponent>(entity))
|
||||
continue;
|
||||
|
||||
if (HasComp<VampireComponent>(entity))
|
||||
continue;
|
||||
|
||||
if (HasComp<HumanoidAppearanceComponent>(entity))
|
||||
{
|
||||
_stun.TryParalyze(entity, duration ?? TimeSpan.FromSeconds(3), false);
|
||||
_chat.TryEmoteWithoutChat(entity, _prototypeManager.Index<EmotePrototype>(VampireComponent.ScreamEmoteProto), true);
|
||||
}
|
||||
|
||||
if (damage != null)
|
||||
_damageableSystem.TryChangeDamage(entity, damage);
|
||||
}
|
||||
}
|
||||
private void Glare(Entity<VampireComponent> vampire, EntityUid? target, TimeSpan? duration, DamageSpecifier? damage = null)
|
||||
{
|
||||
if (!target.HasValue)
|
||||
return;
|
||||
|
||||
if (HasComp<VampireComponent>(target))
|
||||
return;
|
||||
|
||||
if (HasComp<FlashImmunityComponent>(target))
|
||||
return;
|
||||
|
||||
if (HasComp<BibleUserComponent>(target))
|
||||
{
|
||||
_stun.TryParalyze(vampire, duration ?? TimeSpan.FromSeconds(3), true);
|
||||
_chat.TryEmoteWithoutChat(vampire.Owner, _prototypeManager.Index<EmotePrototype>(VampireComponent.ScreamEmoteProto), true);
|
||||
if (damage != null)
|
||||
_damageableSystem.TryChangeDamage(vampire.Owner, damage);
|
||||
return;
|
||||
}
|
||||
|
||||
_stun.TryParalyze(target.Value, duration ?? TimeSpan.FromSeconds(3), true);
|
||||
}
|
||||
private void PolymorphSelf(Entity<VampireComponent> vampire, string? polymorphTarget)
|
||||
{
|
||||
if (polymorphTarget == null)
|
||||
return;
|
||||
|
||||
_polymorph.PolymorphEntity(vampire, polymorphTarget);
|
||||
}
|
||||
private void BloodSteal(Entity<VampireComponent> vampire)
|
||||
{
|
||||
var transform = Transform(vampire.Owner);
|
||||
|
||||
var targets = new HashSet<EntityUid>();
|
||||
|
||||
foreach (var entity in _entityLookup.GetEntitiesInRange(transform.Coordinates, 3, LookupFlags.Approximate | LookupFlags.Dynamic))
|
||||
{
|
||||
if (entity == vampire.Owner)
|
||||
continue;
|
||||
|
||||
if (!HasComp<HumanoidAppearanceComponent>(entity))
|
||||
continue;
|
||||
|
||||
if (_rotting.IsRotten(entity))
|
||||
continue;
|
||||
|
||||
if (HasComp<BibleUserComponent>(entity))
|
||||
continue;
|
||||
|
||||
if (!TryComp<BloodstreamComponent>(entity, out var bloodstream) || bloodstream.BloodSolution == null)
|
||||
continue;
|
||||
|
||||
var victimBloodRemaining = bloodstream.BloodSolution.Value.Comp.Solution.Volume;
|
||||
if (victimBloodRemaining <= 0)
|
||||
continue;
|
||||
|
||||
var volumeToConsume = (FixedPoint2) Math.Min((float) victimBloodRemaining.Value, 20); //HARDCODE, 20u of blood per person per use
|
||||
|
||||
targets.Add(entity);
|
||||
|
||||
//Transfer 80% to the vampire
|
||||
var bloodSolution = _solution.SplitSolution(bloodstream.BloodSolution.Value, volumeToConsume * 0.80);
|
||||
//And spill 20% on the floor
|
||||
_blood.TryModifyBloodLevel(entity, -(volumeToConsume * 0.2));
|
||||
|
||||
//Dont check this time, if we are full - just continue anyway
|
||||
TryIngestBlood(vampire, bloodSolution);
|
||||
|
||||
AddBloodEssence(vampire, volumeToConsume * 0.80);
|
||||
|
||||
_beam.TryCreateBeam(vampire, entity, "Lightning");
|
||||
|
||||
_popup.PopupEntity(Loc.GetString("vampire-bloodsteal-other"), entity, entity, Shared.Popups.PopupType.LargeCaution);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Update abilities, add new unlocks
|
||||
//UpdateAbilities(vampire);
|
||||
}
|
||||
private bool CloakOfDarkness(Entity<VampireComponent> vampire, float upkeep, float passiveVisibilityRate, float movementVisibilityRate)
|
||||
{
|
||||
if (HasComp<VampireSealthComponent>(vampire))
|
||||
{
|
||||
RemComp<StealthOnMoveComponent>(vampire);
|
||||
RemComp<StealthComponent>(vampire);
|
||||
RemComp<VampireSealthComponent>(vampire);
|
||||
_popup.PopupEntity(Loc.GetString("vampire-cloak-disable"), vampire, vampire);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
EnsureComp<StealthComponent>(vampire);
|
||||
var stealthMovement = EnsureComp<StealthOnMoveComponent>(vampire);
|
||||
stealthMovement.PassiveVisibilityRate = passiveVisibilityRate;
|
||||
stealthMovement.MovementVisibilityRate = movementVisibilityRate;
|
||||
var vampireStealth = EnsureComp<VampireSealthComponent>(vampire);
|
||||
vampireStealth.Upkeep = upkeep;
|
||||
_popup.PopupEntity(Loc.GetString("vampire-cloak-enable"), vampire, vampire);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Hypnotise
|
||||
private bool TryHypnotise(Entity<VampireComponent> vampire, EntityUid? target, TimeSpan? duration, TimeSpan? delay)
|
||||
{
|
||||
if (target == null)
|
||||
return false;
|
||||
|
||||
var attempt = new FlashAttemptEvent(target.Value, vampire.Owner, vampire.Owner);
|
||||
RaiseLocalEvent(target.Value, attempt, true);
|
||||
|
||||
if (attempt.Cancelled)
|
||||
return false;
|
||||
|
||||
var doAfterEventArgs = new DoAfterArgs(EntityManager, vampire, delay ?? TimeSpan.FromSeconds(5),
|
||||
new VampireHypnotiseDoAfterEvent() { Duration = duration },
|
||||
eventTarget: vampire,
|
||||
target: target,
|
||||
used: target)
|
||||
{
|
||||
BreakOnMove = true,
|
||||
BreakOnDamage = true,
|
||||
MovementThreshold = 0.01f,
|
||||
DistanceThreshold = 1.0f,
|
||||
NeedHand = false,
|
||||
};
|
||||
|
||||
if (_doAfter.TryStartDoAfter(doAfterEventArgs))
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("vampire-hypnotise-other"), target.Value, Shared.Popups.PopupType.SmallCaution);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private void HypnotiseDoAfter(Entity<VampireComponent> vampire, ref VampireHypnotiseDoAfterEvent args)
|
||||
{
|
||||
if (!args.Target.HasValue)
|
||||
return;
|
||||
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
_statusEffects.TryAddStatusEffect<ForcedSleepingComponent>(args.Target.Value, VampireComponent.SleepStatusEffectProto, args.Duration ?? TimeSpan.FromSeconds(30), false);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Deaths Embrace
|
||||
/// <summary>
|
||||
/// When the vampire dies, attempt to activate the Deaths Embrace power
|
||||
/// </summary>
|
||||
private void OnVampireStateChanged(EntityUid uid, VampireDeathsEmbraceComponent component, MobStateChangedEvent args)
|
||||
{
|
||||
if (args.OldMobState != MobState.Dead && args.NewMobState == MobState.Dead)
|
||||
{
|
||||
//Home still exists?
|
||||
TryMoveToCoffin((uid, component));
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// When the vampire is inserted into a container (ie locker, crate etc) check for a coffin, and bind their home to it
|
||||
/// </summary>
|
||||
private void OnInsertedIntoContainer(EntityUid uid, VampireDeathsEmbraceComponent component, EntGotInsertedIntoContainerMessage args)
|
||||
{
|
||||
if (HasComp<CoffinComponent>(args.Container.Owner))
|
||||
{
|
||||
component.HomeCoffin = args.Container.Owner;
|
||||
var vhComp = EnsureComp<VampireHealingComponent>(args.Entity);
|
||||
vhComp.Healing = component.CoffinHealing;
|
||||
_popup.PopupEntity(Loc.GetString("vampire-deathsembrace-bind"), uid, uid);
|
||||
_admin.Add(LogType.Damaged, LogImpact.Low, $"{ToPrettyString(uid):user} bound a new coffin");
|
||||
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// When leaving a container, remove the healing component
|
||||
/// </summary>
|
||||
private void OnRemovedFromContainer(EntityUid uid, VampireDeathsEmbraceComponent component, EntGotRemovedFromContainerMessage args)
|
||||
{
|
||||
RemComp<VampireHealingComponent>(args.Entity);
|
||||
}
|
||||
/// <summary>
|
||||
/// Attempt to move the vampire to their bound coffin
|
||||
/// </summary>
|
||||
private bool TryMoveToCoffin(Entity<VampireDeathsEmbraceComponent> vampire)
|
||||
{
|
||||
if (!vampire.Comp.HomeCoffin.HasValue)
|
||||
return false;
|
||||
|
||||
//Someone smashed your crib bro'
|
||||
if (!Exists(vampire.Comp.HomeCoffin.Value) || LifeStage(vampire.Comp.HomeCoffin.Value) >= EntityLifeStage.Terminating)
|
||||
{
|
||||
vampire.Comp.HomeCoffin = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
//Your crib.. is not a crib, how?
|
||||
if (!TryComp<EntityStorageComponent>(vampire.Comp.HomeCoffin, out var coffinEntityStorage))
|
||||
{
|
||||
vampire.Comp.HomeCoffin = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
//I guess its full?
|
||||
if (!_entityStorage.CanInsert(vampire, vampire.Comp.HomeCoffin.Value, coffinEntityStorage))
|
||||
return false;
|
||||
|
||||
Spawn("Smoke", Transform(vampire).Coordinates);
|
||||
|
||||
_entityStorage.CloseStorage(vampire.Comp.HomeCoffin.Value, coffinEntityStorage);
|
||||
|
||||
if (_entityStorage.Insert(vampire, vampire.Comp.HomeCoffin.Value, coffinEntityStorage))
|
||||
{
|
||||
_admin.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(vampire):user} was moved to their home coffin");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
/// <summary>
|
||||
/// Heal the vampire while they are in the coffin
|
||||
/// Revive them if they are dead and below a ~150ish damage
|
||||
/// </summary>
|
||||
private void DoCoffinHeal(EntityUid vampire, VampireHealingComponent healing)
|
||||
{
|
||||
if (healing.Healing == null)
|
||||
return;
|
||||
|
||||
_damageableSystem.TryChangeDamage(vampire, healing.Healing, true, origin: vampire);
|
||||
|
||||
//If they are dead and we are below the death threshold - revive
|
||||
if (!TryComp<MobStateComponent>(vampire, out var mobStateComponent))
|
||||
return;
|
||||
|
||||
if (!_mobState.IsDead(vampire, mobStateComponent))
|
||||
return;
|
||||
|
||||
if (!_mobThreshold.TryGetThresholdForState(vampire, MobState.Dead, out var threshold))
|
||||
return;
|
||||
|
||||
if (!TryComp<DamageableComponent>(vampire, out var damageableComponent))
|
||||
return;
|
||||
|
||||
//Should be around 150 total damage ish
|
||||
if (damageableComponent.TotalDamage < threshold * 0.75)
|
||||
{
|
||||
_mobState.ChangeMobState(vampire, MobState.Critical, mobStateComponent, vampire);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Blood Drinking
|
||||
/// <summary>
|
||||
/// Toggle if fangs are extended
|
||||
/// </summary>
|
||||
private bool ToggleFangs(Entity<VampireComponent> vampire)
|
||||
{
|
||||
if (HasComp<VampireFangsExtendedComponent>(vampire))
|
||||
{
|
||||
RemComp<VampireFangsExtendedComponent>(vampire);
|
||||
var popupText = Loc.GetString("vampire-fangs-retracted");
|
||||
_admin.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(vampire):user} retracted their fangs");
|
||||
_popup.PopupEntity(popupText, vampire.Owner, vampire.Owner);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
EnsureComp<VampireFangsExtendedComponent>(vampire);
|
||||
var popupText = Loc.GetString("vampire-fangs-extended");
|
||||
_admin.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(vampire):user} extended their fangs");
|
||||
_popup.PopupEntity(popupText, vampire.Owner, vampire.Owner);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Check and start drinking blood from a humanoid
|
||||
/// </summary>
|
||||
private bool TryDrink(Entity<VampireComponent> vampire, EntityUid target, TimeSpan doAfterDelay)
|
||||
{
|
||||
//Do a precheck
|
||||
if (!HasComp<VampireFangsExtendedComponent>(vampire))
|
||||
return false;
|
||||
|
||||
if (!HasComp<TransformComponent>(vampire))
|
||||
return false;
|
||||
|
||||
if (!_interaction.InRangeUnobstructed(vampire.Owner, target, popup: true))
|
||||
return false;
|
||||
|
||||
if (_food.IsMouthBlocked(target, vampire))
|
||||
return false;
|
||||
|
||||
if (_rotting.IsRotten(target))
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("vampire-blooddrink-rotted"), vampire, vampire, PopupType.SmallCaution);
|
||||
return false;
|
||||
}
|
||||
|
||||
var doAfterEventArgs = new DoAfterArgs(EntityManager, vampire, doAfterDelay,
|
||||
new VampireDrinkBloodDoAfterEvent() { Volume = vampire.Comp.MouthVolume },
|
||||
eventTarget: vampire,
|
||||
target: target,
|
||||
used: target)
|
||||
{
|
||||
BreakOnMove = true,
|
||||
BreakOnDamage = true,
|
||||
MovementThreshold = 0.01f,
|
||||
DistanceThreshold = 1.0f,
|
||||
NeedHand = false,
|
||||
Hidden = true
|
||||
};
|
||||
|
||||
_doAfter.TryStartDoAfter(doAfterEventArgs);
|
||||
return true;
|
||||
}
|
||||
private void DrinkDoAfter(Entity<VampireComponent> entity, ref VampireDrinkBloodDoAfterEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
return;
|
||||
|
||||
if (!HasComp<VampireFangsExtendedComponent>(entity))
|
||||
return;
|
||||
|
||||
if (_food.IsMouthBlocked(entity, entity))
|
||||
return;
|
||||
|
||||
if (_rotting.IsRotten(args.Target!.Value))
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("vampire-blooddrink-rotted"), args.User, args.User, PopupType.SmallCaution);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryComp<BloodstreamComponent>(args.Target, out var targetBloodstream) || targetBloodstream == null || targetBloodstream.BloodSolution == null)
|
||||
return;
|
||||
|
||||
//Ensure there is enough blood to drain
|
||||
var victimBloodRemaining = targetBloodstream.BloodSolution.Value.Comp.Solution.Volume;
|
||||
if (victimBloodRemaining <= 0)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("vampire-blooddrink-empty"), entity.Owner, entity.Owner, PopupType.SmallCaution);
|
||||
return;
|
||||
}
|
||||
|
||||
var volumeToConsume = (FixedPoint2) Math.Min((float) victimBloodRemaining.Value, args.Volume);
|
||||
var volumeToDrain = (FixedPoint2) Math.Min((float) victimBloodRemaining.Value, args.Volume * 8);
|
||||
|
||||
if (_mind.TryGetMind(entity, out var mindId, out var mind))
|
||||
if (_mind.TryGetObjectiveComp<BloodDrainConditionComponent>(mindId, out var objective, mind))
|
||||
objective.BloodDranked += entity.Comp.TotalBloodDrank;
|
||||
|
||||
//Slurp
|
||||
_audio.PlayPvs(entity.Comp.BloodDrainSound, entity.Owner, AudioParams.Default.WithVolume(-3f));
|
||||
|
||||
//Spill an extra 5% on the floor
|
||||
_blood.TryModifyBloodLevel(args.Target.Value, -(volumeToDrain * 0.05));
|
||||
|
||||
//Thou shall not feed upon the blood of the holy
|
||||
//TODO: Replace with raised event?
|
||||
if (HasComp<BibleUserComponent>(args.Target))
|
||||
{
|
||||
_damageableSystem.TryChangeDamage(entity, VampireComponent.HolyDamage, true);
|
||||
_popup.PopupEntity(Loc.GetString("vampire-ingest-holyblood"), entity, entity, PopupType.LargeCaution);
|
||||
_admin.Add(LogType.Damaged, LogImpact.Low, $"{ToPrettyString(entity):user} attempted to drink {volumeToConsume}u of {ToPrettyString(args.Target):target}'s holy blood");
|
||||
return;
|
||||
}
|
||||
//Check for zombie
|
||||
else
|
||||
{
|
||||
//Pull out some of the blood
|
||||
var bloodSolution = _solution.SplitSolution(targetBloodstream.BloodSolution.Value, volumeToConsume);
|
||||
|
||||
if (!TryIngestBlood(entity, bloodSolution))
|
||||
{
|
||||
//Undo, put the blood back
|
||||
_solution.AddSolution(targetBloodstream.BloodSolution.Value, bloodSolution);
|
||||
return;
|
||||
}
|
||||
|
||||
_admin.Add(LogType.Damaged, LogImpact.Low, $"{ToPrettyString(entity):user} drank {volumeToConsume}u of {ToPrettyString(args.Target):target}'s blood");
|
||||
AddBloodEssence(entity, volumeToConsume * 0.95);
|
||||
|
||||
args.Repeat = true;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Attempt to insert the solution into the first stomach that has space available
|
||||
/// </summary>
|
||||
private bool TryIngestBlood(Entity<VampireComponent> vampire, Solution ingestedSolution, bool force = false)
|
||||
{
|
||||
//Get all stomaches
|
||||
if (TryComp<BodyComponent>(vampire.Owner, out var body) && _body.TryGetBodyOrganEntityComps<StomachComponent>((vampire.Owner, body), out var stomachs))
|
||||
{
|
||||
//Pick the first one that has space available
|
||||
var firstStomach = stomachs.FirstOrNull(stomach => _stomach.CanTransferSolution(stomach.Owner, ingestedSolution, stomach.Comp1));
|
||||
if (firstStomach == null)
|
||||
{
|
||||
//We are full
|
||||
_popup.PopupEntity(Loc.GetString("vampire-full-stomach"), vampire.Owner, vampire.Owner, PopupType.SmallCaution);
|
||||
return false;
|
||||
}
|
||||
//Fill the stomach with that delicious blood
|
||||
return _stomach.TryTransferSolution(firstStomach.Value.Owner, ingestedSolution, firstStomach.Value.Comp1);
|
||||
}
|
||||
|
||||
//No stomach
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
private bool IsPowerUnlocked(VampireComponent vampire, string name)
|
||||
{
|
||||
return vampire.UnlockedPowers.ContainsKey(name);
|
||||
}
|
||||
/*private bool IsPowerActive(VampireComponent vampire, VampirePowerProtype def) => IsPowerActive(vampire, def.ID);
|
||||
private bool IsPowerActive(VampireComponent vampire, string name)
|
||||
{
|
||||
return vampire.ActivePowers.Contains(name);
|
||||
}
|
||||
private bool SetPowerActive(VampireComponent vampire, string name, bool active)
|
||||
{
|
||||
if (active)
|
||||
{
|
||||
return vampire.ActivePowers.Add(name);
|
||||
}
|
||||
else
|
||||
{
|
||||
return vampire.ActivePowers.Remove(name);
|
||||
}
|
||||
}*/
|
||||
/// <summary>
|
||||
/// Gets the Action EntityUid for a specific power
|
||||
/// </summary>
|
||||
private EntityUid? GetPowerEntity(VampireComponent vampire, string name)
|
||||
{
|
||||
if (!vampire.UnlockedPowers.TryGetValue(name, out var ability))
|
||||
return null;
|
||||
|
||||
return ability;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache all power prototypes in a dictionary by keyed by ID
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private FrozenDictionary<string, VampirePowerProtype> BuildPowerCache()
|
||||
{
|
||||
var protos = _prototypeManager.EnumeratePrototypes<VampirePowerProtype>();
|
||||
return protos.ToFrozenDictionary(x => x.ID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cache all passive prototypes in a dictionary by keyed by listing id
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private FrozenDictionary<string, VampirePassiveProtype> BuildPassiveCache()
|
||||
{
|
||||
var protos = _prototypeManager.EnumeratePrototypes<VampirePassiveProtype>();
|
||||
return protos.ToFrozenDictionary(x => x.CatalogEntry);
|
||||
}
|
||||
}
|
||||
26
Content.Server/Vampire/VampireSystem.Objectives.cs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
using Content.Server.Objectives.Components;
|
||||
using Content.Server.Objectives.Systems;
|
||||
using Content.Shared.Objectives.Components;
|
||||
using Content.Shared.Vampire.Components;
|
||||
using Content.Shared.Vampire;
|
||||
|
||||
namespace Content.Server.Vampire;
|
||||
|
||||
public sealed partial class VampireSystem
|
||||
{
|
||||
[Dependency] private readonly NumberObjectiveSystem _number = default!;
|
||||
|
||||
private void InitializeObjectives()
|
||||
{
|
||||
|
||||
SubscribeLocalEvent<BloodDrainConditionComponent, ObjectiveGetProgressEvent>(OnBloodDrainGetProgress);
|
||||
}
|
||||
|
||||
private void OnBloodDrainGetProgress(EntityUid uid, BloodDrainConditionComponent comp, ref ObjectiveGetProgressEvent args)
|
||||
{
|
||||
var target = _number.GetTarget(uid);
|
||||
if (target != 0)
|
||||
args.Progress = MathF.Min(comp.BloodDranked / target, 1f);
|
||||
else args.Progress = 1f;
|
||||
}
|
||||
}
|
||||
145
Content.Server/Vampire/VampireSystem.Transform.cs
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Temperature.Components;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Rotting;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Chemistry.Reaction;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.Vampire;
|
||||
using Content.Shared.Vampire.Components;
|
||||
using Content.Shared.Weapons.Melee;
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Server.Vampire;
|
||||
|
||||
public sealed partial class VampireSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert the players into a vampire, all programatic because i dont want to replace the players body
|
||||
/// </summary>
|
||||
private void MakeVampire(EntityUid vampireUid)
|
||||
{
|
||||
var vampireComponent = EnsureComp<VampireComponent>(vampireUid);
|
||||
var vampire = new Entity<VampireComponent>(vampireUid, vampireComponent);
|
||||
|
||||
//Render them unable to rot, immune to pressure and thirst
|
||||
RemComp<PerishableComponent>(vampire);
|
||||
RemComp<BarotraumaComponent>(vampire);
|
||||
RemComp<ThirstComponent>(vampire); //Unsure, should vampires thirst.. or hunger?
|
||||
|
||||
//Render immune to cold, but not heat
|
||||
if (TryComp<TemperatureComponent>(vampire, out var temperatureComponent))
|
||||
temperatureComponent.ColdDamageThreshold = Atmospherics.TCMB;
|
||||
|
||||
MakeVulnerableToHoly(vampire);
|
||||
|
||||
//Initialise currency
|
||||
vampireComponent.Balance = new() { { VampireComponent.CurrencyProto, 0 } };
|
||||
|
||||
//Add the summon heirloom ability
|
||||
AddStartingAbilities(vampire);
|
||||
|
||||
//Order of operation requirement, must be called after initialising balance
|
||||
UpdateBloodDisplay(vampire);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add vulnerability to holy water when ingested or slashed, and take damage from the bible
|
||||
/// </summary>
|
||||
private void MakeVulnerableToHoly(Entity<VampireComponent> vampire)
|
||||
{
|
||||
//React to being beaten with the bible
|
||||
EnsureComp<UnholyComponent>(vampire);
|
||||
|
||||
//Take damage from holy water splash
|
||||
if (TryComp<ReactiveComponent>(vampire, out var reactive))
|
||||
{
|
||||
if (reactive.ReactiveGroups == null)
|
||||
reactive.ReactiveGroups = new();
|
||||
|
||||
if (!reactive.ReactiveGroups.ContainsKey("Unholy"))
|
||||
{
|
||||
reactive.ReactiveGroups.Add("Unholy", new() { ReactionMethod.Touch });
|
||||
}
|
||||
}
|
||||
|
||||
if (!TryComp<BodyComponent>(vampire, out var bodyComponent))
|
||||
return;
|
||||
|
||||
//Add vampire and bloodsucker to all metabolizing organs
|
||||
//And restrict diet to Pills (and liquids)
|
||||
foreach (var organ in _body.GetBodyOrgans(vampire, bodyComponent))
|
||||
{
|
||||
if (TryComp<MetabolizerComponent>(organ.Id, out var metabolizer))
|
||||
{
|
||||
if (TryComp<StomachComponent>(organ.Id, out var stomachComponent))
|
||||
{
|
||||
//Override the stomach, prevents humans getting sick when ingesting blood
|
||||
_metabolism.ClearMetabolizerTypes(metabolizer);
|
||||
_stomach.SetSpecialDigestible(stomachComponent, VampireComponent.AcceptableFoods);
|
||||
}
|
||||
|
||||
_metabolism.TryAddMetabolizerType(metabolizer, VampireComponent.MetabolizerVampire);
|
||||
_metabolism.TryAddMetabolizerType(metabolizer, VampireComponent.MetabolizerBloodsucker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddStartingAbilities(EntityUid vampire)
|
||||
{
|
||||
if (!TryComp<VampireComponent>(vampire, out var comp))
|
||||
return;
|
||||
|
||||
foreach (var actionId in comp.BaseVampireActions)
|
||||
{
|
||||
var action = _action.AddAction(vampire, actionId);
|
||||
|
||||
if (!action.HasValue)
|
||||
return;
|
||||
|
||||
if (!TryComp<InstantActionComponent>(action, out var instantActionComponent))
|
||||
return;
|
||||
|
||||
var actionEvent = instantActionComponent.Event as VampireSelfPowerEvent;
|
||||
|
||||
if (actionEvent == null)
|
||||
return;
|
||||
|
||||
comp.UnlockedPowers.Add(actionEvent.DefinitionName, action);
|
||||
|
||||
}
|
||||
|
||||
UpdateBloodDisplay(vampire);
|
||||
}
|
||||
|
||||
//Remove weakeness to holy items
|
||||
private void MakeImmuneToHoly(EntityUid vampire)
|
||||
{
|
||||
if (!TryComp<BodyComponent>(vampire, out var bodyComponent))
|
||||
return;
|
||||
|
||||
//Add vampire and bloodsucker to all metabolizing organs
|
||||
//And restrict diet to Pills (and liquids)
|
||||
foreach (var organ in _body.GetBodyOrgans(vampire, bodyComponent))
|
||||
{
|
||||
if (TryComp<MetabolizerComponent>(organ.Id, out var metabolizer))
|
||||
{
|
||||
_metabolism.TryRemoveMetabolizerType(metabolizer, VampireComponent.MetabolizerVampire);
|
||||
}
|
||||
}
|
||||
|
||||
if (TryComp<ReactiveComponent>(vampire, out var reactive))
|
||||
{
|
||||
if (reactive.ReactiveGroups == null)
|
||||
return;
|
||||
|
||||
reactive.ReactiveGroups.Remove("Unholy");
|
||||
}
|
||||
|
||||
RemComp<UnholyComponent>(vampire);
|
||||
}
|
||||
}
|
||||
482
Content.Server/Vampire/VampireSystem.cs
Normal file
|
|
@ -0,0 +1,482 @@
|
|||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Atmos.Rotting;
|
||||
using Content.Server.Beam;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Interaction;
|
||||
using Content.Server.Nutrition.EntitySystems;
|
||||
using Content.Server.Polymorph.Systems;
|
||||
using Content.Server.Storage.EntitySystems;
|
||||
using Content.Server.Mind;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Body.Systems;
|
||||
using Content.Shared.Buckle;
|
||||
using Content.Shared.Bed.Sleep;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Construction.Components;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Prayer;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Content.Shared.Stunnable;
|
||||
using Content.Shared.Vampire;
|
||||
using Content.Shared.Vampire.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.Vampire;
|
||||
|
||||
public sealed partial class VampireSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly MindSystem _mind = default!;
|
||||
[Dependency] private readonly IAdminLogManager _admin = default!;
|
||||
[Dependency] private readonly FoodSystem _food = default!;
|
||||
[Dependency] private readonly EntityStorageSystem _entityStorage = default!;
|
||||
[Dependency] private readonly BloodstreamSystem _blood = default!;
|
||||
[Dependency] private readonly RottingSystem _rotting = default!;
|
||||
[Dependency] private readonly StomachSystem _stomach = default!;
|
||||
[Dependency] private readonly PolymorphSystem _polymorph = default!;
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
[Dependency] private readonly BeamSystem _beam = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly IMapManager _mapMan = default!;
|
||||
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _action = default!;
|
||||
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
||||
[Dependency] private readonly SharedBodySystem _body = default!;
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solution = default!;
|
||||
[Dependency] private readonly SharedStunSystem _stun = default!;
|
||||
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
||||
[Dependency] private readonly MobThresholdSystem _mobThreshold = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _entityLookup = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly MetabolizerSystem _metabolism = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||
|
||||
private Dictionary<string, EntityUid> _actionEntities = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<VampireComponent, ComponentStartup>(OnComponentStartup);
|
||||
|
||||
//SubscribeLocalEvent<VampireComponent, VampireSelfPowerEvent>(OnUseSelfPower);
|
||||
//SubscribeLocalEvent<VampireComponent, VampireTargetedPowerEvent>(OnUseTargetedPower);
|
||||
SubscribeLocalEvent<VampireComponent, ExaminedEvent>(OnExamined);
|
||||
SubscribeLocalEvent<VampireComponent, VampireBloodChangedEvent>(OnVampireBloodChangedEvent);
|
||||
|
||||
SubscribeLocalEvent<VampireComponent, ComponentGetState>(GetState);
|
||||
SubscribeLocalEvent<VampireComponent, VampireMutationPrototypeSelectedMessage>(OnMutationSelected);
|
||||
|
||||
InitializePowers();
|
||||
InitializeObjectives();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles healing and damaging in space
|
||||
/// </summary>
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var stealthQuery = EntityQueryEnumerator<VampireComponent, VampireSealthComponent>();
|
||||
while (stealthQuery.MoveNext(out var uid, out var vampire, out var stealth))
|
||||
{
|
||||
if (vampire == null || stealth == null)
|
||||
continue;
|
||||
|
||||
if (stealth.NextStealthTick <= 0)
|
||||
{
|
||||
stealth.NextStealthTick = 1;
|
||||
if (!SubtractBloodEssence((uid, vampire), stealth.Upkeep))
|
||||
RemCompDeferred<VampireSealthComponent>(uid);
|
||||
}
|
||||
stealth.NextStealthTick -= frameTime;
|
||||
}
|
||||
|
||||
var healingQuery = EntityQueryEnumerator<VampireComponent, VampireHealingComponent>();
|
||||
while (healingQuery.MoveNext(out var uid, out _, out var healing))
|
||||
{
|
||||
if (healing == null)
|
||||
continue;
|
||||
|
||||
if (healing.NextHealTick <= 0)
|
||||
{
|
||||
healing.NextHealTick = 1;
|
||||
DoCoffinHeal(uid, healing);
|
||||
}
|
||||
healing.NextHealTick -= frameTime;
|
||||
}
|
||||
|
||||
/*var query = EntityQueryEnumerator<VampireComponent>();
|
||||
while (query.MoveNext(out var uid, out var vampireComponent))
|
||||
{
|
||||
var vampire = (uid, vampireComponent);
|
||||
|
||||
if (IsInSpace(uid))
|
||||
{
|
||||
if (vampireComponent.NextSpaceDamageTick <= 0)
|
||||
{
|
||||
vampireComponent.NextSpaceDamageTick = 1;
|
||||
DoSpaceDamage(vampire);
|
||||
}
|
||||
vampireComponent.NextSpaceDamageTick -= frameTime;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
private void OnComponentStartup(EntityUid uid, VampireComponent component, ComponentStartup args)
|
||||
{
|
||||
//MakeVampire(uid);
|
||||
}
|
||||
|
||||
private void OnExamined(EntityUid uid, VampireComponent component, ExaminedEvent args)
|
||||
{
|
||||
if (HasComp<VampireFangsExtendedComponent>(uid) && args.IsInDetailsRange && !_food.IsMouthBlocked(uid))
|
||||
args.AddMarkup($"{Loc.GetString("vampire-fangs-extended-examine")}{Environment.NewLine}");
|
||||
}
|
||||
private bool AddBloodEssence(Entity<VampireComponent> vampire, FixedPoint2 quantity)
|
||||
{
|
||||
if (quantity < 0)
|
||||
return false;
|
||||
|
||||
vampire.Comp.TotalBloodDrank += quantity.Float();
|
||||
vampire.Comp.Balance[VampireComponent.CurrencyProto] += quantity;
|
||||
|
||||
UpdateBloodDisplay(vampire);
|
||||
|
||||
var ev = new VampireBloodChangedEvent();
|
||||
RaiseLocalEvent(vampire, ev);
|
||||
|
||||
return true;
|
||||
}
|
||||
private bool SubtractBloodEssence(Entity<VampireComponent> vampire, FixedPoint2 quantity)
|
||||
{
|
||||
if (quantity < 0)
|
||||
return false;
|
||||
|
||||
if (vampire.Comp.Balance[VampireComponent.CurrencyProto] < quantity)
|
||||
return false;
|
||||
|
||||
vampire.Comp.Balance[VampireComponent.CurrencyProto] -= quantity;
|
||||
|
||||
UpdateBloodDisplay(vampire);
|
||||
|
||||
var ev = new VampireBloodChangedEvent();
|
||||
RaiseLocalEvent(vampire, ev);
|
||||
|
||||
return true;
|
||||
}
|
||||
/// <summary>
|
||||
/// Use the charges display on SummonHeirloom to show the remaining blood essence
|
||||
/// </summary>
|
||||
/// <param name="vampire"></param>
|
||||
public void UpdateBloodDisplay(EntityUid vampire)
|
||||
{
|
||||
if (!TryComp<VampireComponent>(vampire, out var comp))
|
||||
return;
|
||||
|
||||
//Sanity check, you never know who is going to touch this code
|
||||
if (!comp.Balance.TryGetValue(VampireComponent.CurrencyProto, out var balance))
|
||||
return;
|
||||
|
||||
var chargeDisplay = (int) Math.Round((decimal) balance);
|
||||
var mutationsAction = GetPowerEntity(comp, VampireComponent.MutationsActionPrototype);
|
||||
|
||||
if (mutationsAction == null)
|
||||
return;
|
||||
|
||||
_action.SetCharges(mutationsAction, chargeDisplay);
|
||||
}
|
||||
|
||||
private void OnVampireBloodChangedEvent(EntityUid uid, VampireComponent component, VampireBloodChangedEvent args)
|
||||
{
|
||||
EntityUid? newEntity = null;
|
||||
EntityUid entity = default;
|
||||
// Mutations
|
||||
if (GetBloodEssence(uid) >= FixedPoint2.New(50) && !_actionEntities.TryGetValue(VampireComponent.MutationsActionPrototype, out entity))
|
||||
{
|
||||
_action.AddAction(uid, ref newEntity, VampireComponent.MutationsActionPrototype);
|
||||
if (newEntity != null)
|
||||
_actionEntities[VampireComponent.MutationsActionPrototype] = newEntity.Value;
|
||||
}
|
||||
else if (GetBloodEssence(uid) < FixedPoint2.New(50) && _actionEntities.TryGetValue(VampireComponent.MutationsActionPrototype, out entity))
|
||||
{
|
||||
if (!TryComp(uid, out ActionsComponent? comp))
|
||||
return;
|
||||
|
||||
_action.RemoveAction(uid, entity, comp);
|
||||
_actionContainer.RemoveAction(entity);
|
||||
_actionEntities.Remove(VampireComponent.MutationsActionPrototype);
|
||||
}
|
||||
|
||||
//Hemomancer
|
||||
|
||||
if (GetBloodEssence(uid) >= FixedPoint2.New(200) && !_actionEntities.TryGetValue("ActionVampireBloodSteal", out entity) && component.CurrentMutation == VampireMutationsType.Hemomancer)
|
||||
{
|
||||
_action.AddAction(uid, ref newEntity , "ActionVampireBloodSteal");
|
||||
if (newEntity != null)
|
||||
{
|
||||
_actionEntities["ActionVampireBloodSteal"] = newEntity.Value;
|
||||
if (!component.UnlockedPowers.ContainsKey("BloodSteal"))
|
||||
{
|
||||
component.UnlockedPowers.Add("BloodSteal", newEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (GetBloodEssence(uid) < FixedPoint2.New(200) && _actionEntities.TryGetValue("ActionVampireBloodSteal", out entity))
|
||||
{
|
||||
if (!TryComp(uid, out ActionsComponent? comp))
|
||||
return;
|
||||
|
||||
_action.RemoveAction(uid, entity, comp);
|
||||
_actionContainer.RemoveAction(entity);
|
||||
_actionEntities.Remove("ActionVampireBloodSteal");
|
||||
}
|
||||
|
||||
if (GetBloodEssence(uid) >= FixedPoint2.New(300) && !_actionEntities.TryGetValue("ActionVampireScreech", out entity) && component.CurrentMutation == VampireMutationsType.Hemomancer)
|
||||
{
|
||||
_action.AddAction(uid, ref newEntity , "ActionVampireScreech");
|
||||
if (newEntity != null)
|
||||
{
|
||||
_actionEntities["ActionVampireScreech"] = newEntity.Value;
|
||||
if (!component.UnlockedPowers.ContainsKey("Screech"))
|
||||
{
|
||||
component.UnlockedPowers.Add("Screech", newEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (GetBloodEssence(uid) < FixedPoint2.New(300) && _actionEntities.TryGetValue("ActionVampireScreech", out entity))
|
||||
{
|
||||
if (!TryComp(uid, out ActionsComponent? comp))
|
||||
return;
|
||||
|
||||
_action.RemoveAction(uid, entity, comp);
|
||||
_actionContainer.RemoveAction(entity);
|
||||
_actionEntities.Remove("ActionVampireScreech");
|
||||
}
|
||||
|
||||
//Umbrae
|
||||
|
||||
if (GetBloodEssence(uid) >= FixedPoint2.New(200) && !_actionEntities.TryGetValue("ActionVampireGlare", out entity) && component.CurrentMutation == VampireMutationsType.Umbrae)
|
||||
{
|
||||
_action.AddAction(uid, ref newEntity , "ActionVampireGlare");
|
||||
if (newEntity != null)
|
||||
{
|
||||
_actionEntities["ActionVampireGlare"] = newEntity.Value;
|
||||
if (!component.UnlockedPowers.ContainsKey("Glare"))
|
||||
{
|
||||
component.UnlockedPowers.Add("Glare", newEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (GetBloodEssence(uid) < FixedPoint2.New(200) && _actionEntities.TryGetValue("ActionVampireGlare", out entity))
|
||||
{
|
||||
if (!TryComp(uid, out ActionsComponent? comp))
|
||||
return;
|
||||
|
||||
_action.RemoveAction(uid, entity, comp);
|
||||
_actionContainer.RemoveAction(entity);
|
||||
_actionEntities.Remove("ActionVampireGlare");
|
||||
}
|
||||
|
||||
if (GetBloodEssence(uid) >= FixedPoint2.New(300) && !_actionEntities.TryGetValue("ActionVampireCloakOfDarkness", out entity) && component.CurrentMutation == VampireMutationsType.Umbrae)
|
||||
{
|
||||
_action.AddAction(uid, ref newEntity , "ActionVampireCloakOfDarkness");
|
||||
if (newEntity != null)
|
||||
{
|
||||
_actionEntities["ActionVampireCloakOfDarkness"] = newEntity.Value;
|
||||
if (!component.UnlockedPowers.ContainsKey("CloakOfDarkness"))
|
||||
{
|
||||
component.UnlockedPowers.Add("CloakOfDarkness", newEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (GetBloodEssence(uid) < FixedPoint2.New(300) && _actionEntities.TryGetValue("ActionVampireCloakOfDarkness", out entity))
|
||||
{
|
||||
if (!TryComp(uid, out ActionsComponent? comp))
|
||||
return;
|
||||
|
||||
_action.RemoveAction(uid, entity, comp);
|
||||
_actionContainer.RemoveAction(entity);
|
||||
_actionEntities.Remove("ActionVampireCloakOfDarkness");
|
||||
}
|
||||
|
||||
//Gargantua
|
||||
|
||||
if (GetBloodEssence(uid) >= FixedPoint2.New(200) && !_actionEntities.TryGetValue("ActionVampireUnnaturalStrength", out entity) && component.CurrentMutation == VampireMutationsType.Gargantua)
|
||||
{
|
||||
var vampire = new Entity<VampireComponent>(uid, component);
|
||||
|
||||
UnnaturalStrength(vampire);
|
||||
|
||||
_actionEntities["ActionVampireUnnaturalStrength"] = vampire;
|
||||
}
|
||||
|
||||
if (GetBloodEssence(uid) >= FixedPoint2.New(300) && !_actionEntities.TryGetValue("ActionVampireSupernaturalStrength", out entity) && component.CurrentMutation == VampireMutationsType.Gargantua)
|
||||
{
|
||||
var vampire = new Entity<VampireComponent>(uid, component);
|
||||
|
||||
SupernaturalStrength(vampire);
|
||||
|
||||
_actionEntities["ActionVampireSupernaturalStrength"] = vampire;
|
||||
}
|
||||
|
||||
//Bestia
|
||||
|
||||
if (GetBloodEssence(uid) >= FixedPoint2.New(200) && !_actionEntities.TryGetValue("ActionVampireBatform", out entity) && component.CurrentMutation == VampireMutationsType.Bestia)
|
||||
{
|
||||
_action.AddAction(uid, ref newEntity , "ActionVampireBatform");
|
||||
if (newEntity != null)
|
||||
{
|
||||
_actionEntities["ActionVampireBatform"] = newEntity.Value;
|
||||
if (!component.UnlockedPowers.ContainsKey("PolymorphBat"))
|
||||
{
|
||||
component.UnlockedPowers.Add("PolymorphBat", newEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (GetBloodEssence(uid) < FixedPoint2.New(200) && _actionEntities.TryGetValue("ActionVampireBatform", out entity))
|
||||
{
|
||||
if (!TryComp(uid, out ActionsComponent? comp))
|
||||
return;
|
||||
|
||||
_action.RemoveAction(uid, entity, comp);
|
||||
_actionContainer.RemoveAction(entity);
|
||||
_actionEntities.Remove("ActionVampireBatform");
|
||||
}
|
||||
|
||||
if (GetBloodEssence(uid) >= FixedPoint2.New(300) && !_actionEntities.TryGetValue("ActionVampireMouseform", out entity) && component.CurrentMutation == VampireMutationsType.Bestia)
|
||||
{
|
||||
_action.AddAction(uid, ref newEntity , "ActionVampireMouseform");
|
||||
if (newEntity != null)
|
||||
{
|
||||
_actionEntities["ActionVampireMouseform"] = newEntity.Value;
|
||||
if (!component.UnlockedPowers.ContainsKey("PolymorphMouse"))
|
||||
{
|
||||
component.UnlockedPowers.Add("PolymorphMouse", newEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (GetBloodEssence(uid) < FixedPoint2.New(300) && _actionEntities.TryGetValue("ActionVampireMouseform", out entity))
|
||||
{
|
||||
if (!TryComp(uid, out ActionsComponent? comp))
|
||||
return;
|
||||
|
||||
_action.RemoveAction(uid, entity, comp);
|
||||
_actionContainer.RemoveAction(entity);
|
||||
_actionEntities.Remove("ActionVampireMouseform");
|
||||
}
|
||||
}
|
||||
|
||||
private FixedPoint2 GetBloodEssence(EntityUid vampire)
|
||||
{
|
||||
if (!TryComp<VampireComponent>(vampire, out var comp))
|
||||
return 0;
|
||||
|
||||
if (!comp.Balance.TryGetValue(VampireComponent.CurrencyProto, out var val))
|
||||
return 0;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
private void DoSpaceDamage(Entity<VampireComponent> vampire)
|
||||
{
|
||||
_damageableSystem.TryChangeDamage(vampire, VampireComponent.SpaceDamage, true, origin: vampire);
|
||||
_popup.PopupEntity(Loc.GetString("vampire-startlight-burning"), vampire, vampire, PopupType.LargeCaution);
|
||||
}
|
||||
private bool IsInSpace(EntityUid vampireUid)
|
||||
{
|
||||
var vampireTransform = Transform(vampireUid);
|
||||
var vampirePosition = _transform.GetMapCoordinates(vampireTransform);
|
||||
|
||||
if (!_mapMan.TryFindGridAt(vampirePosition, out _, out var grid))
|
||||
return true;
|
||||
|
||||
if (!_mapSystem.TryGetTileRef(vampireUid, grid, vampireTransform.Coordinates, out var tileRef))
|
||||
return true;
|
||||
|
||||
return tileRef.Tile.IsEmpty || tileRef.IsSpace();
|
||||
}
|
||||
|
||||
private bool IsNearPrayable(EntityUid vampireUid)
|
||||
{
|
||||
var mapCoords = _transform.GetMapCoordinates(vampireUid);
|
||||
|
||||
var nearbyPrayables = _entityLookup.GetEntitiesInRange<PrayableComponent>(mapCoords, 5);
|
||||
foreach (var prayable in nearbyPrayables)
|
||||
{
|
||||
if (Transform(prayable).Anchored)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OnMutationSelected(EntityUid uid, VampireComponent component, VampireMutationPrototypeSelectedMessage args)
|
||||
{
|
||||
if (component.CurrentMutation == args.SelectedId)
|
||||
return;
|
||||
ChangeMutation(uid, args.SelectedId, component);
|
||||
}
|
||||
private void ChangeMutation(EntityUid uid, VampireMutationsType newMutation, VampireComponent component)
|
||||
{
|
||||
var vampire = new Entity<VampireComponent>(uid, component);
|
||||
if (SubtractBloodEssence(vampire, FixedPoint2.New(50)))
|
||||
{
|
||||
component.CurrentMutation = newMutation;
|
||||
UpdateUi(uid, component);
|
||||
var ev = new VampireBloodChangedEvent();
|
||||
RaiseLocalEvent(uid, ev);
|
||||
TryOpenUi(uid, component.Owner, component);
|
||||
}
|
||||
}
|
||||
|
||||
private void GetState(EntityUid uid, VampireComponent component, ref ComponentGetState args)
|
||||
{
|
||||
args.State = new VampireMutationComponentState
|
||||
{
|
||||
SelectedMutation = component.CurrentMutation
|
||||
};
|
||||
}
|
||||
|
||||
private void TryOpenUi(EntityUid uid, EntityUid user, VampireComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
if (!TryComp(user, out ActorComponent? actor))
|
||||
return;
|
||||
_uiSystem.TryToggleUi(uid, VampireMutationUiKey.Key, actor.PlayerSession);
|
||||
}
|
||||
|
||||
public void UpdateUi(EntityUid uid, VampireComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
var state = new VampireMutationBoundUserInterfaceState(component.VampireMutations, component.CurrentMutation);
|
||||
_uiSystem.SetUiState(uid, VampireMutationUiKey.Key, state);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
namespace Content.Shared.Store.Events;
|
||||
|
||||
public record struct StorePurchasedListingEvent(EntityUid Purchaser, ListingData Listing, EntityUid? Item, EntityUid? Action);
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Linq;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Store.Events;
|
||||
using Content.Shared.Store.Components;
|
||||
using Content.Shared.StoreDiscount.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
|
|
|||
268
Content.Shared/Vampire/VampireComponent.cs
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
using Content.Shared.Body.Prototypes;
|
||||
using Content.Shared.Chat.Prototypes;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Content.Shared.Store;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Vampire.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class VampireComponent : Component
|
||||
{
|
||||
//Static prototype references
|
||||
[ValidatePrototypeId<StatusEffectPrototype>]
|
||||
public static readonly string SleepStatusEffectProto = "ForcedSleep";
|
||||
[ValidatePrototypeId<EmotePrototype>]
|
||||
public static readonly string ScreamEmoteProto = "Scream";
|
||||
[ValidatePrototypeId<CurrencyPrototype>]
|
||||
public static readonly string CurrencyProto = "BloodEssence";
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly), DataField("defaultMutation")]
|
||||
public VampireMutationsType DefaultMutation = VampireMutationsType.None;
|
||||
[ViewVariables(VVAccess.ReadOnly), DataField("currentMutation")]
|
||||
public VampireMutationsType CurrentMutation = VampireMutationsType.None;
|
||||
|
||||
public readonly HashSet<VampireMutationsType> VampireMutations = new()
|
||||
{
|
||||
VampireMutationsType.None,
|
||||
VampireMutationsType.Hemomancer,
|
||||
VampireMutationsType.Umbrae,
|
||||
VampireMutationsType.Gargantua,
|
||||
//VampireMutationsType.Dantalion,
|
||||
VampireMutationsType.Bestia
|
||||
};
|
||||
|
||||
public static readonly EntityWhitelist AcceptableFoods = new()
|
||||
{
|
||||
Tags = new() { "Pill" }
|
||||
};
|
||||
[ValidatePrototypeId<MetabolizerTypePrototype>]
|
||||
public static readonly string MetabolizerVampire = "Vampire";
|
||||
[ValidatePrototypeId<MetabolizerTypePrototype>]
|
||||
public static readonly string MetabolizerBloodsucker = "Bloodsucker";
|
||||
|
||||
public static readonly DamageSpecifier MeleeDamage = new()
|
||||
{
|
||||
DamageDict = new Dictionary<string, FixedPoint2>() { { "Slash", 10 } }
|
||||
};
|
||||
public static readonly DamageSpecifier HolyDamage = new()
|
||||
{
|
||||
DamageDict = new Dictionary<string, FixedPoint2>() { { "Burn", 10 } }
|
||||
};
|
||||
public static readonly DamageSpecifier SpaceDamage = new()
|
||||
{
|
||||
DamageDict = new Dictionary<string, FixedPoint2>() { { "Burn", 2.5 } }
|
||||
};
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>]
|
||||
public static readonly string MutationsActionPrototype = "ActionVampireOpenMutationsMenu";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public EntityUid? MutationsAction;
|
||||
|
||||
public readonly List<ProtoId<EntityPrototype>> BaseVampireActions = new()
|
||||
{
|
||||
"ActionVampireToggleFangs",
|
||||
"ActionVampireHypnotise"
|
||||
};
|
||||
|
||||
[ValidatePrototypeId<VampirePowerProtype>]
|
||||
public static readonly string DrinkBloodPrototype = "DrinkBlood";
|
||||
|
||||
/// <summary>
|
||||
/// Total blood drank, counter for end of round screen
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float TotalBloodDrank = 0;
|
||||
|
||||
/// <summary>
|
||||
/// How much blood per mouthful
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float MouthVolume = 5;
|
||||
|
||||
/// <summary>
|
||||
/// All unlocked abilities
|
||||
/// </summary>
|
||||
public Dictionary<string, EntityUid?> UnlockedPowers = new();
|
||||
|
||||
/// <summary>
|
||||
/// Current available balance, used to sync currency across heirlooms and add essence as we feed
|
||||
/// </summary>
|
||||
public Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> Balance = default!;
|
||||
|
||||
public readonly SoundSpecifier BloodDrainSound = new SoundPathSpecifier("/Audio/Items/drink.ogg", new AudioParams() { Volume = -3f, MaxDistance = 3f });
|
||||
public readonly SoundSpecifier AbilityPurchaseSound = new SoundPathSpecifier("/Audio/Items/drink.ogg");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Contains all details about the ability and its effects or restrictions
|
||||
/// </summary>
|
||||
[DataDefinition]
|
||||
[Prototype("vampirePower")]
|
||||
public sealed partial class VampirePowerProtype : IPrototype
|
||||
{
|
||||
[ViewVariables]
|
||||
[IdDataField]
|
||||
public string ID { get; private set; }
|
||||
|
||||
[DataField]
|
||||
public float ActivationCost = 0;
|
||||
[DataField]
|
||||
public bool UsableWhileCuffed = true;
|
||||
[DataField]
|
||||
public bool UsableWhileStunned = true;
|
||||
[DataField]
|
||||
public bool UsableWhileMuffled = true;
|
||||
[DataField]
|
||||
public DamageSpecifier? Damage = default!;
|
||||
[DataField]
|
||||
public TimeSpan? Duration = TimeSpan.Zero;
|
||||
[DataField]
|
||||
public TimeSpan? DoAfterDelay = TimeSpan.Zero;
|
||||
[DataField]
|
||||
public string? PolymorphTarget = default!;
|
||||
[DataField]
|
||||
public float Upkeep = 0;
|
||||
}
|
||||
|
||||
[DataDefinition]
|
||||
[Prototype("vampirePassive")]
|
||||
public sealed partial class VampirePassiveProtype : IPrototype
|
||||
{
|
||||
[ViewVariables]
|
||||
[IdDataField]
|
||||
public string ID { get; private set; }
|
||||
|
||||
[DataField(required: true)]
|
||||
public string CatalogEntry = string.Empty;
|
||||
|
||||
[DataField]
|
||||
public ComponentRegistry CompsToAdd = new();
|
||||
|
||||
[DataField]
|
||||
public ComponentRegistry CompsToRemove = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks an entity as taking damage when hit by a bible, rather than being healed
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class UnholyComponent : Component { }
|
||||
|
||||
/// <summary>
|
||||
/// Marks a container as a coffin, for the purposes of vampire healing
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class CoffinComponent : Component { }
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class VampireFangsExtendedComponent : Component { }
|
||||
|
||||
/// <summary>
|
||||
/// When added, heals the entity by the specified amount
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class VampireHealingComponent : Component
|
||||
{
|
||||
public double NextHealTick = 0;
|
||||
|
||||
public DamageSpecifier? Healing = default!;
|
||||
}
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class VampireDeathsEmbraceComponent : Component
|
||||
{
|
||||
[ViewVariables()]
|
||||
public EntityUid? HomeCoffin = default!;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
public float Cost = 0;
|
||||
|
||||
[DataField]
|
||||
public DamageSpecifier CoffinHealing = default!;
|
||||
}
|
||||
[RegisterComponent]
|
||||
public sealed partial class VampireSealthComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float NextStealthTick = 0;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float Upkeep = 0;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum VampireMutationsType : byte
|
||||
{
|
||||
None,
|
||||
Hemomancer,
|
||||
Umbrae,
|
||||
Gargantua,
|
||||
Dantalion,
|
||||
Bestia
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class VampireMutationComponentState : ComponentState
|
||||
{
|
||||
public VampireMutationsType SelectedMutation;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class VampireMutationBoundUserInterfaceState : BoundUserInterfaceState
|
||||
{
|
||||
public readonly HashSet<VampireMutationsType> MutationList;
|
||||
public readonly VampireMutationsType SelectedMutation;
|
||||
|
||||
public VampireMutationBoundUserInterfaceState(HashSet<VampireMutationsType> mutationList, VampireMutationsType selectedId)
|
||||
{
|
||||
MutationList = mutationList;
|
||||
SelectedMutation = selectedId;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class VampireMutationPrototypeSelectedMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly VampireMutationsType SelectedId;
|
||||
|
||||
public VampireMutationPrototypeSelectedMessage(VampireMutationsType selectedId)
|
||||
{
|
||||
SelectedId = selectedId;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum VampireMutationUiKey : byte
|
||||
{
|
||||
Key
|
||||
}
|
||||
|
||||
/*[Serializable, NetSerializable]
|
||||
public enum VampirePowerKey : byte
|
||||
{
|
||||
ToggleFangs,
|
||||
Glare,
|
||||
DeathsEmbrace,
|
||||
Screech,
|
||||
Hypnotise,
|
||||
Polymorph,
|
||||
NecroticTouch,
|
||||
BloodSteal,
|
||||
CloakOfDarkness,
|
||||
StellarWeakness,
|
||||
SummonHeirloom,
|
||||
|
||||
//Passives
|
||||
UnnaturalStrength,
|
||||
SupernaturalStrength
|
||||
}*/
|
||||
61
Content.Shared/Vampire/VampireEvents.cs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
using Content.Shared.Actions;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Vampire.Components;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Shared.Vampire;
|
||||
|
||||
//Use power events
|
||||
public sealed partial class VampireToggleFangsEvent : VampireSelfPowerEvent { }
|
||||
public sealed partial class VampireOpenMutationsMenu : InstantActionEvent { }
|
||||
public sealed partial class VampireScreechEvent : VampireSelfPowerEvent { }
|
||||
public sealed partial class VampirePolymorphEvent : VampireSelfPowerEvent { }
|
||||
public sealed partial class VampireBloodStealEvent : VampireSelfPowerEvent { }
|
||||
public sealed partial class VampireCloakOfDarknessEvent : VampireSelfPowerEvent { }
|
||||
|
||||
public sealed partial class VampireGlareEvent : VampireTargetedPowerEvent { }
|
||||
public sealed partial class VampireHypnotiseEvent : VampireTargetedPowerEvent { }
|
||||
|
||||
|
||||
public abstract partial class VampireSelfPowerEvent : InstantActionEvent
|
||||
{
|
||||
[DataField(customTypeSerializer: typeof(PrototypeIdSerializer<VampirePowerProtype>))]
|
||||
public string DefinitionName = default!;
|
||||
};
|
||||
public abstract partial class VampireTargetedPowerEvent : EntityTargetActionEvent
|
||||
{
|
||||
[DataField(customTypeSerializer: typeof(PrototypeIdSerializer<VampirePowerProtype>))]
|
||||
public string DefinitionName = default!;
|
||||
};
|
||||
public sealed partial class VampirePassiveActionEvent : BaseActionEvent
|
||||
{
|
||||
[DataField(customTypeSerializer: typeof(PrototypeIdSerializer<VampirePowerProtype>))]
|
||||
public string DefinitionName = default!;
|
||||
};
|
||||
|
||||
//Purchase passive events
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class VampirePurchaseUnnaturalStrength : EntityEventArgs { }
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class VampireBloodChangedEvent : EntityEventArgs { }
|
||||
|
||||
//Doafter events
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class VampireDrinkBloodDoAfterEvent : DoAfterEvent
|
||||
{
|
||||
[DataField]
|
||||
public float Volume = 0;
|
||||
|
||||
public override DoAfterEvent Clone() => this;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class VampireHypnotiseDoAfterEvent : DoAfterEvent
|
||||
{
|
||||
[DataField]
|
||||
public TimeSpan? Duration = TimeSpan.Zero;
|
||||
|
||||
public override DoAfterEvent Clone() => this;
|
||||
}
|
||||
BIN
Resources/Audio/Ambience/Antag/vampire_start.ogg
Normal file
BIN
Resources/Audio/Effects/Vampire/glare.ogg
Normal file
BIN
Resources/Audio/Effects/Vampire/screech_tone.ogg
Normal file
72
Resources/Locale/en-US/_strings/Vampires/vampires.ftl
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
vampires-title = Vampires
|
||||
|
||||
vampire-fangs-extended-examine = You see a glint of [color=white]sharp teeth[/color]
|
||||
vampire-fangs-extended = You extend your fangs
|
||||
vampire-fangs-retracted = You retract your fangs
|
||||
|
||||
vampire-blooddrink-empty = This body is devoid of blood
|
||||
vampire-blooddrink-rotted = Their body is rotting and their blood tainted
|
||||
vampire-blooddrink-zombie = Their blood is tainted by death
|
||||
|
||||
vampire-startlight-burning = You feel your skin burn in the light of a thousand suns
|
||||
|
||||
vampire-not-enough-blood = You dont have enough blood
|
||||
vampire-cuffed = You need your hands free!
|
||||
vampire-stunned = You cant concentrate enough!
|
||||
vampire-muffled = Your mouth is muzzled
|
||||
vampire-full-stomach = You are bloated with blood
|
||||
|
||||
vampire-deathsembrace-bind = Feels like home
|
||||
|
||||
vampire-ingest-holyblood = Your mouth burns!
|
||||
|
||||
vampire-cloak-enable = You wrap shadows around your form
|
||||
vampire-cloak-disable = You release your grip on the shadows
|
||||
|
||||
vampire-bloodsteal-other = You feel blood being ripped from your body!
|
||||
vampire-hypnotise-other = {CAPITALIZE(THE($user))} stares deeply into {MAKEPLURAL(THE($target))} eyes!
|
||||
|
||||
store-currency-display-blood-essence = Blood Essence
|
||||
store-category-vampirepowers = Powers
|
||||
store-category-vampirepassives = Passives
|
||||
|
||||
#Powers
|
||||
vampire-power-summonheirloom = Summon Heirloom
|
||||
vampire-power-summonheirloom-description = Summon a family heirloom, gifted by lilith herself.
|
||||
|
||||
vampire-power-blessing = Blessing of Lilith
|
||||
vampire-power-blessing-description = Swear your soul to Lilith, receive her blessing, and feast upon the bounty around you.
|
||||
|
||||
vampire-power-togglefangs = Toggle Fangs
|
||||
vampire-power-togglefangs-description = Extend or retract your fangs. Walking around with your fangs out might reveal your true nature.
|
||||
|
||||
vampire-power-glare = Glare
|
||||
vampire-power-glare-description = Release a blinding flash from your eyes, stunning a unprotected mortal for 10 seconds. Activation Cost: 20 Essence. Cooldown: 60 Seconds
|
||||
|
||||
vampire-power-hypnotise = Hypnotise
|
||||
vampire-power-hypnotise-description = Stare deeply into a mortals eyes, forcing them to sleep for 60 seconds. Activation Cost: 20 Essence. Activation Delay: 5 Seconds. Cooldown: 5 Minutes
|
||||
|
||||
vampire-power-screech = Screech
|
||||
vampire-power-screech-description = Release a piercing scream, stunning unprotected mortals and shattering fragile objects nearby. Activation Cost: 20 Essence. Activation Delay: 5 Seconds. Cooldown: 5 Minutes
|
||||
|
||||
vampire-power-bloodsteal = Blood Steal
|
||||
vampire-power-bloodsteal-description = Wrench the blood from all bodies nearby - living or dead. Activation Cost: 20 Essence. Cooldown: 60 Seconds
|
||||
|
||||
vampire-power-batform = Bat Form
|
||||
vampire-power-batform-description = Assume for form of a bat. Fast, Hard to Hit, Likes fruit. Activation Cost: 20 Essence. Cooldown: 30 Seconds
|
||||
|
||||
vampire-power-mouseform = Mouse Form
|
||||
vampire-power-mouseform-description = Assume for form of a mouse. Fast, Small, Immune to doors. Activation Cost: 20 Essence. Cooldown: 30 Seconds
|
||||
|
||||
vampire-power-cloakofdarkness = Cloak of Darkness
|
||||
vampire-power-cloakofdarkness-description = Cloak yourself from mortal eyes, rendering you invisible while stationary. Activation Cost: 30 Essence. Upkeep: 1 Essence/Second Cooldown: 10 Seconds
|
||||
|
||||
#Passives
|
||||
vampire-passive-unholystrength = Unholy Strength
|
||||
vampire-passive-unholystrength-description = Infuse your upper body muscles with essence, granting you claws and increased strength. Effect: 10 Slash per hit
|
||||
|
||||
vampire-passive-supernaturalstrength = Supernatural Strength
|
||||
vampire-passive-supernaturalstrength-description = Increase your upper body muscles strength further, no barrier shall stand in your way. Effect: 15 Slash per hit, able to pry open doors by hand.
|
||||
|
||||
vampire-passive-deathsembrace = Deaths Embrace
|
||||
vampire-passive-deathsembrace-description = Embrace death and it shall pass you over. Effect: Heal when in a coffin, automatically return to your coffin upon death for 100 blood essence.
|
||||
|
|
@ -3,6 +3,9 @@ bible-heal-success-others = {CAPITALIZE(THE($user))} hits {THE($target)} with {T
|
|||
bible-heal-success-none-self = You hit {THE($target)} with {THE($bible)}, but they have no wounds you can heal!
|
||||
bible-heal-success-none-others = {CAPITALIZE(THE($user))} hits {THE($target)} with {THE($bible)}!
|
||||
|
||||
bible-damage-unholy-self = You hit {THE($target)} with {THE($bible)}, and they are burned by a flash of holy fire!
|
||||
bible-damage-unholy-others = {CAPITALIZE(THE($user))} hits {THE($target)} with {THE($bible)}, and they are burned by a flash of holy fire!
|
||||
|
||||
bible-heal-fail-self = You hit {THE($target)} with {THE($bible)}, and it lands with a sad thwack, dazing {OBJECT($target)}!
|
||||
bible-heal-fail-others = {CAPITALIZE(THE($user))} hits {THE($target)} with {THE($bible)}, and it lands with a sad thack, dazing {OBJECT($target)}!
|
||||
bible-sizzle = The book sizzles in your hands!
|
||||
|
|
|
|||
18
Resources/Locale/ru-RU/_prototypes/actions/vampire.ftl
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
ent-ActionVampireOpenMutationsMenu = Меню мутаций
|
||||
.desc = Открывает меню с мутациями вампира.
|
||||
ent-ActionVampireToggleFangs = Высунуть клыки
|
||||
.desc = Выдвигайте или втягивайте клыки. Если вы будете ходить с выпущенными клыками, это может раскрыть вашу истинную сущность.
|
||||
ent-ActionVampireGlare = Блик
|
||||
.desc = Выпустите из глаз ослепительную вспышку, оглушающую незащищенного смертного на 10 секунд. Стоимость активации: 20 крови. Время перезарядки: 60 секунд
|
||||
ent-ActionVampireHypnotise = Гипноз
|
||||
.desc = Глубоко загляните в глаза смертного, заставляя его уснуть на 60 секунд. Стоимость активации: 20 крови. Задержка активации: 5 секунд. Время перезарядки: 5 минут
|
||||
ent-ActionVampireScreech = Визг
|
||||
.desc = Издайте пронзительный крик, оглушая незащищенных смертных и разбивая вдребезги хрупкие предметы поблизости. Стоимость активации: 20 крови. Задержка активации: 5 секунд. Время перезарядки: 5 минут
|
||||
ent-ActionVampireBloodSteal = Кража крови
|
||||
.desc = Выжимает кровь из всех тел поблизости - живых или мертвых. Стоимость активации: 20 крови. Время перезарядки: 60 секунд
|
||||
ent-ActionVampireBatform = Форма летучей мыши
|
||||
.desc = Принимает форму летучей мыши. Быстрая, трудноуязвимая, любит фрукты. Стоимость активации: 20 крови. Время перезарядки: 30 секунд
|
||||
ent-ActionVampireMouseform = Мышиная форма
|
||||
.desc = Примите облик мыши. Быстрая, маленькая, невосприимчива к дверям. Стоимость активации: 20 крови. Время перезарядки: 30 секунд
|
||||
ent-ActionVampireCloakOfDarkness = Плащ тьмы
|
||||
.desc = Замаскируйте себя от глаз смертных, делая вас невидимым в неподвижном состоянии. Стоимость активации: 30 крови. Трата: 1 кровь/секунда Время перезарядки: 10 секунд
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
ent-VampireSurviveObjective = Выжить
|
||||
.desc = Я должен выжить, чего бы этого мне не стоило.
|
||||
ent-VampireEscapeObjective = Улететь со станции живым и свободным.
|
||||
.desc = Я должен улететь на эвакуационном шаттле. Свободным.
|
||||
94
Resources/Locale/ru-RU/_strings/Vampires/vampires.ftl
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
vampires-title = Вампиры
|
||||
|
||||
vampire-fangs-extended-examine = Вы видите блеск [color=white]острых клыков[/color].
|
||||
vampire-fangs-extended = Вы вытягиваете свои клыки
|
||||
vampire-fangs-retracted = Вы втягиваете свои клыки
|
||||
|
||||
vampire-blooddrink-empty = Это тело лишено крови
|
||||
vampire-blooddrink-rotted = Их тела гниют, а кровь запятнана
|
||||
vampire-blooddrink-zombie = Их кровь запятнана смертью
|
||||
|
||||
vampire-startlight-burning = Вы чувствуете, как ваша кожа горит в свете тысячи солнц
|
||||
|
||||
vampire-not-enough-blood = У вас недостаточно крови
|
||||
vampire-cuffed = Вам нужны свободные руки!
|
||||
vampire-stunned = Вы не сможете сосредоточиться!
|
||||
vampire-muffled = Ваш рот закрыт намордником
|
||||
vampire-full-stomach = Вас раздуло от крови
|
||||
|
||||
vampire-deathsembrace-bind = Чувствуешь себя как дома
|
||||
|
||||
vampire-ingest-holyblood = Ваш рот горит!
|
||||
|
||||
vampire-cloak-enable = Вы окутываете тенью свою форму
|
||||
vampire-cloak-disable = Вы ослабляете хватку теней.
|
||||
|
||||
vampire-bloodsteal-other = Вы чувствуете, как кровь вырывается из вашего тела!
|
||||
vampire-hypnotise-other = {CAPITALIZE(THE($user))} пристально вглядывается в {MAKEPLURAL(THE($target))} глаза!
|
||||
|
||||
store-currency-display-blood-essence = Кровавая эссенция
|
||||
store-category-vampirepowers = Силы
|
||||
store-category-vampirepassives = Пассивные
|
||||
|
||||
#Powers
|
||||
|
||||
#Passives
|
||||
vampire-passive-unholystrength = Нечестивая сила
|
||||
vampire-passive-unholystrength-description = Наполните мышцы верхней части тела кровью, наделяя вас когтями и повышенной силой. Эффект: 10 порезов за удар
|
||||
|
||||
vampire-passive-supernaturalstrength = Сверхъестественная сила
|
||||
vampire-passive-supernaturalstrength-description = Увеличьте силу мышц верхней части тела, и ни одна преграда не встанет на вашем пути. Эффект: 15 порезов за удар, возможность открывать двери руками.
|
||||
|
||||
vampire-passive-deathsembrace = Объятия смерти
|
||||
vampire-passive-deathsembrace-description = Примите смерть, и она обойдет вас стороной. Эффект: исцеление в гробу, автоматическое возвращение в гроб после смерти за 100 эссенции крови.
|
||||
|
||||
#Mutation menu
|
||||
|
||||
vampire-mutation-menu-ui-window-name = Меню мутаций
|
||||
|
||||
vampire-mutation-none-info = Ничего не выбрано
|
||||
|
||||
vampire-mutation-hemomancer-info =
|
||||
Гемомансер
|
||||
|
||||
Фокусируеться на кровавой магии и манипуляции крови вокруг себя.
|
||||
|
||||
Способности:
|
||||
|
||||
- Визг
|
||||
- Кража крови
|
||||
|
||||
vampire-mutation-umbrae-info =
|
||||
Тень
|
||||
|
||||
Фокусируется на темноте, стелсе, мобильности.
|
||||
|
||||
Способности:
|
||||
|
||||
- Блик
|
||||
- Плащ тьмы
|
||||
|
||||
vampire-mutation-gargantua-info =
|
||||
Гаргантюа
|
||||
|
||||
Фокусируется на ближнем уроне и стойкости.
|
||||
|
||||
Способности:
|
||||
|
||||
- Нечестивая сила
|
||||
- Сверхъестественная сила
|
||||
|
||||
vampire-mutation-bestia-info =
|
||||
Бестия
|
||||
|
||||
Фокусируется на превращении и собирании трофеев
|
||||
|
||||
Способности:
|
||||
|
||||
- Форма летучей мыши
|
||||
- Мышиная форма
|
||||
|
||||
## Objectives
|
||||
|
||||
objective-condition-drain-title = Выпить { $count } крови.
|
||||
objective-condition-drain-description = Я должен выпить { $count } крови. Это необходимо для моего выживания и дальнейшей эволюции.
|
||||
|
|
@ -6,7 +6,8 @@ admin-verb-make-nuclear-operative = Сделать цель одиноким Я
|
|||
admin-verb-make-pirate = Сделать цель пиратом\капером. Учтите, что это не меняет игровой режим.
|
||||
admin-verb-make-head-rev = Сделать цель главой революции.
|
||||
admin-verb-make-thief = Сделать цель вором.
|
||||
admin-verb-make-changeling = Сделать цель генокрадом
|
||||
admin-verb-make-changeling = Сделать цель генокрадом.
|
||||
admin-verb-make-vampire = Сделать цель вампиром.
|
||||
admin-verb-text-make-traitor = Сделать предателем
|
||||
admin-verb-text-make-initial-infected = Сделать нулевым пациентом
|
||||
admin-verb-text-make-zombie = Сделать зомби
|
||||
|
|
@ -15,3 +16,4 @@ admin-verb-text-make-pirate = Сделать пиратом
|
|||
admin-verb-text-make-head-rev = Сделать Главой революции
|
||||
admin-verb-text-make-thief = Сделать вором
|
||||
admin-verb-text-make-changeling = Сделать генокрадом
|
||||
admin-verb-text-make-vampire = Сделать вампиром
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
vampire-roundend-name = вампир
|
||||
objective-issuer-vampire = [color=red]Жажда крови[/color]
|
||||
roundend-prepend-vampire-drained-named = [color=white]{ $name }[/color] выпил в общей сложности [color=red]{ $number }[/color] крови.
|
||||
roundend-prepend-vampire-drained = Кто-то выпил в общей сложности [color=red]{ $number }[/color] крови.
|
||||
vampire-gamemode-title = Вампиры
|
||||
vampire-gamemode-description = Кровожадные вампиры пробрались на станцию чтобы вдоволь напиться крови!
|
||||
vampire-role-greeting =
|
||||
Вы вампир, который пробрался на станцию под видом работника!
|
||||
Ваши задачи указаны в меню персонажа.
|
||||
Пейте кровь и эволюционируйте, чтобы выполнить их!
|
||||
vampire-role-greeting-short = Вы вампир, который пробрался на станцию под видом работника!
|
||||
roles-antag-vamire-name = Вампир
|
||||
|
|
@ -9,3 +9,4 @@ metabolizer-type-plant = Растение
|
|||
metabolizer-type-dwarf = Дварф
|
||||
metabolizer-type-moth = Ниан
|
||||
metabolizer-type-arachnid = Арахнид
|
||||
metabolizer-type-vampire = Вампир
|
||||
176
Resources/Prototypes/Actions/vampire.yml
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
- type: entity
|
||||
id: ActionVampireOpenMutationsMenu
|
||||
name: Mutations menu
|
||||
description: "Opens menu with vampires mutations."
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: InstantAction
|
||||
itemIconStyle: NoItem
|
||||
icon:
|
||||
sprite: Interface/Actions/actions_vampire.rsi
|
||||
state: summonheirloom
|
||||
event:
|
||||
!type:VampireOpenMutationsMenu
|
||||
useDelay: 5
|
||||
|
||||
- type: entity
|
||||
id: ActionVampireToggleFangs
|
||||
name: Toggle Fangs
|
||||
description: "Extend or retract your fangs. Walking around with your fangs out might reveal your true nature."
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: InstantAction
|
||||
checkCanInteract: false
|
||||
priority: 1
|
||||
itemIconStyle: NoItem
|
||||
icon:
|
||||
sprite: Interface/Actions/actions_vampire.rsi
|
||||
state: fangs_retracted
|
||||
iconOn:
|
||||
sprite: Interface/Actions/actions_vampire.rsi
|
||||
state: fangs_extended
|
||||
event:
|
||||
!type:VampireToggleFangsEvent
|
||||
definitionName: ToggleFangs
|
||||
|
||||
- type: entity
|
||||
id: ActionVampireGlare
|
||||
name: Glare
|
||||
description: "Release a blinding flash from your eyes, stunning a unprotected mortal for 10 seconds. Activation Cost: 20 Essence. Cooldown: 60 Seconds"
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: EntityTargetAction
|
||||
whitelist:
|
||||
components:
|
||||
- Body
|
||||
priority: 2
|
||||
itemIconStyle: NoItem
|
||||
icon:
|
||||
sprite: Interface/Actions/actions_vampire.rsi
|
||||
state: glare
|
||||
sound: !type:SoundPathSpecifier
|
||||
path: /Audio/Effects/Vampire/glare.ogg
|
||||
event:
|
||||
!type:VampireGlareEvent
|
||||
definitionName: Glare
|
||||
useDelay: 60
|
||||
|
||||
- type: entity
|
||||
id: ActionVampireHypnotise
|
||||
name: Hypnotise
|
||||
description: "Stare deeply into a mortals eyes, forcing them to sleep for 60 seconds. Activation Cost: 20 Essence. Activation Delay: 5 Seconds. Cooldown: 5 Minutes"
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: EntityTargetAction
|
||||
whitelist:
|
||||
components:
|
||||
- HumanoidAppearance
|
||||
canTargetSelf: false
|
||||
interactOnMiss: false
|
||||
priority: 2
|
||||
itemIconStyle: NoItem
|
||||
icon:
|
||||
sprite: Interface/Actions/actions_vampire.rsi
|
||||
state: hypnotise
|
||||
event:
|
||||
!type:VampireHypnotiseEvent
|
||||
definitionName: Hypnotise
|
||||
useDelay: 300
|
||||
|
||||
- type: entity
|
||||
id: ActionVampireScreech
|
||||
name: Screech
|
||||
description: "Release a piercing scream, stunning unprotected mortals and shattering fragile objects nearby. Activation Cost: 20 Essence. Activation Delay: 5 Seconds. Cooldown: 5 Minutes"
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: InstantAction
|
||||
checkCanInteract: false
|
||||
priority: 2
|
||||
itemIconStyle: NoItem
|
||||
icon:
|
||||
sprite: Interface/Actions/actions_vampire.rsi
|
||||
state: screech
|
||||
sound: !type:SoundPathSpecifier
|
||||
path: /Audio/Effects/Vampire/screech_tone.ogg
|
||||
event:
|
||||
!type:VampireScreechEvent
|
||||
definitionName: Screech
|
||||
useDelay: 60
|
||||
|
||||
- type: entity
|
||||
id: ActionVampireBloodSteal
|
||||
name: Blood Steal
|
||||
description: "Wrench the blood from all bodies nearby - living or dead. Activation Cost: 20 Essence. Cooldown: 60 Seconds"
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: InstantAction
|
||||
checkCanInteract: false
|
||||
priority: 2
|
||||
itemIconStyle: NoItem
|
||||
icon:
|
||||
sprite: Interface/Actions/actions_vampire.rsi
|
||||
state: bloodsteal
|
||||
sound: !type:SoundPathSpecifier
|
||||
path: /Audio/Effects/demon_consume.ogg
|
||||
event:
|
||||
!type:VampireBloodStealEvent
|
||||
definitionName: BloodSteal
|
||||
useDelay: 60
|
||||
|
||||
- type: entity
|
||||
id: ActionVampireBatform
|
||||
name: Bat Form
|
||||
description: "Assume for form of a bat. Fast, Hard to Hit, Likes fruit. Activation Cost: 20 Essence. Cooldown: 30 Seconds"
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: InstantAction
|
||||
checkCanInteract: false
|
||||
priority: 2
|
||||
itemIconStyle: NoItem
|
||||
icon:
|
||||
sprite: Interface/Actions/actions_vampire.rsi
|
||||
state: batform
|
||||
sound: !type:SoundPathSpecifier
|
||||
path: /Audio/Effects/teleport_arrival.ogg
|
||||
event:
|
||||
!type:VampirePolymorphEvent
|
||||
definitionName: PolymorphBat
|
||||
useDelay: 30
|
||||
|
||||
- type: entity
|
||||
id: ActionVampireMouseform
|
||||
name: Mouse Form
|
||||
description: "Assume for form of a mouse. Fast, Small, Immune to doors. Activation Cost: 20 Essence. Cooldown: 30 Seconds"
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: InstantAction
|
||||
checkCanInteract: false
|
||||
priority: 2
|
||||
itemIconStyle: NoItem
|
||||
icon:
|
||||
sprite: Interface/Actions/actions_vampire.rsi
|
||||
state: mouseform
|
||||
sound: !type:SoundPathSpecifier
|
||||
path: /Audio/Effects/teleport_arrival.ogg
|
||||
event:
|
||||
!type:VampirePolymorphEvent
|
||||
definitionName: PolymorphMouse
|
||||
useDelay: 30
|
||||
|
||||
- type: entity
|
||||
id: ActionVampireCloakOfDarkness
|
||||
name: Cloak of Darkness
|
||||
description: "Cloak yourself from mortal eyes, rendering you invisible while stationary. Activation Cost: 30 Essence. Upkeep: 1 Essence/Second Cooldown: 10 Seconds"
|
||||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: InstantAction
|
||||
checkCanInteract: false
|
||||
priority: 2
|
||||
itemIconStyle: NoItem
|
||||
icon:
|
||||
sprite: Interface/Actions/actions_vampire.rsi
|
||||
state: cloakofdarkness
|
||||
event:
|
||||
!type:VampireCloakOfDarknessEvent
|
||||
definitionName: CloakOfDarkness
|
||||
useDelay: 10
|
||||
|
|
@ -44,3 +44,7 @@
|
|||
- type: metabolizerType
|
||||
id: Arachnid
|
||||
name: metabolizer-type-arachnid
|
||||
|
||||
- type: metabolizerType
|
||||
id: Vampire
|
||||
name: metabolizer-type-vampire
|
||||
|
|
|
|||
|
|
@ -6,3 +6,6 @@
|
|||
|
||||
- type: reactiveGroup
|
||||
id: Acidic
|
||||
|
||||
- type: reactiveGroup
|
||||
id: Unholy
|
||||
|
|
|
|||
25
Resources/Prototypes/Entities/Effects/vampire.yml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
- type: entity
|
||||
name: Blood Drain
|
||||
id: beam_blooddrain
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: /Textures/Effects/lightning.rsi
|
||||
drawdepth: Effects
|
||||
layers:
|
||||
- state: "lightning_1"
|
||||
shader: unshaded
|
||||
- type: Physics
|
||||
canCollide: false
|
||||
- type: PointLight
|
||||
enabled: true
|
||||
color: "#800000"
|
||||
radius: 3.5
|
||||
softness: 1
|
||||
autoRot: true
|
||||
castShadows: false
|
||||
- type: Beam
|
||||
- type: TimedDespawn
|
||||
lifetime: 3
|
||||
- type: Tag
|
||||
tags:
|
||||
- HideContextMenu
|
||||
19
Resources/Prototypes/Entities/Mobs/Player/vampire.yml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
- type: entity
|
||||
name: bat
|
||||
parent: MobBat
|
||||
id: MobBatVampire
|
||||
description: Some cultures find them terrifying, others crunchy on the teeth.
|
||||
components:
|
||||
- type: Tag
|
||||
tags:
|
||||
- DoorBumpOpener
|
||||
- VimPilot
|
||||
- CannotSuicide
|
||||
- type: MindContainer
|
||||
showExamineInfo: true
|
||||
- type: NpcFactionMember
|
||||
factions:
|
||||
- PetsNT
|
||||
- type: Alerts
|
||||
|
||||
|
||||
|
|
@ -195,6 +195,8 @@
|
|||
type: StrippableBoundUserInterface
|
||||
enum.StoreUiKey.Key:
|
||||
type: StoreBoundUserInterface
|
||||
enum.VampireMutationUiKey.Key:
|
||||
type: VampireMutationBoundUserInterface
|
||||
- type: Puller
|
||||
- type: Speech
|
||||
speechSounds: Alto
|
||||
|
|
|
|||
|
|
@ -18,6 +18,12 @@
|
|||
damageOnUntrainedUse: ## What a non-chaplain takes when attempting to heal someone
|
||||
groups:
|
||||
Burn: 10
|
||||
damageOnUnholyUse: ## What an unholy creature takes when picking up the bible
|
||||
groups:
|
||||
Burn: 30
|
||||
damageUnholy: ## What damage is dealt when a chaplain hits an unholy creature
|
||||
groups:
|
||||
Burn: 20
|
||||
- type: Prayable
|
||||
bibleUserOnly: true
|
||||
- type: Summonable
|
||||
|
|
@ -65,6 +71,12 @@
|
|||
damageOnUntrainedUse:
|
||||
types:
|
||||
Caustic: 50
|
||||
damageOnUnholyUse: ## What an unholy creature takes when picking up the bible
|
||||
types:
|
||||
Caustic: 50
|
||||
damageUnholy: ## What damage is dealt when a chaplain hits an unholy creature
|
||||
types:
|
||||
types: 20
|
||||
failChance: 0
|
||||
locPrefix: "necro"
|
||||
healSound: "/Audio/Effects/lightburn.ogg"
|
||||
|
|
|
|||
|
|
@ -508,6 +508,7 @@
|
|||
max: 4
|
||||
- !type:DoActsBehavior
|
||||
acts: [ "Destruction" ]
|
||||
- type: Coffin
|
||||
- type: Construction
|
||||
graph: CrateCoffin
|
||||
node: cratecoffin
|
||||
|
|
|
|||
|
|
@ -45,6 +45,21 @@
|
|||
lateJoinAdditional: true
|
||||
mindRoles:
|
||||
- MindRoleChangeling
|
||||
|
||||
- type: entity
|
||||
parent: BaseGameRule
|
||||
id: Vampire
|
||||
components:
|
||||
- type: VampireRule
|
||||
- type: AntagSelection
|
||||
agentName: vampire-roundend-name
|
||||
definitions:
|
||||
- prefRoles: [ Vampire ]
|
||||
max: 4
|
||||
playerRatio: 30
|
||||
lateJoinAdditional: true
|
||||
mindRoles:
|
||||
- MindRoleVampire
|
||||
|
||||
- type: entity
|
||||
categories: [ HideSpawnMenu ]
|
||||
|
|
|
|||
|
|
@ -117,5 +117,5 @@
|
|||
id: ThiefObjectiveGroupEscape
|
||||
weights:
|
||||
EscapeThiefShuttleObjective: 1
|
||||
|
||||
|
||||
#Crew, wizard, when you code it...
|
||||
|
|
|
|||
171
Resources/Prototypes/Objectives/vampire.yml
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
# Base
|
||||
|
||||
- type: entity
|
||||
abstract: true
|
||||
parent: BaseObjective
|
||||
id: BaseVampireObjective
|
||||
components:
|
||||
- type: Objective
|
||||
difficulty: 1.5
|
||||
issuer: objective-issuer-vampire
|
||||
- type: RoleRequirement
|
||||
roles:
|
||||
mindRoles:
|
||||
- VampireRole
|
||||
|
||||
- type: entity
|
||||
abstract: true
|
||||
parent: [BaseVampireObjective, BaseStealObjective]
|
||||
id: BaseVampireStealObjective
|
||||
components:
|
||||
- type: StealCondition
|
||||
verifyMapExistence: false
|
||||
- type: Objective
|
||||
difficulty: 1.5
|
||||
- type: ObjectiveLimit
|
||||
limit: 2
|
||||
|
||||
# Steal
|
||||
|
||||
- type: entity
|
||||
parent: BaseVampireStealObjective
|
||||
id: CMOHyposprayVampireStealObjective
|
||||
components:
|
||||
- type: NotJobRequirement
|
||||
job: ChiefMedicalOfficer
|
||||
- type: StealCondition
|
||||
owner: job-name-cmo
|
||||
stealGroup: Hypospray
|
||||
|
||||
- type: entity
|
||||
parent: BaseVampireStealObjective
|
||||
id: RDHardsuitVampireStealObjective
|
||||
components:
|
||||
- type: NotJobRequirement
|
||||
job: ResearchDirector
|
||||
- type: StealCondition
|
||||
owner: job-name-rd
|
||||
stealGroup: ClothingOuterHardsuitRd
|
||||
- type: Objective
|
||||
difficulty: 1
|
||||
|
||||
- type: entity
|
||||
parent: BaseVampireStealObjective
|
||||
id: EnergyShotgunVampireStealObjective
|
||||
components:
|
||||
- type: Objective
|
||||
difficulty: 2
|
||||
- type: NotJobRequirement
|
||||
job: HeadOfSecurity
|
||||
- type: StealCondition
|
||||
stealGroup: WeaponEnergyShotgun
|
||||
owner: job-name-hos
|
||||
|
||||
- type: entity
|
||||
parent: BaseVampireStealObjective
|
||||
id: MagbootsVampireStealObjective
|
||||
components:
|
||||
- type: NotJobRequirement
|
||||
job: ChiefEngineer
|
||||
- type: StealCondition
|
||||
stealGroup: ClothingShoesBootsMagAdv
|
||||
owner: job-name-ce
|
||||
|
||||
- type: entity
|
||||
parent: BaseVampireStealObjective
|
||||
id: ClipboardVampireStealObjective
|
||||
components:
|
||||
- type: NotJobRequirement
|
||||
job: Quartermaster
|
||||
- type: StealCondition
|
||||
stealGroup: BoxFolderQmClipboard
|
||||
owner: job-name-qm
|
||||
|
||||
- type: entity
|
||||
abstract: true
|
||||
parent: BaseVampireStealObjective
|
||||
id: BaseCaptainVampireObjective
|
||||
components:
|
||||
- type: Objective
|
||||
difficulty: 2.5
|
||||
- type: NotJobRequirement
|
||||
job: Captain
|
||||
|
||||
- type: entity
|
||||
parent: BaseCaptainVampireObjective
|
||||
id: CaptainIDVampireStealObjective
|
||||
components:
|
||||
- type: StealCondition
|
||||
stealGroup: CaptainIDCard
|
||||
|
||||
- type: entity
|
||||
parent: BaseCaptainVampireObjective
|
||||
id: CaptainJetpackVampireStealObjective
|
||||
components:
|
||||
- type: StealCondition
|
||||
stealGroup: JetpackCaptainFilled
|
||||
|
||||
- type: entity
|
||||
parent: BaseCaptainVampireObjective
|
||||
id: CaptainGunVampireStealObjective
|
||||
components:
|
||||
- type: StealCondition
|
||||
stealGroup: WeaponAntiqueLaser
|
||||
owner: job-name-captain
|
||||
|
||||
# States
|
||||
|
||||
- type: entity
|
||||
parent: [BaseVampireObjective, BaseSurviveObjective]
|
||||
id: VampireSurviveObjective
|
||||
name: Survive
|
||||
description: I must survive no matter what.
|
||||
components:
|
||||
- type: Objective
|
||||
icon:
|
||||
sprite: Interface/Actions/actions_vampire.rsi
|
||||
state: deathsembrace
|
||||
|
||||
- type: entity
|
||||
parent: [BaseVampireObjective, BaseLivingObjective]
|
||||
id: VampireEscapeObjective
|
||||
name: Escape to centcomm alive and unrestrained.
|
||||
description: I need to escape on the evacuation shuttle. Undercover.
|
||||
components:
|
||||
- type: Objective
|
||||
difficulty: 1.3
|
||||
icon:
|
||||
sprite: Structures/Furniture/chairs.rsi
|
||||
state: shuttle
|
||||
- type: EscapeShuttleCondition
|
||||
|
||||
# Kill
|
||||
|
||||
- type: entity
|
||||
parent: [BaseVampireObjective, BaseKillObjective]
|
||||
id: VampireKillRandomPersonObjective
|
||||
description: Do it however you like, just make sure they don't make it to centcomm.
|
||||
components:
|
||||
- type: Objective
|
||||
difficulty: 1.75
|
||||
unique: false
|
||||
- type: TargetObjective
|
||||
title: objective-condition-kill-person-title
|
||||
- type: PickRandomPerson
|
||||
|
||||
# Drain
|
||||
|
||||
- type: entity
|
||||
parent: BaseVampireObjective
|
||||
id: VampireDrainObjective
|
||||
components:
|
||||
- type: Objective
|
||||
icon:
|
||||
sprite: Interface/Actions/actions_vampire.rsi
|
||||
state: fangs_extended
|
||||
- type: NumberObjective
|
||||
min: 100
|
||||
max: 500
|
||||
title: objective-condition-drain-title
|
||||
description: objective-condition-drain-description
|
||||
- type: BloodDrainCondition
|
||||
|
|
@ -186,3 +186,11 @@
|
|||
forced: true
|
||||
revertOnCrit: false
|
||||
revertOnDeath: false
|
||||
|
||||
#Vampire
|
||||
- type: polymorph
|
||||
id: VampireBat
|
||||
configuration:
|
||||
entity: MobBat
|
||||
revertOnDeath: true
|
||||
revertOnCrit: true
|
||||
|
|
|
|||
|
|
@ -1059,10 +1059,28 @@
|
|||
factor: 3
|
||||
Medicine:
|
||||
effects:
|
||||
#If vampire
|
||||
- !type:HealthChange
|
||||
conditions:
|
||||
- !type:OrganType
|
||||
type: Vampire
|
||||
damage:
|
||||
types:
|
||||
Heat: 2
|
||||
- !type:Emote
|
||||
conditions:
|
||||
- !type:OrganType
|
||||
type: Vampire
|
||||
emote: Scream
|
||||
probability: 0.3
|
||||
#If not vampire
|
||||
- !type:HealthChange
|
||||
conditions:
|
||||
- !type:TotalDamage
|
||||
max: 50
|
||||
- !type:OrganType
|
||||
type: Vampire
|
||||
shouldHave: false
|
||||
damage:
|
||||
types:
|
||||
Blunt: -0.2
|
||||
|
|
@ -1071,6 +1089,15 @@
|
|||
Shock: -0.2
|
||||
Cold: -0.2
|
||||
reactiveEffects:
|
||||
Unholy:
|
||||
methods: [ Touch ]
|
||||
effects:
|
||||
- !type:HealthChange
|
||||
damage:
|
||||
types:
|
||||
Heat: 5
|
||||
- !type:Emote
|
||||
emote: Scream
|
||||
Extinguish:
|
||||
methods: [ Touch ]
|
||||
effects:
|
||||
|
|
|
|||
7
Resources/Prototypes/Roles/Antags/vampire.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
- type: antag
|
||||
id: Vampire
|
||||
name: roles-antag-vamire-name
|
||||
antagonist: true
|
||||
setPreference: true
|
||||
objective: roles-antag-vampire-description
|
||||
# guides: [ Changelings ]
|
||||
|
|
@ -189,3 +189,12 @@
|
|||
- type: MindRole
|
||||
antagPrototype: Changeling
|
||||
- type: ChangelingRole
|
||||
|
||||
- type: entity
|
||||
parent: BaseMindRoleAntag
|
||||
id: MindRoleVampire
|
||||
name: Vampire Role
|
||||
components:
|
||||
- type: MindRole
|
||||
antagPrototype: Vampire
|
||||
- type: VampireRole
|
||||
|
|
|
|||
|
|
@ -63,3 +63,14 @@
|
|||
icon:
|
||||
sprite: /Textures/Interface/Misc/job_icons.rsi
|
||||
state: Changeling
|
||||
|
||||
- type: factionIcon
|
||||
id: VampireFaction
|
||||
priority: 11
|
||||
showTo:
|
||||
components:
|
||||
- ShowAntagIcons
|
||||
- Vampire
|
||||
icon:
|
||||
sprite: /Textures/Interface/Misc/job_icons.rsi
|
||||
state: Vampire
|
||||
|
|
|
|||
|
|
@ -10,6 +10,11 @@
|
|||
displayName: store-currency-display-stolen-essence
|
||||
canWithdraw: false
|
||||
|
||||
- type: currency
|
||||
id: BloodEssence
|
||||
displayName: store-currency-display-blood-essence
|
||||
canWithdraw: false
|
||||
|
||||
- type: currency
|
||||
id: WizCoin
|
||||
displayName: store-currency-display-wizcoin
|
||||
|
|
|
|||
99
Resources/Prototypes/Vampire/vampire-powers.yml
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
- type: vampirePower
|
||||
id: SummonHeirloom
|
||||
usableWhileCuffed: false
|
||||
usableWhileStunned: false
|
||||
|
||||
- type: vampirePower
|
||||
id: ToggleFangs
|
||||
|
||||
- type: vampirePower
|
||||
id: DrinkBlood
|
||||
doAfterDelay: 1
|
||||
|
||||
- type: vampirePower
|
||||
id: Glare
|
||||
duration: 10
|
||||
|
||||
- type: vampirePower
|
||||
id: Hypnotise
|
||||
duration: 60
|
||||
doAfterDelay: 5
|
||||
|
||||
- type: vampirePower
|
||||
id: Screech
|
||||
duration: 3
|
||||
damage:
|
||||
types:
|
||||
Blunt: 10
|
||||
Structural: 40
|
||||
usableWhileMuffled: false
|
||||
activationCost: 10
|
||||
|
||||
- type: vampirePower
|
||||
id: BloodSteal
|
||||
usableWhileStunned: false
|
||||
usableWhileCuffed: false
|
||||
activationCost: 20
|
||||
|
||||
- type: vampirePower
|
||||
id: PolymorphBat
|
||||
usableWhileStunned: false
|
||||
usableWhileCuffed: false
|
||||
polymorphTarget: mobBatVampire
|
||||
activationCost: 20
|
||||
|
||||
- type: vampirePower
|
||||
id: PolymorphMouse
|
||||
usableWhileStunned: false
|
||||
usableWhileCuffed: false
|
||||
polymorphTarget: MobMouse
|
||||
activationCost: 20
|
||||
|
||||
- type: vampirePower
|
||||
id: CloakOfDarkness
|
||||
usableWhileStunned: false
|
||||
activationCost: 30
|
||||
upkeep: 1
|
||||
|
||||
- type: vampirePassive
|
||||
id: UnholyStrength
|
||||
catalogEntry: VampireUnholyStrength # Must match the catalog id
|
||||
compsToAdd:
|
||||
- type: MeleeWeapon
|
||||
damage:
|
||||
types:
|
||||
Slash: 10
|
||||
animation: WeaponArcClaw
|
||||
soundHit: /Audio/Weapons/slash.ogg
|
||||
|
||||
- type: vampirePassive
|
||||
id: SupernaturalStrength
|
||||
catalogEntry: VampireUnnaturalStrength # Must match the catalog id
|
||||
compsToAdd:
|
||||
- type: MeleeWeapon
|
||||
damage:
|
||||
types:
|
||||
Slash: 15
|
||||
animation: WeaponArcClaw
|
||||
soundHit: /Audio/Weapons/slash.ogg
|
||||
- type: Prying
|
||||
force: True
|
||||
pryPowered: True
|
||||
|
||||
- type: vampirePassive
|
||||
id: DeathsEmbrace
|
||||
catalogEntry: VampireDeathsEmbrace # Must match the catalog id
|
||||
compsToAdd:
|
||||
- type: VampireDeathsEmbrace
|
||||
cost: 100
|
||||
coffinHealing:
|
||||
groups:
|
||||
Toxin: 2
|
||||
Genetic: 2
|
||||
Airloss: 2
|
||||
Brute: 2
|
||||
types:
|
||||
Burn: 1
|
||||
Cold: 2
|
||||
Shock: 2
|
||||
Caustic: 2
|
||||
|
|
@ -271,3 +271,21 @@
|
|||
- BasicStationEventScheduler
|
||||
- MeteorSwarmScheduler
|
||||
- BasicRoundstartVariation
|
||||
|
||||
- type: gamePreset
|
||||
id: Vampire
|
||||
alias:
|
||||
- vamp
|
||||
- vamps
|
||||
- vampire
|
||||
- vampires
|
||||
name: vampire-gamemode-title
|
||||
description: vampire-gamemode-description
|
||||
showInVote: true
|
||||
minPlayers: 50 # Sunrise-Edit: 50 players for VAMPIRES
|
||||
rules:
|
||||
- Vampire
|
||||
- SubGamemodesRule
|
||||
- BasicStationEventScheduler
|
||||
- MeteorSwarmScheduler
|
||||
- BasicRoundstartVariation
|
||||
|
|
|
|||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 722 B |
|
After Width: | Height: | Size: 965 B |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 651 B |
|
After Width: | Height: | Size: 900 B |
|
After Width: | Height: | Size: 936 B |
|
After Width: | Height: | Size: 658 B |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"copyright" : "Taken from https://github.com/goonstation/goonstation at https://github.com/goonstation/goonstation/commit/4059e4be90832b02b1228b1bee3db342094e4f1e",
|
||||
"license" : "CC-BY-SA-3.0",
|
||||
"version": 1,
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "fangs_retracted"
|
||||
},
|
||||
{
|
||||
"name": "fangs_extended"
|
||||
},
|
||||
{
|
||||
"name": "glare"
|
||||
},
|
||||
{
|
||||
"name": "screech"
|
||||
},
|
||||
{
|
||||
"name": "hypnotise"
|
||||
},
|
||||
{
|
||||
"name": "bloodsteal"
|
||||
},
|
||||
{
|
||||
"name": "batform"
|
||||
},
|
||||
{
|
||||
"name": "mouseform"
|
||||
},
|
||||
{
|
||||
"name": "summonheirloom"
|
||||
},
|
||||
{
|
||||
"name": "cloakofdarkness"
|
||||
},
|
||||
{
|
||||
"name": "unholystrength"
|
||||
},
|
||||
{
|
||||
"name": "supernaturalstrength"
|
||||
},
|
||||
{
|
||||
"name": "deathsembrace"
|
||||
},
|
||||
{
|
||||
"name": "mutation"
|
||||
},
|
||||
{
|
||||
"name": "stellarweakness"
|
||||
},
|
||||
{
|
||||
"name": "fullpotential"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 873 B |
|
After Width: | Height: | Size: 689 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 2 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
BIN
Resources/Textures/Interface/Misc/job_icons.rsi/Vampire.png
Normal file
|
After Width: | Height: | Size: 140 B |
|
|
@ -205,6 +205,9 @@
|
|||
},
|
||||
{
|
||||
"name": "Changeling"
|
||||
},
|
||||
{
|
||||
"name": "Vampire"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,13 +137,14 @@ ignore_list:
|
|||
- 'TR-263'
|
||||
|
||||
ignore_files:
|
||||
- italian.ftl
|
||||
- russian.ftl
|
||||
- popup.ftl
|
||||
- controls.ftl
|
||||
- input.ftl
|
||||
- speech-chatsan.ftl
|
||||
- speech-liar.ftl
|
||||
- german.ftl
|
||||
- southern.ftl
|
||||
- tts-voices-sunrise.ftl
|
||||
- 'italian.ftl'
|
||||
- 'russian.ftl'
|
||||
- 'popup.ftl'
|
||||
- 'controls.ftl'
|
||||
- 'input.ftl'
|
||||
- 'speech-chatsan.ftl'
|
||||
- 'speech-liar.ftl'
|
||||
- 'german.ftl'
|
||||
- 'southern.ftl'
|
||||
- 'tts-voices-sunrise.ftl'
|
||||
- 'lobby.ftl'
|
||||