Examine prediction (#23565)

* Initial prediction

* new group handling

* groups for all examines that use multiple rn

* compile

* why was it doing this??

* handle newlines with sorting properly
This commit is contained in:
Kara
2024-01-05 23:53:13 -07:00
committed by GitHub
parent 731cfc278a
commit 0ae3858b69
41 changed files with 693 additions and 431 deletions

View File

@@ -81,8 +81,11 @@ namespace Content.Client.Construction
private void HandleConstructionGhostExamined(EntityUid uid, ConstructionGhostComponent component, ExaminedEvent args) private void HandleConstructionGhostExamined(EntityUid uid, ConstructionGhostComponent component, ExaminedEvent args)
{ {
if (component.Prototype == null) return; if (component.Prototype == null)
return;
using (args.PushGroup(nameof(ConstructionGhostComponent)))
{
args.PushMarkup(Loc.GetString( args.PushMarkup(Loc.GetString(
"construction-ghost-examine-message", "construction-ghost-examine-message",
("name", component.Prototype.Name))); ("name", component.Prototype.Name)));
@@ -98,12 +101,12 @@ namespace Content.Client.Construction
return; return;
} }
foreach (ConstructionGraphStep step in edge.Steps) foreach (var step in edge.Steps)
{ {
args.Message.PushNewline();
step.DoExamine(args); step.DoExamine(args);
} }
} }
}
public event EventHandler<CraftingAvailabilityChangedArgs>? CraftingAvailabilityChanged; public event EventHandler<CraftingAvailabilityChangedArgs>? CraftingAvailabilityChanged;
public event EventHandler<string>? ConstructionGuideAvailable; public event EventHandler<string>? ConstructionGuideAvailable;

View File

@@ -366,13 +366,14 @@ namespace Content.Client.Examine
var canSeeClearly = !HasComp<BlurryVisionComponent>(playerEnt); var canSeeClearly = !HasComp<BlurryVisionComponent>(playerEnt);
OpenTooltip(playerEnt.Value, entity, centeredOnCursor, false, knowTarget: canSeeClearly); OpenTooltip(playerEnt.Value, entity, centeredOnCursor, false, knowTarget: canSeeClearly);
if (IsClientSide(entity)
|| _client.RunLevel == ClientRunLevel.SinglePlayerGame) // i.e. a replay // Always update tooltip info from client first.
{ // If we get it wrong, server will correct us later anyway.
// This will usually be correct (barring server-only components, which generally only adds, not replaces text)
message = GetExamineText(entity, playerEnt); message = GetExamineText(entity, playerEnt);
UpdateTooltipInfo(playerEnt.Value, entity, message); UpdateTooltipInfo(playerEnt.Value, entity, message);
}
else if (!IsClientSide(entity))
{ {
// Ask server for extra examine info. // Ask server for extra examine info.
if (entity != _lastExaminedEntity) if (entity != _lastExaminedEntity)
@@ -383,7 +384,6 @@ namespace Content.Client.Examine
} }
RaiseLocalEvent(entity, new ClientExaminedEvent(entity, playerEnt.Value)); RaiseLocalEvent(entity, new ClientExaminedEvent(entity, playerEnt.Value));
_lastExaminedEntity = entity; _lastExaminedEntity = entity;
} }

View File

@@ -110,6 +110,7 @@ namespace Content.Server.Atmos.EntitySystems
private void OnExamined(EntityUid uid, GasTankComponent component, ExaminedEvent args) private void OnExamined(EntityUid uid, GasTankComponent component, ExaminedEvent args)
{ {
using(args.PushGroup(nameof(GasTankComponent)));
if (args.IsInDetailsRange) if (args.IsInDetailsRange)
args.PushMarkup(Loc.GetString("comp-gas-tank-examine", ("pressure", Math.Round(component.Air?.Pressure ?? 0)))); args.PushMarkup(Loc.GetString("comp-gas-tank-examine", ("pressure", Math.Round(component.Air?.Pressure ?? 0))));
if (component.IsConnected) if (component.IsConnected)

View File

@@ -51,6 +51,8 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
return; return;
} }
using (args.PushGroup(nameof(GasRecyclerComponent)))
{
if (comp.Reacting) if (comp.Reacting)
{ {
args.PushMarkup(Loc.GetString("gas-recycler-reacting")); args.PushMarkup(Loc.GetString("gas-recycler-reacting"));
@@ -68,6 +70,7 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
} }
} }
} }
}
private void OnUpdate(Entity<GasRecyclerComponent> ent, ref AtmosDeviceUpdateEvent args) private void OnUpdate(Entity<GasRecyclerComponent> ent, ref AtmosDeviceUpdateEvent args)
{ {

View File

@@ -90,11 +90,14 @@ public sealed partial class BotanySystem : EntitySystem
if (!TryGetSeed(component, out var seed)) if (!TryGetSeed(component, out var seed))
return; return;
using (args.PushGroup(nameof(SeedComponent)))
{
var name = Loc.GetString(seed.DisplayName); var name = Loc.GetString(seed.DisplayName);
args.PushMarkup(Loc.GetString($"seed-component-description", ("seedName", name))); args.PushMarkup(Loc.GetString($"seed-component-description", ("seedName", name)));
args.PushMarkup(Loc.GetString($"seed-component-plant-yield-text", ("seedYield", seed.Yield))); args.PushMarkup(Loc.GetString($"seed-component-plant-yield-text", ("seedYield", seed.Yield)));
args.PushMarkup(Loc.GetString($"seed-component-plant-potency-text", ("seedPotency", seed.Potency))); args.PushMarkup(Loc.GetString($"seed-component-plant-potency-text", ("seedPotency", seed.Potency)));
} }
}
#region SeedPrototype prototype stuff #region SeedPrototype prototype stuff

View File

@@ -77,6 +77,8 @@ public sealed class PlantHolderSystem : EntitySystem
var (_, component) = entity; var (_, component) = entity;
using (args.PushGroup(nameof(PlantHolderComponent)))
{
if (component.Seed == null) if (component.Seed == null)
{ {
args.PushMarkup(Loc.GetString("plant-holder-component-nothing-planted-message")); args.PushMarkup(Loc.GetString("plant-holder-component-nothing-planted-message"));
@@ -132,6 +134,7 @@ public sealed class PlantHolderSystem : EntitySystem
args.PushMarkup(Loc.GetString("plant-holder-component-gas-missing-warning")); args.PushMarkup(Loc.GetString("plant-holder-component-gas-missing-warning"));
} }
} }
}
private void OnInteractUsing(Entity<PlantHolderComponent> entity, ref InteractUsingEvent args) private void OnInteractUsing(Entity<PlantHolderComponent> entity, ref InteractUsingEvent args)
{ {

View File

@@ -46,7 +46,7 @@ namespace Content.Server.Construction.Conditions
if (entityManager.EntitySysManager.GetEntitySystem<MachineFrameSystem>().IsComplete(machineFrame)) if (entityManager.EntitySysManager.GetEntitySystem<MachineFrameSystem>().IsComplete(machineFrame))
return false; return false;
args.Message.AddMarkup(Loc.GetString("construction-condition-machine-frame-requirement-label") + "\n"); args.PushMarkup(Loc.GetString("construction-condition-machine-frame-requirement-label"));
foreach (var (part, required) in machineFrame.Requirements) foreach (var (part, required) in machineFrame.Requirements)
{ {
var amount = required - machineFrame.Progress[part]; var amount = required - machineFrame.Progress[part];
@@ -54,10 +54,9 @@ namespace Content.Server.Construction.Conditions
if(amount == 0) if(amount == 0)
continue; continue;
args.Message.AddMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry", args.PushMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry",
("amount", amount), ("amount", amount),
("elementName", Loc.GetString(part))) ("elementName", Loc.GetString(part))));
+ "\n");
} }
foreach (var (material, required) in machineFrame.MaterialRequirements) foreach (var (material, required) in machineFrame.MaterialRequirements)
@@ -67,10 +66,9 @@ namespace Content.Server.Construction.Conditions
if(amount == 0) if(amount == 0)
continue; continue;
args.Message.AddMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry", args.PushMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry",
("amount", amount), ("amount", amount),
("elementName", Loc.GetString(material))) ("elementName", Loc.GetString(material))));
+ "\n");
} }
foreach (var (compName, info) in machineFrame.ComponentRequirements) foreach (var (compName, info) in machineFrame.ComponentRequirements)
@@ -80,10 +78,9 @@ namespace Content.Server.Construction.Conditions
if(amount == 0) if(amount == 0)
continue; continue;
args.Message.AddMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry", args.PushMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry",
("amount", info.Amount), ("amount", info.Amount),
("elementName", Loc.GetString(info.ExamineName))) ("elementName", Loc.GetString(info.ExamineName))));
+ "\n");
} }
foreach (var (tagName, info) in machineFrame.TagRequirements) foreach (var (tagName, info) in machineFrame.TagRequirements)
@@ -93,7 +90,7 @@ namespace Content.Server.Construction.Conditions
if(amount == 0) if(amount == 0)
continue; continue;
args.Message.AddMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry", args.PushMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry",
("amount", info.Amount), ("amount", info.Amount),
("elementName", Loc.GetString(info.ExamineName))) ("elementName", Loc.GetString(info.ExamineName)))
+ "\n"); + "\n");

View File

@@ -66,6 +66,8 @@ namespace Content.Server.Construction
} }
private void HandleConstructionExamined(EntityUid uid, ConstructionComponent component, ExaminedEvent args) private void HandleConstructionExamined(EntityUid uid, ConstructionComponent component, ExaminedEvent args)
{
using (args.PushGroup(nameof(ConstructionComponent)))
{ {
if (GetTargetNode(uid, component) is {} target) if (GetTargetNode(uid, component) is {} target)
{ {
@@ -109,6 +111,8 @@ namespace Content.Server.Construction
} }
} }
}
/// <summary> /// <summary>
/// Returns a <see cref="ConstructionGuide"/> for a given <see cref="ConstructionPrototype"/>, /// Returns a <see cref="ConstructionGuide"/> for a given <see cref="ConstructionPrototype"/>,

View File

@@ -66,6 +66,8 @@ public sealed class DefusableSystem : SharedDefusableSystem
if (!args.IsInDetailsRange) if (!args.IsInDetailsRange)
return; return;
using (args.PushGroup(nameof(DefusableComponent)))
{
if (!comp.Usable) if (!comp.Usable)
{ {
args.PushMarkup(Loc.GetString("defusable-examine-defused", ("name", uid))); args.PushMarkup(Loc.GetString("defusable-examine-defused", ("name", uid)));
@@ -86,6 +88,7 @@ public sealed class DefusableSystem : SharedDefusableSystem
{ {
args.PushMarkup(Loc.GetString("defusable-examine-inactive", ("name", uid))); args.PushMarkup(Loc.GetString("defusable-examine-inactive", ("name", uid)));
} }
}
args.PushMarkup(Loc.GetString("defusable-examine-bolts", ("down", comp.Bolted))); args.PushMarkup(Loc.GetString("defusable-examine-bolts", ("down", comp.Bolted)));
} }

View File

@@ -216,7 +216,7 @@ public sealed class DrainSystem : SharedDrainSystem
var text = drainSolution.AvailableVolume != 0 var text = drainSolution.AvailableVolume != 0
? Loc.GetString("drain-component-examine-volume", ("volume", drainSolution.AvailableVolume)) ? Loc.GetString("drain-component-examine-volume", ("volume", drainSolution.AvailableVolume))
: Loc.GetString("drain-component-examine-hint-full"); : Loc.GetString("drain-component-examine-hint-full");
args.Message.AddMarkup($"\n\n{text}"); args.PushMarkup(text);
} }
private void OnInteract(Entity<DrainComponent> entity, ref AfterInteractUsingEvent args) private void OnInteract(Entity<DrainComponent> entity, ref AfterInteractUsingEvent args)

View File

@@ -42,12 +42,15 @@ public sealed partial class PuddleSystem
} }
private void OnExamined(Entity<SpillableComponent> entity, ref ExaminedEvent args) private void OnExamined(Entity<SpillableComponent> entity, ref ExaminedEvent args)
{
using (args.PushGroup(nameof(SpillableComponent)))
{ {
args.PushMarkup(Loc.GetString("spill-examine-is-spillable")); args.PushMarkup(Loc.GetString("spill-examine-is-spillable"));
if (HasComp<MeleeWeaponComponent>(entity)) if (HasComp<MeleeWeaponComponent>(entity))
args.PushMarkup(Loc.GetString("spill-examine-spillable-weapon")); args.PushMarkup(Loc.GetString("spill-examine-spillable-weapon"));
} }
}
private void OnOverflow(Entity<SpillableComponent> entity, ref SolutionContainerOverflowEvent args) private void OnOverflow(Entity<SpillableComponent> entity, ref SolutionContainerOverflowEvent args)
{ {

View File

@@ -360,6 +360,8 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
} }
private void HandlePuddleExamined(Entity<PuddleComponent> entity, ref ExaminedEvent args) private void HandlePuddleExamined(Entity<PuddleComponent> entity, ref ExaminedEvent args)
{
using (args.PushGroup(nameof(PuddleComponent)))
{ {
if (TryComp<StepTriggerComponent>(entity, out var slippery) && slippery.Active) if (TryComp<StepTriggerComponent>(entity, out var slippery) && slippery.Active)
{ {
@@ -367,7 +369,8 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
} }
if (HasComp<EvaporationComponent>(entity) && if (HasComp<EvaporationComponent>(entity) &&
_solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution)) _solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.SolutionName,
ref entity.Comp.Solution, out var solution))
{ {
if (CanFullyEvaporate(solution)) if (CanFullyEvaporate(solution))
args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating")); args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating"));
@@ -379,6 +382,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
else else
args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-no")); args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-no"));
} }
}
private void OnAnchorChanged(Entity<PuddleComponent> entity, ref AnchorStateChangedEvent args) private void OnAnchorChanged(Entity<PuddleComponent> entity, ref AnchorStateChangedEvent args)
{ {

View File

@@ -44,8 +44,7 @@ public sealed class RandomGiftSystem : EntitySystem
return; return;
var name = _prototype.Index<EntityPrototype>(component.SelectedEntity).Name; var name = _prototype.Index<EntityPrototype>(component.SelectedEntity).Name;
args.Message.PushNewline(); args.PushText(Loc.GetString("gift-packin-contains", ("name", name)));
args.Message.AddText(Loc.GetString("gift-packin-contains", ("name", name)));
} }
private void OnUseInHand(EntityUid uid, RandomGiftComponent component, UseInHandEvent args) private void OnUseInHand(EntityUid uid, RandomGiftComponent component, UseInHandEvent args)

