mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-09 10:21:18 +03:00
Stable to master sink after release 2026-08-31 (#45785)
* HOTFIX TeleportActionSystem map coords (#45659) * Fix target map coords * Improve performance * Bonus improvement * refactor: updated changelog * refactor: fix changelog entry --------- Co-authored-by: Fildrance <fildrance@gmail.com> * [HOTFIX] fixing chameleon projector (#45689) SpriteSystem.CopySprite instead of direct Copy Component * Hotfix - Grappling gun assuming either end has physics component (#45683) a can of monster does miracles at 4am * Hotfix: Return hunger slowdown back to Starving from Peckish (#45693) move hunger slowdown back to starving * HOTFIX Set AI camera static to accessible variant by default (#45673) * disable AI static * comment * [HOTFIX] Prevent Off-Station Objective Targets (#45692) * init * targets * attempt 1 at this * init * rename file * dont roll unkillable antags for kill objectives * refactor: do not allocate on MindGetAllAntagTags when fail to get MindComponent, remove unneeded linq * refactor: cleanup and added test for new condition. might be oddly specific but... * oversight * refactor: test cleanup after review * refactor: fix changelog (AGAIN GOD DAMMIT) * refactor: revert changelog thing, make it separate PR --------- Co-authored-by: Fildrance <fildrance@gmail.com> * [HOTIFX] Changelog actualisation for stable release (#45738) * [HOTFIX] fix: make changelog discord publication not fail on missing 'labels' (#45779) fix: make changelog discord publication not fail on missing 'labels' tag in changelog entry (which does not exist). --------- Co-authored-by: Connor Huffine <chuffine@gmail.com> Co-authored-by: xsainteer <156868231+xsainteer@users.noreply.github.com> Co-authored-by: Velken <8467292+Velken@users.noreply.github.com> Co-authored-by: Whatstone <166147148+whatston3@users.noreply.github.com> Co-authored-by: Errant <35878406+Errant-4@users.noreply.github.com> Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
This commit is contained in:
commit
9c8df525bc
29 changed files with 869 additions and 328 deletions
|
|
@ -28,7 +28,7 @@ public sealed partial class ChameleonProjectorSystem : SharedChameleonProjectorS
|
|||
|
||||
private void OnHandleState(Entity<ChameleonDisguiseComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
CopyComp<SpriteComponent>(ent);
|
||||
CopySprite(ent);
|
||||
CopyComp<GenericVisualizerComponent>(ent);
|
||||
CopyComp<SolutionContainerVisualsComponent>(ent);
|
||||
CopyComp<BurnStateVisualsComponent>(ent);
|
||||
|
|
@ -38,6 +38,18 @@ public sealed partial class ChameleonProjectorSystem : SharedChameleonProjectorS
|
|||
_appearance.QueueUpdate(ent, appearance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the source entity/prototype's sprite onto the disguise.
|
||||
/// </summary>
|
||||
private void CopySprite(Entity<ChameleonDisguiseComponent> ent)
|
||||
{
|
||||
if (!GetSrcEntity<SpriteComponent>(ent.Comp, out var src))
|
||||
return;
|
||||
|
||||
var dest = EnsureComp<SpriteComponent>(ent);
|
||||
_sprite.CopySprite(src, (ent.Owner, dest));
|
||||
}
|
||||
|
||||
private void OnStartup(Entity<ChameleonDisguisedComponent> ent, ref ComponentStartup args)
|
||||
{
|
||||
if (!_spriteQuery.TryComp(ent, out var sprite))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,160 @@
|
|||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.IntegrationTests.Fixtures;
|
||||
using Content.IntegrationTests.Fixtures.Attributes;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Roles;
|
||||
using Content.Shared.EntityConditions;
|
||||
using Content.Shared.EntityConditions.Conditions.Mind;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.EntityTable;
|
||||
|
||||
[TestOf(typeof(AntagonistTagCondition))]
|
||||
[TestOf(typeof(AntagonistTagEntityConditionSystem))]
|
||||
public sealed class AntagonistTagConditionTest : GameTest
|
||||
{
|
||||
[SidedDependency(Side.Server)]
|
||||
private readonly SharedEntityConditionsSystem _conditions = null!;
|
||||
|
||||
[SidedDependency(Side.Server)]
|
||||
private readonly MindSystem _minds = null!;
|
||||
|
||||
[SidedDependency(Side.Server)]
|
||||
private readonly RoleSystem _roles = null!;
|
||||
|
||||
private const string OnStationAntagRole = "AntagTagTestAntagRoleOnStation";
|
||||
private const string UnkillableAntagRole = "AntagTagTestAntagRoleUnkillable";
|
||||
|
||||
[TestPrototypes]
|
||||
private const string Prototypes =
|
||||
$"""
|
||||
- type: antag
|
||||
id: AntagTagTestAntagOnStation
|
||||
name: roles-antag-generic-solo-antagonist-name
|
||||
antagonist: true
|
||||
objective: never-shown
|
||||
tags:
|
||||
- OnStation
|
||||
|
||||
- type: antag
|
||||
id: AntagTagTestAntagUnkillable
|
||||
name: roles-antag-generic-solo-antagonist-name
|
||||
antagonist: true
|
||||
objective: never-shown
|
||||
tags:
|
||||
- OnStation
|
||||
- Unkillable
|
||||
|
||||
- type: entity
|
||||
parent: BaseMindRole
|
||||
id: {OnStationAntagRole}
|
||||
components:
|
||||
- type: MindRole
|
||||
antag: true
|
||||
antagPrototype: AntagTagTestAntagOnStation
|
||||
|
||||
- type: entity
|
||||
parent: BaseMindRole
|
||||
id: {UnkillableAntagRole}
|
||||
components:
|
||||
- type: MindRole
|
||||
antag: true
|
||||
antagPrototype: AntagTagTestAntagUnkillable
|
||||
""";
|
||||
|
||||
private static readonly HashSet<ProtoId<AntagTagPrototype>> OnStationTags = AntagTags("OnStation");
|
||||
private static readonly HashSet<ProtoId<AntagTagPrototype>> UnkillableTags = AntagTags("Unkillable");
|
||||
|
||||
[Test]
|
||||
[RunOnSide(Side.Server)]
|
||||
public void NonAntag_AlwaysPasses_WhenAllowNonAntags()
|
||||
{
|
||||
var mind = CreateMind();
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
// Positive filter (changeling.yml: "Only allow on-station targets.").
|
||||
var onStation = Cond(OnStationTags);
|
||||
Assert.That(_conditions.TryCondition(mind, onStation), Is.True);
|
||||
|
||||
// Inverted filters still pass non-antags.
|
||||
var unkillable = Cond(UnkillableTags, inverted: true);
|
||||
Assert.That(_conditions.TryCondition(mind, unkillable), Is.True);
|
||||
var onStationInverted = Cond(OnStationTags, inverted: true);
|
||||
Assert.That(_conditions.TryCondition(mind, onStationInverted), Is.True);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[RunOnSide(Side.Server)]
|
||||
public void NonAntag_InvertedFlagWithAllowNonAntagFalse_WhenDisallowed()
|
||||
{
|
||||
var mind = CreateMind();
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
var onStation = Cond(OnStationTags, allowNonAntags: false);
|
||||
Assert.That(_conditions.TryCondition(mind, onStation), Is.False);
|
||||
var onStationInverted = Cond(OnStationTags, allowNonAntags: false, inverted: true);
|
||||
Assert.That(_conditions.TryCondition(mind, onStationInverted), Is.True);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[RunOnSide(Side.Server)]
|
||||
public void CombinedFilter_MatchesChangelingObjectiveIntent()
|
||||
{
|
||||
var nonAntag = CreateMind();
|
||||
var onStationAntag = CreateMind(OnStationAntagRole);
|
||||
var unkillableAntag = CreateMind(UnkillableAntagRole);
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(PassesAll(nonAntag), Is.True);
|
||||
Assert.That(PassesAll(onStationAntag), Is.True);
|
||||
Assert.That(PassesAll(unkillableAntag), Is.False);
|
||||
}
|
||||
|
||||
bool PassesAll(EntityUid mind)
|
||||
{
|
||||
var onStation = Cond(OnStationTags);
|
||||
var unkillableInverted = Cond(UnkillableTags, inverted: true);
|
||||
return _conditions.TryCondition(mind, onStation)
|
||||
&& _conditions.TryCondition(mind, unkillableInverted);
|
||||
}
|
||||
}
|
||||
|
||||
private static HashSet<ProtoId<AntagTagPrototype>> AntagTags(params string[] ids)
|
||||
=> ids.Select(id => new ProtoId<AntagTagPrototype>(id)).ToHashSet();
|
||||
|
||||
private static AntagonistTagCondition Cond(
|
||||
HashSet<ProtoId<AntagTagPrototype>> tags,
|
||||
bool allowNonAntags = true,
|
||||
bool inverted = false
|
||||
) => new()
|
||||
{
|
||||
Tags = tags,
|
||||
AllowNonAntags = allowNonAntags,
|
||||
Inverted = inverted
|
||||
};
|
||||
|
||||
private EntityUid CreateMind(string? roleProto = null)
|
||||
{
|
||||
var mind = _minds.CreateMind(null);
|
||||
|
||||
// Give the mind an owned entity so MindAddRole doesn't log an error about it.
|
||||
var owned = SSpawn(null);
|
||||
SEntMan.EnsureComponent<MindContainerComponent>(owned);
|
||||
_minds.TransferTo(mind.Owner, owned);
|
||||
|
||||
if (roleProto != null)
|
||||
_roles.MindAddRole(mind.Owner, roleProto, silent: true);
|
||||
|
||||
return mind.Owner;
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,9 @@ public sealed partial class CCVars
|
|||
/// Replaces the AI static camera effect with a plain gradient.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> DisableAiStatic =
|
||||
CVarDef.Create("accessibility.disable_ai_static", false, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
CVarDef.Create("accessibility.disable_ai_static", true, CVar.CLIENTONLY | CVar.ARCHIVE);
|
||||
//TODO return default value to False
|
||||
// This was set to True pending evaluation of the static effect visuals, due to headache potential of the current static shader
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the movement in the blurry shader for a static effect.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
using Content.Shared.Mind;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.EntityConditions.Conditions.Mind;
|
||||
|
||||
public sealed partial class AntagonistTagEntityConditionSystem : EntityConditionSystem<MindComponent, AntagonistTagCondition>
|
||||
{
|
||||
[Dependency] private SharedRoleSystem _roleSystem = default!;
|
||||
|
||||
protected override void Condition(Entity<MindComponent> entity, ref EntityConditionEvent<AntagonistTagCondition> args)
|
||||
{
|
||||
var conditionTags = args.Condition.Tags;
|
||||
|
||||
if (!_roleSystem.TryGetAllAntagTags(entity.AsNullable(), out var antagTags))
|
||||
{
|
||||
args.Result = args.Condition is { AllowNonAntags: true, Inverted: false };
|
||||
return;
|
||||
}
|
||||
|
||||
args.Result = antagTags.Overlaps(conditionTags);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given mind is an antagonist with specified tag.
|
||||
/// </summary>
|
||||
public sealed partial class AntagonistTagCondition : EntityConditionBase<AntagonistTagCondition>
|
||||
{
|
||||
/// <summary>
|
||||
/// The tags this check will succeed for.
|
||||
/// For example, if "OnStation" is provided, all on-station antags will pass the check.
|
||||
/// If <see cref="AllowNonAntags"/> is true, it will additionally allow every non-antag to pass the check.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public HashSet<ProtoId<AntagTagPrototype>> Tags = new();
|
||||
|
||||
/// <summary>
|
||||
/// Whether non-antagonists should always pass this condition.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool AllowNonAntags = true;
|
||||
|
||||
public override string EntityConditionGuidebookText(IPrototypeManager prototype)
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -338,7 +338,7 @@ public abstract partial class SharedChameleonProjectorSystem : EntitySystem
|
|||
/// <summary>
|
||||
/// Try to get a single component from the source entity/prototype.
|
||||
/// </summary>
|
||||
private bool GetSrcComp<T>(ChameleonDisguiseComponent comp, [NotNullWhen(true)] out T? src) where T : Component, new()
|
||||
protected bool GetSrcComp<T>(ChameleonDisguiseComponent comp, [NotNullWhen(true)] out T? src) where T : Component, new()
|
||||
{
|
||||
if (TryComp(comp.SourceEntity, out src))
|
||||
return true;
|
||||
|
|
@ -351,6 +351,29 @@ public abstract partial class SharedChameleonProjectorSystem : EntitySystem
|
|||
|
||||
return proto.TryComp(out src, EntityManager.ComponentFactory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to get a single component, paired with its owning entity, from the source entity/prototype.
|
||||
/// </summary>
|
||||
protected bool GetSrcEntity<T>(ChameleonDisguiseComponent comp, out Entity<T?> src) where T : Component, new()
|
||||
{
|
||||
if (TryComp<T>(comp.SourceEntity, out var liveComp))
|
||||
{
|
||||
src = (comp.SourceEntity, liveComp);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (comp.SourceProto is { } protoId
|
||||
&& ProtoMan.TryIndex<EntityPrototype>(protoId, out var proto)
|
||||
&& proto.TryComp<T>(out var protoComp, EntityManager.ComponentFactory))
|
||||
{
|
||||
src = (EntityUid.Invalid, protoComp);
|
||||
return true;
|
||||
}
|
||||
|
||||
src = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -56,4 +56,25 @@ public sealed partial class AntagPrototype : IPrototype
|
|||
/// </summary>
|
||||
[DataField]
|
||||
public List<ProtoId<GuideEntryPrototype>>? Guides;
|
||||
|
||||
/// <summary>
|
||||
/// The tags of this antagonist.
|
||||
/// Can be used to specify the type of gameplay loop they follow.
|
||||
/// Used for filtering purposes.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public HashSet<ProtoId<AntagTagPrototype>> Tags = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to describe the type of gameplay loop some antagonists follow.
|
||||
/// Such as whether they are on-station antags or off-station.
|
||||
/// </summary>
|
||||
[Prototype]
|
||||
public sealed partial class AntagTagPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
// Can potentially be expanded in the future to show up in things like guidebooks etc.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
|
|
@ -16,6 +14,9 @@ using Robust.Shared.Player;
|
|||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Collections.Frozen;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Shared.Roles;
|
||||
|
||||
|
|
@ -541,6 +542,41 @@ public abstract partial class SharedRoleSystem : EntitySystem
|
|||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all <see cref="AntagTagPrototype"/> IDs on a mind and returns them.
|
||||
/// </summary>
|
||||
/// <param name="mind">The mind entity.</param>
|
||||
/// <param name="tags">Antag tags from all roles that mind entity had. Null if mind had no roles marked as antag.</param>
|
||||
/// <returns>A hashset of <see cref="AntagTagPrototype"/> IDs on the mind.</returns>
|
||||
public bool TryGetAllAntagTags(Entity<MindComponent?> mind, [NotNullWhen(true)] out IReadOnlySet<ProtoId<AntagTagPrototype>>? tags)
|
||||
{
|
||||
tags = FrozenSet<ProtoId<AntagTagPrototype>>.Empty;
|
||||
if (!Resolve(mind.Owner, ref mind.Comp))
|
||||
return false;
|
||||
|
||||
var roles = MindGetAllRoleInfo(mind);
|
||||
var antagTags = new HashSet<ProtoId<AntagTagPrototype>>();
|
||||
|
||||
var foundAntag = false;
|
||||
foreach (var role in roles)
|
||||
{
|
||||
if (!role.Antagonist)
|
||||
continue;
|
||||
|
||||
if (!ProtoMan.TryIndex<AntagPrototype>(role.Prototype, out var antag))
|
||||
continue;
|
||||
|
||||
foundAntag = true;
|
||||
foreach (var tag in antag.Tags)
|
||||
{
|
||||
antagTags.Add(tag);
|
||||
}
|
||||
}
|
||||
|
||||
tags = antagTags;
|
||||
return foundAntag;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all Roles of a mind Entity and returns their data as RoleInfo
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -57,18 +57,18 @@ public sealed partial class TeleportActionSystem : EntitySystem
|
|||
return false;
|
||||
|
||||
var xform = Transform(user);
|
||||
var mapId = _transform.GetMapId(target);
|
||||
if (xform.MapID != mapId ||
|
||||
!_examine.InRangeUnOccluded(user, target, SharedInteractionSystem.MaxRaycastRange))
|
||||
var targetXform = Transform(target.EntityId);
|
||||
if (!_examine.InRangeUnOccluded(user, target, SharedInteractionSystem.MaxRaycastRange))
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("teleport-action-popup-cant-see"), user, user);
|
||||
return false;
|
||||
}
|
||||
|
||||
var mapTarget = _transform.GetWorldPositionRotation(target.EntityId);
|
||||
var targetMapCoordinates = new MapCoordinates(mapTarget.WorldPosition + target.Position, mapId);
|
||||
var targetEntPosRot = _transform.GetWorldPositionRotation(targetXform);
|
||||
var targetRotated = targetEntPosRot.WorldRotation.RotateVec(target.Position);
|
||||
var targetMapCoordinates = new MapCoordinates(targetEntPosRot.WorldPosition + targetRotated, targetXform.MapID);
|
||||
|
||||
if (IsDestinationBlocked(user, targetMapCoordinates, mapTarget.WorldRotation))
|
||||
if (IsDestinationBlocked(user, targetMapCoordinates, targetEntPosRot.WorldRotation))
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("teleport-action-popup-blocked"), user, user);
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -302,13 +302,22 @@ public abstract partial class SharedGrapplingGunSystem : VirtualController
|
|||
|
||||
var grapplerUidA = _container.TryGetOuterContainer(physicalHook, Transform(physicalHook), out var containerA) ? containerA.Owner : physicalHook;
|
||||
var grapplerOffsetA = _transform.GetRelativePosition(Transform(joint.BodyAUid), grapplerUidA);
|
||||
var grapplerBodyA = Comp<PhysicsComponent>(grapplerUidA);
|
||||
|
||||
var grapplerUidB = _container.TryGetOuterContainer(physicalGrapple, Transform(physicalGrapple), out var containerB) ? containerB.Owner : physicalGrapple;
|
||||
if (attachedToGrid)
|
||||
grapplerUidB = _transform.GetGrid(joint.BodyBUid) ?? grapplerUidB;
|
||||
|
||||
// Don't try to push grids without physics (e.g. Planets).
|
||||
if (attachedToGrid && _transform.GetGrid(joint.BodyBUid) is { } gridB && HasComp<PhysicsComponent>(gridB))
|
||||
grapplerUidB = gridB;
|
||||
|
||||
var grapplerOffsetB = _transform.GetRelativePosition(Transform(joint.BodyBUid), grapplerUidB);
|
||||
var grapplerBodyB = Comp<PhysicsComponent>(grapplerUidB);
|
||||
|
||||
// Either end may lack physics in which case there's nothing to apply an impulse to.
|
||||
if (!TryComp<PhysicsComponent>(grapplerUidA, out var grapplerBodyA) ||
|
||||
!TryComp<PhysicsComponent>(grapplerUidB, out var grapplerBodyB))
|
||||
{
|
||||
Dirty(uid, jointComp);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Note that this way of calculating the impulse does not take into account objects being stuck on things, e.g. a movable grapple point stuck behind a wall.
|
||||
// Ideally the contraction of the joint itself should take this into account, but alas, this works for now.
|
||||
|
|
@ -425,11 +434,11 @@ public abstract partial class SharedGrapplingGunSystem : VirtualController
|
|||
if (!jointComp.GetJoints.TryGetValue(GrapplingJoint, out var joint))
|
||||
continue;
|
||||
|
||||
if (Transform(entity).Anchored && _transform.GetGrid(entity.Owner) != null)
|
||||
// Grids without physics (e.g. planets) can't be used as a relay body, so relay to the entity itself instead.
|
||||
if (Transform(entity).Anchored && _transform.GetGrid(entity.Owner) is { } grid && HasComp<PhysicsComponent>(grid))
|
||||
{
|
||||
joint.LocalAnchorA = _transform.GetRelativePosition(Transform(hook), _transform.GetGrid(entity.Owner)!.Value);
|
||||
_joints.SetRelay(hook, _transform.GetGrid(entity.Owner));
|
||||
|
||||
joint.LocalAnchorA = _transform.GetRelativePosition(Transform(hook), grid);
|
||||
_joints.SetRelay(hook, grid);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1248,15 +1248,8 @@ Entries:
|
|||
url: https://github.com/space-wizards/space-station-14/pull/39046
|
||||
- author: slarticodefast
|
||||
changes:
|
||||
- &o0
|
||||
message: The listplayers command now requires PII permissions due to showing IP addresses.
|
||||
- message: The listplayers command now requires PII permissions due to showing IP addresses.
|
||||
type: Tweak
|
||||
id: 169
|
||||
time: '2025-09-13T15:53:48.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/40324
|
||||
- author: slarticodefast
|
||||
changes:
|
||||
- *o0
|
||||
id: 170
|
||||
time: '2025-09-16T07:59:11.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/40395
|
||||
|
|
@ -1736,6 +1729,7 @@ Entries:
|
|||
type: Add
|
||||
id: 233
|
||||
time: '2026-07-26T09:36:27.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/44812
|
||||
- author: Centronias
|
||||
changes:
|
||||
- message: Satiation reduction command "unsatiate"
|
||||
|
|
@ -1750,5 +1744,6 @@ Entries:
|
|||
type: Remove
|
||||
id: 234
|
||||
time: '2026-08-08T22:59:23.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/34166
|
||||
Name: Admin
|
||||
Order: 3
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1181,6 +1181,7 @@ Entries:
|
|||
type: Tweak
|
||||
id: 154
|
||||
time: '2026-07-27T00:00:03.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/44889
|
||||
- author: Emisse
|
||||
changes:
|
||||
- message: Several job slots have been tweaked across the board to accommodate a smaller maxpop.
|
||||
|
|
@ -1189,6 +1190,7 @@ Entries:
|
|||
type: Remove
|
||||
id: 155
|
||||
time: '2026-07-29T14:47:34.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/44996
|
||||
- author: SuperDicq
|
||||
changes:
|
||||
- message: Added Sushi Station!
|
||||
|
|
@ -1197,36 +1199,42 @@ Entries:
|
|||
type: Tweak
|
||||
id: 156
|
||||
time: '2026-08-01T17:20:31.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/45064
|
||||
- author: ProPeperos
|
||||
changes:
|
||||
- message: On all Cargo shuttles - Tweaked atmos systems and small fixes
|
||||
type: Tweak
|
||||
id: 157
|
||||
time: '2026-08-01T19:47:13.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/44125
|
||||
- author: ProPeperos
|
||||
changes:
|
||||
- message: On Fland - Reworked the TEG room + small tweaks around atmos.
|
||||
type: Tweak
|
||||
id: 158
|
||||
time: '2026-08-01T19:47:13.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/44751
|
||||
- author: ProPeperos
|
||||
changes:
|
||||
- message: On Bagel - Replaced some pressure pumps to manual valves in atmos
|
||||
type: Tweak
|
||||
id: 159
|
||||
time: '2026-08-01T20:12:53.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/44010
|
||||
- author: ProPeperos
|
||||
changes:
|
||||
- message: On Box station - Added AI restoration console in RD office and minor issue fixes
|
||||
type: Add
|
||||
id: 160
|
||||
time: '2026-08-03T18:19:36.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/45099
|
||||
- author: ProPeperos
|
||||
changes:
|
||||
- message: On Elkridge - Added AI restoration console to RD office and other issue fixes
|
||||
type: Add
|
||||
id: 161
|
||||
time: '2026-08-03T18:36:28.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/45111
|
||||
- author: ProPeperos
|
||||
changes:
|
||||
- message: On Bagel - Moved exosuit fabricator and material silo in science and other minor tweaks and fixes.
|
||||
|
|
@ -1235,6 +1243,7 @@ Entries:
|
|||
type: Add
|
||||
id: 162
|
||||
time: '2026-08-03T18:42:03.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/45096
|
||||
- author: ProPeperos
|
||||
changes:
|
||||
- message: On Plasma - Added missing AI Restoration computer and other issue fixes
|
||||
|
|
@ -1245,18 +1254,21 @@ Entries:
|
|||
type: Fix
|
||||
id: 163
|
||||
time: '2026-08-03T21:20:28.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/45125
|
||||
- author: ProPeperos
|
||||
changes:
|
||||
- message: On Packed - You will no longer spawn in cryogenics pod at round start and other tweaks and fixes
|
||||
type: Fix
|
||||
id: 164
|
||||
time: '2026-08-05T19:30:13.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/45160
|
||||
- author: ProPeperos
|
||||
changes:
|
||||
- message: On Elkridge - You will no longer spawn in cryogenics pod at round start
|
||||
type: Fix
|
||||
id: 165
|
||||
time: '2026-08-05T20:08:46.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/45161
|
||||
- author: Spessmann
|
||||
changes:
|
||||
- message: On Snowball added wheelchairs to med
|
||||
|
|
@ -1265,46 +1277,54 @@ Entries:
|
|||
type: Remove
|
||||
id: 166
|
||||
time: '2026-08-09T04:34:51.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/45131
|
||||
- author: ProPeperos
|
||||
changes:
|
||||
- message: Updated Box to comply with the Respritening.
|
||||
type: Tweak
|
||||
id: 167
|
||||
time: '2026-08-22T23:10:16.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/45567
|
||||
- author: ProPeperos
|
||||
changes:
|
||||
- message: Updated Snowball to comply with the Respritening.
|
||||
type: Tweak
|
||||
id: 168
|
||||
time: '2026-08-22T23:10:33.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/45571
|
||||
- author: SuperDicq
|
||||
changes:
|
||||
- message: Updated Sushi Station to comply with the Respritening.
|
||||
type: Tweak
|
||||
id: 169
|
||||
time: '2026-08-22T23:10:41.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/45532
|
||||
- author: ScarKy0
|
||||
changes:
|
||||
- message: Updated event shuttles to comply with the Respritening.
|
||||
type: Tweak
|
||||
id: 170
|
||||
time: '2026-08-22T23:11:05.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/45566
|
||||
- author: ketufaispikinut
|
||||
changes:
|
||||
- message: Added energy gates to Elkridge's arrivals.
|
||||
type: Add
|
||||
id: 171
|
||||
time: '2026-08-22T23:11:14.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/45575
|
||||
- author: ScarKy0
|
||||
changes:
|
||||
- message: Updated Marathon to comply with the Respritening.
|
||||
type: Tweak
|
||||
id: 172
|
||||
time: '2026-08-22T23:13:04.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/45557
|
||||
- author: ProPeperos
|
||||
changes:
|
||||
- message: Updated Packed to comply with the Respritening.
|
||||
type: Tweak
|
||||
id: 173
|
||||
time: '2026-08-22T23:13:15.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/45568
|
||||
Order: 2
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
satiations:
|
||||
Hunger:
|
||||
thresholds:
|
||||
Peckish: 0.75
|
||||
Starving: 0.75
|
||||
Thirst:
|
||||
thresholds:
|
||||
Parched: 0.75
|
||||
|
|
|
|||
|
|
@ -60,6 +60,13 @@
|
|||
components:
|
||||
- type: PickRandomPerson
|
||||
conditions:
|
||||
- !type:AntagonistTagCondition # Only roll on-station targets.
|
||||
tags:
|
||||
- OnStation
|
||||
- !type:AntagonistTagCondition # Don't roll unkillable targets.
|
||||
inverted: true
|
||||
tags:
|
||||
- Unkillable
|
||||
# Can't have multiple objectives to kill the same person.
|
||||
- !type:ObjectiveTargetCondition
|
||||
inverted: true
|
||||
|
|
|
|||
|
|
@ -70,11 +70,20 @@
|
|||
- type: ChangelingEscapeIdentityCondition
|
||||
- type: PickRandomPerson
|
||||
conditions:
|
||||
- !type:RoleCondition
|
||||
inverted: true # Do not roll changelings here. Their initial identity will be basically impossible to get after they transform.
|
||||
- !type:AntagonistTagCondition # The only targets we want to roll for this objective are on-station.
|
||||
tags:
|
||||
- OnStation
|
||||
- !type:AntagonistTagCondition # We don't want to roll unkillable antagonists, because cannot be devoured.
|
||||
inverted: true
|
||||
tags:
|
||||
- Unkillable
|
||||
# Kill and Escape objectives should have different targets.
|
||||
- !type:ObjectiveTargetCondition
|
||||
inverted: true
|
||||
whitelist:
|
||||
components:
|
||||
- ChangelingRole
|
||||
- KillPersonCondition
|
||||
- ChangelingEscapeIdentityCondition
|
||||
|
||||
- type: entity
|
||||
parent: [BaseChangelingObjective, BaseLivingObjective]
|
||||
|
|
@ -91,11 +100,9 @@
|
|||
- type: ChangelingEscapeDepartmentCondition
|
||||
- type: PickRandomPerson
|
||||
conditions:
|
||||
- !type:RoleCondition # Do not roll changelings here. It will cause problems with weird job mindroles as they transform n stuff.
|
||||
inverted: true
|
||||
whitelist:
|
||||
components:
|
||||
- ChangelingRole
|
||||
- !type:AntagonistTagCondition # Only allow on-station targets.
|
||||
tags:
|
||||
- OnStation # Also don't bother checking for unkillable cause here we only need the DEPARTMENT of the target and I don't foresee departments full of unkillable antags in the future.
|
||||
- !type:RoleCondition
|
||||
whitelist:
|
||||
components:
|
||||
|
|
@ -114,12 +121,20 @@
|
|||
title: objective-condition-kill-maroon-title
|
||||
- type: PickRandomPerson
|
||||
conditions:
|
||||
# Can't have multiple objectives to kill the same person.
|
||||
- !type:AntagonistTagCondition # Only roll on-station targets.
|
||||
tags:
|
||||
- OnStation
|
||||
- !type:AntagonistTagCondition # Don't roll unkillable targets.
|
||||
inverted: true
|
||||
tags:
|
||||
- Unkillable
|
||||
# Kill and Escape objectives should have different targets.
|
||||
- !type:ObjectiveTargetCondition
|
||||
inverted: true
|
||||
whitelist:
|
||||
components:
|
||||
- KillPersonCondition
|
||||
- ChangelingEscapeIdentityCondition
|
||||
- type: KillPersonCondition
|
||||
requireDead: true
|
||||
requireMaroon: true
|
||||
|
|
|
|||
17
Resources/Prototypes/Roles/Antags/antagTags.yml
Normal file
17
Resources/Prototypes/Roles/Antags/antagTags.yml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
- type: antagTag
|
||||
id: OffStation # The antag comes from space or is otherwise uninvolved in the station gameplay loop. (Ex. Ninja, Nukies, Blob)
|
||||
|
||||
- type: antagTag
|
||||
id: OnStation # The antag comes from the station or is otherwise involved in the station gameplay loop. (Ex. Traitor, Thieves, Lings)
|
||||
|
||||
- type: antagTag
|
||||
id: Unkillable # The antag cannot be killed by conventional means or is fully unkillable. (Ex. Lings)
|
||||
|
||||
- type: antagTag
|
||||
id: Roundstart # This antag is selected at the start of the round. (Ex. Traitors, Initial Infected, Revs)
|
||||
|
||||
- type: antagTag
|
||||
id: Midround # This antag can be chosen midround via events. (Ex. Clones, Initial Infected)
|
||||
|
||||
- type: antagTag
|
||||
id: Converted # This antag is selected by being converted midround. (Ex. Revs, Zombies, Xorgs)
|
||||
|
|
@ -5,6 +5,10 @@
|
|||
setPreference: true
|
||||
objective: roles-antag-changeling-objective
|
||||
guides: [ Changelings ]
|
||||
tags:
|
||||
- OnStation
|
||||
- Roundstart
|
||||
- Unkillable
|
||||
requirements:
|
||||
- !type:OverallPlaytimeRequirement
|
||||
time: 5h
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@
|
|||
name: roles-antag-dragon-name
|
||||
antagonist: true
|
||||
objective: roles-antag-dragon-objective
|
||||
tags:
|
||||
- OffStation
|
||||
- Midround
|
||||
|
||||
- type: antagSpecifier
|
||||
parent: RestrictedAntag
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@
|
|||
setPreference: false
|
||||
objective: roles-antag-space-ninja-objective
|
||||
guides: [ SpaceNinja ]
|
||||
tags:
|
||||
- OffStation
|
||||
- Midround
|
||||
|
||||
- type: antagSpecifier
|
||||
parent: RestrictedAntag
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@
|
|||
- !type:OverallPlaytimeRequirement
|
||||
time: 5h
|
||||
guides: [ NuclearOperatives ]
|
||||
tags:
|
||||
- OffStation
|
||||
- Roundstart
|
||||
|
||||
- type: antag
|
||||
id: NukeopsMedic
|
||||
|
|
@ -22,6 +25,9 @@
|
|||
role: JobChemist
|
||||
time: 3h
|
||||
guides: [ NuclearOperatives ]
|
||||
tags:
|
||||
- OffStation
|
||||
- Roundstart
|
||||
|
||||
- type: antag
|
||||
id: NukeopsCommander
|
||||
|
|
@ -37,6 +43,9 @@
|
|||
time: 5h
|
||||
# should be changed to nukie playtime when thats tracked (wyci)
|
||||
guides: [ NuclearOperatives ]
|
||||
tags:
|
||||
- OffStation
|
||||
- Roundstart
|
||||
|
||||
- type: antagSpecifier
|
||||
abstract: true
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@
|
|||
antagonist: true
|
||||
objective: roles-antag-paradox-clone-objective
|
||||
setPreference: false
|
||||
tags:
|
||||
- OffStation
|
||||
- Midround
|
||||
|
||||
- type: antagSpecifier
|
||||
parent: RestrictedAntag
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@
|
|||
setPreference: true
|
||||
objective: roles-antag-rev-head-objective
|
||||
guides: [ Revolutionaries ]
|
||||
tags:
|
||||
- OnStation
|
||||
- Roundstart
|
||||
requirements:
|
||||
- !type:OverallPlaytimeRequirement
|
||||
time: 1h
|
||||
|
|
@ -44,6 +47,9 @@
|
|||
setPreference: false
|
||||
objective: roles-antag-rev-objective
|
||||
guides: [ Revolutionaries ]
|
||||
tags:
|
||||
- OnStation
|
||||
- Converted
|
||||
|
||||
- type: startingGear
|
||||
id: HeadRevGear
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@
|
|||
setPreference: true
|
||||
objective: roles-antag-thief-objective
|
||||
guides: [ Thieves ]
|
||||
tags:
|
||||
- OnStation
|
||||
- Roundstart
|
||||
requirements:
|
||||
- !type:OverallPlaytimeRequirement
|
||||
time: 1h
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@
|
|||
setPreference: true
|
||||
objective: roles-antag-syndicate-agent-objective
|
||||
guides: [ Traitors ]
|
||||
tags:
|
||||
- OnStation
|
||||
- Roundstart
|
||||
requirements:
|
||||
- !type:OverallPlaytimeRequirement
|
||||
time: 1h
|
||||
|
|
@ -32,6 +35,9 @@
|
|||
setPreference: true
|
||||
objective: roles-antag-syndicate-agent-sleeper-objective
|
||||
guides: [ Traitors ]
|
||||
tags:
|
||||
- OnStation
|
||||
- Midround
|
||||
requirements:
|
||||
- !type:OverallPlaytimeRequirement
|
||||
time: 1h
|
||||
|
|
@ -108,7 +114,7 @@
|
|||
- WeaponPistolViper
|
||||
- Syringe
|
||||
- Syringe
|
||||
|
||||
|
||||
- type: startingGear
|
||||
id: SyndicateReinforcementSpy
|
||||
equipment:
|
||||
|
|
|
|||
|
|
@ -11,10 +11,14 @@
|
|||
antagonist: true
|
||||
# setPreference: true # Disabled as roundstart gamemode until reworked
|
||||
objective: roles-antag-wizard-objective # TODO: maybe give random objs and stationary ones from AntagObjectives and AntagRandomObjectives
|
||||
guides: [ Wizard ]
|
||||
tags:
|
||||
- OffStation
|
||||
# - Roundstart - Not currently available roundstart
|
||||
- Midround
|
||||
requirements: # I hate time locked roles but this should be enough time for someone to be acclimated
|
||||
- !type:OverallPlaytimeRequirement
|
||||
time: 5h
|
||||
guides: [ Wizard ]
|
||||
|
||||
- type: antagSpecifier
|
||||
parent: RestrictedAntag
|
||||
|
|
|
|||
|
|
@ -4,11 +4,14 @@
|
|||
antagonist: true
|
||||
setPreference: true
|
||||
objective: roles-antag-mothership-core-objective
|
||||
guides: [ Xenoborgs ]
|
||||
tags:
|
||||
- OffStation
|
||||
- Roundstart
|
||||
requirements:
|
||||
- !type:RoleTimeRequirement
|
||||
role: JobBorg
|
||||
time: 18000 # 5 hrs
|
||||
guides: [ Xenoborgs ]
|
||||
|
||||
- type: antagSpecifier
|
||||
parent: RestrictedAntag
|
||||
|
|
@ -22,11 +25,15 @@
|
|||
antagonist: true
|
||||
setPreference: true
|
||||
objective: roles-antag-xenoborg-objective
|
||||
guides: [ Xenoborgs ]
|
||||
tags:
|
||||
- OffStation
|
||||
- Roundstart
|
||||
- Converted
|
||||
requirements:
|
||||
- !type:RoleTimeRequirement
|
||||
role: JobBorg
|
||||
time: 18000 # 5 hrs
|
||||
guides: [ Xenoborgs ]
|
||||
|
||||
- type: antagSpecifier
|
||||
abstract: true
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@
|
|||
setPreference: true
|
||||
objective: roles-antag-initial-infected-objective
|
||||
guides: [ Zombies ]
|
||||
tags:
|
||||
- OnStation
|
||||
- Roundstart
|
||||
- Midround # Breakout event
|
||||
requirements:
|
||||
- !type:OverallPlaytimeRequirement
|
||||
time: 1h
|
||||
|
|
@ -40,3 +44,6 @@
|
|||
setPreference: false
|
||||
objective: roles-antag-zombie-objective
|
||||
guides: [ Zombies ]
|
||||
tags:
|
||||
- OnStation
|
||||
- Converted
|
||||
|
|
|
|||
|
|
@ -184,7 +184,8 @@ def generate_description_for_entries(entries: List[Any]) -> str:
|
|||
for entry in sorted(group, key=lambda x: x["time"]):
|
||||
for change in entry["changes"]:
|
||||
emoji = TYPES_TO_EMOJI.get(change["type"], "")
|
||||
if EXPERIMENTAL_LABEL in entry["labels"]:
|
||||
labels = entry.get("labels") or []
|
||||
if EXPERIMENTAL_LABEL in labels:
|
||||
emoji = f"{emoji}{EXPERIMENTAL_EMOJI}"
|
||||
msg = change["message"]
|
||||
desc.write(f"<li>{emoji} {html.escape(msg)}</li>")
|
||||
|
|
|
|||
|
|
@ -242,7 +242,8 @@ def changelog_entries_to_message_lines(entries: Iterable[ChangelogEntry]) -> lis
|
|||
emoji = TYPES_TO_EMOJI.get(change["type"], "❓")
|
||||
message = change["message"]
|
||||
|
||||
if EXPERIMENTAL_LABEL in entry["labels"]:
|
||||
labels = entry.get("labels") or []
|
||||
if EXPERIMENTAL_LABEL in labels:
|
||||
emoji = f"{emoji}{EXPERIMENTAL_EMOJI}"
|
||||
|
||||
message_lines.append(create_change_line(emoji, message, url))
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue