private void HandleConstructionGhostExamined(EntityUid uid, ConstructionGhostComponent component, ExaminedEvent args)
{
- if (component.Prototype == null) return;
+ if (component.Prototype == null)
+ return;
- args.PushMarkup(Loc.GetString(
- "construction-ghost-examine-message",
- ("name", component.Prototype.Name)));
+ using (args.PushGroup(nameof(ConstructionGhostComponent)))
+ {
+ args.PushMarkup(Loc.GetString(
+ "construction-ghost-examine-message",
+ ("name", component.Prototype.Name)));
- if (!_prototypeManager.TryIndex(component.Prototype.Graph, out ConstructionGraphPrototype? graph))
- return;
+ if (!_prototypeManager.TryIndex(component.Prototype.Graph, out ConstructionGraphPrototype? graph))
+ return;
- var startNode = graph.Nodes[component.Prototype.StartNode];
+ var startNode = graph.Nodes[component.Prototype.StartNode];
- if (!graph.TryPath(component.Prototype.StartNode, component.Prototype.TargetNode, out var path) ||
- !startNode.TryGetEdge(path[0].Name, out var edge))
- {
- return;
- }
+ if (!graph.TryPath(component.Prototype.StartNode, component.Prototype.TargetNode, out var path) ||
+ !startNode.TryGetEdge(path[0].Name, out var edge))
+ {
+ return;
+ }
- foreach (ConstructionGraphStep step in edge.Steps)
- {
- args.Message.PushNewline();
- step.DoExamine(args);
+ foreach (var step in edge.Steps)
+ {
+ step.DoExamine(args);
+ }
}
}
var canSeeClearly = !HasComp<BlurryVisionComponent>(playerEnt);
OpenTooltip(playerEnt.Value, entity, centeredOnCursor, false, knowTarget: canSeeClearly);
- if (IsClientSide(entity)
- || _client.RunLevel == ClientRunLevel.SinglePlayerGame) // i.e. a replay
- {
- message = GetExamineText(entity, playerEnt);
- UpdateTooltipInfo(playerEnt.Value, entity, message);
- }
- else
+
+ // 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);
+ UpdateTooltipInfo(playerEnt.Value, entity, message);
+
+ if (!IsClientSide(entity))
{
// Ask server for extra examine info.
if (entity != _lastExaminedEntity)
}
RaiseLocalEvent(entity, new ClientExaminedEvent(entity, playerEnt.Value));
-
_lastExaminedEntity = entity;
}
private void OnExamined(EntityUid uid, GasTankComponent component, ExaminedEvent args)
{
+ using(args.PushGroup(nameof(GasTankComponent)));
if (args.IsInDetailsRange)
args.PushMarkup(Loc.GetString("comp-gas-tank-examine", ("pressure", Math.Round(component.Air?.Pressure ?? 0))));
if (component.IsConnected)
return;
}
- if (comp.Reacting)
- {
- args.PushMarkup(Loc.GetString("gas-recycler-reacting"));
- }
- else
+ using (args.PushGroup(nameof(GasRecyclerComponent)))
{
- if (inlet.Air.Pressure < comp.MinPressure)
+ if (comp.Reacting)
{
- args.PushMarkup(Loc.GetString("gas-recycler-low-pressure"));
+ args.PushMarkup(Loc.GetString("gas-recycler-reacting"));
}
-
- if (inlet.Air.Temperature < comp.MinTemp)
+ else
{
- args.PushMarkup(Loc.GetString("gas-recycler-low-temperature"));
+ if (inlet.Air.Pressure < comp.MinPressure)
+ {
+ args.PushMarkup(Loc.GetString("gas-recycler-low-pressure"));
+ }
+
+ if (inlet.Air.Temperature < comp.MinTemp)
+ {
+ args.PushMarkup(Loc.GetString("gas-recycler-low-temperature"));
+ }
}
}
}
if (!TryGetSeed(component, out var seed))
return;
- var name = Loc.GetString(seed.DisplayName);
- 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-potency-text", ("seedPotency", seed.Potency)));
+ using (args.PushGroup(nameof(SeedComponent)))
+ {
+ var name = Loc.GetString(seed.DisplayName);
+ 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-potency-text", ("seedPotency", seed.Potency)));
+ }
}
#region SeedPrototype prototype stuff
var (_, component) = entity;
- if (component.Seed == null)
- {
- args.PushMarkup(Loc.GetString("plant-holder-component-nothing-planted-message"));
- }
- else if (!component.Dead)
+ using (args.PushGroup(nameof(PlantHolderComponent)))
{
- var displayName = Loc.GetString(component.Seed.DisplayName);
- args.PushMarkup(Loc.GetString("plant-holder-component-something-already-growing-message",
+ if (component.Seed == null)
+ {
+ args.PushMarkup(Loc.GetString("plant-holder-component-nothing-planted-message"));
+ }
+ else if (!component.Dead)
+ {
+ var displayName = Loc.GetString(component.Seed.DisplayName);
+ args.PushMarkup(Loc.GetString("plant-holder-component-something-already-growing-message",
("seedName", displayName),
("toBeForm", displayName.EndsWith('s') ? "are" : "is")));
- if (component.Health <= component.Seed.Endurance / 2)
+ if (component.Health <= component.Seed.Endurance / 2)
+ {
+ args.PushMarkup(Loc.GetString(
+ "plant-holder-component-something-already-growing-low-health-message",
+ ("healthState",
+ Loc.GetString(component.Age > component.Seed.Lifespan
+ ? "plant-holder-component-plant-old-adjective"
+ : "plant-holder-component-plant-unhealthy-adjective"))));
+ }
+ }
+ else
{
- args.PushMarkup(Loc.GetString(
- "plant-holder-component-something-already-growing-low-health-message",
- ("healthState",
- Loc.GetString(component.Age > component.Seed.Lifespan
- ? "plant-holder-component-plant-old-adjective"
- : "plant-holder-component-plant-unhealthy-adjective"))));
+ args.PushMarkup(Loc.GetString("plant-holder-component-dead-plant-matter-message"));
}
- }
- else
- {
- args.PushMarkup(Loc.GetString("plant-holder-component-dead-plant-matter-message"));
- }
- if (component.WeedLevel >= 5)
- args.PushMarkup(Loc.GetString("plant-holder-component-weed-high-level-message"));
+ if (component.WeedLevel >= 5)
+ args.PushMarkup(Loc.GetString("plant-holder-component-weed-high-level-message"));
- if (component.PestLevel >= 5)
- args.PushMarkup(Loc.GetString("plant-holder-component-pest-high-level-message"));
+ if (component.PestLevel >= 5)
+ args.PushMarkup(Loc.GetString("plant-holder-component-pest-high-level-message"));
- args.PushMarkup(Loc.GetString($"plant-holder-component-water-level-message",
- ("waterLevel", (int) component.WaterLevel)));
- args.PushMarkup(Loc.GetString($"plant-holder-component-nutrient-level-message",
- ("nutritionLevel", (int) component.NutritionLevel)));
+ args.PushMarkup(Loc.GetString($"plant-holder-component-water-level-message",
+ ("waterLevel", (int) component.WaterLevel)));
+ args.PushMarkup(Loc.GetString($"plant-holder-component-nutrient-level-message",
+ ("nutritionLevel", (int) component.NutritionLevel)));
- if (component.DrawWarnings)
- {
- if (component.Toxins > 40f)
- args.PushMarkup(Loc.GetString("plant-holder-component-toxins-high-warning"));
+ if (component.DrawWarnings)
+ {
+ if (component.Toxins > 40f)
+ args.PushMarkup(Loc.GetString("plant-holder-component-toxins-high-warning"));
- if (component.ImproperLight)
- args.PushMarkup(Loc.GetString("plant-holder-component-light-improper-warning"));
+ if (component.ImproperLight)
+ args.PushMarkup(Loc.GetString("plant-holder-component-light-improper-warning"));
- if (component.ImproperHeat)
- args.PushMarkup(Loc.GetString("plant-holder-component-heat-improper-warning"));
+ if (component.ImproperHeat)
+ args.PushMarkup(Loc.GetString("plant-holder-component-heat-improper-warning"));
- if (component.ImproperPressure)
- args.PushMarkup(Loc.GetString("plant-holder-component-pressure-improper-warning"));
+ if (component.ImproperPressure)
+ args.PushMarkup(Loc.GetString("plant-holder-component-pressure-improper-warning"));
- if (component.MissingGas > 0)
- args.PushMarkup(Loc.GetString("plant-holder-component-gas-missing-warning"));
+ if (component.MissingGas > 0)
+ args.PushMarkup(Loc.GetString("plant-holder-component-gas-missing-warning"));
+ }
}
}
if (entityManager.EntitySysManager.GetEntitySystem<MachineFrameSystem>().IsComplete(machineFrame))
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)
{
var amount = required - machineFrame.Progress[part];
if(amount == 0)
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),
- ("elementName", Loc.GetString(part)))
- + "\n");
+ ("elementName", Loc.GetString(part))));
}
foreach (var (material, required) in machineFrame.MaterialRequirements)
if(amount == 0)
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),
- ("elementName", Loc.GetString(material)))
- + "\n");
+ ("elementName", Loc.GetString(material))));
}
foreach (var (compName, info) in machineFrame.ComponentRequirements)
if(amount == 0)
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),
- ("elementName", Loc.GetString(info.ExamineName)))
- + "\n");
+ ("elementName", Loc.GetString(info.ExamineName))));
}
foreach (var (tagName, info) in machineFrame.TagRequirements)
if(amount == 0)
continue;
- args.Message.AddMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry",
- ("amount", info.Amount),
- ("elementName", Loc.GetString(info.ExamineName)))
- + "\n");
+ args.PushMarkup(Loc.GetString("construction-condition-machine-frame-required-element-entry",
+ ("amount", info.Amount),
+ ("elementName", Loc.GetString(info.ExamineName)))
+ + "\n");
}
return true;
private void HandleConstructionExamined(EntityUid uid, ConstructionComponent component, ExaminedEvent args)
{
- if (GetTargetNode(uid, component) is {} target)
+ using (args.PushGroup(nameof(ConstructionComponent)))
{
- if (target.Name == component.DeconstructionNode)
+ if (GetTargetNode(uid, component) is {} target)
{
- args.PushMarkup(Loc.GetString("deconstruction-header-text") + "\n");
+ if (target.Name == component.DeconstructionNode)
+ {
+ args.PushMarkup(Loc.GetString("deconstruction-header-text") + "\n");
+ }
+ else
+ {
+ args.PushMarkup(Loc.GetString(
+ "construction-component-to-create-header",
+ ("targetName", target.Name)) + "\n");
+ }
}
- else
+
+ if (component.EdgeIndex == null && GetTargetEdge(uid, component) is {} targetEdge)
{
- args.PushMarkup(Loc.GetString(
- "construction-component-to-create-header",
- ("targetName", target.Name)) + "\n");
- }
- }
+ var preventStepExamine = false;
- if (component.EdgeIndex == null && GetTargetEdge(uid, component) is {} targetEdge)
- {
- var preventStepExamine = false;
+ foreach (var condition in targetEdge.Conditions)
+ {
+ preventStepExamine |= condition.DoExamine(args);
+ }
- foreach (var condition in targetEdge.Conditions)
- {
- preventStepExamine |= condition.DoExamine(args);
+ if (!preventStepExamine)
+ targetEdge.Steps[0].DoExamine(args);
+ return;
}
- if (!preventStepExamine)
- targetEdge.Steps[0].DoExamine(args);
- return;
- }
+ if (GetCurrentEdge(uid, component) is {} edge)
+ {
+ var preventStepExamine = false;
- if (GetCurrentEdge(uid, component) is {} edge)
- {
- var preventStepExamine = false;
+ foreach (var condition in edge.Conditions)
+ {
+ preventStepExamine |= condition.DoExamine(args);
+ }
- foreach (var condition in edge.Conditions)
- {
- preventStepExamine |= condition.DoExamine(args);
+ if (!preventStepExamine && component.StepIndex < edge.Steps.Count)
+ edge.Steps[component.StepIndex].DoExamine(args);
}
-
- if (!preventStepExamine && component.StepIndex < edge.Steps.Count)
- edge.Steps[component.StepIndex].DoExamine(args);
}
+
}
if (!args.IsInDetailsRange)
return;
- if (!comp.Usable)
- {
- args.PushMarkup(Loc.GetString("defusable-examine-defused", ("name", uid)));
- }
- else if (comp.Activated && TryComp<ActiveTimerTriggerComponent>(uid, out var activeComp))
+ using (args.PushGroup(nameof(DefusableComponent)))
{
- if (comp.DisplayTime)
+ if (!comp.Usable)
+ {
+ args.PushMarkup(Loc.GetString("defusable-examine-defused", ("name", uid)));
+ }
+ else if (comp.Activated && TryComp<ActiveTimerTriggerComponent>(uid, out var activeComp))
{
- args.PushMarkup(Loc.GetString("defusable-examine-live", ("name", uid),
- ("time", MathF.Floor(activeComp.TimeRemaining))));
+ if (comp.DisplayTime)
+ {
+ args.PushMarkup(Loc.GetString("defusable-examine-live", ("name", uid),
+ ("time", MathF.Floor(activeComp.TimeRemaining))));
+ }
+ else
+ {
+ args.PushMarkup(Loc.GetString("defusable-examine-live-display-off", ("name", uid)));
+ }
}
else
{
- args.PushMarkup(Loc.GetString("defusable-examine-live-display-off", ("name", uid)));
+ args.PushMarkup(Loc.GetString("defusable-examine-inactive", ("name", uid)));
}
}
- else
- {
- args.PushMarkup(Loc.GetString("defusable-examine-inactive", ("name", uid)));
- }
args.PushMarkup(Loc.GetString("defusable-examine-bolts", ("down", comp.Bolted)));
}
var text = drainSolution.AvailableVolume != 0
? Loc.GetString("drain-component-examine-volume", ("volume", drainSolution.AvailableVolume))
: 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 OnExamined(Entity<SpillableComponent> entity, ref ExaminedEvent args)
{
- args.PushMarkup(Loc.GetString("spill-examine-is-spillable"));
+ using (args.PushGroup(nameof(SpillableComponent)))
+ {
+ args.PushMarkup(Loc.GetString("spill-examine-is-spillable"));
- if (HasComp<MeleeWeaponComponent>(entity))
- args.PushMarkup(Loc.GetString("spill-examine-spillable-weapon"));
+ if (HasComp<MeleeWeaponComponent>(entity))
+ args.PushMarkup(Loc.GetString("spill-examine-spillable-weapon"));
+ }
}
private void OnOverflow(Entity<SpillableComponent> entity, ref SolutionContainerOverflowEvent args)
private void HandlePuddleExamined(Entity<PuddleComponent> entity, ref ExaminedEvent args)
{
- if (TryComp<StepTriggerComponent>(entity, out var slippery) && slippery.Active)
+ using (args.PushGroup(nameof(PuddleComponent)))
{
- args.PushMarkup(Loc.GetString("puddle-component-examine-is-slipper-text"));
- }
+ if (TryComp<StepTriggerComponent>(entity, out var slippery) && slippery.Active)
+ {
+ args.PushMarkup(Loc.GetString("puddle-component-examine-is-slipper-text"));
+ }
- if (HasComp<EvaporationComponent>(entity) &&
- _solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.SolutionName, ref entity.Comp.Solution, out var solution))
- {
- if (CanFullyEvaporate(solution))
- args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating"));
- else if (solution.GetTotalPrototypeQuantity(EvaporationReagents) > FixedPoint2.Zero)
- args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-partial"));
+ if (HasComp<EvaporationComponent>(entity) &&
+ _solutionContainerSystem.ResolveSolution(entity.Owner, entity.Comp.SolutionName,
+ ref entity.Comp.Solution, out var solution))
+ {
+ if (CanFullyEvaporate(solution))
+ args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating"));
+ else if (solution.GetTotalPrototypeQuantity(EvaporationReagents) > FixedPoint2.Zero)
+ args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-partial"));
+ else
+ args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-no"));
+ }
else
args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-no"));
}
- else
- args.PushMarkup(Loc.GetString("puddle-component-examine-evaporating-no"));
}
private void OnAnchorChanged(Entity<PuddleComponent> entity, ref AnchorStateChangedEvent args)
{
if (!args.IsInDetailsRange)
return;
-
+
if (TryComp<MindContainerComponent>(uid, out var mind) && mind.HasMind)
{
args.PushMarkup(Loc.GetString(component.ExamineTextMindPresent));
return;
var name = _prototype.Index<EntityPrototype>(component.SelectedEntity).Name;
- args.Message.PushNewline();
- args.Message.AddText(Loc.GetString("gift-packin-contains", ("name", name)));
+ args.PushText(Loc.GetString("gift-packin-contains", ("name", name)));
}
private void OnUseInHand(EntityUid uid, RandomGiftComponent component, UseInHandEvent args)
var charges = UsesRemaining(component, battery);\r
var maxCharges = MaxUses(component, battery);\r
\r
- args.PushMarkup(Loc.GetString("limited-charges-charges-remaining", ("charges", charges)));\r
-\r
- if (charges > 0 && charges == maxCharges)\r
+ using (args.PushGroup(nameof(HolosignProjectorComponent)))\r
{\r
- args.PushMarkup(Loc.GetString("limited-charges-max-charges"));\r
+ args.PushMarkup(Loc.GetString("limited-charges-charges-remaining", ("charges", charges)));\r
+\r
+ if (charges > 0 && charges == maxCharges)\r
+ {\r
+ args.PushMarkup(Loc.GetString("limited-charges-max-charges"));\r
+ }\r
}\r
}\r
\r
if (comp.LabelSlot.Item is not {Valid: true} item)
return;
- if (!args.IsInDetailsRange)
+ using (args.PushGroup(nameof(PaperLabelComponent)))
{
- args.PushMarkup(Loc.GetString("comp-paper-label-has-label-cant-read"));
- return;
- }
+ if (!args.IsInDetailsRange)
+ {
+ args.PushMarkup(Loc.GetString("comp-paper-label-has-label-cant-read"));
+ return;
+ }
- if (!EntityManager.TryGetComponent(item, out PaperComponent? paper))
- // Assuming yaml has the correct entity whitelist, this should not happen.
- return;
+ if (!EntityManager.TryGetComponent(item, out PaperComponent? paper))
+ // Assuming yaml has the correct entity whitelist, this should not happen.
+ return;
- if (string.IsNullOrWhiteSpace(paper.Content))
- {
- args.PushMarkup(Loc.GetString("comp-paper-label-has-label-blank"));
- return;
- }
+ if (string.IsNullOrWhiteSpace(paper.Content))
+ {
+ args.PushMarkup(Loc.GetString("comp-paper-label-has-label-blank"));
+ return;
+ }
- args.PushMarkup(Loc.GetString("comp-paper-label-has-label"));
- var text = paper.Content;
- args.PushMarkup(text.TrimEnd());
+ args.PushMarkup(Loc.GetString("comp-paper-label-has-label"));
+ var text = paper.Content;
+ args.PushMarkup(text.TrimEnd());
+ }
}
private void OnContainerModified(EntityUid uid, PaperLabelComponent label, ContainerModifiedMessage args)
private void OnEmergencyExamine(EntityUid uid, EmergencyLightComponent component, ExaminedEvent args)
{
- args.PushMarkup(
- Loc.GetString("emergency-light-component-on-examine",
- ("batteryStateText",
- Loc.GetString(component.BatteryStateText[component.State]))));
+ using (args.PushGroup(nameof(EmergencyLightComponent)))
+ {
+ args.PushMarkup(
+ Loc.GetString("emergency-light-component-on-examine",
+ ("batteryStateText",
+ Loc.GetString(component.BatteryStateText[component.State]))));
- // Show alert level on the light itself.
- if (!TryComp<AlertLevelComponent>(_station.GetOwningStation(uid), out var alerts))
- return;
+ // Show alert level on the light itself.
+ if (!TryComp<AlertLevelComponent>(_station.GetOwningStation(uid), out var alerts))
+ return;
- if (alerts.AlertLevels == null)
- return;
+ if (alerts.AlertLevels == null)
+ return;
- var name = alerts.CurrentLevel;
+ var name = alerts.CurrentLevel;
- var color = Color.White;
- if (alerts.AlertLevels.Levels.TryGetValue(alerts.CurrentLevel, out var details))
- color = details.Color;
+ var color = Color.White;
+ if (alerts.AlertLevels.Levels.TryGetValue(alerts.CurrentLevel, out var details))
+ color = details.Color;
- args.PushMarkup(
- Loc.GetString("emergency-light-component-on-examine-alert",
- ("color", color.ToHex()),
- ("level", name)));
+ args.PushMarkup(
+ Loc.GetString("emergency-light-component-on-examine-alert",
+ ("color", color.ToHex()),
+ ("level", name)));
+ }
}
private void OnEmergencyLightEvent(EntityUid uid, EmergencyLightComponent component, EmergencyLightEvent args)
private void OnExamined(EntityUid uid, LightReplacerComponent component, ExaminedEvent args)
{
- if (!component.InsertedBulbs.ContainedEntities.Any())
+ using (args.PushGroup(nameof(LightReplacerComponent)))
{
- args.PushMarkup(Loc.GetString("comp-light-replacer-no-lights"));
- return;
- }
- args.PushMarkup(Loc.GetString("comp-light-replacer-has-lights"));
- var groups = new Dictionary<string, int>();
- var metaQuery = GetEntityQuery<MetaDataComponent>();
- foreach (var bulb in component.InsertedBulbs.ContainedEntities)
- {
- var metaData = metaQuery.GetComponent(bulb);
- groups[metaData.EntityName] = groups.GetValueOrDefault(metaData.EntityName) + 1;
- }
+ if (!component.InsertedBulbs.ContainedEntities.Any())
+ {
+ args.PushMarkup(Loc.GetString("comp-light-replacer-no-lights"));
+ return;
+ }
- foreach (var (name, amount) in groups)
- {
- args.PushMarkup(Loc.GetString("comp-light-replacer-light-listing", ("amount", amount), ("name", name)));
+ args.PushMarkup(Loc.GetString("comp-light-replacer-has-lights"));
+ var groups = new Dictionary<string, int>();
+ var metaQuery = GetEntityQuery<MetaDataComponent>();
+ foreach (var bulb in component.InsertedBulbs.ContainedEntities)
+ {
+ var metaData = metaQuery.GetComponent(bulb);
+ groups[metaData.EntityName] = groups.GetValueOrDefault(metaData.EntityName) + 1;
+ }
+
+ foreach (var (name, amount) in groups)
+ {
+ args.PushMarkup(Loc.GetString("comp-light-replacer-light-listing", ("amount", amount), ("name", name)));
+ }
}
}
var container = _itemSlotsSystem.GetItemOrNull(entity.Owner, entity.Comp.SolutionContainerName);
if (args.IsInDetailsRange && container != null && _solutionContainerSystem.TryGetFitsInDispenser(container.Value, out _, out var containerSolution))
{
- args.PushMarkup(Loc.GetString("cryo-pod-examine", ("beaker", Name(container.Value))));
- if (containerSolution.Volume == 0)
+ using (args.PushGroup(nameof(CryoPodComponent)))
{
- args.PushMarkup(Loc.GetString("cryo-pod-empty-beaker"));
+ args.PushMarkup(Loc.GetString("cryo-pod-examine", ("beaker", Name(container.Value))));
+ if (containerSolution.Volume == 0)
+ {
+ args.PushMarkup(Loc.GetString("cryo-pod-empty-beaker"));
+ }
}
}
}
if (!TryComp<AppearanceComponent>(uid, out var appearance))
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, StorageVisuals.HasContents, out var hasContents, appearance) && hasContents)
- {
- args.PushMarkup(Loc.GetString("crematorium-entity-storage-component-on-examine-details-has-contents"));
- }
- else
- {
- args.PushMarkup(Loc.GetString("crematorium-entity-storage-component-on-examine-details-empty"));
+ 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)
+ {
+ args.PushMarkup(Loc.GetString("crematorium-entity-storage-component-on-examine-details-has-contents"));
+ }
+ else
+ {
+ args.PushMarkup(Loc.GetString("crematorium-entity-storage-component-on-examine-details-empty"));
+ }
}
}
return;
// 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);
if (empty)
{
- args.Message.AddMarkup(Loc.GetString("drink-component-on-examine-is-empty"));
+ args.AddMarkup(Loc.GetString("drink-component-on-examine-is-empty"));
return;
}
if (TryComp<ExaminableSolutionComponent>(entity, out var comp))
{
//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
{
> 33 => HalfEmptyOrHalfFull(args),
_ => "drink-component-on-examine-is-mostly-empty",
};
- args.Message.AddMarkup(Loc.GetString(remainingString));
+ args.AddMarkup(Loc.GetString(remainingString));
}
}
if (!args.IsInDetailsRange)
return;
- if (paperComp.Content != "")
- args.PushMarkup(
- Loc.GetString(
- "paper-component-examine-detail-has-words", ("paper", uid)
- )
- );
-
- if (paperComp.StampedBy.Count > 0)
+ using (args.PushGroup(nameof(PaperComponent)))
{
- var commaSeparated = string.Join(", ", paperComp.StampedBy.Select(s => Loc.GetString(s.StampedName)));
- args.PushMarkup(
- Loc.GetString(
- "paper-component-examine-detail-stamped-by", ("paper", uid), ("stamps", commaSeparated))
- );
+ if (paperComp.Content != "")
+ args.PushMarkup(
+ Loc.GetString(
+ "paper-component-examine-detail-has-words", ("paper", uid)
+ )
+ );
+
+ if (paperComp.StampedBy.Count > 0)
+ {
+ var commaSeparated =
+ string.Join(", ", paperComp.StampedBy.Select(s => Loc.GetString(s.StampedName)));
+ args.PushMarkup(
+ Loc.GetString(
+ "paper-component-examine-detail-stamped-by", ("paper", uid), ("stamps", commaSeparated))
+ );
+ }
}
}
private void OnExamined(EntityUid uid, PayloadCaseComponent component, ExaminedEvent args)
{
- if (!args.IsInDetailsRange)
+ using (args.PushGroup(nameof(PayloadCaseComponent)))
{
- args.PushMarkup(Loc.GetString("payload-case-not-close-enough", ("ent", uid)));
- return;
- }
+ if (!args.IsInDetailsRange)
+ {
+ args.PushMarkup(Loc.GetString("payload-case-not-close-enough", ("ent", uid)));
+ return;
+ }
- if (GetAllPayloads(uid).Any())
- {
- args.PushMarkup(Loc.GetString("payload-case-has-payload", ("ent", uid)));
- }
- else
- {
- args.PushMarkup(Loc.GetString("payload-case-does-not-have-payload", ("ent", uid)));
+ if (GetAllPayloads(uid).Any())
+ {
+ args.PushMarkup(Loc.GetString("payload-case-has-payload", ("ent", uid)));
+ }
+ else
+ {
+ args.PushMarkup(Loc.GetString("payload-case-does-not-have-payload", ("ent", uid)));
+ }
}
}
return;
var proto = _protoMan.Index<RadioChannelPrototype>(component.BroadcastChannel);
- args.PushMarkup(Loc.GetString("handheld-radio-component-on-examine", ("frequency", proto.Frequency)));
- args.PushMarkup(Loc.GetString("handheld-radio-component-chennel-examine", ("channel", proto.LocalizedName)));
+
+ using (args.PushGroup(nameof(RadioMicrophoneComponent)))
+ {
+ args.PushMarkup(Loc.GetString("handheld-radio-component-on-examine", ("frequency", proto.Frequency)));
+ args.PushMarkup(Loc.GetString("handheld-radio-component-chennel-examine",
+ ("channel", proto.LocalizedName)));
+ }
}
private void OnListen(EntityUid uid, RadioMicrophoneComponent component, ListenEvent args)
// Powered is already handled by other power components
var enabled = Loc.GetString(component.Enabled ? "thruster-comp-enabled" : "thruster-comp-disabled");
- args.PushMarkup(enabled);
-
- if (component.Type == ThrusterType.Linear &&
- EntityManager.TryGetComponent(uid, out TransformComponent? xform) &&
- xform.Anchored)
+ using (args.PushGroup(nameof(ThrusterComponent)))
{
- var nozzleDir = Loc.GetString("thruster-comp-nozzle-direction",
- ("direction", xform.LocalRotation.Opposite().ToWorldVec().GetDir().ToString().ToLowerInvariant()));
+ args.PushMarkup(enabled);
+
+ if (component.Type == ThrusterType.Linear &&
+ EntityManager.TryGetComponent(uid, out TransformComponent? xform) &&
+ xform.Anchored)
+ {
+ var nozzleDir = Loc.GetString("thruster-comp-nozzle-direction",
+ ("direction", xform.LocalRotation.Opposite().ToWorldVec().GetDir().ToString().ToLowerInvariant()));
- args.PushMarkup(nozzleDir);
+ args.PushMarkup(nozzleDir);
- 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);
+ }
}
}
{
base.Initialize();
- SubscribeLocalEvent<BatteryComponent, ExaminedEvent>(OnExamined);
+ SubscribeLocalEvent<StunbatonComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<StunbatonComponent, SolutionContainerChangedEvent>(OnSolutionChange);
SubscribeLocalEvent<StunbatonComponent, StaminaDamageOnHitAttemptEvent>(OnStaminaHitAttempt);
SubscribeLocalEvent<StunbatonComponent, ItemToggleActivateAttemptEvent>(TryTurnOn);
}
}
- private void OnExamined(Entity<BatteryComponent> entity, ref ExaminedEvent args)
+ private void OnExamined(Entity<StunbatonComponent> entity, ref ExaminedEvent args)
{
var onMsg = _itemToggle.IsActivated(entity.Owner)
? Loc.GetString("comp-stunbaton-examined-on")
: Loc.GetString("comp-stunbaton-examined-off");
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 OnWelderExamine(Entity<WelderComponent> entity, ref ExaminedEvent args)
{
- if (_itemToggle.IsActivated(entity.Owner))
+ using (args.PushGroup(nameof(WelderComponent)))
{
- args.PushMarkup(Loc.GetString("welder-component-on-examine-welder-lit-message"));
- }
- else
- {
- args.PushMarkup(Loc.GetString("welder-component-on-examine-welder-not-lit-message"));
- }
+ if (_itemToggle.IsActivated(entity.Owner))
+ {
+ args.PushMarkup(Loc.GetString("welder-component-on-examine-welder-lit-message"));
+ }
+ else
+ {
+ args.PushMarkup(Loc.GetString("welder-component-on-examine-welder-not-lit-message"));
+ }
- if (args.IsInDetailsRange)
- {
- var (fuel, capacity) = GetWelderFuelAndCapacity(entity.Owner, entity.Comp);
+ if (args.IsInDetailsRange)
+ {
+ var (fuel, capacity) = GetWelderFuelAndCapacity(entity.Owner, entity.Comp);
- args.PushMarkup(Loc.GetString("welder-component-on-examine-detailed-message",
- ("colorName", fuel < capacity / FixedPoint2.New(4f) ? "darkorange" : "orange"),
- ("fuelLeft", fuel),
- ("fuelCapacity", capacity),
- ("status", string.Empty))); // Lit status is handled above
+ args.PushMarkup(Loc.GetString("welder-component-on-examine-detailed-message",
+ ("colorName", fuel < capacity / FixedPoint2.New(4f) ? "darkorange" : "orange"),
+ ("fuelLeft", fuel),
+ ("fuelCapacity", capacity),
+ ("status", string.Empty))); // Lit status is handled above
+ }
}
}
examine = Loc.GetString("traversal-distorter-desc-out");
break;
}
- args.Message.AddMarkup(examine);
+
+ args.PushMarkup(examine);
}
private void OnRefreshParts(EntityUid uid, TraversalDistorterComponent component, RefreshPartsEvent args)
if (!args.IsInDetailsRange)
return;
- args.PushMarkup(Loc.GetString("limited-charges-charges-remaining", ("charges", comp.Charges)));
- if (comp.Charges == comp.MaxCharges)
+ using (args.PushGroup(nameof(LimitedChargesComponent)))
{
- args.PushMarkup(Loc.GetString("limited-charges-max-charges"));
- return;
+ args.PushMarkup(Loc.GetString("limited-charges-charges-remaining", ("charges", comp.Charges)));
+ if (comp.Charges == comp.MaxCharges)
+ {
+ args.PushMarkup(Loc.GetString("limited-charges-max-charges"));
+ }
}
}
.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";
- args.PushMarkup(Loc.GetString(messageString,
- ("color", colorHex),
- ("wordedAmount", Loc.GetString(solution.Contents.Count == 1
- ? "shared-solution-container-component-on-examine-worded-amount-one-reagent"
- : "shared-solution-container-component-on-examine-worded-amount-multiple-reagents")),
- ("desc", primary.LocalizedPhysicalDescription)));
-
- var reagentPrototypes = solution.GetReagentPrototypes(PrototypeManager);
-
- // Sort the reagents by amount, descending then alphabetically
- var sortedReagentPrototypes = reagentPrototypes
- .OrderByDescending(pair => pair.Value.Value)
- .ThenBy(pair => pair.Key.LocalizedName);
-
- // Add descriptions of immediately recognizable reagents, like water or beer
- var recognized = new List<ReagentPrototype>();
- foreach (var keyValuePair in sortedReagentPrototypes)
+ using (args.PushGroup(nameof(ExaminableSolutionComponent)))
{
- var proto = keyValuePair.Key;
- if (!proto.Recognizable)
+ args.PushMarkup(Loc.GetString(messageString,
+ ("color", colorHex),
+ ("wordedAmount", Loc.GetString(solution.Contents.Count == 1
+ ? "shared-solution-container-component-on-examine-worded-amount-one-reagent"
+ : "shared-solution-container-component-on-examine-worded-amount-multiple-reagents")),
+ ("desc", primary.LocalizedPhysicalDescription)));
+
+ var reagentPrototypes = solution.GetReagentPrototypes(PrototypeManager);
+
+ // Sort the reagents by amount, descending then alphabetically
+ var sortedReagentPrototypes = reagentPrototypes
+ .OrderByDescending(pair => pair.Value.Value)
+ .ThenBy(pair => pair.Key.LocalizedName);
+
+ // Add descriptions of immediately recognizable reagents, like water or beer
+ var recognized = new List<ReagentPrototype>();
+ foreach (var keyValuePair in sortedReagentPrototypes)
{
- continue;
- }
+ var proto = keyValuePair.Key;
+ if (!proto.Recognizable)
+ {
+ continue;
+ }
- recognized.Add(proto);
- }
+ recognized.Add(proto);
+ }
- // Skip if there's nothing recognizable
- if (recognized.Count == 0)
- return;
+ // Skip if there's nothing recognizable
+ if (recognized.Count == 0)
+ return;
- var msg = new StringBuilder();
- foreach (var reagent in recognized)
- {
- string part;
- if (reagent == recognized[0])
+ var msg = new StringBuilder();
+ foreach (var reagent in recognized)
{
- part = "examinable-solution-recognized-first";
- }
- else if (reagent == recognized[^1])
- {
- // this loc specifically requires space to be appended, fluent doesnt support whitespace
- msg.Append(' ');
- part = "examinable-solution-recognized-last";
- }
- else
- {
- part = "examinable-solution-recognized-next";
+ string part;
+ if (reagent == recognized[0])
+ {
+ part = "examinable-solution-recognized-first";
+ }
+ else if (reagent == recognized[^1])
+ {
+ // this loc specifically requires space to be appended, fluent doesnt support whitespace
+ msg.Append(' ');
+ part = "examinable-solution-recognized-last";
+ }
+ else
+ {
+ part = "examinable-solution-recognized-next";
+ }
+
+ msg.Append(Loc.GetString(part, ("color", reagent.SubstanceColor.ToHexNoAlpha()),
+ ("chemical", reagent.LocalizedName)));
}
- msg.Append(Loc.GetString(part, ("color", reagent.SubstanceColor.ToHexNoAlpha()),
- ("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)
{
if (!args.IsInDetailsRange)
return;
- args.PushMarkup(Loc.GetString("machine-board-component-on-examine-label"));
- foreach (var (part, amount) in component.Requirements)
- {
- args.PushMarkup(Loc.GetString("machine-board-component-required-element-entry-text",
- ("amount", amount),
- ("requiredElement", Loc.GetString(_prototype.Index<MachinePartPrototype>(part).Name))));
- }
- foreach (var (material, amount) in component.MaterialRequirements)
+ using (args.PushGroup(nameof(MachineBoardComponent)))
{
- args.PushMarkup(Loc.GetString("machine-board-component-required-element-entry-text",
- ("amount", amount),
- ("requiredElement", Loc.GetString(material.Name))));
- }
+ args.PushMarkup(Loc.GetString("machine-board-component-on-examine-label"));
+ foreach (var (part, amount) in component.Requirements)
+ {
+ args.PushMarkup(Loc.GetString("machine-board-component-required-element-entry-text",
+ ("amount", amount),
+ ("requiredElement", Loc.GetString(_prototype.Index<MachinePartPrototype>(part).Name))));
+ }
- foreach (var (_, info) in component.ComponentRequirements)
- {
- args.PushMarkup(Loc.GetString("machine-board-component-required-element-entry-text",
- ("amount", info.Amount),
- ("requiredElement", Loc.GetString(info.ExamineName))));
- }
+ foreach (var (material, amount) in component.MaterialRequirements)
+ {
+ args.PushMarkup(Loc.GetString("machine-board-component-required-element-entry-text",
+ ("amount", amount),
+ ("requiredElement", Loc.GetString(material.Name))));
+ }
- foreach (var (_, info) in component.TagRequirements)
- {
- args.PushMarkup(Loc.GetString("machine-board-component-required-element-entry-text",
- ("amount", info.Amount),
- ("requiredElement", Loc.GetString(info.ExamineName))));
+ foreach (var (_, info) in component.ComponentRequirements)
+ {
+ args.PushMarkup(Loc.GetString("machine-board-component-required-element-entry-text",
+ ("amount", info.Amount),
+ ("requiredElement", Loc.GetString(info.ExamineName))));
+ }
+
+ foreach (var (_, info) in component.TagRequirements)
+ {
+ args.PushMarkup(Loc.GetString("machine-board-component-required-element-entry-text",
+ ("amount", info.Amount),
+ ("requiredElement", Loc.GetString(info.ExamineName))));
+ }
}
}
{
if (!args.IsInDetailsRange)
return;
- 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",
- Loc.GetString(_prototype.Index<MachinePartPrototype>(component.PartType).Name))));
+
+ 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",
+ Loc.GetString(_prototype.Index<MachinePartPrototype>(component.PartType).Name))));
+ }
}
public Dictionary<string, int> GetMachineBoardMaterialCost(Entity<MachineBoardComponent> entity, int coefficient = 1)
if (string.IsNullOrEmpty(Name))
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 void DoExamine(ExaminedEvent examinedEvent)
{
- examinedEvent.Message.AddMarkup(string.IsNullOrEmpty(Name)
+ examinedEvent.PushMarkup(string.IsNullOrEmpty(Name)
? Loc.GetString(
"construction-insert-entity-with-component",
("componentName", Component))// Terrible.
{
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)
private void OnExamined(EntityUid uid, DiceComponent dice, ExaminedEvent args)
{
//No details check, since the sprite updates to show the side.
- 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)));
+ 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-2",
+ ("currentSide", dice.CurrentValue)));
+ }
}
public void SetCurrentSide(EntityUid uid, int side, DiceComponent? die = null)
+using System.Diagnostics;
+using System.Globalization;
using System.Linq;
+using System.Text;
using Content.Shared.Eye.Blinding.Components;
using Content.Shared.Interaction;
using Content.Shared.Mobs.Components;
return message;
}
- var doNewline = false;
+ var hasDescription = false;
//Add an entity description if one is declared
if (!string.IsNullOrEmpty(EntityManager.GetComponent<MetaDataComponent>(entity).EntityDescription))
{
message.AddText(EntityManager.GetComponent<MetaDataComponent>(entity).EntityDescription);
- doNewline = true;
+ hasDescription = true;
}
message.PushColor(Color.DarkGray);
// Raise the event and let things that subscribe to it change the message...
var isInDetailsRange = IsInDetailsRange(examiner.Value, entity);
- var examinedEvent = new ExaminedEvent(message, entity, examiner.Value, isInDetailsRange, doNewline);
- RaiseLocalEvent(entity, examinedEvent, true);
+ var examinedEvent = new ExaminedEvent(message, entity, examiner.Value, isInDetailsRange, hasDescription);
+ RaiseLocalEvent(entity, examinedEvent);
- message.Pop();
+ var newMessage = examinedEvent.GetTotalMessage();
- return message;
+ // pop color tag
+ newMessage.Pop();
+
+ return newMessage;
}
}
/// <summary>
/// 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>
public sealed class ExaminedEvent : EntityEventArgs
{
/// <summary>
/// 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,
- /// since it handles newlines and such correctly.
+ /// You should use <see cref="PushMarkup"/> and similar instead to modify this,
+ /// since it handles newlines/priority and such correctly.
/// </summary>
/// <seealso cref="PushMessage"/>
/// <seealso cref="PushMarkup"/>
/// <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>
/// The entity performing the examining.
/// </summary>
public EntityUid Examined { get; }
- /// <summary>
- /// Whether the examiner is in range of the entity to get some extra details.
- /// </summary>
- public bool IsInDetailsRange { get; }
+ private bool _hasDescription;
- 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;
Examined = examined;
Examiner = examiner;
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>
/// 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>
/// <seealso cref="PushMarkup"/>
/// <seealso cref="PushText"/>
- public void PushMessage(FormattedMessage message)
+ public void PushMessage(FormattedMessage message, int priority=0)
{
if (message.Nodes.Count == 0)
return;
- if (_doNewLine)
- Message.AddText("\n");
-
- Message.AddMessage(message);
- _doNewLine = true;
+ if (_currentGroupPart != null)
+ {
+ message.PushNewline();
+ _currentGroupPart.Message.AddMessage(message);
+ }
+ else
+ {
+ Parts.Add(new ExamineMessagePart(message, priority, true, null));
+ }
}
/// <summary>
/// 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>
/// <seealso cref="PushText"/>
/// <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>
/// 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>
/// <seealso cref="PushMarkup"/>
/// <seealso cref="PushMessage"/>
- public void PushText(string text)
+ public void PushText(string text, int priority=0)
+ {
+ var msg = new FormattedMessage();
+ msg.AddText(text);
+ 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);
- PushMessage(msg);
+ 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>
/// Event raised directed at an entity that someone is attempting to examine
/// </summary>
var held = EnumerateHeld(uid, handsComp)
.Where(x => !HasComp<HandVirtualItemComponent>(x)).ToList();
- if (!held.Any())
+ using (args.PushGroup(nameof(HandsComponent)))
{
- args.PushText(Loc.GetString("comp-hands-examine-empty",
- ("user", Identity.Entity(uid, EntityManager))));
- return;
+ if (!held.Any())
+ {
+ args.PushText(Loc.GetString("comp-hands-examine-empty",
+ ("user", Identity.Entity(uid, EntityManager))));
+ return;
+ }
+
+ var heldList = ContentLocalizationManager.FormatList(held
+ .Select(x => Loc.GetString("comp-hands-examine-wrapper",
+ ("item", Identity.Entity(x, EntityManager)))).ToList());
+
+ args.PushMarkup(Loc.GetString("comp-hands-examine",
+ ("user", Identity.Entity(uid, EntityManager)),
+ ("items", heldList)));
}
-
- var heldList = ContentLocalizationManager.FormatList(held
- .Select(x => Loc.GetString("comp-hands-examine-wrapper",
- ("item", Identity.Entity(x, EntityManager)))).ToList());
-
- args.PushMarkup(Loc.GetString("comp-hands-examine",
- ("user", Identity.Entity(uid, EntityManager)),
- ("items", heldList)));
}
}
private void OnExamine(EntityUid uid, ItemComponent component, ExaminedEvent args)
{
+ // show at end of message generally
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)
var (count, _) = GetChamberMagazineCountCapacity(uid, component);
string boltState;
- if (component.BoltClosed != null)
+ using (args.PushGroup(nameof(ChamberMagazineAmmoProviderComponent)))
{
- if (component.BoltClosed == true)
- boltState = Loc.GetString("gun-chamber-bolt-open-state");
- else
- 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"))));
- }
+ if (component.BoltClosed != null)
+ {
+ if (component.BoltClosed == true)
+ boltState = Loc.GetString("gun-chamber-bolt-open-state");
+ else
+ 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-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)
if (!args.IsInDetailsRange || !component.ShowExamineText)
return;
- 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}")));
+ using (args.PushGroup(nameof(GunComponent)))
+ {
+ 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 void OnExamine(EntityUid uid, WiresPanelComponent component, ExaminedEvent args)
{
- if (!component.Open)
+ using (args.PushGroup(nameof(WiresPanelComponent)))
{
- args.PushMarkup(Loc.GetString("wires-panel-component-on-examine-closed"));
- }
- else
- {
- args.PushMarkup(Loc.GetString("wires-panel-component-on-examine-open"));
-
- if (TryComp<WiresPanelSecurityComponent>(uid, out var wiresPanelSecurity) &&
- wiresPanelSecurity.Examine != null)
+ if (!component.Open)
{
- args.PushMarkup(Loc.GetString(wiresPanelSecurity.Examine));
+ args.PushMarkup(Loc.GetString("wires-panel-component-on-examine-closed"));
+ }
+ else
+ {
+ args.PushMarkup(Loc.GetString("wires-panel-component-on-examine-open"));
+
+ if (TryComp<WiresPanelSecurityComponent>(uid, out var wiresPanelSecurity) &&
+ wiresPanelSecurity.Examine != null)
+ {
+ args.PushMarkup(Loc.GetString(wiresPanelSecurity.Examine));
+ }
}
}
}