View File

@@ -25,6 +25,8 @@ public sealed class HolosignSystem : EntitySystem
var charges = UsesRemaining(component, battery); var charges = UsesRemaining(component, battery);
var maxCharges = MaxUses(component, battery); var maxCharges = MaxUses(component, battery);
using (args.PushGroup(nameof(HolosignProjectorComponent)))
{
args.PushMarkup(Loc.GetString("limited-charges-charges-remaining", ("charges", charges))); args.PushMarkup(Loc.GetString("limited-charges-charges-remaining", ("charges", charges)));
if (charges > 0 && charges == maxCharges) if (charges > 0 && charges == maxCharges)
@@ -32,6 +34,7 @@ public sealed class HolosignSystem : EntitySystem
args.PushMarkup(Loc.GetString("limited-charges-max-charges")); args.PushMarkup(Loc.GetString("limited-charges-max-charges"));
} }
} }
}
private void OnUse(EntityUid uid, HolosignProjectorComponent component, UseInHandEvent args) private void OnUse(EntityUid uid, HolosignProjectorComponent component, UseInHandEvent args)
{ {

View File

@@ -100,6 +100,8 @@ namespace Content.Server.Labels
if (comp.LabelSlot.Item is not {Valid: true} item) if (comp.LabelSlot.Item is not {Valid: true} item)
return; return;
using (args.PushGroup(nameof(PaperLabelComponent)))
{
if (!args.IsInDetailsRange) if (!args.IsInDetailsRange)
{ {
args.PushMarkup(Loc.GetString("comp-paper-label-has-label-cant-read")); args.PushMarkup(Loc.GetString("comp-paper-label-has-label-cant-read"));
@@ -120,6 +122,7 @@ namespace Content.Server.Labels
var text = paper.Content; var text = paper.Content;
args.PushMarkup(text.TrimEnd()); args.PushMarkup(text.TrimEnd());
} }
}
private void OnContainerModified(EntityUid uid, PaperLabelComponent label, ContainerModifiedMessage args) private void OnContainerModified(EntityUid uid, PaperLabelComponent label, ContainerModifiedMessage args)
{ {

View File

@@ -46,6 +46,8 @@ public sealed class EmergencyLightSystem : SharedEmergencyLightSystem
} }
private void OnEmergencyExamine(EntityUid uid, EmergencyLightComponent component, ExaminedEvent args) private void OnEmergencyExamine(EntityUid uid, EmergencyLightComponent component, ExaminedEvent args)
{
using (args.PushGroup(nameof(EmergencyLightComponent)))
{ {
args.PushMarkup( args.PushMarkup(
Loc.GetString("emergency-light-component-on-examine", Loc.GetString("emergency-light-component-on-examine",
@@ -70,6 +72,7 @@ public sealed class EmergencyLightSystem : SharedEmergencyLightSystem
("color", color.ToHex()), ("color", color.ToHex()),
("level", name))); ("level", name)));
} }
}
private void OnEmergencyLightEvent(EntityUid uid, EmergencyLightComponent component, EmergencyLightEvent args) private void OnEmergencyLightEvent(EntityUid uid, EmergencyLightComponent component, EmergencyLightEvent args)
{ {

View File

@@ -32,12 +32,15 @@ public sealed class LightReplacerSystem : EntitySystem
} }
private void OnExamined(EntityUid uid, LightReplacerComponent component, ExaminedEvent args) private void OnExamined(EntityUid uid, LightReplacerComponent component, ExaminedEvent args)
{
using (args.PushGroup(nameof(LightReplacerComponent)))
{ {
if (!component.InsertedBulbs.ContainedEntities.Any()) if (!component.InsertedBulbs.ContainedEntities.Any())
{ {
args.PushMarkup(Loc.GetString("comp-light-replacer-no-lights")); args.PushMarkup(Loc.GetString("comp-light-replacer-no-lights"));
return; return;
} }
args.PushMarkup(Loc.GetString("comp-light-replacer-has-lights")); args.PushMarkup(Loc.GetString("comp-light-replacer-has-lights"));
var groups = new Dictionary<string, int>(); var groups = new Dictionary<string, int>();
var metaQuery = GetEntityQuery<MetaDataComponent>(); var metaQuery = GetEntityQuery<MetaDataComponent>();
@@ -52,6 +55,7 @@ public sealed class LightReplacerSystem : EntitySystem
args.PushMarkup(Loc.GetString("comp-light-replacer-light-listing", ("amount", amount), ("name", name))); args.PushMarkup(Loc.GetString("comp-light-replacer-light-listing", ("amount", amount), ("name", name)));
} }
} }
}
private void OnMapInit(EntityUid uid, LightReplacerComponent component, MapInitEvent args) private void OnMapInit(EntityUid uid, LightReplacerComponent component, MapInitEvent args)
{ {

View File

@@ -207,6 +207,8 @@ public sealed partial class CryoPodSystem : SharedCryoPodSystem
{ {
var container = _itemSlotsSystem.GetItemOrNull(entity.Owner, entity.Comp.SolutionContainerName); var container = _itemSlotsSystem.GetItemOrNull(entity.Owner, entity.Comp.SolutionContainerName);
if (args.IsInDetailsRange && container != null && _solutionContainerSystem.TryGetFitsInDispenser(container.Value, out _, out var containerSolution)) if (args.IsInDetailsRange && container != null && _solutionContainerSystem.TryGetFitsInDispenser(container.Value, out _, out var containerSolution))
{
using (args.PushGroup(nameof(CryoPodComponent)))
{ {
args.PushMarkup(Loc.GetString("cryo-pod-examine", ("beaker", Name(container.Value)))); args.PushMarkup(Loc.GetString("cryo-pod-examine", ("beaker", Name(container.Value))));
if (containerSolution.Volume == 0) if (containerSolution.Volume == 0)
@@ -215,6 +217,7 @@ public sealed partial class CryoPodSystem : SharedCryoPodSystem
} }
} }
} }
}
private void OnPowerChanged(Entity<CryoPodComponent> entity, ref PowerChangedEvent args) private void OnPowerChanged(Entity<CryoPodComponent> entity, ref PowerChangedEvent args)
{ {

View File

@@ -47,11 +47,17 @@ public sealed class CrematoriumSystem : EntitySystem
if (!TryComp<AppearanceComponent>(uid, out var appearance)) if (!TryComp<AppearanceComponent>(uid, out var appearance))
return; return;
if (_appearance.TryGetData<bool>(uid, CrematoriumVisuals.Burning, out var isBurning, appearance) && isBurning) using (args.PushGroup(nameof(CrematoriumComponent)))
{ {
args.PushMarkup(Loc.GetString("crematorium-entity-storage-component-on-examine-details-is-burning", ("owner", uid))); if (_appearance.TryGetData<bool>(uid, CrematoriumVisuals.Burning, out var isBurning, appearance) &&
isBurning)
{
args.PushMarkup(Loc.GetString("crematorium-entity-storage-component-on-examine-details-is-burning",
("owner", uid)));
} }
if (_appearance.TryGetData<bool>(uid, StorageVisuals.HasContents, out var hasContents, appearance) && hasContents)
if (_appearance.TryGetData<bool>(uid, StorageVisuals.HasContents, out var hasContents, appearance) &&
hasContents)
{ {
args.PushMarkup(Loc.GetString("crematorium-entity-storage-component-on-examine-details-has-contents")); args.PushMarkup(Loc.GetString("crematorium-entity-storage-component-on-examine-details-has-contents"));
} }
@@ -60,6 +66,7 @@ public sealed class CrematoriumSystem : EntitySystem
args.PushMarkup(Loc.GetString("crematorium-entity-storage-component-on-examine-details-empty")); args.PushMarkup(Loc.GetString("crematorium-entity-storage-component-on-examine-details-empty"));
} }
} }
}
private void OnAttemptOpen(EntityUid uid, ActiveCrematoriumComponent component, ref StorageOpenAttemptEvent args) private void OnAttemptOpen(EntityUid uid, ActiveCrematoriumComponent component, ref StorageOpenAttemptEvent args)
{ {

View File

@@ -135,19 +135,19 @@ public sealed class DrinkSystem : EntitySystem
return; return;
// put Empty / Xu after Opened, or start a new line // put Empty / Xu after Opened, or start a new line
args.Message.AddMarkup(hasOpenable ? " - " : "\n"); args.AddMarkup(hasOpenable ? " - " : "\n");
var empty = IsEmpty(entity, entity.Comp); var empty = IsEmpty(entity, entity.Comp);
if (empty) if (empty)
{ {
args.Message.AddMarkup(Loc.GetString("drink-component-on-examine-is-empty")); args.AddMarkup(Loc.GetString("drink-component-on-examine-is-empty"));
return; return;
} }
if (TryComp<ExaminableSolutionComponent>(entity, out var comp)) if (TryComp<ExaminableSolutionComponent>(entity, out var comp))
{ {
//provide exact measurement for beakers //provide exact measurement for beakers
args.Message.AddMarkup(Loc.GetString("drink-component-on-examine-exact-volume", ("amount", DrinkVolume(entity, entity.Comp)))); args.AddMarkup(Loc.GetString("drink-component-on-examine-exact-volume", ("amount", DrinkVolume(entity, entity.Comp))));
} }
else else
{ {
@@ -159,7 +159,7 @@ public sealed class DrinkSystem : EntitySystem
> 33 => HalfEmptyOrHalfFull(args), > 33 => HalfEmptyOrHalfFull(args),
_ => "drink-component-on-examine-is-mostly-empty", _ => "drink-component-on-examine-is-mostly-empty",
}; };
args.Message.AddMarkup(Loc.GetString(remainingString)); args.AddMarkup(Loc.GetString(remainingString));
} }
} }

View File

@@ -79,6 +79,8 @@ namespace Content.Server.Paper
if (!args.IsInDetailsRange) if (!args.IsInDetailsRange)
return; return;
using (args.PushGroup(nameof(PaperComponent)))
{
if (paperComp.Content != "") if (paperComp.Content != "")
args.PushMarkup( args.PushMarkup(
Loc.GetString( Loc.GetString(
@@ -88,13 +90,15 @@ namespace Content.Server.Paper
if (paperComp.StampedBy.Count > 0) if (paperComp.StampedBy.Count > 0)
{ {
var commaSeparated = string.Join(", ", paperComp.StampedBy.Select(s => Loc.GetString(s.StampedName))); var commaSeparated =
string.Join(", ", paperComp.StampedBy.Select(s => Loc.GetString(s.StampedName)));
args.PushMarkup( args.PushMarkup(
Loc.GetString( Loc.GetString(
"paper-component-examine-detail-stamped-by", ("paper", uid), ("stamps", commaSeparated)) "paper-component-examine-detail-stamped-by", ("paper", uid), ("stamps", commaSeparated))
); );
} }
} }
}
private void OnInteractUsing(EntityUid uid, PaperComponent paperComp, InteractUsingEvent args) private void OnInteractUsing(EntityUid uid, PaperComponent paperComp, InteractUsingEvent args)
{ {

View File

@@ -120,6 +120,8 @@ public sealed class PayloadSystem : EntitySystem
} }
private void OnExamined(EntityUid uid, PayloadCaseComponent component, ExaminedEvent args) private void OnExamined(EntityUid uid, PayloadCaseComponent component, ExaminedEvent args)
{
using (args.PushGroup(nameof(PayloadCaseComponent)))
{ {
if (!args.IsInDetailsRange) if (!args.IsInDetailsRange)
{ {
@@ -136,6 +138,7 @@ public sealed class PayloadSystem : EntitySystem
args.PushMarkup(Loc.GetString("payload-case-does-not-have-payload", ("ent", uid))); args.PushMarkup(Loc.GetString("payload-case-does-not-have-payload", ("ent", uid)));
} }
} }
}
private void HandleChemicalPayloadTrigger(Entity<ChemicalPayloadComponent> entity, ref TriggerEvent args) private void HandleChemicalPayloadTrigger(Entity<ChemicalPayloadComponent> entity, ref TriggerEvent args)
{ {

View File

@@ -172,8 +172,13 @@ public sealed class RadioDeviceSystem : EntitySystem
return; return;
var proto = _protoMan.Index<RadioChannelPrototype>(component.BroadcastChannel); var proto = _protoMan.Index<RadioChannelPrototype>(component.BroadcastChannel);
using (args.PushGroup(nameof(RadioMicrophoneComponent)))
{
args.PushMarkup(Loc.GetString("handheld-radio-component-on-examine", ("frequency", proto.Frequency))); args.PushMarkup(Loc.GetString("handheld-radio-component-on-examine", ("frequency", proto.Frequency)));
args.PushMarkup(Loc.GetString("handheld-radio-component-chennel-examine", ("channel", proto.LocalizedName))); args.PushMarkup(Loc.GetString("handheld-radio-component-chennel-examine",
("channel", proto.LocalizedName)));
}
} }
private void OnListen(EntityUid uid, RadioMicrophoneComponent component, ListenEvent args) private void OnListen(EntityUid uid, RadioMicrophoneComponent component, ListenEvent args)

View File

@@ -64,6 +64,8 @@ public sealed class ThrusterSystem : EntitySystem
// Powered is already handled by other power components // Powered is already handled by other power components
var enabled = Loc.GetString(component.Enabled ? "thruster-comp-enabled" : "thruster-comp-disabled"); var enabled = Loc.GetString(component.Enabled ? "thruster-comp-enabled" : "thruster-comp-disabled");
using (args.PushGroup(nameof(ThrusterComponent)))
{
args.PushMarkup(enabled); args.PushMarkup(enabled);
if (component.Type == ThrusterType.Linear && if (component.Type == ThrusterType.Linear &&
@@ -77,11 +79,13 @@ public sealed class ThrusterSystem : EntitySystem
var exposed = NozzleExposed(xform); var exposed = NozzleExposed(xform);
var nozzleText = Loc.GetString(exposed ? "thruster-comp-nozzle-exposed" : "thruster-comp-nozzle-not-exposed"); var nozzleText =
Loc.GetString(exposed ? "thruster-comp-nozzle-exposed" : "thruster-comp-nozzle-not-exposed");
args.PushMarkup(nozzleText); args.PushMarkup(nozzleText);
} }
} }
}
private void OnIsHotEvent(EntityUid uid, ThrusterComponent component, IsHotEvent args) private void OnIsHotEvent(EntityUid uid, ThrusterComponent component, IsHotEvent args)
{ {

View File

@@ -25,7 +25,7 @@ namespace Content.Server.Stunnable.Systems
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<BatteryComponent, ExaminedEvent>(OnExamined); SubscribeLocalEvent<StunbatonComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<StunbatonComponent, SolutionContainerChangedEvent>(OnSolutionChange); SubscribeLocalEvent<StunbatonComponent, SolutionContainerChangedEvent>(OnSolutionChange);
SubscribeLocalEvent<StunbatonComponent, StaminaDamageOnHitAttemptEvent>(OnStaminaHitAttempt); SubscribeLocalEvent<StunbatonComponent, StaminaDamageOnHitAttemptEvent>(OnStaminaHitAttempt);
SubscribeLocalEvent<StunbatonComponent, ItemToggleActivateAttemptEvent>(TryTurnOn); SubscribeLocalEvent<StunbatonComponent, ItemToggleActivateAttemptEvent>(TryTurnOn);
@@ -47,16 +47,12 @@ namespace Content.Server.Stunnable.Systems
} }
} }
private void OnExamined(Entity<BatteryComponent> entity, ref ExaminedEvent args) private void OnExamined(Entity<StunbatonComponent> entity, ref ExaminedEvent args)
{ {
var onMsg = _itemToggle.IsActivated(entity.Owner) var onMsg = _itemToggle.IsActivated(entity.Owner)
? Loc.GetString("comp-stunbaton-examined-on") ? Loc.GetString("comp-stunbaton-examined-on")
: Loc.GetString("comp-stunbaton-examined-off"); : Loc.GetString("comp-stunbaton-examined-off");
args.PushMarkup(onMsg); args.PushMarkup(onMsg);
var chargeMessage = Loc.GetString("stunbaton-component-on-examine-charge",
("charge", (int) (entity.Comp.CurrentCharge / entity.Comp.MaxCharge * 100)));
args.PushMarkup(chargeMessage);
} }
private void ToggleDone(Entity<StunbatonComponent> entity, ref ItemToggledEvent args) private void ToggleDone(Entity<StunbatonComponent> entity, ref ItemToggledEvent args)

