Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions Content.Client/_Sunrise/Disease/UI/DiseaseInfoBui.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Content.Shared._Sunrise.Disease;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;

namespace Content.Client._Sunrise.Disease.UI;

[UsedImplicitly]
public sealed class DiseaseInfoBui : BoundUserInterface
{
[ViewVariables]
private DiseaseInfoWindow? _window;

public DiseaseInfoBui(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}

protected override void Open()
{
base.Open();
_window = new DiseaseInfoWindow();
_window.OnClose += Close;
_window.OpenCentered();
}

protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);

if (state is not DiseaseInfoState diseaseState)
return;

_window?.UpdateState(
diseaseState.BaseInfectChance,
diseaseState.CoughSneezeInfectChance,
diseaseState.Lethal,
diseaseState.Shield,
diseaseState.CurrentInfected,
diseaseState.TotalInfected
);
}

protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
_window?.Dispose();
}
}
37 changes: 37 additions & 0 deletions Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<DefaultWindow xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'disease-info-window-title'}"
SetSize="400 350">
<BoxContainer Orientation="Vertical" Margin="10">
<Label Text="{Loc 'disease-info-core-statistics'}" FontColorOverride="LightGray" Margin="0 0 0 5"/>
<BoxContainer Orientation="Horizontal" Margin="10 0 0 0">
<Label Text="{Loc 'disease-info-base-chance'}" MinWidth="200"/>
<Label Name="BaseChanceLabel" Text="0%"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal" Margin="10 0 0 0">
<Label Text="{Loc 'disease-info-cough-sneeze-chance'}" MinWidth="200"/>
<Label Name="InfectChanceLabel" Text="0%"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal" Margin="10 0 0 0">
<Label Text="{Loc 'disease-info-lethal'}" MinWidth="200"/>
<Label Name="LethalLabel" Text="0"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal" Margin="10 0 0 0">
<Label Text="{Loc 'disease-info-shield'}" MinWidth="200"/>
<Label Name="ShieldLabel" Text="0"/>
</BoxContainer>

<Control MinHeight="20"/>

<Label Text="{Loc 'disease-info-infection-statistics'}" FontColorOverride="LightGray" Margin="0 0 0 5"/>
<BoxContainer Orientation="Horizontal" Margin="10 0 0 0">
<Label Text="{Loc 'disease-info-infected-count'}" MinWidth="200"/>
<Label Name="CurrentInfectedLabel" Text="0"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal" Margin="10 0 0 0">
<Label Text="{Loc 'disease-info-total-infected'}" MinWidth="200"/>
<Label Name="TotalInfectedLabel" Text="0"/>
</BoxContainer>
</BoxContainer>
</DefaultWindow>
25 changes: 25 additions & 0 deletions Content.Client/_Sunrise/Disease/UI/DiseaseInfoWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Content.Client.UserInterface.Controls;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;

namespace Content.Client._Sunrise.Disease.UI;

[GenerateTypedNameReferences]
public sealed partial class DiseaseInfoWindow : DefaultWindow
{
public DiseaseInfoWindow()
{
RobustXamlLoader.Load(this);
}

public void UpdateState(float baseChance, float infectChance, int lethal, int shield, int currentInfected, int totalInfected)
{
BaseChanceLabel.Text = (baseChance * 100).ToString("F0") + "%";
InfectChanceLabel.Text = (infectChance * 100).ToString("F0") + "%";
LethalLabel.Text = lethal.ToString();
ShieldLabel.Text = shield.ToString();
CurrentInfectedLabel.Text = currentInfected.ToString();
TotalInfectedLabel.Text = totalInfected.ToString();
}
}
127 changes: 100 additions & 27 deletions Content.Server/_Sunrise/Disease/DiseaseRoleSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

namespace Content.Server._Sunrise.Disease;

using Robust.Server.GameObjects;

public sealed class DiseaseRoleSystem : SharedDiseaseRoleSystem
{
[Dependency] private readonly IRobustRandom _random = default!;
Expand All @@ -29,6 +31,7 @@ public sealed class DiseaseRoleSystem : SharedDiseaseRoleSystem
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly UserInterfaceSystem _ui = default!;

private static readonly List<string> _bloodReagents = new()
{
Expand Down Expand Up @@ -66,16 +69,12 @@ private void OnInfects(InfectEvent args)
{
if (TryComp<DiseaseRoleComponent>(args.Performer, out var component))
{
if (TryRemoveMoney(args.Performer, component.InfectCost))
OnInfect(args, 1.0f);
else
{
_popup.PopupEntity(Loc.GetString("disease-not-enough-evolution-points"), args.Performer, PopupType.Medium);
return;
}
OnInfect(args, 1.0f);
_popup.PopupEntity(Loc.GetString("disease-infect-success"), args.Performer, PopupType.Medium);

// Play Initial Infected antag audio (only for the disease player)
_audio.PlayGlobal("/Audio/Ambience/Antag/zombie_start.ogg", args.Performer);
UpdateUi(args.Performer, component);
}
}

Expand Down Expand Up @@ -105,25 +104,21 @@ private void OnShop(EntityUid uid, DiseaseRoleComponent component, DiseaseShopAc

private void OnDiseaseInfo(EntityUid uid, DiseaseRoleComponent component, DiseaseInfoEvent args)
{
// Create a simple formatted display using StringBuilder for better performance
var infoText = new StringBuilder();
infoText.AppendLine(Loc.GetString("disease-info-header"));
infoText.AppendLine();

// Core Statistics Section
infoText.AppendLine(Loc.GetString("disease-info-core-statistics") + ":");
infoText.AppendLine("├─ " + Loc.GetString("disease-info-base-chance") + ": " + (component.BaseInfectChance * 100).ToString("F0") + "%");
infoText.AppendLine("├─ " + Loc.GetString("disease-info-cough-sneeze-chance") + ": " + (component.CoughSneezeInfectChance * 100).ToString("F0") + "%");
infoText.AppendLine("├─ " + Loc.GetString("disease-info-lethal") + ": " + component.Lethal);
infoText.AppendLine("└─ " + Loc.GetString("disease-info-shield") + ": " + component.Shield);
infoText.AppendLine();

// Infection Statistics Section
infoText.AppendLine(Loc.GetString("disease-info-infection-statistics") + ":");
infoText.AppendLine("├─ " + Loc.GetString("disease-info-infected-count") + ": " + component.Infected.Count);
infoText.Append("└─ " + Loc.GetString("disease-info-total-infected") + ": " + component.SickOfAllTime);

_popup.PopupEntity(infoText.ToString(), uid, uid, PopupType.Large);
_ui.TryToggleUi(uid, DiseaseInfoUiKey.Key, uid);
UpdateUi(uid, component);
}

private void UpdateUi(EntityUid uid, DiseaseRoleComponent component)
{
var state = new DiseaseInfoState(
component.BaseInfectChance,
component.CoughSneezeInfectChance,
component.Lethal,
component.Shield,
component.Infected.Count,
component.SickOfAllTime
);
_ui.SetUiState(uid, DiseaseInfoUiKey.Key, state);
}


Expand Down Expand Up @@ -221,10 +216,84 @@ private void OnStorePurchase(ref StoreBuyFinishedEvent args)
_popup.PopupEntity(Loc.GetString("disease-upgrade-max-reached"), storeOwner, PopupType.Medium);
}
break;
default:
if (!diseaseComp.Symptoms.ContainsKey(args.PurchasedItem.ID))
{
// Check if it's a symptom listing from the ID
var symptom = args.PurchasedItem.ID;
int minLevel = 0;
int maxLevel = 5;

// Ideally we should get this from the listing/action metadata,
// but since the previous system relied on hardcoded event args from actions,
// we might need to assume or look it up.
// For now, let's replicate the levels from the removed actions:

switch (symptom)
{
case "Cough": minLevel = 2; break;
case "Sneeze": minLevel = 3; break;
case "Vomit": minLevel = 3; break;
case "Narcolepsy": minLevel = 3; break;
case "Crying": minLevel = 0; break;
case "Muted": minLevel = 4; break;
case "Slowness": minLevel = 2; break;
case "Bleed": minLevel = 3; break;
case "Blindness": minLevel = 4; break;
case "Insult": minLevel = 2; break;
// Zombie handled separately via special event if needed, or if it's just a symptom?
// The original action raised DiseaseZombieEvent for "Zombie".
// Wait, "Zombie" was a separate category in catalog?
// Checking catalog: "Zombie" listing productAction was ActionDiseaseZombie.
// So Zombie is NOT handled here in default block if we want to keep specific behavior.
}

if (symptom == "Zombie")
{
// Zombie Logic - previously handled by DiseaseZombieEvent which was raised by InstantAction
// Now we trigger it directly on purchase
// We need to manually construct the event or call the logic.
// But wait, the original logic was: Purchase -> Get Item/Action -> Use Action -> Trigger Event.
// If we enable "instant buy", we trigger logic now.
var zombieArgs = new DiseaseZombieEvent(); // Empty event, needed for handler signature?
// Actually the handler uses the event args to get the action to remove it.
// We can just extract the logic.

var infected = diseaseComp.Infected.ToArray();
var convertedCount = 0;

for (int i = 0; i < infected.Length; i++)
{
var target = infected[i];
if (target.IsValid() && !Deleted(target))
{
RemComp<SickComponent>(target);
diseaseComp.Infected.Remove(target);
EnsureComp<ZombifyOnDeathComponent>(target);
EnsureComp<PendingZombieComponent>(target);
convertedCount++;
}
}

if (convertedCount > 0)
{
_popup.PopupEntity(Loc.GetString("disease-zombie-success", ("count", convertedCount)), storeOwner, PopupType.Medium);
}
}
else
{
// Regular Symptom
diseaseComp.Symptoms.Add(symptom, new SymptomData(minLevel, maxLevel));
_popup.PopupEntity(Loc.GetString("disease-upgrade-purchased"), storeOwner, PopupType.Medium);
}
}
break;
}
UpdateUi(storeOwner, diseaseComp);

}

void AddMoney(EntityUid uid, FixedPoint2 value)
private void AddMoney(EntityUid uid, FixedPoint2 value)
{
if (TryComp<DiseaseRoleComponent>(uid, out var diseaseComp))
{
Expand Down Expand Up @@ -313,6 +382,10 @@ private void OnInfectedDeath(MobStateChangedEvent args)
if (Deleted(args.Target))
return;

// Check if the dying entity was actually infected
if (!HasComp<SickComponent>(args.Target))
return;

// Reward all other disease antagonists when any infected dies
var diseaseQuery = EntityQueryEnumerator<DiseaseRoleComponent>();
while (diseaseQuery.MoveNext(out var diseaseUid, out var diseaseComp))
Expand Down
37 changes: 28 additions & 9 deletions Content.Server/_Sunrise/Disease/SickSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,20 @@
using Robust.Shared.Timing;
using Content.Shared._Sunrise.Disease;
using System.Numerics;
using Content.Server.Body.Components;
using Content.Server.Body.Systems;
using Content.Server.Chat.Systems;
using Content.Shared.Interaction.Events;
using Robust.Server.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Random;
using Content.Shared.Humanoid;
using Content.Server.Store.Components;
using Content.Server.Store.Systems;
using Content.Server.Popups;
using Content.Shared.Popups;
using Content.Server.Chat;
using Content.Shared.Stunnable;
using Content.Shared.Damage.Prototypes;
using Content.Shared.Damage;
using Content.Server.Emoting.Systems;
using Content.Server.Speech.EntitySystems;
using Content.Shared.FixedPoint;
using Content.Server.Medical;
using Content.Server.Traits.Assorted;
using Content.Shared.Body.Components;
using Content.Shared.Chat;
Expand All @@ -34,6 +28,8 @@
using Content.Shared.Store.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Inventory;
using Content.Shared.Zombies;
namespace Content.Server._Sunrise.Disease;
public sealed class SickSystem : SharedSickSystem
{
Expand All @@ -48,6 +44,7 @@ public sealed class SickSystem : SharedSickSystem
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly SharedStunSystem _stun = default!;
[Dependency] private readonly UserInterfaceSystem _ui = default!;
private EntityLookupSystem Lookup => _entityManager.System<EntityLookupSystem>();
public override void Initialize()
{
Expand Down Expand Up @@ -115,6 +112,9 @@ public void OnShut(EntityUid uid, SickComponent component, ComponentShutdown arg
solution.AddReagent(reagentId, FixedPoint2.New((int)stream.BloodReferenceSolution.MaxVolume));
}

if (solution.Volume == 0)
return;

_bloodstream.ChangeBloodReagents(uid, solution);
}
}
Expand Down Expand Up @@ -153,6 +153,17 @@ public override void Update(float frameTime)
RaiseNetworkEvent(new ClientInfectEvent(GetNetEntity(uid), GetNetEntity(component.owner)));
diseaseComp.SickOfAllTime++;
AddMoney(component.owner, 5);
_popupSystem.PopupEntity(Loc.GetString("disease-infect-reward", ("points", 5)), component.owner, component.owner, PopupType.Medium);

var state = new DiseaseInfoState(
diseaseComp.BaseInfectChance,
diseaseComp.CoughSneezeInfectChance,
diseaseComp.Lethal,
diseaseComp.Shield,
diseaseComp.Infected.Count,
diseaseComp.SickOfAllTime
);
_ui.SetUiState(component.owner, DiseaseInfoUiKey.Key, state);

component.Inited = true;
}
Expand Down Expand Up @@ -222,7 +233,7 @@ private void UpdateInfection(EntityUid uid, SickComponent component, EntityUid d
if (!HasComp<SleepyComponent>(uid))
{
var c = AddComp<SleepyComponent>(uid);
EntityManager.EntitySysManager.GetEntitySystem<SleepySystem>().SetNarcolepsy(uid, new Vector2(10, 30), new Vector2(300, 600), c);
EntityManager.EntitySysManager.GetEntitySystem<SleepySystem>().SetNarcolepsy(uid, new Vector2(60, 80), new Vector2(8, 12), c);
}
break;
case "Muted":
Expand Down Expand Up @@ -272,7 +283,11 @@ private void OnEmote(EntityUid uid, SickComponent component, ref EmoteEvent args
{
if (HasComp<HumanoidAppearanceComponent>(entity) && !HasComp<SickComponent>(entity) && !HasComp<DiseaseImmuneComponent>(entity))
{
OnInfected(entity, component.owner, disease.CoughSneezeInfectChance);
var ev = new ZombificationResistanceQueryEvent(SlotFlags.HEAD | SlotFlags.MASK | SlotFlags.OUTERCLOTHING);
RaiseLocalEvent(entity, ev);

if (_robustRandom.Prob(ev.TotalCoefficient))
OnInfected(entity, component.owner, disease.CoughSneezeInfectChance);
}
}
}
Expand All @@ -290,7 +305,11 @@ private void OnEmote(EntityUid uid, SickComponent component, ref EmoteEvent args
{
if (HasComp<HumanoidAppearanceComponent>(entity) && !HasComp<SickComponent>(entity) && !HasComp<DiseaseImmuneComponent>(entity))
{
OnInfected(entity, component.owner, disease.CoughSneezeInfectChance);
var ev = new ZombificationResistanceQueryEvent(SlotFlags.HEAD | SlotFlags.MASK | SlotFlags.OUTERCLOTHING);
RaiseLocalEvent(entity, ev);

if (_robustRandom.Prob(ev.TotalCoefficient))
OnInfected(entity, component.owner, disease.CoughSneezeInfectChance);
}
}
}
Expand Down
Loading
Loading