View File

@@ -98,6 +98,8 @@ namespace Content.Server.Tools
} }
private void OnWelderExamine(Entity<WelderComponent> entity, ref ExaminedEvent args) private void OnWelderExamine(Entity<WelderComponent> entity, ref ExaminedEvent args)
{
using (args.PushGroup(nameof(WelderComponent)))
{ {
if (_itemToggle.IsActivated(entity.Owner)) if (_itemToggle.IsActivated(entity.Owner))
{ {
@@ -119,6 +121,7 @@ namespace Content.Server.Tools
("status", string.Empty))); // Lit status is handled above ("status", string.Empty))); // Lit status is handled above
} }
} }
}
private void OnWelderAfterInteract(Entity<WelderComponent> entity, ref AfterInteractEvent args) private void OnWelderAfterInteract(Entity<WelderComponent> entity, ref AfterInteractEvent args)
{ {

View File

@@ -73,7 +73,8 @@ public sealed class TraversalDistorterSystem : EntitySystem
examine = Loc.GetString("traversal-distorter-desc-out"); examine = Loc.GetString("traversal-distorter-desc-out");
break; break;
} }
args.Message.AddMarkup(examine);
args.PushMarkup(examine);
} }
private void OnRefreshParts(EntityUid uid, TraversalDistorterComponent component, RefreshPartsEvent args) private void OnRefreshParts(EntityUid uid, TraversalDistorterComponent component, RefreshPartsEvent args)

View File

@@ -17,11 +17,13 @@ public abstract class SharedChargesSystem : EntitySystem
if (!args.IsInDetailsRange) if (!args.IsInDetailsRange)
return; return;
using (args.PushGroup(nameof(LimitedChargesComponent)))
{
args.PushMarkup(Loc.GetString("limited-charges-charges-remaining", ("charges", comp.Charges))); args.PushMarkup(Loc.GetString("limited-charges-charges-remaining", ("charges", comp.Charges)));
if (comp.Charges == comp.MaxCharges) if (comp.Charges == comp.MaxCharges)
{ {
args.PushMarkup(Loc.GetString("limited-charges-max-charges")); args.PushMarkup(Loc.GetString("limited-charges-max-charges"));
return; }
} }
} }

View File

@@ -742,6 +742,8 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
.ToHexNoAlpha(); //TODO: If the chem has a dark color, the examine text becomes black on a black background, which is unreadable. .ToHexNoAlpha(); //TODO: If the chem has a dark color, the examine text becomes black on a black background, which is unreadable.
var messageString = "shared-solution-container-component-on-examine-main-text"; var messageString = "shared-solution-container-component-on-examine-main-text";
using (args.PushGroup(nameof(ExaminableSolutionComponent)))
{
args.PushMarkup(Loc.GetString(messageString, args.PushMarkup(Loc.GetString(messageString,
("color", colorHex), ("color", colorHex),
("wordedAmount", Loc.GetString(solution.Contents.Count == 1 ("wordedAmount", Loc.GetString(solution.Contents.Count == 1
@@ -796,7 +798,9 @@ public abstract partial class SharedSolutionContainerSystem : EntitySystem
("chemical", reagent.LocalizedName))); ("chemical", reagent.LocalizedName)));
} }
args.PushMarkup(Loc.GetString("examinable-solution-has-recognizable-chemicals", ("recognizedString", msg.ToString()))); args.PushMarkup(Loc.GetString("examinable-solution-has-recognizable-chemicals",
("recognizedString", msg.ToString())));
}
} }
private void OnSolutionExaminableVerb(Entity<ExaminableSolutionComponent> entity, ref GetVerbsEvent<ExamineVerb> args) private void OnSolutionExaminableVerb(Entity<ExaminableSolutionComponent> entity, ref GetVerbsEvent<ExamineVerb> args)

View File

@@ -28,6 +28,9 @@ namespace Content.Shared.Construction
{ {
if (!args.IsInDetailsRange) if (!args.IsInDetailsRange)
return; return;
using (args.PushGroup(nameof(MachineBoardComponent)))
{
args.PushMarkup(Loc.GetString("machine-board-component-on-examine-label")); args.PushMarkup(Loc.GetString("machine-board-component-on-examine-label"));
foreach (var (part, amount) in component.Requirements) foreach (var (part, amount) in component.Requirements)
{ {
@@ -57,15 +60,21 @@ namespace Content.Shared.Construction
("requiredElement", Loc.GetString(info.ExamineName)))); ("requiredElement", Loc.GetString(info.ExamineName))));
} }
} }
}
private void OnMachinePartExamined(EntityUid uid, MachinePartComponent component, ExaminedEvent args) private void OnMachinePartExamined(EntityUid uid, MachinePartComponent component, ExaminedEvent args)
{ {
if (!args.IsInDetailsRange) if (!args.IsInDetailsRange)
return; return;
args.PushMarkup(Loc.GetString("machine-part-component-on-examine-rating-text", ("rating", component.Rating)));
using (args.PushGroup(nameof(MachinePartComponent)))
{
args.PushMarkup(Loc.GetString("machine-part-component-on-examine-rating-text",
("rating", component.Rating)));
args.PushMarkup(Loc.GetString("machine-part-component-on-examine-type-text", ("type", args.PushMarkup(Loc.GetString("machine-part-component-on-examine-type-text", ("type",
Loc.GetString(_prototype.Index<MachinePartPrototype>(component.PartType).Name)))); Loc.GetString(_prototype.Index<MachinePartPrototype>(component.PartType).Name))));
} }
}
public Dictionary<string, int> GetMachineBoardMaterialCost(Entity<MachineBoardComponent> entity, int coefficient = 1) public Dictionary<string, int> GetMachineBoardMaterialCost(Entity<MachineBoardComponent> entity, int coefficient = 1)
{ {

View File

@@ -14,7 +14,7 @@ namespace Content.Shared.Construction.Steps
if (string.IsNullOrEmpty(Name)) if (string.IsNullOrEmpty(Name))
return; return;
examinedEvent.Message.AddMarkup(Loc.GetString("construction-insert-arbitrary-entity", ("stepName", Name))); examinedEvent.PushMarkup(Loc.GetString("construction-insert-arbitrary-entity", ("stepName", Name)));
} }
public override ConstructionGuideEntry GenerateGuideEntry() public override ConstructionGuideEntry GenerateGuideEntry()

View File

@@ -20,7 +20,7 @@ namespace Content.Shared.Construction.Steps
public override void DoExamine(ExaminedEvent examinedEvent) public override void DoExamine(ExaminedEvent examinedEvent)
{ {
examinedEvent.Message.AddMarkup(string.IsNullOrEmpty(Name) examinedEvent.PushMarkup(string.IsNullOrEmpty(Name)
? Loc.GetString( ? Loc.GetString(
"construction-insert-entity-with-component", "construction-insert-entity-with-component",
("componentName", Component))// Terrible. ("componentName", Component))// Terrible.

View File

@@ -20,7 +20,7 @@ namespace Content.Shared.Construction.Steps
{ {
var material = IoCManager.Resolve<IPrototypeManager>().Index<StackPrototype>(MaterialPrototypeId); var material = IoCManager.Resolve<IPrototypeManager>().Index<StackPrototype>(MaterialPrototypeId);
examinedEvent.Message.AddMarkup(Loc.GetString("construction-insert-material-entity", ("amount", Amount), ("materialName", material.Name))); examinedEvent.PushMarkup(Loc.GetString("construction-insert-material-entity", ("amount", Amount), ("materialName", material.Name)));
} }
public override bool EntityValid(EntityUid uid, IEntityManager entityManager, IComponentFactory compFactory) public override bool EntityValid(EntityUid uid, IEntityManager entityManager, IComponentFactory compFactory)

View File

@@ -48,8 +48,12 @@ public abstract class SharedDiceSystem : EntitySystem
private void OnExamined(EntityUid uid, DiceComponent dice, ExaminedEvent args) private void OnExamined(EntityUid uid, DiceComponent dice, ExaminedEvent args)
{ {
//No details check, since the sprite updates to show the side. //No details check, since the sprite updates to show the side.
using (args.PushGroup(nameof(DiceComponent)))
{
args.PushMarkup(Loc.GetString("dice-component-on-examine-message-part-1", ("sidesAmount", dice.Sides))); args.PushMarkup(Loc.GetString("dice-component-on-examine-message-part-1", ("sidesAmount", dice.Sides)));
args.PushMarkup(Loc.GetString("dice-component-on-examine-message-part-2", ("currentSide", dice.CurrentValue))); args.PushMarkup(Loc.GetString("dice-component-on-examine-message-part-2",
("currentSide", dice.CurrentValue)));
}
} }
public void SetCurrentSide(EntityUid uid, int side, DiceComponent? die = null) public void SetCurrentSide(EntityUid uid, int side, DiceComponent? die = null)

View File

@@ -1,4 +1,7 @@
using System.Diagnostics;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Text;
using Content.Shared.Eye.Blinding.Components; using Content.Shared.Eye.Blinding.Components;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
@@ -238,42 +241,60 @@ namespace Content.Shared.Examine
return message; return message;
} }
var doNewline = false; var hasDescription = false;
//Add an entity description if one is declared //Add an entity description if one is declared
if (!string.IsNullOrEmpty(EntityManager.GetComponent<MetaDataComponent>(entity).EntityDescription)) if (!string.IsNullOrEmpty(EntityManager.GetComponent<MetaDataComponent>(entity).EntityDescription))
{ {
message.AddText(EntityManager.GetComponent<MetaDataComponent>(entity).EntityDescription); message.AddText(EntityManager.GetComponent<MetaDataComponent>(entity).EntityDescription);
doNewline = true; hasDescription = true;
} }
message.PushColor(Color.DarkGray); message.PushColor(Color.DarkGray);
// Raise the event and let things that subscribe to it change the message... // Raise the event and let things that subscribe to it change the message...
var isInDetailsRange = IsInDetailsRange(examiner.Value, entity); var isInDetailsRange = IsInDetailsRange(examiner.Value, entity);
var examinedEvent = new ExaminedEvent(message, entity, examiner.Value, isInDetailsRange, doNewline); var examinedEvent = new ExaminedEvent(message, entity, examiner.Value, isInDetailsRange, hasDescription);
RaiseLocalEvent(entity, examinedEvent, true); RaiseLocalEvent(entity, examinedEvent);
message.Pop(); var newMessage = examinedEvent.GetTotalMessage();
return message; // pop color tag
newMessage.Pop();
return newMessage;
} }
} }
/// <summary> /// <summary>
/// Raised when an entity is examined. /// Raised when an entity is examined.
/// If you're pushing multiple messages that should be grouped together (or ordered in some way),
/// call <see cref="PushGroup"/> before pushing and <see cref="PopGroup"/> when finished.
/// </summary> /// </summary>
public sealed class ExaminedEvent : EntityEventArgs public sealed class ExaminedEvent : EntityEventArgs
{ {
/// <summary> /// <summary>
/// The message that will be displayed as the examine text. /// The message that will be displayed as the examine text.
/// For most use cases, you probably want to use <see cref="PushMarkup"/> and similar instead to modify this, /// You should use <see cref="PushMarkup"/> and similar instead to modify this,
/// since it handles newlines and such correctly. /// since it handles newlines/priority and such correctly.
/// </summary> /// </summary>
/// <seealso cref="PushMessage"/> /// <seealso cref="PushMessage"/>
/// <seealso cref="PushMarkup"/> /// <seealso cref="PushMarkup"/>
/// <seealso cref="PushText"/> /// <seealso cref="PushText"/>
public FormattedMessage Message { get; } /// <seealso cref="AddMessage"/>
/// <seealso cref="AddMarkup"/>
/// <seealso cref="AddText"/>
private FormattedMessage Message { get; }
/// <summary>
/// Parts of the examine message that will later be sorted by priority and pushed onto <see cref="Message"/>.
/// </summary>
private List<ExamineMessagePart> Parts { get; } = new();
/// <summary>
/// Whether the examiner is in range of the entity to get some extra details.
/// </summary>
public bool IsInDetailsRange { get; }
/// <summary> /// <summary>
/// The entity performing the examining. /// The entity performing the examining.
@@ -285,62 +306,206 @@ namespace Content.Shared.Examine
/// </summary> /// </summary>
public EntityUid Examined { get; } public EntityUid Examined { get; }
/// <summary> private bool _hasDescription;
/// Whether the examiner is in range of the entity to get some extra details.
/// </summary>
public bool IsInDetailsRange { get; }
private bool _doNewLine; private ExamineMessagePart? _currentGroupPart;
public ExaminedEvent(FormattedMessage message, EntityUid examined, EntityUid examiner, bool isInDetailsRange, bool doNewLine) public ExaminedEvent(FormattedMessage message, EntityUid examined, EntityUid examiner, bool isInDetailsRange, bool hasDescription)
{ {
Message = message; Message = message;
Examined = examined; Examined = examined;
Examiner = examiner; Examiner = examiner;
IsInDetailsRange = isInDetailsRange; IsInDetailsRange = isInDetailsRange;
_doNewLine = doNewLine; _hasDescription = hasDescription;
}
/// <summary>
/// Returns <see cref="Message"/> with all <see cref="Parts"/> appended according to their priority.
/// </summary>
public FormattedMessage GetTotalMessage()
{
int Comparison(ExamineMessagePart a, ExamineMessagePart b)
{
// Try sort by priority, then group, then by string contents
if (a.Priority != b.Priority)
{
// negative so that expected behavior is consistent with what makes sense
// i.e. a negative priority should mean its at the bottom of the list, right?
return -a.Priority.CompareTo(b.Priority);
}
if (a.Group != b.Group)
{
return string.Compare(a.Group, b.Group, StringComparison.Ordinal);
}
return string.Compare(a.Message.ToString(), b.Message.ToString(), StringComparison.Ordinal);
}
// tolist/clone formatted message so calling this multiple times wont fuck shit up
// (if that happens for some reason)
var parts = Parts.ToList();
var totalMessage = new FormattedMessage(Message);
parts.Sort(Comparison);
if (_hasDescription)
{
totalMessage.PushNewline();
}
foreach (var part in parts)
{
totalMessage.AddMessage(part.Message);
if (part.DoNewLine && parts.Last() != part)
totalMessage.PushNewline();
}
return totalMessage;
}
/// <summary>
/// Message group handling. Call this if you want the next set of examine messages that you're adding to have
/// a consistent order with regards to each other. This is done so that client & server will always
/// sort messages the same as well as grouped together properly, even if subscriptions are different.
/// You should wrap it in a using() block so popping automatically occurs.
/// </summary>
public ExamineGroupDisposable PushGroup(string groupName, int priority=0)
{
// Ensure that other examine events correctly ended their groups.
DebugTools.Assert(_currentGroupPart == null);
_currentGroupPart = new ExamineMessagePart(new FormattedMessage(), priority, false, groupName);
return new ExamineGroupDisposable(this);
}
/// <summary>
/// Ends the current group and pushes its groups contents to the message.
/// This will be called automatically if in using a `using` block with <see cref="PushGroup"/>.
/// </summary>
private void PopGroup()
{
DebugTools.Assert(_currentGroupPart != null);
if (_currentGroupPart != null)
Parts.Add(_currentGroupPart);
_currentGroupPart = null;
} }
/// <summary> /// <summary>
/// Push another message into this examine result, on its own line. /// Push another message into this examine result, on its own line.
/// End message will be grouped by <see cref="priority"/>, then by group if one was started
/// then by ordinal comparison.
/// </summary> /// </summary>
/// <seealso cref="PushMarkup"/> /// <seealso cref="PushMarkup"/>
/// <seealso cref="PushText"/> /// <seealso cref="PushText"/>
public void PushMessage(FormattedMessage message) public void PushMessage(FormattedMessage message, int priority=0)
{ {
if (message.Nodes.Count == 0) if (message.Nodes.Count == 0)
return; return;
if (_doNewLine) if (_currentGroupPart != null)
Message.AddText("\n"); {
message.PushNewline();
Message.AddMessage(message); _currentGroupPart.Message.AddMessage(message);
_doNewLine = true; }
else
{
Parts.Add(new ExamineMessagePart(message, priority, true, null));
}
} }
/// <summary> /// <summary>
/// Push another message parsed from markup into this examine result, on its own line. /// Push another message parsed from markup into this examine result, on its own line.
/// End message will be grouped by <see cref="priority"/>, then by group if one was started
/// then by ordinal comparison.
/// </summary> /// </summary>
/// <seealso cref="PushText"/> /// <seealso cref="PushText"/>
/// <seealso cref="PushMessage"/> /// <seealso cref="PushMessage"/>
public void PushMarkup(string markup) public void PushMarkup(string markup, int priority=0)
{ {
PushMessage(FormattedMessage.FromMarkup(markup)); PushMessage(FormattedMessage.FromMarkup(markup), priority);
} }
/// <summary> /// <summary>
/// Push another message containing raw text into this examine result, on its own line. /// Push another message containing raw text into this examine result, on its own line.
/// End message will be grouped by <see cref="priority"/>, then by group if one was started
/// then by ordinal comparison.
/// </summary> /// </summary>
/// <seealso cref="PushMarkup"/> /// <seealso cref="PushMarkup"/>
/// <seealso cref="PushMessage"/> /// <seealso cref="PushMessage"/>
public void PushText(string text) public void PushText(string text, int priority=0)
{ {
var msg = new FormattedMessage(); var msg = new FormattedMessage();
msg.AddText(text); msg.AddText(text);
PushMessage(msg); PushMessage(msg, priority);
}
/// <summary>
/// Adds a message directly without starting a newline after.
/// End message will be grouped by <see cref="priority"/>, then by group if one was started
/// then by ordinal comparison.
/// </summary>
/// <seealso cref="AddMarkup"/>
/// <seealso cref="AddText"/>
public void AddMessage(FormattedMessage message, int priority = 0)
{
if (message.Nodes.Count == 0)
return;
if (_currentGroupPart != null)
{
_currentGroupPart.Message.AddMessage(message);
}
else
{
Parts.Add(new ExamineMessagePart(message, priority, false, null));
} }
} }
/// <summary>
/// Adds markup directly without starting a newline after.
/// End message will be grouped by <see cref="priority"/>, then by group if one was started
/// then by ordinal comparison.
/// </summary>
/// <seealso cref="AddText"/>
/// <seealso cref="AddMessage"/>
public void AddMarkup(string markup, int priority=0)
{
AddMessage(FormattedMessage.FromMarkup(markup), priority);
}
/// <summary>
/// Adds text directly without starting a newline after.
/// End message will be grouped by <see cref="priority"/>, then by group if one was started
/// then by ordinal comparison.
/// </summary>
/// <seealso cref="AddMarkup"/>
/// <seealso cref="AddMessage"/>
public void AddText(string text, int priority=0)
{
var msg = new FormattedMessage();
msg.AddText(text);
AddMessage(msg, priority);
}
public struct ExamineGroupDisposable : IDisposable
{
private ExaminedEvent _event;
public ExamineGroupDisposable(ExaminedEvent @event)
{
_event = @event;
}
public void Dispose()
{
_event.PopGroup();
}
}
private record ExamineMessagePart(FormattedMessage Message, int Priority, bool DoNewLine, string? Group);
}
/// <summary> /// <summary>
/// Event raised directed at an entity that someone is attempting to examine /// Event raised directed at an entity that someone is attempting to examine
/// </summary> /// </summary>

View File

@@ -185,6 +185,8 @@ public abstract partial class SharedHandsSystem : EntitySystem
var held = EnumerateHeld(uid, handsComp) var held = EnumerateHeld(uid, handsComp)
.Where(x => !HasComp<HandVirtualItemComponent>(x)).ToList(); .Where(x => !HasComp<HandVirtualItemComponent>(x)).ToList();
using (args.PushGroup(nameof(HandsComponent)))
{
if (!held.Any()) if (!held.Any())
{ {
args.PushText(Loc.GetString("comp-hands-examine-empty", args.PushText(Loc.GetString("comp-hands-examine-empty",
@@ -201,3 +203,4 @@ public abstract partial class SharedHandsSystem : EntitySystem
("items", heldList))); ("items", heldList)));
} }
} }
}

View File

@@ -112,8 +112,9 @@ public abstract class SharedItemSystem : EntitySystem
private void OnExamine(EntityUid uid, ItemComponent component, ExaminedEvent args) private void OnExamine(EntityUid uid, ItemComponent component, ExaminedEvent args)
{ {
// show at end of message generally
args.PushMarkup(Loc.GetString("item-component-on-examine-size", args.PushMarkup(Loc.GetString("item-component-on-examine-size",
("size", GetItemSizeLocale(component.Size)))); ("size", GetItemSizeLocale(component.Size))), priority: -1);
} }
public ItemSizePrototype GetSizePrototype(ProtoId<ItemSizePrototype> id) public ItemSizePrototype GetSizePrototype(ProtoId<ItemSizePrototype> id)

View File

@@ -265,17 +265,21 @@ public abstract partial class SharedGunSystem
var (count, _) = GetChamberMagazineCountCapacity(uid, component); var (count, _) = GetChamberMagazineCountCapacity(uid, component);
string boltState; string boltState;
using (args.PushGroup(nameof(ChamberMagazineAmmoProviderComponent)))
{
if (component.BoltClosed != null) if (component.BoltClosed != null)
{ {
if (component.BoltClosed == true) if (component.BoltClosed == true)
boltState = Loc.GetString("gun-chamber-bolt-open-state"); boltState = Loc.GetString("gun-chamber-bolt-open-state");
else else
boltState = Loc.GetString("gun-chamber-bolt-closed-state"); boltState = Loc.GetString("gun-chamber-bolt-closed-state");
args.PushMarkup(Loc.GetString("gun-chamber-bolt", ("bolt", boltState), ("color", component.BoltClosed.Value ? Color.FromHex("#94e1f2") : Color.FromHex("#f29d94")))); args.PushMarkup(Loc.GetString("gun-chamber-bolt", ("bolt", boltState),
("color", component.BoltClosed.Value ? Color.FromHex("#94e1f2") : Color.FromHex("#f29d94"))));
} }
args.PushMarkup(Loc.GetString("gun-magazine-examine", ("color", AmmoExamineColor), ("count", count))); args.PushMarkup(Loc.GetString("gun-magazine-examine", ("color", AmmoExamineColor), ("count", count)));
} }
}
private bool TryTakeChamberEntity(EntityUid uid, [NotNullWhen(true)] out EntityUid? entity) private bool TryTakeChamberEntity(EntityUid uid, [NotNullWhen(true)] out EntityUid? entity)
{ {

View File

@@ -14,8 +14,13 @@ public abstract partial class SharedGunSystem
if (!args.IsInDetailsRange || !component.ShowExamineText) if (!args.IsInDetailsRange || !component.ShowExamineText)
return; return;
args.PushMarkup(Loc.GetString("gun-selected-mode-examine", ("color", ModeExamineColor), ("mode", GetLocSelector(component.SelectedMode)))); using (args.PushGroup(nameof(GunComponent)))
args.PushMarkup(Loc.GetString("gun-fire-rate-examine", ("color", FireRateExamineColor), ("fireRate", $"{component.FireRate:0.0}"))); {
args.PushMarkup(Loc.GetString("gun-selected-mode-examine", ("color", ModeExamineColor),
("mode", GetLocSelector(component.SelectedMode))));
args.PushMarkup(Loc.GetString("gun-fire-rate-examine", ("color", FireRateExamineColor),
("fireRate", $"{component.FireRate:0.0}")));
}
} }
private string GetLocSelector(SelectiveFire mode) private string GetLocSelector(SelectiveFire mode)

View File

@@ -14,6 +14,8 @@ public abstract class SharedWiresSystem : EntitySystem
} }
private void OnExamine(EntityUid uid, WiresPanelComponent component, ExaminedEvent args) private void OnExamine(EntityUid uid, WiresPanelComponent component, ExaminedEvent args)
{
using (args.PushGroup(nameof(WiresPanelComponent)))
{ {
if (!component.Open) if (!component.Open)
{ {
@@ -31,3 +33,4 @@ public abstract class SharedWiresSystem : EntitySystem
} }
} }
} }
}