forked from SpaceStation14-Shenanigans/Monolith
Compare commits
243 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 46d0b83273 | |||
| f4bac52d62 | |||
| 654a657788 | |||
| 52137b967a | |||
| 8e0bff68cb | |||
| af99be86ed | |||
| 3ed4f7cc1a | |||
| e72a06c19d | |||
| 6619538fb9 | |||
| 005950f2a0 | |||
| 468be1435c | |||
| 685422209e | |||
| ae58c4d021 | |||
| 464975916d | |||
| 2f39b9edce | |||
| a18831a0c5 | |||
| 42dcb56e22 | |||
| 3eeb83a16e | |||
| abf8bd7218 | |||
| c6de815de4 | |||
| 53b02b5830 | |||
| 40d2f70e08 | |||
| 1791e631f7 | |||
| f79b69a3f6 | |||
| b8d5ca7022 | |||
| f37ef8c994 | |||
| b4ae011b87 | |||
| 0adcb3c8a4 | |||
| ad1a263596 | |||
| 591a6ba333 | |||
| ebf2e56159 | |||
| c6b36bf126 | |||
| 2cd30797ac | |||
| 2aad60a3d7 | |||
| f561fcb45e | |||
| a68c0d48ba | |||
| ec2c5a521d | |||
| 39efe6f747 | |||
| 0816b4457c | |||
| 6c87da3dc3 | |||
| 05d4b2eebd | |||
| 6ee1e7d8f3 | |||
| f097148d99 | |||
| ef2ce6565d | |||
| 98ad67497b | |||
| 67644068b6 | |||
| 1b64380dbb | |||
| 465006f16b | |||
| e1ec12fb3e | |||
| d62e3a42d0 | |||
| 02be1ef282 | |||
| 85c4a0c9ec | |||
| baeb53ee82 | |||
| 933dc75c33 | |||
| fc45f016ee | |||
| 6a719f7c39 | |||
| d83db373fe | |||
| ccec7894c6 | |||
| 3319b5f0d3 | |||
| 81d9ae37e3 | |||
| 1990f2a272 | |||
| 3665639420 | |||
| 20e2ceb0db | |||
| 84a116fd27 | |||
| c3c402d380 | |||
| 92e8677e82 | |||
| 65365135e3 | |||
| 62e372c9d2 | |||
| 4279c1977d | |||
| 6406a7cf4f | |||
| 806e22e8b4 | |||
| c2934d0bd7 | |||
| 5316f1fdd9 | |||
| 66fed8d2a2 | |||
| 28e3584a67 | |||
| 78c94ddbc4 | |||
| cef95a03bf | |||
| bd3a35ee64 | |||
| 0b50c919e4 | |||
| afcf994545 | |||
| 5aaee26a09 | |||
| fb3c49bd09 | |||
| 2c381cbcd0 | |||
| f4e3ac2478 | |||
| 31de4af5e2 | |||
| 5e67e63032 | |||
| f3919fafc1 | |||
| 974b78863b | |||
| 1654bd5cf8 | |||
| d353667f97 | |||
| 47116d8ba7 | |||
| 9bd6d5f3f2 | |||
| 1dc696deb6 | |||
| f2df23dc81 | |||
| 76d6ea96e0 | |||
| 93ba265e00 | |||
| 16f73ec031 | |||
| 9d3b0778b3 | |||
| 32b15951e2 | |||
| 7a0f0d04c5 | |||
| bcce8e67a3 | |||
| 5afcdd9379 | |||
| 01c7ea464a | |||
| 55f6108312 | |||
| 8be27c1658 | |||
| 4df38cc9cc | |||
| 8c0d2a50df | |||
| 2d8105de13 | |||
| 01bb73bf56 | |||
| fb09b866f2 | |||
| 0458a500ff | |||
| 4334b07093 | |||
| ad2dd99a9b | |||
| f11c83a96e | |||
| 36d1740829 | |||
| 1e201bb8d2 | |||
| c7a34dfc22 | |||
| e366c4d7cc | |||
| bc0151e540 | |||
| b2307660de | |||
| 5d5f6b4654 | |||
| 165f9950b5 | |||
| ef9858936d | |||
| d0a95f5d8b | |||
| ee386b0633 | |||
| de38cc18ef | |||
| ca928bdc83 | |||
| cee6ce9566 | |||
| 984a2c9b71 | |||
| 548e9eccb6 | |||
| cd817abd2c | |||
| f276df8fe9 | |||
| dd323be01d | |||
| 0cf9b35f08 | |||
| 114a09f290 | |||
| dc4b50e71e | |||
| c405c32c89 | |||
| 34ff30a736 | |||
| c8e162baa1 | |||
| ffcbc32c43 | |||
| 351b225102 | |||
| 496432881e | |||
| 9fd2a7b5af | |||
| 8972408ec1 | |||
| 5798b57788 | |||
| ec88162ac0 | |||
| 3992aa846f | |||
| 4b62f35f10 | |||
| cc1c91f39f | |||
| c5241854ce | |||
| d02895a50f | |||
| c41d005cf4 | |||
| 013f57a73d | |||
| c3f687a493 | |||
| f837710be1 | |||
| 7b00d623e0 | |||
| dafbb5f3d8 | |||
| d4487d9a5a | |||
| 7b9f602c55 | |||
| 97509dd3b6 | |||
| 55c2a03ea4 | |||
| 543c19645f | |||
| da8b19dd05 | |||
| 4fc44d3a9c | |||
| 55688cc9e0 | |||
| 5357469644 | |||
| 6040135534 | |||
| 52649a871e | |||
| fef33511d8 | |||
| fec5bfce0a | |||
| 11f82f95d3 | |||
| 3dbad97455 | |||
| a193ab828e | |||
| c75083140d | |||
| 106f9b6d28 | |||
| fe24fc22bc | |||
| 85c9636003 | |||
| a1d2f4dcbc | |||
| 3381fe165c | |||
| a913c35b21 | |||
| 0706b759d4 | |||
| 73a91c0b0a | |||
| 9e390680bd | |||
| d3d391f5a0 | |||
| a6506c7f46 | |||
| 958421b6c9 | |||
| c513938ed9 | |||
| a6431c9bab | |||
| f3390a81cc | |||
| 6ba80381cc | |||
| ef095aec88 | |||
| d02738089c | |||
| b9f0de8576 | |||
| 5a6d4b74b1 | |||
| 1ac13bf597 | |||
| 90ee912816 | |||
| 27665829e6 | |||
| e1459b0d43 | |||
| 395d334dd7 | |||
| 75e166e9b3 | |||
| d987cfe88d | |||
| a942f3e7b1 | |||
| 4108933834 | |||
| 6af6f3b386 | |||
| 9a590069d2 | |||
| a60321f201 | |||
| dfabb71045 | |||
| ce034c3f19 | |||
| 79c9dee112 | |||
| 6a048def33 | |||
| e41ef48441 | |||
| 27e7b2f70a | |||
| 2bd317dadb | |||
| 1145822797 | |||
| 27dca32ef3 | |||
| 4bdad8f6ee | |||
| 38ebc2699c | |||
| 82f75ff37f | |||
| c84cde54cb | |||
| f0c6624762 | |||
| 277ad91140 | |||
| b9419b25a1 | |||
| e1a5488f14 | |||
| d9b46ca6dc | |||
| a672e9bd28 | |||
| 2a5fe8fe6a | |||
| e069f2ba9d | |||
| db3675b3e8 | |||
| 79bf0fec68 | |||
| 41b3fb9113 | |||
| 7cd749dfa6 | |||
| eb44deeeda | |||
| 7035e752a9 | |||
| 2fdd1cd573 | |||
| 6c4101e0c0 | |||
| 5f90433697 | |||
| 9f54374593 | |||
| 5272291275 | |||
| 4a0f4cec3a | |||
| 5d68716090 | |||
| a4b1a5317c | |||
| ee48853724 | |||
| a8651f6457 |
@@ -1,5 +1,5 @@
|
||||
name: PR Changelogs
|
||||
concurrency: commit_action
|
||||
concurrency: commit_action-${{ github.event.pull_request.base.ref }} # Mono
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [closed]
|
||||
@@ -16,11 +16,11 @@ jobs:
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout Master
|
||||
- name: Checkout Base Branch # Mono
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.BOT_TOKEN }}
|
||||
ref: "${{ vars.CHANGELOG_BRANCH }}"
|
||||
ref: "${{ github.event.pull_request.base.ref }}" # Mono
|
||||
|
||||
- name: Setup Git
|
||||
run: |
|
||||
@@ -47,9 +47,9 @@ jobs:
|
||||
|
||||
- name: Commit Changelog
|
||||
run: |
|
||||
git pull origin main
|
||||
git pull origin "${{ github.event.pull_request.base.ref }}"
|
||||
git add "${{ env.CHANGELOG_DIR }}"
|
||||
git commit -m "Automated Changelog (#${{ env.PR_NUMBER }})"
|
||||
git push
|
||||
git push origin "${{ github.event.pull_request.base.ref }}"
|
||||
shell: bash
|
||||
continue-on-error: true
|
||||
|
||||
Vendored
+53
-7
@@ -13,6 +13,26 @@
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": "Client - Tools",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build - Tools",
|
||||
"program": "${workspaceFolder}/bin/Content.Client/Content.Client.dll",
|
||||
"args": [],
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": "Client - Release",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build - Release",
|
||||
"program": "${workspaceFolder}/bin/Content.Client/Content.Client.dll",
|
||||
"args": [],
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": "Client (Compatibility renderer)",
|
||||
"type": "coreclr",
|
||||
@@ -31,6 +51,26 @@
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": "Server - Tools",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build - Tools",
|
||||
"program": "${workspaceFolder}/bin/Content.Server/Content.Server.dll",
|
||||
"args": [""],
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": "Server - Release",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build - Release",
|
||||
"program": "${workspaceFolder}/bin/Content.Server/Content.Server.dll",
|
||||
"args": [""],
|
||||
"console": "integratedTerminal",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": "YAML Linter",
|
||||
"type": "coreclr",
|
||||
@@ -40,7 +80,7 @@
|
||||
"cwd": "${workspaceFolder}/Content.YAMLLinter",
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
}
|
||||
},
|
||||
],
|
||||
"compounds": [
|
||||
{
|
||||
@@ -51,14 +91,21 @@
|
||||
],
|
||||
"preLaunchTask": "build"
|
||||
},
|
||||
// Frontier begin
|
||||
{
|
||||
"name": "Server/Client (Release)",
|
||||
"name": "Server/Client - Tools",
|
||||
"configurations": [
|
||||
"Server",
|
||||
"Client"
|
||||
"Server - Tools",
|
||||
"Client - Tools"
|
||||
],
|
||||
"preLaunchTask": "build-release"
|
||||
"preLaunchTask": "build - Tools"
|
||||
},
|
||||
{
|
||||
"name": "Server/Client - Release",
|
||||
"configurations": [
|
||||
"Server - Release",
|
||||
"Client - Release"
|
||||
],
|
||||
"preLaunchTask": "build - Release"
|
||||
},
|
||||
{
|
||||
"name": "Server/Client (No Build)",
|
||||
@@ -67,6 +114,5 @@
|
||||
"Client"
|
||||
]
|
||||
}
|
||||
// Frontier end
|
||||
]
|
||||
}
|
||||
|
||||
Vendored
+26
-6
@@ -21,26 +21,46 @@
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
// Frontier begin
|
||||
{
|
||||
"label": "build-release",
|
||||
"label": "build - Tools",
|
||||
"command": "dotnet",
|
||||
"type": "shell",
|
||||
"args": [
|
||||
"build",
|
||||
"--configuration=Release", // Build in release mode. Note: --, not /. /configuration doesn't work, because Microsoft.
|
||||
"--configuration",
|
||||
"Tools",
|
||||
"/property:GenerateFullPaths=true", // Ask dotnet build to generate full paths for file names.
|
||||
"/consoleloggerparameters:NoSummary" // Do not generate summary otherwise it leads to duplicate errors in Problems panel
|
||||
"/consoleloggerparameters:'ForceNoAlign;NoSummary'", // Do not generate summary otherwise it leads to duplicate errors in Problems panel
|
||||
],
|
||||
"group": {
|
||||
"kind": "build"
|
||||
"kind": "build",
|
||||
"isDefault": false
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "silent"
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "build - Release",
|
||||
"command": "dotnet",
|
||||
"type": "shell",
|
||||
"args": [
|
||||
"build",
|
||||
"--configuration",
|
||||
"Release",
|
||||
"/property:GenerateFullPaths=true", // Ask dotnet build to generate full paths for file names.
|
||||
"/consoleloggerparameters:'ForceNoAlign;NoSummary'", // Do not generate summary otherwise it leads to duplicate errors in Problems panel
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": false
|
||||
},
|
||||
"presentation": {
|
||||
"reveal": "silent"
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
// Frontier end
|
||||
{
|
||||
"label": "build-yaml-linter",
|
||||
"command": "dotnet",
|
||||
|
||||
@@ -32,6 +32,10 @@ public sealed class CharacterInfoSystem : EntitySystem
|
||||
private void OnCharacterInfoEvent(CharacterInfoEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
var entity = GetEntity(msg.NetEntity);
|
||||
// Mono - fix
|
||||
if (TerminatingOrDeleted(entity))
|
||||
return;
|
||||
|
||||
var data = new CharacterData(entity, msg.JobTitle, msg.Objectives, msg.Briefing, Name(entity));
|
||||
|
||||
OnCharacterUpdate?.Invoke(data);
|
||||
|
||||
@@ -38,6 +38,7 @@ using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Replays;
|
||||
using Robust.Shared.Timing;
|
||||
using Content.Client._NF.Emp.Overlays; // Frontier
|
||||
using Content.Client._Mono.Company; // Mono
|
||||
|
||||
namespace Content.Client.Entry
|
||||
{
|
||||
@@ -75,6 +76,7 @@ namespace Content.Client.Entry
|
||||
[Dependency] private readonly DebugMonitorManager _debugMonitorManager = default!;
|
||||
[Dependency] private readonly TitleWindowManager _titleWindowManager = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
[Dependency] private readonly CompanyManager _companyManager = default!; // Mono
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
@@ -137,6 +139,7 @@ namespace Content.Client.Entry
|
||||
_extendedDisconnectInformation.Initialize();
|
||||
_jobRequirements.Initialize();
|
||||
_playbackMan.Initialize();
|
||||
_companyManager.Initialize();
|
||||
|
||||
//AUTOSCALING default Setup!
|
||||
_configManager.SetCVar("interface.resolutionAutoScaleUpperCutoffX", 1080);
|
||||
|
||||
@@ -24,6 +24,7 @@ using Content.Shared.Administration.Managers;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Players.PlayTimeTracking;
|
||||
using Content.Shared.Players.RateLimiting;
|
||||
using Content.Client._Mono.Company; // Mono
|
||||
|
||||
namespace Content.Client.IoC
|
||||
{
|
||||
@@ -60,6 +61,7 @@ namespace Content.Client.IoC
|
||||
collection.Register<PlayerRateLimitManager>();
|
||||
collection.Register<SharedPlayerRateLimitManager, PlayerRateLimitManager>();
|
||||
collection.Register<TitleWindowManager>();
|
||||
collection.Register<CompanyManager>(); // Mono
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public sealed class HandheldLightSystem : SharedHandheldLightSystem
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_appearance.TryGetData<bool>(uid, ToggleableLightVisuals.Enabled, out var enabled, args.Component))
|
||||
if (!_appearance.TryGetData<bool>(uid, ToggleableVisuals.Enabled, out var enabled, args.Component))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using Content.Client._Mono.Company; // Mono
|
||||
using Content.Client._Mono.MonoCoins;
|
||||
using Content.Client.Guidebook;
|
||||
using Content.Client.Humanoid;
|
||||
@@ -41,6 +42,7 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly JobRequirementsManager _requirements = default!;
|
||||
[Dependency] private readonly MarkingManager _markings = default!;
|
||||
[Dependency] private readonly CompanyManager _companyManager = default!; // Mono
|
||||
[UISystemDependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
|
||||
[UISystemDependency] private readonly ClientInventorySystem _inventory = default!;
|
||||
[UISystemDependency] private readonly StationSpawningSystem _spawn = default!;
|
||||
@@ -350,7 +352,8 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
|
||||
_prototypeManager,
|
||||
_resourceCache,
|
||||
_requirements,
|
||||
_markings);
|
||||
_markings,
|
||||
_companyManager); // Mono
|
||||
|
||||
_profileEditor.OnOpenGuidebook += _guide.OpenHelp;
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
using Direction = Robust.Shared.Maths.Direction;
|
||||
using Content.Client._Mono.Company;
|
||||
|
||||
namespace Content.Client.Lobby.UI
|
||||
{
|
||||
@@ -52,6 +53,7 @@ namespace Content.Client.Lobby.UI
|
||||
private readonly JobRequirementsManager _requirements;
|
||||
private readonly LobbyUIController _controller;
|
||||
private readonly EntityWhitelistSystem _whitelist; // Frontier
|
||||
private readonly CompanyManager _companyManager; // Mono
|
||||
|
||||
private FlavorText.FlavorText? _flavorText;
|
||||
private TextEdit? _flavorTextEdit;
|
||||
@@ -116,7 +118,8 @@ namespace Content.Client.Lobby.UI
|
||||
IPrototypeManager prototypeManager,
|
||||
IResourceManager resManager,
|
||||
JobRequirementsManager requirements,
|
||||
MarkingManager markings)
|
||||
MarkingManager markings,
|
||||
CompanyManager manager) // Mono
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_sawmill = logManager.GetSawmill("profile.editor");
|
||||
@@ -127,6 +130,7 @@ namespace Content.Client.Lobby.UI
|
||||
_prototypeManager = prototypeManager;
|
||||
_markingManager = markings;
|
||||
_preferencesManager = preferencesManager;
|
||||
_companyManager = manager; // Mono
|
||||
_resManager = resManager;
|
||||
_requirements = requirements;
|
||||
_controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
||||
@@ -446,12 +450,9 @@ namespace Content.Client.Lobby.UI
|
||||
// Clear any existing items
|
||||
CompanyButton.Clear();
|
||||
|
||||
var username = _playerManager.LocalPlayer?.Session?.Name; //Lua modified - company login support
|
||||
|
||||
// Add all companies from prototypes - use consistent sorting with UpdateCompanyControls
|
||||
var companies = _prototypeManager.EnumeratePrototypes<CompanyPrototype>()
|
||||
//.Where(c => !c.Disabled) // Filter out disabled companies
|
||||
.Where(c => !c.Disabled || (username != null && c.Logins.Contains(username))) //Lua modified - company login support
|
||||
.Where(c => !c.Disabled && _companyManager.IsAllowed(c.ID))
|
||||
.ToList();
|
||||
companies.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
|
||||
|
||||
@@ -2194,11 +2195,8 @@ namespace Content.Client.Lobby.UI
|
||||
if (Profile is null)
|
||||
return;
|
||||
|
||||
var username = _playerManager.LocalPlayer?.Session?.Name; //Lua modified - company login support
|
||||
|
||||
var companies = _prototypeManager.EnumeratePrototypes<CompanyPrototype>()
|
||||
//.Where(c => !c.Disabled) // Filter out disabled companies
|
||||
.Where(c => !c.Disabled || (username != null && c.Logins.Contains(username))) //Lua modified - company login support
|
||||
.Where(c => !c.Disabled && _companyManager.IsAllowed(c))
|
||||
.ToList();
|
||||
companies.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<Control xmlns="https://spacestation14.io">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<Label Name="TitleLabel" Access="Public" />
|
||||
<Label Name="ExampleLabel" Access="Public" />
|
||||
<ColorSelectorSliders Name="Slider" Access="Public" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
@@ -0,0 +1,31 @@
|
||||
using Content.Client.Options.UI;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client.Options.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Standard UI control used for color sliders in the options menu. Intended for use with <see cref="OptionsTabControlRow"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="OptionsTabControlRow.AddOptionColorSlider"/>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class OptionColorSlider : Control
|
||||
{
|
||||
/// <summary>
|
||||
/// The text describing what this slider affects.
|
||||
/// </summary>
|
||||
public string? Title
|
||||
{
|
||||
get => TitleLabel.Text;
|
||||
set => TitleLabel.Text = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The example text showing the current color of the slider.
|
||||
/// </summary>
|
||||
public string? Example
|
||||
{
|
||||
get => ExampleLabel.Text;
|
||||
set => ExampleLabel.Text = value;
|
||||
}
|
||||
}
|
||||
@@ -143,6 +143,19 @@ public sealed partial class OptionsTabControlRow : Control
|
||||
return AddOption(new OptionSliderIntCVar(this, _cfg, cVar, slider, min, max, format ?? FormatInt));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a color slider option, backed by a simple string CVar.
|
||||
/// </summary>
|
||||
/// <param name="cVar">The CVar represented by the slider.</param>
|
||||
/// <param name="slider">The UI control for the option.</param>
|
||||
/// <returns>The option instance backing the added option.</returns>
|
||||
public OptionColorSliderCVar AddOptionColorSlider(
|
||||
CVarDef<string> cVar,
|
||||
OptionColorSlider slider)
|
||||
{
|
||||
return AddOption(new OptionColorSliderCVar(this, _cfg, cVar, slider));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a drop-down option, backed by a CVar.
|
||||
/// </summary>
|
||||
@@ -518,6 +531,54 @@ public sealed class OptionSliderFloatCVar : BaseOptionCVar<float>
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class OptionColorSliderCVar : BaseOptionCVar<string>
|
||||
{
|
||||
private readonly OptionColorSlider _slider;
|
||||
|
||||
protected override string Value
|
||||
{
|
||||
get => _slider.Slider.Color.ToHex();
|
||||
set
|
||||
{
|
||||
_slider.Slider.Color = Color.FromHex(value);
|
||||
UpdateLabelColor();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of this type.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// It is generally more convenient to call overloads on <see cref="OptionsTabControlRow"/>
|
||||
/// such as <see cref="OptionsTabControlRow.AddOptionPercentSlider"/> instead of instantiating this type directly.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="controller">The control row that owns this option.</param>
|
||||
/// <param name="cfg">The configuration manager to get and set values from.</param>
|
||||
/// <param name="cVar">The CVar that is being controlled by this option.</param>
|
||||
/// <param name="slider">The UI control for the option.</param>
|
||||
public OptionColorSliderCVar(
|
||||
OptionsTabControlRow controller,
|
||||
IConfigurationManager cfg,
|
||||
CVarDef<string> cVar,
|
||||
OptionColorSlider slider) : base(controller, cfg, cVar)
|
||||
{
|
||||
_slider = slider;
|
||||
|
||||
slider.Slider.OnColorChanged += _ =>
|
||||
{
|
||||
ValueChanged();
|
||||
UpdateLabelColor();
|
||||
};
|
||||
}
|
||||
|
||||
private void UpdateLabelColor()
|
||||
{
|
||||
_slider.ExampleLabel.FontColorOverride = Color.FromHex(Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of a CVar option that simply corresponds with an integer <see cref="OptionSlider"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
<ui:OptionSlider Name="SpeechBubbleTextOpacitySlider" Title="{Loc 'ui-options-speech-bubble-text-opacity'}" />
|
||||
<ui:OptionSlider Name="SpeechBubbleSpeakerOpacitySlider" Title="{Loc 'ui-options-speech-bubble-speaker-opacity'}" />
|
||||
<ui:OptionSlider Name="SpeechBubbleBackgroundOpacitySlider" Title="{Loc 'ui-options-speech-bubble-background-opacity'}" />
|
||||
<CheckBox Name="AutoFillHighlightsCheckBox" Text="{Loc 'ui-options-auto-fill-highlights'}" />
|
||||
<ui:OptionColorSlider Name="HighlightsColorSlider"
|
||||
Title="{Loc 'ui-options-highlights-color'}"
|
||||
Example="{Loc 'ui-options-highlights-color-example'}"/>
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
<ui:OptionsTabControlRow Name="Control" Access="Public" />
|
||||
|
||||
@@ -20,6 +20,8 @@ public sealed partial class AccessibilityTab : Control
|
||||
Control.AddOptionPercentSlider(CCVars.SpeechBubbleTextOpacity, SpeechBubbleTextOpacitySlider);
|
||||
Control.AddOptionPercentSlider(CCVars.SpeechBubbleSpeakerOpacity, SpeechBubbleSpeakerOpacitySlider);
|
||||
Control.AddOptionPercentSlider(CCVars.SpeechBubbleBackgroundOpacity, SpeechBubbleBackgroundOpacitySlider);
|
||||
Control.AddOptionCheckBox(CCVars.ChatAutoFillHighlights, AutoFillHighlightsCheckBox);
|
||||
Control.AddOptionColorSlider(CCVars.ChatHighlightsColor, HighlightsColorSlider);
|
||||
|
||||
Control.Initialize();
|
||||
}
|
||||
|
||||
@@ -77,6 +77,16 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow
|
||||
if (_selected is {} selected && !_cyborgs.ContainsKey(selected))
|
||||
_selected = null;
|
||||
|
||||
// Corvax-Next-AiRemoteControl-Start
|
||||
var isAiControllable = false;
|
||||
|
||||
if (_selected != null)
|
||||
{
|
||||
_cyborgs.TryGetValue(_selected, out var data);
|
||||
isAiControllable = data.IsAiControllable;
|
||||
}
|
||||
// Corvax-Next-AiRemoteControl-End
|
||||
|
||||
var hasCyborgs = _cyborgs.Count > 0;
|
||||
NoCyborgs.Visible = !hasCyborgs;
|
||||
CyborgsContainer.Visible = hasCyborgs;
|
||||
|
||||
@@ -33,9 +33,22 @@ public sealed partial class BorgSelectTypeMenu : FancyWindow
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
foreach (var borgType in _prototypeManager.EnumeratePrototypes<BorgTypePrototype>().OrderBy(PrototypeName))
|
||||
// Goobstation: Customizable borgs sprites
|
||||
ConfirmTypeButton.OnPressed += ConfirmButtonPressed;
|
||||
HelpGuidebookIds = GuidebookEntries;
|
||||
|
||||
SubtypeSelection.SubtypeSelected += () =>
|
||||
ConfirmTypeButton.Disabled = false;
|
||||
}
|
||||
|
||||
// foreach (var borgType in _prototypeManager.EnumeratePrototypes<BorgTypePrototype>().OrderBy(PrototypeName))
|
||||
// Mono: Selectable borg whitelist
|
||||
public void Populate(IReadOnlyList<ProtoId<BorgTypePrototype>> whitelist)
|
||||
{
|
||||
IEnumerable<BorgTypePrototype> types = whitelist.Select(id => _prototypeManager.Index(id)).OrderBy(PrototypeName);
|
||||
foreach (var borgType in types)
|
||||
// Mono: Selectable borg whitelist end
|
||||
{
|
||||
// Goobstation-Start: Customizable borgs sprites
|
||||
var chassisList = new EntityPrototypeView
|
||||
{
|
||||
Scale = new Vector2(2, 2),
|
||||
@@ -49,13 +62,6 @@ public sealed partial class BorgSelectTypeMenu : FancyWindow
|
||||
};
|
||||
SelectionsContainer.AddChild(chassisList);
|
||||
}
|
||||
|
||||
ConfirmTypeButton.OnPressed += ConfirmButtonPressed;
|
||||
HelpGuidebookIds = GuidebookEntries;
|
||||
|
||||
|
||||
SubtypeSelection.SubtypeSelected += () =>
|
||||
ConfirmTypeButton.Disabled = false;
|
||||
// Goobstation-End: Customizable borgs sprites
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,12 @@ public sealed class BorgSelectTypeUserInterface : BoundUserInterface
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
//Mono: Selectable borg whitelist
|
||||
EntMan.TryGetComponent<BorgSwitchableTypeComponent>(Owner, out var comp);
|
||||
var whitelist = comp?.TypeWhitelist ?? [];
|
||||
|
||||
_menu = this.CreateWindow<BorgSelectTypeMenu>();
|
||||
_menu.Populate(whitelist); //Mono
|
||||
_menu.ConfirmedBorgType += (prototype, subtype) => SendPredictedMessage(new BorgSelectTypeMessage(prototype, subtype));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,18 +46,15 @@ public sealed class BorgSwitchableTypeSystem : SharedBorgSwitchableTypeSystem
|
||||
{
|
||||
if (TryComp(entity, out SpriteComponent? sprite))
|
||||
{
|
||||
_sprite.LayerSetRsiState((entity, sprite), BorgVisualLayers.Body, prototype.SpriteBodyState);
|
||||
_sprite.LayerSetRsiState((entity, sprite), BorgVisualLayers.Light, prototype.SpriteBodyState);
|
||||
_sprite.LayerSetRsiState((entity, sprite), BorgVisualLayers.LightStatus, prototype.SpriteBodyState);
|
||||
|
||||
var rsiPath = SpriteSpecifierSerializer.TextureRoot / subtypePrototype.SpritePath;
|
||||
if (_resourceCache.TryGetResource<RSIResource>(rsiPath, out var resource))
|
||||
{
|
||||
_sprite.LayerSetRsi((entity, sprite), BorgVisualLayers.Body, resource.RSI);
|
||||
_sprite.LayerSetRsi((entity, sprite), BorgVisualLayers.Light, resource.RSI);
|
||||
_sprite.LayerSetRsi((entity, sprite), BorgVisualLayers.LightStatus, resource.RSI);
|
||||
_sprite.LayerSetRsi((entity, sprite), BorgVisualLayers.Body, resource.RSI, prototype.SpriteBodyState);
|
||||
_sprite.LayerSetRsi((entity, sprite), BorgVisualLayers.Light, resource.RSI, prototype.SpriteBodyState);
|
||||
_sprite.LayerSetRsi((entity, sprite), BorgVisualLayers.LightStatus, resource.RSI, prototype.SpriteToggleLightState);
|
||||
}
|
||||
_sprite.LayerSetRsiState((entity, sprite), BorgVisualLayers.Body, prototype.SpriteBodyState);
|
||||
_sprite.LayerSetRsiState((entity, sprite), BorgVisualLayers.LightStatus, prototype.SpriteBodyState);
|
||||
_sprite.LayerSetRsiState((entity, sprite), BorgVisualLayers.LightStatus, prototype.SpriteToggleLightState);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
using Content.Shared.Hands.Components;
|
||||
|
||||
namespace Content.Client.Toggleable;
|
||||
|
||||
/// <summary>
|
||||
/// Component that handles the toggling the visuals of some light emitting entity.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This will toggle the visibility of layers on an entity's sprite, the in-hand visuals, and the clothing/equipment
|
||||
/// visuals. This will modify the color of any attached point lights.
|
||||
/// </remarks>
|
||||
[RegisterComponent]
|
||||
public sealed partial class ToggleableLightVisualsComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Sprite layer that will have its visibility toggled when this item is toggled.
|
||||
/// </summary>
|
||||
[DataField("spriteLayer")]
|
||||
public string? SpriteLayer = "light";
|
||||
|
||||
/// <summary>
|
||||
/// Layers to add to the sprite of the player that is holding this entity (while the component is toggled on).
|
||||
/// </summary>
|
||||
[DataField("inhandVisuals")]
|
||||
public Dictionary<HandLocation, List<PrototypeLayerData>> InhandVisuals = new();
|
||||
|
||||
/// <summary>
|
||||
/// Layers to add to the sprite of the player that is wearing this entity (while the component is toggled on).
|
||||
/// </summary>
|
||||
[DataField("clothingVisuals")]
|
||||
public Dictionary<string, List<PrototypeLayerData>> ClothingVisuals = new();
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
using Content.Client.Clothing;
|
||||
using Content.Client.Items.Systems;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Toggleable;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Client.Toggleable;
|
||||
|
||||
public sealed class ToggleableLightVisualsSystem : VisualizerSystem<ToggleableLightVisualsComponent>
|
||||
{
|
||||
[Dependency] private readonly SharedItemSystem _itemSys = default!;
|
||||
[Dependency] private readonly SharedPointLightSystem _lights = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<ToggleableLightVisualsComponent, GetInhandVisualsEvent>(OnGetHeldVisuals, after: new[] { typeof(ItemSystem) });
|
||||
SubscribeLocalEvent<ToggleableLightVisualsComponent, GetEquipmentVisualsEvent>(OnGetEquipmentVisuals, after: new[] { typeof(ClientClothingSystem) });
|
||||
}
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid, ToggleableLightVisualsComponent component, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (!AppearanceSystem.TryGetData<bool>(uid, ToggleableLightVisuals.Enabled, out var enabled, args.Component))
|
||||
return;
|
||||
|
||||
var modulate = AppearanceSystem.TryGetData<Color>(uid, ToggleableLightVisuals.Color, out var color, args.Component);
|
||||
|
||||
// Update the item's sprite
|
||||
if (args.Sprite != null && component.SpriteLayer != null && args.Sprite.LayerMapTryGet(component.SpriteLayer, out var layer))
|
||||
{
|
||||
args.Sprite.LayerSetVisible(layer, enabled);
|
||||
if (modulate)
|
||||
args.Sprite.LayerSetColor(layer, color);
|
||||
}
|
||||
|
||||
// Update any point-lights
|
||||
if (TryComp(uid, out PointLightComponent? light))
|
||||
{
|
||||
DebugTools.Assert(!light.NetSyncEnabled, "light visualizers require point lights without net-sync");
|
||||
_lights.SetEnabled(uid, enabled, light);
|
||||
if (enabled && modulate)
|
||||
{
|
||||
_lights.SetColor(uid, color, light);
|
||||
}
|
||||
}
|
||||
|
||||
// update clothing & in-hand visuals.
|
||||
_itemSys.VisualsChanged(uid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add the unshaded light overlays to any clothing sprites.
|
||||
/// </summary>
|
||||
private void OnGetEquipmentVisuals(EntityUid uid, ToggleableLightVisualsComponent component, GetEquipmentVisualsEvent args)
|
||||
{
|
||||
if (!TryComp(uid, out AppearanceComponent? appearance)
|
||||
|| !AppearanceSystem.TryGetData<bool>(uid, ToggleableLightVisuals.Enabled, out var enabled, appearance)
|
||||
|| !enabled)
|
||||
return;
|
||||
|
||||
if (!TryComp(args.Equipee, out InventoryComponent? inventory))
|
||||
return;
|
||||
List<PrototypeLayerData>? layers = null;
|
||||
|
||||
// attempt to get species specific data
|
||||
if (inventory.SpeciesId != null)
|
||||
component.ClothingVisuals.TryGetValue($"{args.Slot}-{inventory.SpeciesId}", out layers);
|
||||
|
||||
// No species specific data. Try to default to generic data.
|
||||
if (layers == null && !component.ClothingVisuals.TryGetValue(args.Slot, out layers))
|
||||
return;
|
||||
|
||||
var modulate = AppearanceSystem.TryGetData<Color>(uid, ToggleableLightVisuals.Color, out var color, appearance);
|
||||
|
||||
var i = 0;
|
||||
foreach (var layer in layers)
|
||||
{
|
||||
var key = layer.MapKeys?.FirstOrDefault();
|
||||
if (key == null)
|
||||
{
|
||||
key = i == 0 ? $"{args.Slot}-toggle" : $"{args.Slot}-toggle-{i}";
|
||||
i++;
|
||||
}
|
||||
|
||||
if (modulate)
|
||||
layer.Color = color;
|
||||
|
||||
args.Layers.Add((key, layer));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGetHeldVisuals(EntityUid uid, ToggleableLightVisualsComponent component, GetInhandVisualsEvent args)
|
||||
{
|
||||
if (!TryComp(uid, out AppearanceComponent? appearance)
|
||||
|| !AppearanceSystem.TryGetData<bool>(uid, ToggleableLightVisuals.Enabled, out var enabled, appearance)
|
||||
|| !enabled)
|
||||
return;
|
||||
|
||||
if (!component.InhandVisuals.TryGetValue(args.Location, out var layers))
|
||||
return;
|
||||
|
||||
var modulate = AppearanceSystem.TryGetData<Color>(uid, ToggleableLightVisuals.Color, out var color, appearance);
|
||||
|
||||
var i = 0;
|
||||
var defaultKey = $"inhand-{args.Location.ToString().ToLowerInvariant()}-toggle";
|
||||
foreach (var layer in layers)
|
||||
{
|
||||
var key = layer.MapKeys?.FirstOrDefault();
|
||||
if (key == null)
|
||||
{
|
||||
key = i == 0 ? defaultKey : $"{defaultKey}-{i}";
|
||||
i++;
|
||||
}
|
||||
|
||||
if (modulate)
|
||||
layer.Color = color;
|
||||
|
||||
args.Layers.Add((key, layer));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Content.Shared.Hands.Components;
|
||||
|
||||
namespace Content.Client.Toggleable;
|
||||
|
||||
/// <summary>
|
||||
/// Component that handles toggling the visuals of an entity, including layers on an entity's sprite,
|
||||
/// the in-hand visuals, and the clothing/equipment visuals.
|
||||
/// </summary>
|
||||
/// <see cref="ToggleableVisualsSystem"/>
|
||||
[RegisterComponent]
|
||||
public sealed partial class ToggleableVisualsComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Sprite layer that will have its visibility toggled when this item is toggled.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public string? SpriteLayer;
|
||||
|
||||
/// <summary>
|
||||
/// Layers to add to the sprite of the player that is holding this entity (while the component is toggled on).
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Dictionary<HandLocation, List<PrototypeLayerData>> InhandVisuals = new();
|
||||
|
||||
/// <summary>
|
||||
/// Layers to add to the sprite of the player that is wearing this entity (while the component is toggled on).
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Dictionary<string, List<PrototypeLayerData>> ClothingVisuals = new();
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
using System.Linq;
|
||||
using Content.Client.Clothing;
|
||||
using Content.Client.Items.Systems;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Light.Components;
|
||||
using Content.Shared.Toggleable;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Toggleable;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the behavior of <see cref="ToggleableVisualsComponent"/> by reacting to
|
||||
/// <see cref="AppearanceChangeEvent"/>, for the sprite directly; <see cref="OnGetHeldVisuals"/> for the
|
||||
/// in-hand visuals; and <see cref="OnGetEquipmentVisuals"/> for the clothing visuals.
|
||||
/// </summary>
|
||||
/// <see cref="ToggleableVisualsComponent"/>
|
||||
public sealed class ToggleableVisualsSystem : VisualizerSystem<ToggleableVisualsComponent>
|
||||
{
|
||||
[Dependency] private readonly SharedItemSystem _item = default!;
|
||||
[Dependency] private readonly SharedPointLightSystem _pointLight = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<ToggleableVisualsComponent, GetInhandVisualsEvent>(OnGetHeldVisuals,
|
||||
after: [typeof(ItemSystem)]);
|
||||
SubscribeLocalEvent<ToggleableVisualsComponent, GetEquipmentVisualsEvent>(OnGetEquipmentVisuals,
|
||||
after: [typeof(ClientClothingSystem)]);
|
||||
}
|
||||
|
||||
protected override void OnAppearanceChange(EntityUid uid,
|
||||
ToggleableVisualsComponent component,
|
||||
ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (!AppearanceSystem.TryGetData<bool>(uid, ToggleableVisuals.Enabled, out var enabled, args.Component))
|
||||
return;
|
||||
|
||||
var modulateColor =
|
||||
AppearanceSystem.TryGetData<Color>(uid, ToggleableVisuals.Color, out var color, args.Component);
|
||||
|
||||
// Update the item's sprite
|
||||
if (args.Sprite != null && component.SpriteLayer != null &&
|
||||
SpriteSystem.LayerMapTryGet((uid, args.Sprite), component.SpriteLayer, out var layer, false))
|
||||
{
|
||||
SpriteSystem.LayerSetVisible((uid, args.Sprite), layer, enabled);
|
||||
if (modulateColor)
|
||||
SpriteSystem.LayerSetColor((uid, args.Sprite), component.SpriteLayer, color);
|
||||
}
|
||||
|
||||
// If there's a `ItemTogglePointLightComponent` that says to apply the color to attached lights, do so.
|
||||
if (TryComp<ItemTogglePointLightComponent>(uid, out var toggleLights) &&
|
||||
TryComp(uid, out PointLightComponent? light))
|
||||
{
|
||||
DebugTools.Assert(!light.NetSyncEnabled,
|
||||
$"{typeof(ItemTogglePointLightComponent)} requires point lights without net-sync");
|
||||
_pointLight.SetEnabled(uid, enabled, light);
|
||||
if (modulateColor && toggleLights.ToggleableVisualsColorModulatesLights)
|
||||
{
|
||||
_pointLight.SetColor(uid, color, light);
|
||||
}
|
||||
}
|
||||
|
||||
// update clothing & in-hand visuals.
|
||||
_item.VisualsChanged(uid);
|
||||
}
|
||||
|
||||
private void OnGetEquipmentVisuals(EntityUid uid,
|
||||
ToggleableVisualsComponent component,
|
||||
GetEquipmentVisualsEvent args)
|
||||
{
|
||||
if (!TryComp(uid, out AppearanceComponent? appearance)
|
||||
|| !AppearanceSystem.TryGetData<bool>(uid, ToggleableVisuals.Enabled, out var enabled, appearance)
|
||||
|| !enabled)
|
||||
return;
|
||||
|
||||
if (!TryComp(args.Equipee, out InventoryComponent? inventory))
|
||||
return;
|
||||
List<PrototypeLayerData>? layers = null;
|
||||
|
||||
// attempt to get species specific data
|
||||
if (inventory.SpeciesId != null)
|
||||
component.ClothingVisuals.TryGetValue($"{args.Slot}-{inventory.SpeciesId}", out layers);
|
||||
|
||||
// No species specific data. Try to default to generic data.
|
||||
if (layers == null && !component.ClothingVisuals.TryGetValue(args.Slot, out layers))
|
||||
return;
|
||||
|
||||
var modulateColor = AppearanceSystem.TryGetData<Color>(uid, ToggleableVisuals.Color, out var color, appearance);
|
||||
|
||||
var i = 0;
|
||||
foreach (var layer in layers)
|
||||
{
|
||||
var key = layer.MapKeys?.FirstOrDefault();
|
||||
if (key == null)
|
||||
{
|
||||
key = i == 0 ? $"{args.Slot}-toggle" : $"{args.Slot}-toggle-{i}";
|
||||
i++;
|
||||
}
|
||||
|
||||
if (modulateColor)
|
||||
layer.Color = color;
|
||||
|
||||
args.Layers.Add((key, layer));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGetHeldVisuals(EntityUid uid, ToggleableVisualsComponent component, GetInhandVisualsEvent args)
|
||||
{
|
||||
if (!TryComp(uid, out AppearanceComponent? appearance)
|
||||
|| !AppearanceSystem.TryGetData<bool>(uid, ToggleableVisuals.Enabled, out var enabled, appearance)
|
||||
|| !enabled)
|
||||
return;
|
||||
|
||||
if (!component.InhandVisuals.TryGetValue(args.Location, out var layers))
|
||||
return;
|
||||
|
||||
var modulateColor = AppearanceSystem.TryGetData<Color>(uid, ToggleableVisuals.Color, out var color, appearance);
|
||||
|
||||
var i = 0;
|
||||
var defaultKey = $"inhand-{args.Location.ToString().ToLowerInvariant()}-toggle";
|
||||
foreach (var layer in layers)
|
||||
{
|
||||
var key = layer.MapKeys?.FirstOrDefault();
|
||||
if (key == null)
|
||||
{
|
||||
key = i == 0 ? defaultKey : $"{defaultKey}-{i}";
|
||||
i++;
|
||||
}
|
||||
|
||||
if (modulateColor)
|
||||
layer.Color = color;
|
||||
|
||||
args.Layers.Add((key, layer));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controllers;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Client.CharacterInfo;
|
||||
using static Content.Client.CharacterInfo.CharacterInfoSystem;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Chat;
|
||||
|
||||
public sealed partial class ChatUIController : IOnSystemChanged<CharacterInfoSystem>
|
||||
{
|
||||
[Dependency] private readonly ILocalizationManager _loc = default!;
|
||||
[UISystemDependency] private readonly CharacterInfoSystem _characterInfo = default!;
|
||||
|
||||
private static readonly Regex StartDoubleQuote = new("\"$");
|
||||
private static readonly Regex EndDoubleQuote = new("^\"|(?<=^@)\"");
|
||||
private static readonly Regex StartAtSign = new("^@");
|
||||
|
||||
|
||||
private readonly List<string> _highlights = new();
|
||||
private readonly List<string> _autoFillRawKeywords = new();
|
||||
|
||||
private string? _highlightsColor;
|
||||
private bool _autoFillHighlightsEnabled;
|
||||
private bool _charInfoIsAttach = false;
|
||||
private string? _currentCharacterName;
|
||||
|
||||
|
||||
public event Action<string>? HighlightsUpdated;
|
||||
public event Action<string>? AutoFillUpdated;
|
||||
|
||||
private void InitializeHighlights()
|
||||
{
|
||||
_config.OnValueChanged(CCVars.ChatAutoFillHighlights, (value) => { _autoFillHighlightsEnabled = value; }, true);
|
||||
_config.OnValueChanged(CCVars.ChatHighlightsColor, (value) => { _highlightsColor = value; }, true);
|
||||
|
||||
var cvar = _config.GetCVar(CCVars.ChatHighlights);
|
||||
}
|
||||
|
||||
public void OnSystemLoaded(CharacterInfoSystem system)
|
||||
{
|
||||
system.OnCharacterUpdate += OnCharacterUpdated;
|
||||
}
|
||||
|
||||
public void OnSystemUnloaded(CharacterInfoSystem system)
|
||||
{
|
||||
system.OnCharacterUpdate -= OnCharacterUpdated;
|
||||
}
|
||||
|
||||
private void UpdateAutoFillHighlights()
|
||||
{
|
||||
_currentCharacterName = null;
|
||||
_autoFillRawKeywords.Clear();
|
||||
_highlights.Clear();
|
||||
AutoFillUpdated?.Invoke("");
|
||||
HighlightsUpdated?.Invoke("");
|
||||
|
||||
_charInfoIsAttach = true;
|
||||
_characterInfo.RequestCharacterInfo();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the user's custom keywords for the given character into the CVar,
|
||||
/// then rebuilds the active highlight list.
|
||||
/// Called when the user submits keywords from the popup.
|
||||
/// </summary>
|
||||
public void UpdateHighlights(string customKeywords)
|
||||
{
|
||||
if (_currentCharacterName == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SaveCustomKeywords(_currentCharacterName, customKeywords);
|
||||
RebuildHighlightsList(customKeywords);
|
||||
HighlightsUpdated?.Invoke(customKeywords);
|
||||
}
|
||||
|
||||
private void OnCharacterUpdated(CharacterData data)
|
||||
{
|
||||
|
||||
if (!_charInfoIsAttach)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var (_, job, _, _, entityName) = data;
|
||||
_currentCharacterName = entityName;
|
||||
|
||||
|
||||
_autoFillRawKeywords.Clear();
|
||||
var autoFillDisplay = "";
|
||||
|
||||
if (_autoFillHighlightsEnabled)
|
||||
{
|
||||
|
||||
// Special rules for names can be added here
|
||||
|
||||
var nameHighlights = "@" + entityName;
|
||||
|
||||
// Split on spaces first (handles any number of space-separated words).
|
||||
if (nameHighlights.Contains(' '))
|
||||
nameHighlights = nameHighlights.Replace(" ", "\n@");
|
||||
|
||||
// Handle hyphenated tokens: single hyphen splits normally (e.g. "First-Last"),
|
||||
// multiple hyphens use the lizard rule — keep only first and last part (e.g. "Eats-The-Food" → @Eats @Food).
|
||||
var dashParts = nameHighlights.Split('-');
|
||||
if (dashParts.Length == 2)
|
||||
nameHighlights = nameHighlights.Replace("-", "\n@");
|
||||
else if (dashParts.Length > 2)
|
||||
nameHighlights = dashParts[0] + "\n@" + dashParts[^1];
|
||||
|
||||
|
||||
foreach (var token in nameHighlights.Split('\n', StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
_autoFillRawKeywords.Add(token);
|
||||
}
|
||||
|
||||
var jobKey = job.Replace(' ', '-').ToLower();
|
||||
|
||||
if (_loc.TryGetString($"highlights-{jobKey}", out var jobMatches))
|
||||
{
|
||||
foreach (var jobWord in jobMatches.Replace(", ", "\n").Split('\n', StringSplitOptions.RemoveEmptyEntries))
|
||||
_autoFillRawKeywords.Add(jobWord);
|
||||
}
|
||||
autoFillDisplay = string.Join("\n", _autoFillRawKeywords);
|
||||
}
|
||||
|
||||
var customKeywords = LoadCustomKeywords(entityName);
|
||||
|
||||
RebuildHighlightsList(customKeywords);
|
||||
|
||||
AutoFillUpdated?.Invoke(autoFillDisplay);
|
||||
HighlightsUpdated?.Invoke(customKeywords);
|
||||
_charInfoIsAttach = false;
|
||||
}
|
||||
|
||||
|
||||
private string LoadCustomKeywords(string characterName)
|
||||
{
|
||||
var cvar = _config.GetCVar(CCVars.ChatHighlights);
|
||||
if (string.IsNullOrEmpty(cvar))
|
||||
return "";
|
||||
|
||||
// Shouldn't be needed as no-one should have data by the time this rolls out but we never know
|
||||
if (!cvar.Contains("||"))
|
||||
{
|
||||
return cvar;
|
||||
}
|
||||
|
||||
foreach (var line in cvar.Split('\n', StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
var sep = line.IndexOf("||", StringComparison.Ordinal);
|
||||
if (sep < 0)
|
||||
continue;
|
||||
|
||||
if (line[..sep] == characterName)
|
||||
return DecodeKeywords(line[(sep + 2)..]);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
private void SaveCustomKeywords(string characterName, string customKeywords)
|
||||
{
|
||||
var cvar = _config.GetCVar(CCVars.ChatHighlights);
|
||||
|
||||
var entries = new Dictionary<string, string>();
|
||||
if (!string.IsNullOrEmpty(cvar) && cvar.Contains("||"))
|
||||
{
|
||||
foreach (var line in cvar.Split('\n', StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
var sep = line.IndexOf("||", StringComparison.Ordinal);
|
||||
if (sep < 0)
|
||||
continue;
|
||||
entries[line[..sep]] = line[(sep + 2)..];
|
||||
}
|
||||
}
|
||||
|
||||
entries[characterName] = EncodeKeywords(customKeywords);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
foreach (var (name, encoded) in entries)
|
||||
sb.Append(name).Append("||").Append(encoded).Append('\n');
|
||||
|
||||
var newValue = sb.ToString().TrimEnd('\n');
|
||||
_config.SetCVar(CCVars.ChatHighlights, newValue);
|
||||
_config.SaveToFile();
|
||||
}
|
||||
|
||||
private static string EncodeKeywords(string raw)
|
||||
{
|
||||
return raw
|
||||
.Replace(@"\", @"\\")
|
||||
.Replace("|", @"\|")
|
||||
.Replace("\n", @"\n");
|
||||
}
|
||||
|
||||
private static string DecodeKeywords(string encoded)
|
||||
{
|
||||
var sb = new StringBuilder(encoded.Length);
|
||||
for (var i = 0; i < encoded.Length; i++)
|
||||
{
|
||||
if (encoded[i] == '\\' && i + 1 < encoded.Length)
|
||||
{
|
||||
switch (encoded[i + 1])
|
||||
{
|
||||
case 'n': sb.Append('\n'); i++; break;
|
||||
case '|': sb.Append('|'); i++; break;
|
||||
case '\\': sb.Append('\\'); i++; break;
|
||||
default: sb.Append(encoded[i]); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(encoded[i]);
|
||||
}
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private void RebuildHighlightsList(string customRaw)
|
||||
{
|
||||
_highlights.Clear();
|
||||
|
||||
foreach (var raw in _autoFillRawKeywords)
|
||||
_highlights.Add(ProcessKeyword(raw));
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(customRaw))
|
||||
{
|
||||
foreach (var raw in customRaw.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
|
||||
_highlights.Add(ProcessKeyword(raw));
|
||||
}
|
||||
|
||||
_highlights.Sort((x, y) => y.Length.CompareTo(x.Length));
|
||||
}
|
||||
|
||||
private string ProcessKeyword(string raw)
|
||||
{
|
||||
// Replace every "\" to prevent "\n", "\0", etc. in regex.
|
||||
var keyword = raw.Replace(@"\", @"\\");
|
||||
|
||||
// Escape special regex characters.
|
||||
keyword = Regex.Escape(keyword);
|
||||
|
||||
// Re-escape "[" for markup-adjacent positions.
|
||||
keyword = keyword.Replace(@"\[", @"\\\[");
|
||||
|
||||
// words in double quotes will only be hightlighted as unique, not within another word
|
||||
if (keyword.Any(c => c == '"'))
|
||||
{
|
||||
keyword = StartDoubleQuote.Replace(keyword, "(?!\\w)");
|
||||
keyword = EndDoubleQuote.Replace(keyword, "(?<!\\w)");
|
||||
}
|
||||
|
||||
// Make it so it doesn't hightlight your own name when you are talking
|
||||
keyword = StartAtSign.Replace(keyword, @"(?<=\[BubbleContent\].*)");
|
||||
|
||||
return keyword;
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Chat;
|
||||
|
||||
public sealed class ChatUIController : UIController
|
||||
public sealed partial class ChatUIController : UIController
|
||||
{
|
||||
[Dependency] private readonly IClientAdminManager _admin = default!;
|
||||
[Dependency] private readonly IChatManager _manager = default!;
|
||||
@@ -248,6 +248,8 @@ public sealed class ChatUIController : UIController
|
||||
|
||||
_config.OnValueChanged(CCVars.ChatWindowOpacity, OnChatWindowOpacityChanged);
|
||||
|
||||
InitializeHighlights();
|
||||
|
||||
}
|
||||
|
||||
public void OnScreenLoad()
|
||||
@@ -434,6 +436,7 @@ public sealed class ChatUIController : UIController
|
||||
private void OnAttachedChanged(EntityUid uid)
|
||||
{
|
||||
UpdateChannelPermissions();
|
||||
UpdateAutoFillHighlights();
|
||||
}
|
||||
|
||||
private void AddSpeechBubble(ChatMessage msg, SpeechBubble.SpeechType speechType)
|
||||
@@ -877,6 +880,11 @@ public sealed class ChatUIController : UIController
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var highlight in _highlights)
|
||||
{
|
||||
msg.WrappedMessage = SharedChatSystem.InjectTagAroundString(msg, highlight, "color", _highlightsColor);
|
||||
}
|
||||
|
||||
// Log all incoming chat to repopulate when filter is un-toggled
|
||||
if (!msg.HideChat)
|
||||
{
|
||||
@@ -912,7 +920,6 @@ public sealed class ChatUIController : UIController
|
||||
case ChatChannel.Dead:
|
||||
if (_ghost is not {IsGhost: true})
|
||||
break;
|
||||
|
||||
AddSpeechBubble(msg, SpeechBubble.SpeechType.Say);
|
||||
break;
|
||||
|
||||
|
||||
@@ -1,10 +1,30 @@
|
||||
<controls:ChannelFilterPopup
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Systems.Chat.Controls">
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Systems.Chat.Controls"
|
||||
xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
|
||||
<PanelContainer Name="FilterPopupPanel" StyleClasses="BorderedWindowPanel">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Control MinSize="4 0"/>
|
||||
<BoxContainer Name="FilterVBox" MinWidth="110" Margin="0 10" Orientation="Vertical" SeparationOverride="4"/>
|
||||
<BoxContainer Name="HighlightsVBox" MinWidth="120" Margin="0 10" Orientation="Vertical" SeparationOverride="4">
|
||||
<BoxContainer Name="AutoFillVBox" Orientation="Vertical" SeparationOverride="2" Visible="False">
|
||||
<Label Text="{Loc 'hud-chatbox-autofill-highlights'}"/>
|
||||
<PanelContainer>
|
||||
<PanelContainer.PanelOverride>
|
||||
<graphics:StyleBoxFlat BackgroundColor="#262736"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
<RichTextLabel Name="AutoFillLabel" Margin="5 5"/>
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
<Label Text="{Loc 'hud-chatbox-highlights'}"/>
|
||||
<PanelContainer>
|
||||
<PanelContainer.PanelOverride>
|
||||
<graphics:StyleBoxFlat BackgroundColor="#323446"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
<TextEdit Name="HighlightEdit" MinHeight="150" Margin="5 5"/>
|
||||
</PanelContainer>
|
||||
<Button Name="HighlightButton" Text="{Loc 'hud-chatbox-highlights-button'}" ToolTip="{Loc 'hud-chatbox-highlights-tooltip'}"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</controls:ChannelFilterPopup>
|
||||
|
||||
@@ -2,6 +2,7 @@ using Content.Shared.Chat;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.Client.UserInterface.Controls.BaseButton;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Chat.Controls;
|
||||
@@ -30,10 +31,13 @@ public sealed partial class ChannelFilterPopup : Popup
|
||||
private readonly Dictionary<ChatChannel, ChannelFilterCheckbox> _filterStates = new();
|
||||
|
||||
public event Action<ChatChannel, bool>? OnChannelFilter;
|
||||
public event Action<string>? OnNewHighlights;
|
||||
|
||||
public ChannelFilterPopup()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
HighlightButton.OnPressed += HighlightsEntered;
|
||||
HighlightEdit.Placeholder = new Rope.Leaf(Loc.GetString("hud-chatbox-highlights-placeholder"));
|
||||
}
|
||||
|
||||
public bool IsActive(ChatChannel channel)
|
||||
@@ -95,4 +99,36 @@ public sealed partial class ChannelFilterPopup : Popup
|
||||
if (_filterStates.TryGetValue(channel, out var checkbox))
|
||||
checkbox.UpdateUnreadCount(unread);
|
||||
}
|
||||
|
||||
// Highlight
|
||||
|
||||
/// <summary>
|
||||
/// Updates the read-only auto-fill section with name/job tokens.
|
||||
/// </summary>
|
||||
public void UpdateAutoFill(string autoFillKeywords)
|
||||
{
|
||||
AutoFillVBox.Visible = !string.IsNullOrEmpty(autoFillKeywords);
|
||||
if (string.IsNullOrEmpty(autoFillKeywords))
|
||||
return;
|
||||
|
||||
var msg = new FormattedMessage();
|
||||
var lines = autoFillKeywords.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||
for (var i = 0; i < lines.Length; i++)
|
||||
{
|
||||
if (i > 0) msg.PushNewline();
|
||||
msg.AddText(lines[i]);
|
||||
}
|
||||
AutoFillLabel.SetMessage(msg);
|
||||
}
|
||||
|
||||
public void UpdateHighlights(string highlights)
|
||||
{
|
||||
HighlightEdit.TextRope = new Rope.Leaf(highlights);
|
||||
}
|
||||
|
||||
private void HighlightsEntered(ButtonEventArgs _args)
|
||||
{
|
||||
OnNewHighlights?.Invoke(Rope.Collapse(HighlightEdit.TextRope));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -47,9 +47,12 @@ public partial class ChatBox : UIWidget
|
||||
ChatInput.Input.OnFocusExit += OnFocusExit;
|
||||
ChatInput.ChannelSelector.OnChannelSelect += OnChannelSelect;
|
||||
ChatInput.FilterButton.Popup.OnChannelFilter += OnChannelFilter;
|
||||
ChatInput.FilterButton.Popup.OnNewHighlights += OnNewHighlights;
|
||||
|
||||
_controller = UserInterfaceManager.GetUIController<ChatUIController>();
|
||||
_controller.MessageAdded += OnMessageAdded;
|
||||
_controller.HighlightsUpdated += OnHighlightsUpdated;
|
||||
_controller.AutoFillUpdated += OnAutoFillUpdated;
|
||||
_controller.RegisterChat(this);
|
||||
|
||||
// WD EDIT START
|
||||
@@ -133,6 +136,21 @@ public partial class ChatBox : UIWidget
|
||||
}
|
||||
}
|
||||
|
||||
private void OnNewHighlights(string highlighs)
|
||||
{
|
||||
_controller.UpdateHighlights(highlighs);
|
||||
}
|
||||
|
||||
private void OnHighlightsUpdated(string highlights)
|
||||
{
|
||||
ChatInput.FilterButton.Popup.UpdateHighlights(highlights);
|
||||
}
|
||||
|
||||
private void OnAutoFillUpdated(string autoFillKeywords)
|
||||
{
|
||||
ChatInput.FilterButton.Popup.UpdateAutoFill(autoFillKeywords);
|
||||
}
|
||||
|
||||
public void AddLine(string message, Color color, int repeat = 0) // WD EDIT
|
||||
{
|
||||
var formatted = new FormattedMessage(4); // WD EDIT // specifying size beforehand smells like a useless microoptimisation, but i'll give them the benefit of doubt
|
||||
|
||||
@@ -76,7 +76,7 @@ public sealed class DamageOverlayUiController : UIController
|
||||
{
|
||||
if (mobState == null && !EntityManager.TryGetComponent(entity, out mobState) ||
|
||||
thresholds == null && !EntityManager.TryGetComponent(entity, out thresholds) ||
|
||||
damageable == null && !EntityManager.TryGetComponent(entity, out damageable))
|
||||
damageable == null && !EntityManager.TryGetComponent(entity, out damageable))
|
||||
return;
|
||||
|
||||
if (!_mobThresholdSystem.TryGetIncapThreshold(entity, out var foundThreshold, thresholds))
|
||||
@@ -94,47 +94,48 @@ public sealed class DamageOverlayUiController : UIController
|
||||
switch (mobState.CurrentState)
|
||||
{
|
||||
case MobState.Alive:
|
||||
{
|
||||
if (EntityManager.HasComponent<PainNumbnessComponent>(entity))
|
||||
{
|
||||
_overlay.BruteLevel = 0;
|
||||
}
|
||||
else if (damageable.DamagePerGroup.TryGetValue("Brute", out var bruteDamage))
|
||||
{
|
||||
_overlay.BruteLevel = FixedPoint2.Min(1f, bruteDamage / critThreshold).Float();
|
||||
}
|
||||
if (!EntityManager.HasComponent<PainNumbnessComponent>(entity)) // Mono - makes this look better
|
||||
{
|
||||
if (damageable.DamagePerGroup.TryGetValue("Brute", out var bruteDamage))
|
||||
_overlay.BruteLevel = FixedPoint2.Min(1f, bruteDamage / critThreshold).Float();
|
||||
if (damageable.DamagePerGroup.TryGetValue("Burn", out var burnDamage)) // Adds burn damage to pain overlay - Mono
|
||||
_overlay.BruteLevel = FixedPoint2.Min(1f, _overlay.BruteLevel + (burnDamage / critThreshold)).Float();
|
||||
}
|
||||
else
|
||||
_overlay.BruteLevel = 0;
|
||||
|
||||
if (damageable.DamagePerGroup.TryGetValue("Airloss", out var oxyDamage))
|
||||
{
|
||||
_overlay.OxygenLevel = FixedPoint2.Min(1f, oxyDamage / critThreshold).Float();
|
||||
}
|
||||
if (damageable.DamagePerGroup.TryGetValue("Airloss", out var oxyDamage))
|
||||
{
|
||||
_overlay.OxygenLevel = FixedPoint2.Min(1f, oxyDamage / critThreshold).Float();
|
||||
}
|
||||
|
||||
if (_overlay.BruteLevel < 0.05f) // Don't show damage overlay if they're near enough to max.
|
||||
{
|
||||
_overlay.BruteLevel = 0;
|
||||
}
|
||||
if (_overlay.BruteLevel < 0.05f) // Don't show damage overlay if they're near enough to max.
|
||||
{
|
||||
_overlay.BruteLevel = 0;
|
||||
}
|
||||
|
||||
_overlay.CritLevel = 0;
|
||||
_overlay.DeadLevel = 0;
|
||||
break;
|
||||
}
|
||||
_overlay.CritLevel = 0;
|
||||
_overlay.DeadLevel = 0;
|
||||
break;
|
||||
}
|
||||
case MobState.Critical:
|
||||
{
|
||||
if (!_mobThresholdSystem.TryGetDeadPercentage(entity,
|
||||
FixedPoint2.Max(0.0, damageable.TotalDamage), out var critLevel))
|
||||
return;
|
||||
_overlay.CritLevel = critLevel.Value.Float();
|
||||
{
|
||||
if (!_mobThresholdSystem.TryGetDeadPercentage(entity,
|
||||
FixedPoint2.Max(0.0, damageable.TotalDamage), out var critLevel))
|
||||
return;
|
||||
_overlay.CritLevel = critLevel.Value.Float();
|
||||
|
||||
_overlay.BruteLevel = 0;
|
||||
_overlay.DeadLevel = 0;
|
||||
break;
|
||||
}
|
||||
_overlay.BruteLevel = 0;
|
||||
_overlay.DeadLevel = 0;
|
||||
break;
|
||||
}
|
||||
case MobState.Dead:
|
||||
{
|
||||
_overlay.BruteLevel = 0;
|
||||
_overlay.CritLevel = 0;
|
||||
break;
|
||||
}
|
||||
{
|
||||
_overlay.BruteLevel = 0;
|
||||
_overlay.CritLevel = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace Content.Client.VendingMachines.UI
|
||||
/// Populates the list of available items on the vending machine interface
|
||||
/// and sets icons based on their prototypes
|
||||
/// </summary>
|
||||
public void Populate(List<VendingMachineInventoryEntry> inventory, float priceModifier, int balance, int? cashSlotBalance) // Frontier: add balance, cashSlotBalance
|
||||
public void Populate(List<VendingMachineInventoryEntry> inventory, float priceModifier, int balance, int? cashSlotBalance, bool requiresCash) // Frontier: add balance, cashSlotBalance
|
||||
{
|
||||
UpdateBalance(balance); // Frontier
|
||||
UpdateCashSlotBalance(cashSlotBalance); // Frontier
|
||||
@@ -124,84 +124,11 @@ namespace Content.Client.VendingMachines.UI
|
||||
_dummies.Add(entry.ID, dummy);
|
||||
}
|
||||
|
||||
// Frontier: item pricing
|
||||
// ok so we dont really have access to the pricing system so we are doing a quick price check
|
||||
// based on prototype info since the items inside a vending machine dont actually exist as entities
|
||||
// until they are spawned. So this little alg does the following:
|
||||
// first, checks for a staticprice component, and if it has one, checks to make sure its not 0 since
|
||||
// stacks and other items have 0 cost.
|
||||
// If the price is 0, then we check for both a stack price and a stack component, since if it has one
|
||||
// it should have the other too, and then calculates the price based on that.
|
||||
// If the price is still 0 or non-existant (this is the case for food and containers since their value is
|
||||
// determined dynamically by their contents/inventory), it then falls back to the default mystery
|
||||
// hardcoded value of 20xMarketModifier.
|
||||
var cost = 20;
|
||||
if (prototype != null && prototype.TryGetComponent<StaticPriceComponent>(out var priceComponent, _entityManager.ComponentFactory))
|
||||
{
|
||||
if (priceComponent.Price != 0)
|
||||
{
|
||||
var price = (float)priceComponent.Price;
|
||||
cost = (int)(price * priceModifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (prototype.TryGetComponent<StackPriceComponent>(out var stackPrice, _entityManager.ComponentFactory)
|
||||
&& prototype.TryGetComponent<StackComponent>(out var stack, _entityManager.ComponentFactory))
|
||||
{
|
||||
var price = stackPrice.Price * stack.Count;
|
||||
cost = (int)(price * priceModifier);
|
||||
}
|
||||
else
|
||||
cost = (int)(cost * priceModifier);
|
||||
}
|
||||
}
|
||||
else
|
||||
cost = (int)(cost * priceModifier);
|
||||
|
||||
if (prototype != null && prototype.TryGetComponent<SolutionContainerManagerComponent>(out var priceSolutions, _entityManager.ComponentFactory))
|
||||
{
|
||||
if (priceSolutions.Solutions != null)
|
||||
{
|
||||
foreach (var solution in priceSolutions.Solutions.Values)
|
||||
{
|
||||
foreach (var (reagent, quantity) in solution.Contents)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.Prototype,
|
||||
out var reagentProto))
|
||||
continue;
|
||||
|
||||
// TODO check ReagentData for price information?
|
||||
var costReagent = quantity.Float() * reagentProto.PricePerUnit;
|
||||
cost += (int)(costReagent * priceModifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// End Frontier: item pricing
|
||||
|
||||
// Frontier: calculate vending price (this duplicates Content.Server.PricingSystem.GetVendPrice - this should be moved to Content.Shared if possible)
|
||||
if (prototype != null)
|
||||
{
|
||||
var price = 0.0;
|
||||
|
||||
if (prototype.TryGetComponent<StaticPriceComponent>(out var staticComp, _entityManager.ComponentFactory) && staticComp.VendPrice > 0.0)
|
||||
{
|
||||
price += staticComp.VendPrice;
|
||||
}
|
||||
else if (prototype.TryGetComponent<StackPriceComponent>(out var stackComp, _entityManager.ComponentFactory) && stackComp.VendPrice > 0.0)
|
||||
{
|
||||
price += stackComp.VendPrice;
|
||||
}
|
||||
|
||||
// If there is anything that explicitly sets vending price - higher OR lower, override the base.
|
||||
if (price > 0.0)
|
||||
{
|
||||
cost = (int)price;
|
||||
}
|
||||
}
|
||||
// End Frontier
|
||||
|
||||
var itemName = Identity.Name(dummy, _entityManager);
|
||||
var cost = 0; // mono
|
||||
if (requiresCash) // frontier
|
||||
cost = GetPrice(entry, prototype, priceModifier);
|
||||
|
||||
string itemText;
|
||||
|
||||
// Frontier: unlimited vending
|
||||
@@ -211,7 +138,6 @@ namespace Content.Client.VendingMachines.UI
|
||||
itemText = $"[{BankSystemExtensions.ToSpesoString(cost)}] {itemName}";
|
||||
// End Frontier: unlimited vending
|
||||
|
||||
|
||||
if (itemText.Length > longestEntry.Length)
|
||||
longestEntry = itemText;
|
||||
|
||||
@@ -223,6 +149,88 @@ namespace Content.Client.VendingMachines.UI
|
||||
SetSizeAfterUpdate(longestEntry.Length, inventory.Count);
|
||||
}
|
||||
|
||||
// Mono: Moved out frontier pricing logic to the separate method
|
||||
private int GetPrice(VendingMachineInventoryEntry entry, EntityPrototype? prototype, float priceModifier){
|
||||
// Frontier: item pricing
|
||||
// ok so we dont really have access to the pricing system so we are doing a quick price check
|
||||
// based on prototype info since the items inside a vending machine dont actually exist as entities
|
||||
// until they are spawned. So this little alg does the following:
|
||||
// first, checks for a staticprice component, and if it has one, checks to make sure its not 0 since
|
||||
// stacks and other items have 0 cost.
|
||||
// If the price is 0, then we check for both a stack price and a stack component, since if it has one
|
||||
// it should have the other too, and then calculates the price based on that.
|
||||
// If the price is still 0 or non-existant (this is the case for food and containers since their value is
|
||||
// determined dynamically by their contents/inventory), it then falls back to the default mystery
|
||||
// hardcoded value of 20xMarketModifier.
|
||||
var cost = 20;
|
||||
if (prototype != null && prototype.TryGetComponent<StaticPriceComponent>(out var priceComponent, _entityManager.ComponentFactory))
|
||||
{
|
||||
if (priceComponent.Price != 0)
|
||||
{
|
||||
var price = (float)priceComponent.Price;
|
||||
cost = (int)(price * priceModifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (prototype.TryGetComponent<StackPriceComponent>(out var stackPrice, _entityManager.ComponentFactory)
|
||||
&& prototype.TryGetComponent<StackComponent>(out var stack, _entityManager.ComponentFactory))
|
||||
{
|
||||
var price = stackPrice.Price * stack.Count;
|
||||
cost = (int)(price * priceModifier);
|
||||
}
|
||||
else
|
||||
cost = (int)(cost * priceModifier);
|
||||
}
|
||||
}
|
||||
else
|
||||
cost = (int)(cost * priceModifier);
|
||||
|
||||
if (prototype != null && prototype.TryGetComponent<SolutionContainerManagerComponent>(out var priceSolutions, _entityManager.ComponentFactory))
|
||||
{
|
||||
if (priceSolutions.Solutions != null)
|
||||
{
|
||||
foreach (var solution in priceSolutions.Solutions.Values)
|
||||
{
|
||||
foreach (var (reagent, quantity) in solution.Contents)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.Prototype,
|
||||
out var reagentProto))
|
||||
continue;
|
||||
|
||||
// TODO check ReagentData for price information?
|
||||
var costReagent = quantity.Float() * reagentProto.PricePerUnit;
|
||||
cost += (int)(costReagent * priceModifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// End Frontier: item pricing
|
||||
|
||||
// Frontier: calculate vending price (this duplicates Content.Server.PricingSystem.GetVendPrice - this should be moved to Content.Shared if possible)
|
||||
if (prototype != null)
|
||||
{
|
||||
var price = 0.0;
|
||||
|
||||
if (prototype.TryGetComponent<StaticPriceComponent>(out var staticComp, _entityManager.ComponentFactory) && staticComp.VendPrice > 0.0)
|
||||
{
|
||||
price += staticComp.VendPrice;
|
||||
}
|
||||
else if (prototype.TryGetComponent<StackPriceComponent>(out var stackComp, _entityManager.ComponentFactory) && stackComp.VendPrice > 0.0)
|
||||
{
|
||||
price += stackComp.VendPrice;
|
||||
}
|
||||
|
||||
// If there is anything that explicitly sets vending price - higher OR lower, override the base.
|
||||
if (price > 0.0)
|
||||
{
|
||||
cost = (int)price;
|
||||
}
|
||||
}
|
||||
|
||||
return cost;
|
||||
// End Frontier
|
||||
}
|
||||
|
||||
// Frontier
|
||||
public void UpdateBalance(int balance)
|
||||
{
|
||||
|
||||
@@ -30,6 +30,8 @@ namespace Content.Client.VendingMachines
|
||||
[ViewVariables]
|
||||
private int _cashSlotBalance = 0;
|
||||
// End Frontier
|
||||
[ViewVariables]
|
||||
private bool _requiresCash; // mono
|
||||
|
||||
public VendingMachineBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
@@ -74,6 +76,7 @@ namespace Content.Client.VendingMachines
|
||||
if (EntMan.TryGetComponent<VendingMachineComponent>(Owner, out var vendingMachine))
|
||||
{
|
||||
_cashSlotBalance = vendingMachine.CashSlotBalance;
|
||||
_requiresCash = vendingMachine.RequiresCash; // mono
|
||||
if (vendingMachine.CashSlotName != null)
|
||||
cashSlotValue = _cashSlotBalance;
|
||||
}
|
||||
@@ -83,7 +86,7 @@ namespace Content.Client.VendingMachines
|
||||
}
|
||||
// End Frontier
|
||||
|
||||
_menu?.Populate(_cachedInventory, _mod, _balance, cashSlotValue); // Frontier: add _balance
|
||||
_menu?.Populate(_cachedInventory, _mod, _balance, cashSlotValue, _requiresCash); // Frontier: add _balance, mono: add _requiresCash
|
||||
}
|
||||
|
||||
private void OnItemSelected(GUIBoundKeyEventArgs args, ListData data)
|
||||
|
||||
@@ -64,7 +64,11 @@ namespace Content.Client.Voting.UI
|
||||
var entry = _vote.Entries[i];
|
||||
if (_vote.DisplayVotes)
|
||||
{
|
||||
_voteButtons[i].Text = Loc.GetString("ui-vote-button", ("text", entry.Text), ("votes", entry.Votes));
|
||||
// Mono
|
||||
if (entry.RealVotes != entry.Votes)
|
||||
_voteButtons[i].Text = Loc.GetString("ui-vote-button-weighted", ("text", entry.Text), ("votes", entry.Votes), ("realvotes", entry.RealVotes));
|
||||
else
|
||||
_voteButtons[i].Text = Loc.GetString("ui-vote-button", ("text", entry.Text), ("votes", entry.Votes));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -192,6 +192,7 @@ namespace Content.Client.Voting
|
||||
for (var i = 0; i < message.Options.Length; i++)
|
||||
{
|
||||
existingVote.Entries[i].Votes = message.Options[i].votes;
|
||||
existingVote.Entries[i].RealVotes = message.Options[i].realVotes; // Mono
|
||||
}
|
||||
|
||||
if (@new && _popupContainer != null)
|
||||
@@ -259,6 +260,7 @@ namespace Content.Client.Voting
|
||||
{
|
||||
public string Text { get; }
|
||||
public int Votes { get; set; }
|
||||
public int RealVotes; // Mono
|
||||
|
||||
public VoteEntry(string text)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
|
||||
// SPDX-FileCopyrightText: 2025 ImHoks <imhokzzzz@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2025 KillanGenifer <killangenifer@gmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
using Content.Shared._CorvaxNext.Silicons.Borgs;
|
||||
|
||||
namespace Content.Client._CorvaxNext.Silicons.Borgs;
|
||||
|
||||
public sealed partial class AiRemoteControlSystem : SharedAiRemoteControlSystem
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
Margin="0 0 0 10">
|
||||
<PanelContainer VerticalExpand="True" StyleClasses="BackgroundDark">
|
||||
<BoxContainer Orientation="Vertical"
|
||||
HorizontalExpand="True"
|
||||
VerticalExpand="True"
|
||||
Margin="5 5 5 5">
|
||||
<customControls:HSeparator Margin="0 5 0 5"/>
|
||||
<RichTextLabel Name="DeviceName"/>
|
||||
<BoxContainer Name="RemoteControlButtons" Orientation="Horizontal" HorizontalExpand="True" Margin="0 5 0 0">
|
||||
<Button Name="MoveButton" StyleClasses="chatSelectorOptionButton" Text="{Loc ai-remote-ui-menu-moveto}"/>
|
||||
<Control Margin="2 0 0 0"/>
|
||||
<Button Name="TakeControlButton" StyleClasses="chatSelectorOptionButton" Text="{Loc ai-remote-control}"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
</Control>
|
||||
@@ -0,0 +1,39 @@
|
||||
// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
|
||||
// SPDX-FileCopyrightText: 2025 ImHoks <imhokzzzz@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2025 KillanGenifer <killangenifer@gmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Content.Shared._CorvaxNext.Silicons.Borgs.Components;
|
||||
|
||||
namespace Content.Client._CorvaxNext.Silicons.Laws.Ui;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class RemoteDeviceDisplay : Control
|
||||
{
|
||||
public event Action<RemoteDeviceActionEvent>? OnRemoteDeviceAction;
|
||||
|
||||
public RemoteDeviceDisplay(NetEntity netEntityUid, string displayName)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
DeviceName.SetMessage(displayName);
|
||||
|
||||
MoveButton.OnPressed += _ =>
|
||||
{
|
||||
OnRemoteDeviceAction?.Invoke(new RemoteDeviceActionEvent(
|
||||
RemoteDeviceActionEvent.RemoteDeviceActionType.MoveToDevice,
|
||||
netEntityUid));
|
||||
};
|
||||
|
||||
TakeControlButton.OnPressed += _ =>
|
||||
{
|
||||
OnRemoteDeviceAction?.Invoke(new RemoteDeviceActionEvent(
|
||||
RemoteDeviceActionEvent.RemoteDeviceActionType.TakeControl,
|
||||
netEntityUid));
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
|
||||
// SPDX-FileCopyrightText: 2025 ImHoks <imhokzzzz@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2025 KillanGenifer <killangenifer@gmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
using Content.Shared._CorvaxNext.Silicons.Borgs.Components;
|
||||
using Robust.Client.UserInterface;
|
||||
using static Content.Shared._CorvaxNext.Silicons.Borgs.Components.AiRemoteControllerComponent;
|
||||
|
||||
namespace Content.Client._CorvaxNext.Silicons.Laws.Ui;
|
||||
|
||||
public sealed class RemoteDevicesBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private RemoteDevicesMenu? _menu;
|
||||
private EntityUid _owner;
|
||||
|
||||
public RemoteDevicesBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
_owner = owner;
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_menu = this.CreateWindow<RemoteDevicesMenu>();
|
||||
_menu.OnRemoteDeviceAction += SendAction;
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (state is not RemoteDevicesBuiState msg)
|
||||
return;
|
||||
|
||||
_menu?.Update(_owner, msg);
|
||||
}
|
||||
|
||||
public void SendAction(RemoteDeviceActionEvent action)
|
||||
{
|
||||
SendMessage(new RemoteDeviceActionMessage(action));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
Title="{Loc ai-remote-ui-menu-title}"
|
||||
MinSize="355 100"
|
||||
SetSize="355 415">
|
||||
<BoxContainer Orientation="Vertical"
|
||||
HorizontalExpand="True"
|
||||
VerticalExpand="True">
|
||||
<PanelContainer VerticalExpand="True" Margin="10 10 10 10">
|
||||
<PanelContainer.PanelOverride>
|
||||
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
<ScrollContainer
|
||||
HScrollEnabled="False"
|
||||
HorizontalExpand="True"
|
||||
VerticalExpand="True">
|
||||
<BoxContainer
|
||||
Name="RemoteDevicesDisplayContainer"
|
||||
Orientation="Vertical"
|
||||
VerticalExpand="True"
|
||||
Margin="10 10 10 0">
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
@@ -0,0 +1,43 @@
|
||||
// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
|
||||
// SPDX-FileCopyrightText: 2025 ImHoks <imhokzzzz@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2025 KillanGenifer <killangenifer@gmail.com>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using FancyWindow = Content.Client.UserInterface.Controls.FancyWindow;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Content.Shared._CorvaxNext.Silicons.Borgs.Components;
|
||||
|
||||
namespace Content.Client._CorvaxNext.Silicons.Laws.Ui;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class RemoteDevicesMenu : FancyWindow
|
||||
{
|
||||
public event Action<RemoteDeviceActionEvent>? OnRemoteDeviceAction;
|
||||
|
||||
public RemoteDevicesMenu()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
|
||||
public void Update(EntityUid uid, RemoteDevicesBuiState state)
|
||||
{
|
||||
RemoteDevicesDisplayContainer.Children.Clear();
|
||||
|
||||
if (state.DeviceList == null)
|
||||
return;
|
||||
|
||||
foreach (var device in state.DeviceList)
|
||||
{
|
||||
var control = new RemoteDeviceDisplay(device.NetEntityUid, device.DisplayName);
|
||||
|
||||
control.OnRemoteDeviceAction += action =>
|
||||
{
|
||||
OnRemoteDeviceAction?.Invoke(action);
|
||||
};
|
||||
|
||||
RemoteDevicesDisplayContainer.AddChild(control);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<ui:RadialMenu xmlns="https://spacestation14.io"
|
||||
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls;assembly=Content.Client"
|
||||
BackButtonStyleClass="RadialMenuBackButton"
|
||||
CloseButtonStyleClass="RadialMenuCloseButton"
|
||||
VerticalExpand="True"
|
||||
HorizontalExpand="True"
|
||||
MinSize="450 450">
|
||||
|
||||
<ui:RadialContainer Name="Main" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"
|
||||
ReserveSpaceForHiddenChildren="False" />
|
||||
|
||||
</ui:RadialMenu>
|
||||
@@ -0,0 +1,93 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared._Goobstation.Weapons.AmmoSelector;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._Goobstation.AmmoSelector;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class AmmoSelectorMenu : RadialMenu
|
||||
{
|
||||
[Dependency] private readonly EntityManager _entManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
private SpriteSystem _sprites;
|
||||
|
||||
public event Action<ProtoId<SelectableAmmoPrototype>>? SendAmmoSelectorSystemMessageAction;
|
||||
|
||||
private EntityUid _item;
|
||||
|
||||
public AmmoSelectorMenu()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
RobustXamlLoader.Load(this);
|
||||
_sprites = _entManager.System<SpriteSystem>();
|
||||
}
|
||||
|
||||
public void SetEntity(EntityUid uid)
|
||||
{
|
||||
_item = uid;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
var main = FindControl<RadialContainer>("Main");
|
||||
main.RemoveAllChildren();
|
||||
|
||||
if (!_entManager.TryGetComponent(_item, out AmmoSelectorComponent? ammoSelector))
|
||||
return;
|
||||
|
||||
foreach (var ammo in ammoSelector.Prototypes)
|
||||
{
|
||||
if (!_protoManager.TryIndex(ammo, out var prototype))
|
||||
continue;
|
||||
|
||||
var button = new AmmoSelectorMenuButton
|
||||
{
|
||||
SetSize = new Vector2(64, 64),
|
||||
ToolTip = Loc.GetString(prototype.Desc),
|
||||
ProtoId = prototype.ID
|
||||
};
|
||||
|
||||
var texture = new TextureRect
|
||||
{
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
Texture = _sprites.Frame0(prototype.Icon),
|
||||
TextureScale = new Vector2(2f, 2f)
|
||||
};
|
||||
|
||||
button.AddChild(texture);
|
||||
main.AddChild(button);
|
||||
}
|
||||
|
||||
AddAmmoSelectorMenuButtonOnClickActions(main);
|
||||
}
|
||||
|
||||
private void AddAmmoSelectorMenuButtonOnClickActions(RadialContainer control)
|
||||
{
|
||||
foreach (var child in control.Children)
|
||||
{
|
||||
if (child is not AmmoSelectorMenuButton castChild)
|
||||
continue;
|
||||
|
||||
castChild.OnButtonUp += _ =>
|
||||
{
|
||||
SendAmmoSelectorSystemMessageAction?.Invoke(castChild.ProtoId);
|
||||
Close();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class AmmoSelectorMenuButton : RadialMenuTextureButtonWithSector
|
||||
{
|
||||
public ProtoId<SelectableAmmoPrototype> ProtoId { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using Content.Shared._Goobstation.Weapons.AmmoSelector;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._Goobstation.AmmoSelector;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class AmmoSelectorMenuBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IClyde _displayManager = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
|
||||
private AmmoSelectorMenu? _menu;
|
||||
|
||||
public AmmoSelectorMenuBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_menu = this.CreateWindow<AmmoSelectorMenu>();
|
||||
_menu.SetEntity(Owner);
|
||||
_menu.SendAmmoSelectorSystemMessageAction += SendAmmoSelectorSystemMessage;
|
||||
|
||||
var vpSize = _displayManager.ScreenSize;
|
||||
_menu.OpenCenteredAt(_inputManager.MouseScreenPosition.Position / vpSize);
|
||||
}
|
||||
|
||||
public void SendAmmoSelectorSystemMessage(ProtoId<SelectableAmmoPrototype> protoId)
|
||||
{
|
||||
SendPredictedMessage(new AmmoSelectedMessage(protoId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using Content.Shared._Mono.CCVar;
|
||||
using Content.Shared._Mono.Company;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client._Mono.Company;
|
||||
|
||||
public sealed partial class CompanyManager
|
||||
{
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly IConfigurationManager _config = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
|
||||
private HashSet<ProtoId<CompanyPrototype>> _whitelist = new();
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_net.RegisterNetMessage<MsgCompanyWhitelist>(OnWhitelistMsg);
|
||||
}
|
||||
|
||||
private void OnWhitelistMsg(MsgCompanyWhitelist msg)
|
||||
{
|
||||
_whitelist = msg.Whitelist;
|
||||
}
|
||||
|
||||
public bool IsPlayerWhitelisted(ProtoId<CompanyPrototype> company)
|
||||
{
|
||||
return _whitelist.Contains(company);
|
||||
}
|
||||
|
||||
public bool IsAllowed(ProtoId<CompanyPrototype> company)
|
||||
{
|
||||
if (!_config.GetCVar(MonoCVars.CompanyWhitelist))
|
||||
return true;
|
||||
|
||||
if (!_proto.TryIndex(company, out var companyPrototype) ||
|
||||
!companyPrototype.Whitelisted)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return IsPlayerWhitelisted(company);
|
||||
}
|
||||
}
|
||||
@@ -39,6 +39,8 @@ namespace Content.MapRenderer.Painters
|
||||
// Seriously whoever made MapPainter use GameMapPrototype I wish you step on a lego one time.
|
||||
Map = map,
|
||||
});
|
||||
pair.ServerLogHandler.FailureLevel = LogLevel.Fatal;
|
||||
pair.ClientLogHandler.FailureLevel = LogLevel.Fatal;
|
||||
|
||||
await foreach (var image in RenderPair(stopwatch, pair))
|
||||
yield return image;
|
||||
@@ -56,6 +58,8 @@ namespace Content.MapRenderer.Painters
|
||||
Connected = true,
|
||||
Fresh = false,
|
||||
});
|
||||
pair.ServerLogHandler.FailureLevel = LogLevel.Fatal;
|
||||
pair.ClientLogHandler.FailureLevel = LogLevel.Fatal;
|
||||
|
||||
var server = pair.Server;
|
||||
var client = pair.Client;
|
||||
@@ -69,6 +73,14 @@ namespace Content.MapRenderer.Painters
|
||||
|
||||
foreach (var grid in sMapManager.GetAllGrids(mapId))
|
||||
sEntityManager.QueueDeleteEntity(grid);
|
||||
});
|
||||
|
||||
await pair.RunTicksSync(10);
|
||||
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
var mapId = sEntityManager.System<GameTicker>().DefaultMap;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
+2176
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,22 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Postgres
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class CompanyMembers2 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+2176
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Postgres
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class CompanyMembers : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "company_members",
|
||||
columns: table => new
|
||||
{
|
||||
player_user_id = table.Column<Guid>(type: "uuid", nullable: false),
|
||||
company_id = table.Column<string>(type: "text", nullable: false),
|
||||
owner = table.Column<bool>(type: "boolean", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_company_members", x => new { x.player_user_id, x.company_id });
|
||||
table.ForeignKey(
|
||||
name: "FK_company_members_player_player_user_id",
|
||||
column: x => x.player_user_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "company_members");
|
||||
}
|
||||
}
|
||||
}
|
||||
+36
-1
@@ -20,7 +20,7 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "9.0.1")
|
||||
.HasAnnotation("ProductVersion", "10.0.0")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||
|
||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||
@@ -581,6 +581,26 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.ToTable("blacklist", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.CompanyMember", b =>
|
||||
{
|
||||
b.Property<Guid>("PlayerUserId")
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("player_user_id");
|
||||
|
||||
b.Property<string>("CompanyId")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("company_id");
|
||||
|
||||
b.Property<bool>("Owner")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("owner");
|
||||
|
||||
b.HasKey("PlayerUserId", "CompanyId")
|
||||
.HasName("PK_company_members");
|
||||
|
||||
b.ToTable("company_members", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ConnectionLog", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -1680,6 +1700,19 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.Navigation("Profile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.CompanyMember", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Player", "Player")
|
||||
.WithMany("CompanyMembers")
|
||||
.HasForeignKey("PlayerUserId")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("FK_company_members_player_player_user_id");
|
||||
|
||||
b.Navigation("Player");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ConnectionLog", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Server", "Server")
|
||||
@@ -2080,6 +2113,8 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
|
||||
b.Navigation("AdminWatchlistsReceived");
|
||||
|
||||
b.Navigation("CompanyMembers");
|
||||
|
||||
b.Navigation("JobWhitelists");
|
||||
});
|
||||
|
||||
|
||||
+2100
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Sqlite
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class CompanyMembers : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "company_members",
|
||||
columns: table => new
|
||||
{
|
||||
player_user_id = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||
company_id = table.Column<string>(type: "TEXT", nullable: false),
|
||||
owner = table.Column<bool>(type: "INTEGER", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_company_members", x => new { x.player_user_id, x.company_id });
|
||||
table.ForeignKey(
|
||||
name: "FK_company_members_player_player_user_id",
|
||||
column: x => x.player_user_id,
|
||||
principalTable: "player",
|
||||
principalColumn: "user_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "company_members");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "9.0.1");
|
||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.0");
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Admin", b =>
|
||||
{
|
||||
@@ -550,6 +550,26 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
b.ToTable("blacklist", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.CompanyMember", b =>
|
||||
{
|
||||
b.Property<Guid>("PlayerUserId")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("player_user_id");
|
||||
|
||||
b.Property<string>("CompanyId")
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("company_id");
|
||||
|
||||
b.Property<bool>("Owner")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("owner");
|
||||
|
||||
b.HasKey("PlayerUserId", "CompanyId")
|
||||
.HasName("PK_company_members");
|
||||
|
||||
b.ToTable("company_members", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ConnectionLog", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -1604,6 +1624,19 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
b.Navigation("Profile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.CompanyMember", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Player", "Player")
|
||||
.WithMany("CompanyMembers")
|
||||
.HasForeignKey("PlayerUserId")
|
||||
.HasPrincipalKey("UserId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("FK_company_members_player_player_user_id");
|
||||
|
||||
b.Navigation("Player");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ConnectionLog", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Server", "Server")
|
||||
@@ -1632,7 +1665,7 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
|
||||
b1.HasKey("ConnectionLogId");
|
||||
|
||||
b1.ToTable("connection_log");
|
||||
b1.ToTable("connection_log", (string)null);
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("ConnectionLogId")
|
||||
@@ -1677,7 +1710,7 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
|
||||
b1.HasKey("PlayerId");
|
||||
|
||||
b1.ToTable("player");
|
||||
b1.ToTable("player", (string)null);
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("PlayerId")
|
||||
@@ -1800,7 +1833,7 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
|
||||
b1.HasKey("ServerBanId");
|
||||
|
||||
b1.ToTable("server_ban");
|
||||
b1.ToTable("server_ban", (string)null);
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("ServerBanId")
|
||||
@@ -1877,7 +1910,7 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
|
||||
b1.HasKey("ServerRoleBanId");
|
||||
|
||||
b1.ToTable("server_role_ban");
|
||||
b1.ToTable("server_role_ban", (string)null);
|
||||
|
||||
b1.WithOwner()
|
||||
.HasForeignKey("ServerRoleBanId")
|
||||
@@ -2004,6 +2037,8 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
|
||||
b.Navigation("AdminWatchlistsReceived");
|
||||
|
||||
b.Navigation("CompanyMembers");
|
||||
|
||||
b.Navigation("JobWhitelists");
|
||||
});
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ namespace Content.Server.Database
|
||||
public DbSet<RoleWhitelist> RoleWhitelists { get; set; } = null!;
|
||||
public DbSet<BanTemplate> BanTemplate { get; set; } = null!;
|
||||
public DbSet<IPIntelCache> IPIntelCache { get; set; } = null!;
|
||||
public DbSet<CompanyMember> CompanyMembers { get; set; } = null!;
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
@@ -371,6 +372,14 @@ namespace Content.Server.Database
|
||||
.OwnsOne(p => p.HWId)
|
||||
.Property(p => p.Type)
|
||||
.HasDefaultValue(HwidType.Legacy);
|
||||
|
||||
// Mono
|
||||
modelBuilder.Entity<CompanyMember>()
|
||||
.HasOne(w => w.Player)
|
||||
.WithMany(p => p.CompanyMembers)
|
||||
.HasForeignKey(w => w.PlayerUserId)
|
||||
.HasPrincipalKey(p => p.UserId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
}
|
||||
|
||||
public virtual IQueryable<AdminLog> SearchLogs(IQueryable<AdminLog> query, string searchText)
|
||||
@@ -600,6 +609,7 @@ namespace Content.Server.Database
|
||||
public List<ServerRoleBan> AdminServerRoleBansCreated { get; set; } = null!;
|
||||
public List<ServerRoleBan> AdminServerRoleBansLastEdited { get; set; } = null!;
|
||||
public List<RoleWhitelist> JobWhitelists { get; set; } = null!;
|
||||
public List<CompanyMember> CompanyMembers { get; set; } = null!; // Mono
|
||||
}
|
||||
|
||||
[Table("whitelist")]
|
||||
@@ -1337,4 +1347,18 @@ namespace Content.Server.Database
|
||||
/// </summary>
|
||||
public float Score { get; set; }
|
||||
}
|
||||
|
||||
// Mono-Start
|
||||
[PrimaryKey(nameof(PlayerUserId), nameof(CompanyId))]
|
||||
public class CompanyMember
|
||||
{
|
||||
[Required, ForeignKey("Player")]
|
||||
public Guid PlayerUserId { get; set; }
|
||||
public Player Player { get; set; } = default!;
|
||||
public bool Owner { get; set; } = false;
|
||||
|
||||
[Required]
|
||||
public string CompanyId { get; set; } = default!;
|
||||
}
|
||||
// Mono-End
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ public sealed class AmeNodeGroup : BaseNodeGroup
|
||||
// return MathF.Max(200000f * MathF.Log10(2 * fuel * MathF.Pow(cores, (float)-0.5)), 0); // Frontier: preferring old calculation for now
|
||||
// return 200000f * MathF.Log10(fuel * fuel) * MathF.Pow(0.75f, cores - 1); // Frontier: preferring old calculation for now
|
||||
if (cores > 0 && fuel > 0) // Mono - default zero power unless conditions are met
|
||||
return 200000f * MathF.Log10(fuel * fuel) * MathF.Pow( 1.11111f, cores - 1); // Mono - Added positive returns from extra cores. Running AME at 2 fuel per core yields roughly the same power per core at all sizes
|
||||
return 200000f * MathF.Log10(fuel * fuel) * MathF.Pow( 1.11111f, fuel / 2f - 1); // Mono - Exponential scaling at high injection
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -287,7 +287,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
// This is intended so that matches & candles can re-use code for un-shaded layers on in-hand sprites.
|
||||
// However, this could cause conflicts if something is ACTUALLY both a toggleable light and flammable.
|
||||
// if that ever happens, then fire visuals will need to implement their own in-hand sprite management.
|
||||
_appearance.SetData(uid, ToggleableLightVisuals.Enabled, flammable.OnFire, appearance);
|
||||
_appearance.SetData(uid, ToggleableVisuals.Enabled, flammable.OnFire, appearance);
|
||||
}
|
||||
|
||||
public void AdjustFireStacks(EntityUid uid, float relativeFireStacks, FlammableComponent? flammable = null, bool ignite = false)
|
||||
|
||||
@@ -61,4 +61,16 @@ public sealed partial class ThermalRegulatorComponent : Component
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float ThermalRegulationTemperatureThreshold;
|
||||
|
||||
/// <summary>
|
||||
/// Process regulation while they're dead if there is a MobStateComponent? - Mono Hydrakin
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool ProcessWhileDead = false;
|
||||
|
||||
/// <summary>
|
||||
/// Process regulation in critical condition if there is a MobStateComponent? - Mono Hydrakin
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool ProcessWhileCrit = true;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ using Content.Server.Body.Components;
|
||||
using Content.Server.Temperature.Components;
|
||||
using Content.Server.Temperature.Systems;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Mobs; // Mono
|
||||
using Content.Shared.Mobs.Components; // Mono
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Body.Systems;
|
||||
@@ -51,6 +53,14 @@ public sealed class ThermalRegulatorSystem : EntitySystem
|
||||
if (!Resolve(ent, ref ent.Comp2, logMissing: false))
|
||||
return;
|
||||
|
||||
// mono begin
|
||||
if (ent.Comp1.ProcessWhileDead == false && TryComp<MobStateComponent>(ent, out var mobComp1) && mobComp1.CurrentState == MobState.Dead)
|
||||
return;
|
||||
|
||||
if (ent.Comp1.ProcessWhileCrit == false && TryComp<MobStateComponent>(ent, out var mobComp2) && mobComp2.CurrentState == MobState.Critical)
|
||||
return;
|
||||
// mono end
|
||||
|
||||
var totalMetabolismTempChange = ent.Comp1.MetabolismHeat - ent.Comp1.RadiatedHeat;
|
||||
|
||||
// implicit heat regulation
|
||||
|
||||
@@ -28,8 +28,6 @@ public sealed partial class BotanySystem : EntitySystem
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||
[Dependency] private readonly RandomHelperSystem _randomHelper = default!;
|
||||
[Dependency] private readonly ContrabandTurnInSystem _contraband = default!; // Frontier
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -100,7 +98,6 @@ public sealed partial class BotanySystem : EntitySystem
|
||||
public EntityUid SpawnSeedPacket(SeedData proto, EntityCoordinates coords, EntityUid user, float? healthOverride = null)
|
||||
{
|
||||
var seed = SpawnAtPosition(proto.PacketPrototype, coords); // Frontier: Spawn<SpawnAtPosition
|
||||
_contraband.ClearContrabandValue(seed); // Frontier
|
||||
var seedComp = EnsureComp<SeedComponent>(seed);
|
||||
seedComp.Seed = proto;
|
||||
seedComp.HealthOverride = healthOverride;
|
||||
@@ -160,7 +157,6 @@ public sealed partial class BotanySystem : EntitySystem
|
||||
var product = _robustRandom.Pick(proto.ProductPrototypes);
|
||||
|
||||
var entity = SpawnAtPosition(product, position); // Frontier: Spawn<SpawnAtPosition
|
||||
_contraband.ClearContrabandValue(entity); // Frontier
|
||||
_randomHelper.RandomOffset(entity, 0.25f);
|
||||
products.Add(entity);
|
||||
|
||||
|
||||
@@ -6,8 +6,10 @@ using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server._Mono.Company;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Shared._Mono.Company;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Ghost.Roles;
|
||||
@@ -1940,6 +1942,120 @@ INSERT INTO player_round (players_id, rounds_id) VALUES ({players[player]}, {id}
|
||||
|
||||
#endregion
|
||||
|
||||
// Mono
|
||||
#region Company
|
||||
|
||||
public async Task<bool> AddCompanyMember(Guid player, ProtoId<CompanyPrototype> company)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
var exists = await db.DbContext.CompanyMembers
|
||||
.Where(w => w.PlayerUserId == player)
|
||||
.Where(w => w.CompanyId == company.Id)
|
||||
.AnyAsync();
|
||||
|
||||
if (exists)
|
||||
return false;
|
||||
|
||||
var member = new CompanyMember
|
||||
{
|
||||
PlayerUserId = player,
|
||||
CompanyId = company,
|
||||
};
|
||||
db.DbContext.CompanyMembers.Add(member);
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetPlayerCompanies(Guid player, CancellationToken cancel)
|
||||
{
|
||||
await using var db = await GetDb(cancel);
|
||||
return await db.DbContext.CompanyMembers
|
||||
.Where(w => w.PlayerUserId == player)
|
||||
.Select(w => w.CompanyId)
|
||||
.ToListAsync(cancel);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CompanyMemberRecord>> GetCompanyMembers(ProtoId<CompanyPrototype> company, CancellationToken cancel)
|
||||
{
|
||||
await using var db = await GetDb(cancel);
|
||||
var members = await db.DbContext.CompanyMembers
|
||||
.Where(w => w.CompanyId == company.Id)
|
||||
.Include(c => c.Player)
|
||||
.ToListAsync(cancel);
|
||||
|
||||
return members.Select(m => new CompanyMemberRecord()
|
||||
{
|
||||
Company = company,
|
||||
Owner = m.Owner,
|
||||
PlayerUserId = m.PlayerUserId,
|
||||
LastSeenUserName = m.Player.LastSeenUserName,
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CompanyMemberRecord>> GetAllCompanyMembers(CancellationToken cancel)
|
||||
{
|
||||
await using var db = await GetDb(cancel);
|
||||
var members = await db.DbContext.CompanyMembers
|
||||
.Include(c => c.Player)
|
||||
.ToListAsync(cancel);
|
||||
|
||||
return members.Select(m => new CompanyMemberRecord()
|
||||
{
|
||||
Company = m.CompanyId,
|
||||
Owner = m.Owner,
|
||||
PlayerUserId = m.PlayerUserId,
|
||||
LastSeenUserName = m.Player.LastSeenUserName,
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<CompanyMemberRecord?> GetCompanyMember(ProtoId<CompanyPrototype> company, Guid player, CancellationToken cancel)
|
||||
{
|
||||
await using var db = await GetDb(cancel);
|
||||
var member = await db.DbContext.CompanyMembers
|
||||
.Where(w => w.CompanyId == company.Id)
|
||||
.Where(w => w.PlayerUserId == player)
|
||||
.Include(c => c.Player)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
if (member == null)
|
||||
return null;
|
||||
|
||||
return new CompanyMemberRecord()
|
||||
{
|
||||
Company = company,
|
||||
LastSeenUserName = member.Player.LastSeenUserName,
|
||||
Owner = member.Owner,
|
||||
PlayerUserId = member.PlayerUserId,
|
||||
};
|
||||
}
|
||||
|
||||
public async Task SetCompanyOwner(ProtoId<CompanyPrototype> company, Guid player, bool owner)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
await db.DbContext.CompanyMembers
|
||||
.Where(w => w.CompanyId == company.Id)
|
||||
.Where(w => w.PlayerUserId == player)
|
||||
.ExecuteUpdateAsync(setters => setters.SetProperty(m => m.Owner, owner));
|
||||
}
|
||||
|
||||
public async Task<bool> RemoveCompanyMember(Guid player, ProtoId<CompanyPrototype> company)
|
||||
{
|
||||
await using var db = await GetDb();
|
||||
var entry = await db.DbContext.CompanyMembers
|
||||
.Where(w => w.PlayerUserId == player)
|
||||
.Where(w => w.CompanyId == company.Id)
|
||||
.SingleOrDefaultAsync();
|
||||
|
||||
if (entry == null)
|
||||
return false;
|
||||
|
||||
db.DbContext.CompanyMembers.Remove(entry);
|
||||
await db.DbContext.SaveChangesAsync();
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public abstract Task SendNotification(DatabaseNotification notification);
|
||||
|
||||
// SQLite returns DateTime as Kind=Unspecified, Npgsql actually knows for sure it's Kind=Utc.
|
||||
|
||||
@@ -22,6 +22,8 @@ using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
using LogLevel = Robust.Shared.Log.LogLevel;
|
||||
using MSLogLevel = Microsoft.Extensions.Logging.LogLevel;
|
||||
using Content.Shared._Mono.Company;
|
||||
using Content.Server._Mono.Company; // Mono
|
||||
|
||||
namespace Content.Server.Database
|
||||
{
|
||||
@@ -351,6 +353,19 @@ namespace Content.Server.Database
|
||||
|
||||
#endregion
|
||||
|
||||
// Mono
|
||||
#region Company
|
||||
Task AddCompanyMember(Guid player, ProtoId<CompanyPrototype> company);
|
||||
|
||||
Task<List<string>> GetPlayerCompanies(Guid player, CancellationToken cancel = default);
|
||||
Task<IEnumerable<CompanyMemberRecord>> GetCompanyMembers(ProtoId<CompanyPrototype> company, CancellationToken cancel = default);
|
||||
Task<IEnumerable<CompanyMemberRecord>> GetAllCompanyMembers(CancellationToken cancel = default);
|
||||
Task<CompanyMemberRecord?> GetCompanyMember(ProtoId<CompanyPrototype> company, Guid player, CancellationToken cancel = default);
|
||||
Task SetCompanyOwner(ProtoId<CompanyPrototype> company, Guid player, bool owner);
|
||||
Task<bool> RemoveCompanyMember(Guid player, ProtoId<CompanyPrototype> company);
|
||||
|
||||
#endregion
|
||||
|
||||
#region DB Notifications
|
||||
|
||||
void SubscribeToNotifications(Action<DatabaseNotification> handler);
|
||||
@@ -1058,7 +1073,7 @@ namespace Content.Server.Database
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.RemoveJobWhitelist(player, job));
|
||||
}
|
||||
|
||||
|
||||
// Frontier: ghost role DB ops
|
||||
public Task AddGhostRoleWhitelist(Guid player, ProtoId<GhostRolePrototype> ghostRole)
|
||||
{
|
||||
@@ -1095,6 +1110,50 @@ namespace Content.Server.Database
|
||||
return RunDbCommand(() => _db.CleanIPIntelCache(range));
|
||||
}
|
||||
|
||||
// Mono-Start
|
||||
public Task AddCompanyMember(Guid player, ProtoId<CompanyPrototype> company)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.AddCompanyMember(player, company));
|
||||
}
|
||||
|
||||
public Task<List<string>> GetPlayerCompanies(Guid player, CancellationToken cancel = default)
|
||||
{
|
||||
DbReadOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.GetPlayerCompanies(player, cancel));
|
||||
}
|
||||
|
||||
public Task<IEnumerable<CompanyMemberRecord>> GetCompanyMembers(ProtoId<CompanyPrototype> company, CancellationToken cancel = default)
|
||||
{
|
||||
DbReadOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.GetCompanyMembers(company, cancel));
|
||||
}
|
||||
|
||||
public Task<IEnumerable<CompanyMemberRecord>> GetAllCompanyMembers(CancellationToken cancel = default)
|
||||
{
|
||||
DbReadOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.GetAllCompanyMembers(cancel));
|
||||
}
|
||||
|
||||
public Task<CompanyMemberRecord?> GetCompanyMember(ProtoId<CompanyPrototype> company, Guid player, CancellationToken cancel = default)
|
||||
{
|
||||
DbReadOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.GetCompanyMember(company, player, cancel));
|
||||
}
|
||||
|
||||
public Task SetCompanyOwner(ProtoId<CompanyPrototype> company, Guid player, bool owner)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.SetCompanyOwner(company, player, owner));
|
||||
}
|
||||
|
||||
public Task<bool> RemoveCompanyMember(Guid player, ProtoId<CompanyPrototype> company)
|
||||
{
|
||||
DbWriteOpsMetric.Inc();
|
||||
return RunDbCommand(() => _db.RemoveCompanyMember(player, company));
|
||||
}
|
||||
// Mono-End
|
||||
|
||||
public void SubscribeToNotifications(Action<DatabaseNotification> handler)
|
||||
{
|
||||
lock (_notificationHandlers)
|
||||
|
||||
@@ -171,7 +171,7 @@ namespace Content.Server.EntityEffects.Effects
|
||||
interruptsDoAfters: false,
|
||||
// Shitmed Change Start
|
||||
targetPart: TargetBodyPart.All,
|
||||
partMultiplier: 0.5f,
|
||||
partMultiplier: 1.00f, // Mono, 0.5f->1.00f
|
||||
canSever: false);
|
||||
// Shitmed Change End
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Server._Mono.Company; // Mono
|
||||
using Content.Server._NF.Auth;
|
||||
using Content.Server.Acz;
|
||||
using Content.Server.Administration;
|
||||
@@ -155,6 +156,7 @@ namespace Content.Server.Entry
|
||||
|
||||
_euiManager.Initialize();
|
||||
|
||||
IoCManager.Resolve<CompanyManager>().Initialize(); // Mono
|
||||
IoCManager.Resolve<IGameMapManager>().Initialize();
|
||||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<GameTicker>().PostInitialize();
|
||||
IoCManager.Resolve<IBanManager>().Initialize();
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
namespace Content.Server.Explosion.Components;
|
||||
|
||||
/// <summary>
|
||||
/// calls the trigger when the object is initialized
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TriggerOnSpawnComponent : Component
|
||||
namespace Content.Server.Explosion.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// calls the trigger when the object is initialized
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TriggerOnSpawnComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Starts a timer on spawn rather than instantly triggering
|
||||
/// </summary>
|
||||
[DataField("timerOnly")] // Mono: Added "timerOnly" boolean
|
||||
public bool timerOnly = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Server._Mono.Planets;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Explosion.Components;
|
||||
@@ -39,6 +40,7 @@ using Content.Shared.Projectiles; // Frontier: embed triggers
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.Map.Components;
|
||||
|
||||
namespace Content.Server.Explosion.EntitySystems
|
||||
{
|
||||
@@ -83,6 +85,7 @@ namespace Content.Server.Explosion.EntitySystems
|
||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||
[Dependency] private readonly ElectrocutionSystem _electrocution = default!;
|
||||
[Dependency] private readonly StationSystem _station = default!; // Frontier: medical insurance
|
||||
[Dependency] private readonly SharedMapSystem _map = default!; // Frontier: medical insurance
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -252,19 +255,19 @@ namespace Content.Server.Explosion.EntitySystems
|
||||
var posText = $"({x}, {y})";
|
||||
|
||||
// Frontier: Gets station location of the implant
|
||||
var station = _station.GetOwningStation(uid);
|
||||
var stationText = station is null ? null : $"{Name(station.Value)} ";
|
||||
var grid = ownerXform.GridUid;
|
||||
var gridText = grid is null ? "" : MetaData(grid.Value).EntityName;
|
||||
|
||||
if (stationText == null)
|
||||
stationText = "";
|
||||
if (HasComp<MapComponent>(grid) && !HasComp<PlanetMapComponent>(grid))
|
||||
gridText = "";
|
||||
|
||||
// Frontier: Gets species of the implant user
|
||||
var speciesText = $"";
|
||||
if (TryComp<HumanoidAppearanceComponent>(implanted.ImplantedEntity, out var species))
|
||||
speciesText = $" ({species!.Species})";
|
||||
speciesText = $" ({species.Species})";
|
||||
|
||||
var critMessage = Loc.GetString(component.CritMessage, ("user", implanted.ImplantedEntity.Value), ("specie", speciesText), ("grid", stationText!), ("position", posText));
|
||||
var deathMessage = Loc.GetString(component.DeathMessage, ("user", implanted.ImplantedEntity.Value), ("specie", speciesText), ("grid", stationText!), ("position", posText));
|
||||
var critMessage = Loc.GetString(component.CritMessage, ("user", implanted.ImplantedEntity.Value), ("specie", speciesText), ("grid", gridText), ("position", posText));
|
||||
var deathMessage = Loc.GetString(component.DeathMessage, ("user", implanted.ImplantedEntity.Value), ("specie", speciesText), ("grid", gridText), ("position", posText));
|
||||
|
||||
if (!TryComp<MobStateComponent>(implanted.ImplantedEntity, out var mobstate))
|
||||
return;
|
||||
@@ -300,7 +303,10 @@ namespace Content.Server.Explosion.EntitySystems
|
||||
|
||||
private void OnSpawnTriggered(EntityUid uid, TriggerOnSpawnComponent component, MapInitEvent args)
|
||||
{
|
||||
Trigger(uid);
|
||||
if (component.timerOnly == true)
|
||||
StartTimer(uid,uid);
|
||||
else
|
||||
Trigger(uid);
|
||||
}
|
||||
|
||||
private void OnActivate(EntityUid uid, TriggerOnActivateComponent component, ActivateInWorldEvent args)
|
||||
|
||||
@@ -130,6 +130,10 @@ public sealed partial class GameTicker
|
||||
if (MetaData(ruleEntity).EntityPrototype?.ID is not { } id) // you really fucked up
|
||||
return false;
|
||||
|
||||
// Mono - ensure delayed start rules don't trigger ahead of time.
|
||||
if (TryComp<DelayedStartRuleComponent>(ruleEntity, out var delayComp) && delayComp.RuleStartTime > _gameTiming.CurTime)
|
||||
return false;
|
||||
|
||||
// If we already have it, then we just skip the delay as it has already happened.
|
||||
if (!RemComp<DelayedStartRuleComponent>(ruleEntity) && ruleData.Delay != null)
|
||||
{
|
||||
|
||||
@@ -42,5 +42,11 @@ namespace Content.Server.GameTicking.Presets
|
||||
/// </summary>
|
||||
[DataField("supportedMaps", customTypeSerializer: typeof(PrototypeIdSerializer<GameMapPoolPrototype>))]
|
||||
public string? MapPool;
|
||||
|
||||
/// <summary>
|
||||
/// Mono - as how much votes should each player click count.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float Weight = 1f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Server._Mono.Company; // Mono
|
||||
using Content.Server._NF.Auth;
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.Administration.Logs;
|
||||
@@ -78,6 +79,7 @@ namespace Content.Server.IoC
|
||||
IoCManager.Register<MultiServerKickManager>();
|
||||
IoCManager.Register<CVarControlManager>();
|
||||
IoCManager.Register<MiniAuthManager>(); //Frontier
|
||||
IoCManager.Register<CompanyManager>(); // Mono
|
||||
|
||||
IoCManager.Register<DiscordLink>();
|
||||
IoCManager.Register<DiscordChatLink>();
|
||||
|
||||
@@ -59,7 +59,6 @@ namespace Content.Server.Lathe
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solution = default!;
|
||||
[Dependency] private readonly StackSystem _stack = default!;
|
||||
[Dependency] private readonly TransformSystem _transform = default!;
|
||||
[Dependency] private readonly ContrabandTurnInSystem _contraband = default!; // Frontier
|
||||
[Dependency] private readonly DeviceLinkSystem _deviceLink = default!; // Mono
|
||||
|
||||
/// <summary>
|
||||
@@ -326,11 +325,7 @@ namespace Content.Server.Lathe
|
||||
|
||||
// Frontier: adjust price before merge (stack prices changed once)
|
||||
if (result.Valid)
|
||||
{
|
||||
ModifyPrintedEntityPrice(uid, comp, result);
|
||||
|
||||
_contraband.ClearContrabandValue(result);
|
||||
}
|
||||
// End Frontier
|
||||
|
||||
_stack.TryMergeToContacts(result);
|
||||
|
||||
@@ -121,8 +121,14 @@ public sealed partial class MechSystem : SharedMechSystem
|
||||
if (!TryComp<BatteryComponent>(ent.Comp.BatterySlot.ContainedEntity, out var battery))
|
||||
return;
|
||||
|
||||
ent.Comp.CriticalPowerState = battery.CurrentCharge / battery.MaxCharge <= 0.05f;
|
||||
//ent.Comp.CriticalPowerState = battery.CurrentCharge / battery.MaxCharge <= 0.05f;
|
||||
// Mono
|
||||
var criticalState = battery.CurrentCharge / battery.MaxCharge <= 0.05f;
|
||||
if (criticalState == ent.Comp.CriticalPowerState)
|
||||
return;
|
||||
|
||||
ent.Comp.CriticalPowerState = criticalState;
|
||||
// End Mono
|
||||
Dirty(ent);
|
||||
_movementSpeed.RefreshMovementSpeedModifiers(ent);
|
||||
}
|
||||
|
||||
@@ -35,12 +35,13 @@ public sealed class JetpackSystem : SharedJetpackSystem
|
||||
/// </summary>
|
||||
private void OnJetpackActivated(EntityUid uid, ActiveJetpackComponent component, ComponentStartup args)
|
||||
{
|
||||
if (TryComp<JetpackComponent>(uid, out var jetpack) && jetpack.Stealth == false)
|
||||
if (TryComp<JetpackComponent>(uid, out var jetpack) && jetpack.DetectionRange > 0)
|
||||
{
|
||||
var blip = EnsureComp<RadarBlipComponent>(uid);
|
||||
blip.Config.Color = Color.Cyan;
|
||||
blip.Config.Bounds = new(-0.25f, -0.25f, 0.25f, 0.25f);
|
||||
blip.VisibleFromOtherGrids = true;
|
||||
blip.MaxDistance = jetpack.DetectionRange;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// Mono - whole file
|
||||
namespace Content.Server.NPC.Queries.Considerations;
|
||||
|
||||
/// <summary>
|
||||
/// Returns 1f if the target is alive or can't be alive, or 0f if not.
|
||||
/// </summary>
|
||||
public sealed partial class TargetIsAliveOrNACon : UtilityConsideration
|
||||
{
|
||||
|
||||
}
|
||||
@@ -6,9 +6,9 @@ using Content.Shared.Whitelist;
|
||||
namespace Content.Server.NPC.Queries.Queries;
|
||||
|
||||
/// <summary>
|
||||
/// Returns nearby shuttles considered hostile from <see cref="FactionSystem"/>
|
||||
/// Returns nearby entities tagged with ShipNpcTargetComponent.
|
||||
/// </summary>
|
||||
public sealed partial class NearbyHostileShuttlesQuery : UtilityQuery
|
||||
public sealed partial class NearbyNpcTargetsQuery : UtilityQuery
|
||||
{
|
||||
[DataField]
|
||||
public float Range = 2000f;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Server.NPC.Queries.Considerations;
|
||||
using Content.Server.NPC.Queries.Queries;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array; // Mono
|
||||
|
||||
namespace Content.Server.NPC.Queries;
|
||||
|
||||
@@ -10,12 +11,22 @@ namespace Content.Server.NPC.Queries;
|
||||
/// These results are then run through the considerations.
|
||||
/// </summary>
|
||||
[Prototype]
|
||||
public sealed partial class UtilityQueryPrototype : IPrototype
|
||||
public sealed partial class UtilityQueryPrototype : IPrototype, IInheritingPrototype // Mono
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("query")]
|
||||
// <Mono>
|
||||
[ParentDataField(typeof(AbstractPrototypeIdArraySerializer<UtilityQueryPrototype>))]
|
||||
public string[]? Parents { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
[NeverPushInheritance]
|
||||
[AbstractDataField]
|
||||
public bool Abstract { get; private set; }
|
||||
// </Mono>
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("query"), AlwaysPushInheritance]
|
||||
public List<UtilityQuery> Query = new();
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite), DataField("considerations")]
|
||||
|
||||
@@ -390,6 +390,11 @@ public sealed class NPCUtilitySystem : EntitySystem
|
||||
return _statusEffectsSystem.HasStatusEffect(targetUid, "Stun") ? 1f : 0f;
|
||||
}
|
||||
// End Frontier
|
||||
// Mono
|
||||
case TargetIsAliveOrNACon:
|
||||
{
|
||||
return !TryComp<MobStateComponent>(targetUid, out var mobState) || _mobState.IsAlive(targetUid, mobState) ? 1f : 0f;
|
||||
}
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
@@ -494,7 +499,7 @@ public sealed class NPCUtilitySystem : EntitySystem
|
||||
break;
|
||||
}
|
||||
// Mono - TODO: consider factions
|
||||
case NearbyHostileShuttlesQuery shuttlesQuery:
|
||||
case NearbyNpcTargetsQuery shuttlesQuery:
|
||||
{
|
||||
var xform = Transform(owner);
|
||||
var ownGrid = xform.GridUid;
|
||||
@@ -502,11 +507,13 @@ public sealed class NPCUtilitySystem : EntitySystem
|
||||
{
|
||||
var targetXform = Transform(target);
|
||||
var targetGrid = targetXform.GridUid;
|
||||
if (targetComp.NeedGrid && targetGrid == null ||
|
||||
targetGrid == ownGrid ||
|
||||
(_transform.GetWorldPosition(target) - _transform.GetWorldPosition(xform)).Length() > shuttlesQuery.Range ||
|
||||
targetComp.NeedPower && !this.IsPowered(target, EntityManager) ||
|
||||
targetGrid != null && _whitelistSystem.IsBlacklistPass(shuttlesQuery.Blacklist, targetGrid.Value))
|
||||
if (targetComp.NeedGrid != NpcTargetGridMode.Either // if we care about grid..
|
||||
// ..and our (non-)need for grid is equal to the (non-)absence of a grid
|
||||
&& (targetComp.NeedGrid == NpcTargetGridMode.OnGrid) == (targetGrid == null)
|
||||
|| targetGrid == ownGrid
|
||||
|| (_transform.GetWorldPosition(target) - _transform.GetWorldPosition(xform)).Length() > shuttlesQuery.Range
|
||||
|| targetComp.NeedPower && !this.IsPowered(target, EntityManager)
|
||||
|| targetGrid != null && _whitelistSystem.IsBlacklistPass(shuttlesQuery.Blacklist, targetGrid.Value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -273,12 +273,20 @@ public sealed class MoverController : SharedMoverController
|
||||
// Mono: all below code handling shuttle movement has been heavily modified by Monolith
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Get a shuttle's torque.
|
||||
/// </summary>
|
||||
public float GetTorque(ShuttleComponent shuttle)
|
||||
{
|
||||
return shuttle.AngularThrust * shuttle.AngularMultiplier;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a shuttle's angular acceleration.
|
||||
/// </summary>
|
||||
public float GetAngularAcceleration(ShuttleComponent shuttle, PhysicsComponent body)
|
||||
{
|
||||
return shuttle.AngularThrust * body.InvI;
|
||||
return GetTorque(shuttle) * body.InvI;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -302,7 +310,7 @@ public sealed class MoverController : SharedMoverController
|
||||
// prevent NaNs
|
||||
dir *= dir.X == 0 ? vertScale : dir.Y == 0 ? horizScale : MathF.Min(horizScale, vertScale);
|
||||
|
||||
return dir;
|
||||
return dir * shuttle.AccelerationMultiplier;
|
||||
}
|
||||
|
||||
public Vector2 GetDirectionAccel(Vector2 dir, ShuttleComponent shuttle, PhysicsComponent body)
|
||||
@@ -323,7 +331,7 @@ public sealed class MoverController : SharedMoverController
|
||||
var twr = thrust.Length() / body.Mass;
|
||||
var twrMult = MathF.Pow(twr / shuttle.BaseMaxVelocityTWR, shuttle.MaxVelocityScalingExponent);
|
||||
|
||||
return vel.Normalized() * MathF.Min(shuttle.BaseMaxLinearVelocity * twrMult, MathF.Min(shuttle.UpperMaxVelocity, shuttle.SetMaxVelocity));
|
||||
return vel.Normalized() * MathF.Min(shuttle.BaseMaxLinearVelocity * twrMult * shuttle.MaxVelMultiplier, MathF.Min(shuttle.UpperMaxVelocity, shuttle.SetMaxVelocity));
|
||||
}
|
||||
|
||||
private void HandleShuttleMovement(float frameTime)
|
||||
@@ -335,6 +343,9 @@ public sealed class MoverController : SharedMoverController
|
||||
// query all our pilots for input
|
||||
var toRemove = new List<EntityUid>();
|
||||
|
||||
var angularMul = 0f;
|
||||
var accelMul = 0f;
|
||||
var maxVelMul = 0f;
|
||||
foreach (var pilot in piloted.InputSources)
|
||||
{
|
||||
var inputsEv = new GetShuttleInputsEvent(frameTime, uid);
|
||||
@@ -343,7 +354,12 @@ public sealed class MoverController : SharedMoverController
|
||||
if (!inputsEv.GotInput)
|
||||
toRemove.Add(pilot);
|
||||
else if (inputsEv.Input != null)
|
||||
{
|
||||
inputs.Add(inputsEv.Input.Value);
|
||||
angularMul += inputsEv.AngularMul;
|
||||
accelMul += inputsEv.AccelMul;
|
||||
maxVelMul += inputsEv.MaxVelMul;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var remUid in toRemove)
|
||||
@@ -359,6 +375,7 @@ public sealed class MoverController : SharedMoverController
|
||||
{
|
||||
_thruster.DisableLinearThrusters(shuttle);
|
||||
PhysicsSystem.SetSleepingAllowed(uid, body, true);
|
||||
shuttle.AngularMultiplier = shuttle.AccelerationMultiplier = shuttle.MaxVelMultiplier = 1f;
|
||||
continue;
|
||||
}
|
||||
PhysicsSystem.SetSleepingAllowed(uid, body, false);
|
||||
@@ -377,6 +394,13 @@ public sealed class MoverController : SharedMoverController
|
||||
angularInput /= count;
|
||||
brakeInput /= count;
|
||||
|
||||
angularMul /= count;
|
||||
accelMul /= count;
|
||||
maxVelMul /= count;
|
||||
shuttle.AngularMultiplier = angularMul;
|
||||
shuttle.AccelerationMultiplier = accelMul;
|
||||
shuttle.MaxVelMultiplier = maxVelMul;
|
||||
|
||||
var shuttleNorthAngle = _xformSystem.GetWorldRotation(uid);
|
||||
|
||||
// handle movement: brake
|
||||
@@ -524,7 +548,7 @@ public sealed class MoverController : SharedMoverController
|
||||
}
|
||||
else
|
||||
{
|
||||
var torque = shuttle.AngularThrust * -angularInput;
|
||||
var torque = GetTorque(shuttle) * -angularInput;
|
||||
|
||||
// Need to cap the velocity if 1 tick of input brings us over cap so we don't continuously
|
||||
// edge onto the cap over and over.
|
||||
|
||||
@@ -9,9 +9,10 @@ public record struct ShuttleInput(Vector2 Strafe, float Rotation, float Brakes);
|
||||
/// <summary>
|
||||
/// Raised on pilots to get inputs given to a shuttle.
|
||||
/// If GotInput is false, this piloted is removed from input sources.
|
||||
/// Also queries for multipliers to acceleration and max speed.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct GetShuttleInputsEvent(float FrameTime, EntityUid ShuttleUid, ShuttleInput? Input = null, bool GotInput = false);
|
||||
public record struct GetShuttleInputsEvent(float FrameTime, EntityUid ShuttleUid, ShuttleInput? Input = null, bool GotInput = false, float AngularMul = 1f, float AccelMul = 1f, float MaxVelMul = 1f);
|
||||
|
||||
[ByRefEvent]
|
||||
public record struct PilotedShuttleRelayedEvent<TEvent>(TEvent Args);
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace Content.Server.Preferences.Managers
|
||||
IEnumerable<KeyValuePair<NetUserId, ICharacterProfile>> GetSelectedProfilesForPlayers(List<NetUserId> userIds);
|
||||
bool HavePreferencesLoaded(ICommonSession session);
|
||||
Task RefreshPreferencesAsync(ICommonSession session, CancellationToken cancel);
|
||||
Task SetProfile(NetUserId userId, int slot, ICharacterProfile profile);
|
||||
Task SetProfile(NetUserId userId, int slot, ICharacterProfile profile,
|
||||
bool authoritative = true); // Mono
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,10 +87,11 @@ namespace Content.Server.Preferences.Managers
|
||||
if (message.Profile == null)
|
||||
_sawmill.Error($"User {userId} sent a {nameof(MsgUpdateCharacter)} with a null profile in slot {message.Slot}.");
|
||||
else
|
||||
await SetProfile(userId, message.Slot, message.Profile);
|
||||
await SetProfile(userId, message.Slot, message.Profile, false);
|
||||
}
|
||||
|
||||
public async Task SetProfile(NetUserId userId, int slot, ICharacterProfile profile)
|
||||
public async Task SetProfile(NetUserId userId, int slot, ICharacterProfile profile,
|
||||
bool authoritative = true) // Mono
|
||||
{
|
||||
if (!_cachedPlayerPrefs.TryGetValue(userId, out var prefsData) || !prefsData.PrefsLoaded)
|
||||
{
|
||||
@@ -105,6 +106,14 @@ namespace Content.Server.Preferences.Managers
|
||||
var session = _playerManager.GetSessionById(userId);
|
||||
|
||||
profile.EnsureValid(session, _dependencies);
|
||||
// Mono
|
||||
if (!authoritative && profile is HumanoidCharacterProfile humanoid)
|
||||
{
|
||||
if (curPrefs.Characters.TryGetValue(slot, out var oldProfile) && oldProfile is HumanoidCharacterProfile oldHumanoid)
|
||||
profile = humanoid.WithBankBalance(oldHumanoid.BankBalance);
|
||||
else
|
||||
profile = humanoid.WithBankBalance(HumanoidCharacterProfile.DefaultBalance);
|
||||
}
|
||||
|
||||
var profiles = new Dictionary<int, ICharacterProfile>(curPrefs.Characters)
|
||||
|
||||
|
||||
@@ -69,6 +69,8 @@ public sealed partial class ResearchSystem
|
||||
if (serverComponent.Clients.Contains(client))
|
||||
return;
|
||||
|
||||
UnregisterClient(client, clientComponent); // Mono Research Fix, unregister a client if applicable before registering to a new server
|
||||
|
||||
serverComponent.Clients.Add(client);
|
||||
clientComponent.Server = server;
|
||||
SyncClientWithServer(client, clientComponent: clientComponent);
|
||||
|
||||
@@ -22,6 +22,7 @@ using Robust.Shared.Timing;
|
||||
using Content.Shared.DeviceNetwork.Components;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
using Content.Server._NF.SectorServices; // Frontier
|
||||
using Content.Shared.GameTicking.Components; // Mono
|
||||
|
||||
namespace Content.Server.RoundEnd
|
||||
{
|
||||
@@ -354,9 +355,17 @@ namespace Content.Server.RoundEnd
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
// Check if we should auto-call.
|
||||
int mins = _autoCalledBefore ? _cfg.GetCVar(CCVars.EmergencyShuttleAutoCallExtensionTime)
|
||||
: _cfg.GetCVar(CCVars.EmergencyShuttleAutoCallTime);
|
||||
if (mins != 0 && _gameTiming.CurTime - AutoCallStartTime > TimeSpan.FromMinutes(mins))
|
||||
var mins = TimeSpan.FromMinutes(_autoCalledBefore ? _cfg.GetCVar(CCVars.EmergencyShuttleAutoCallExtensionTime)
|
||||
: _cfg.GetCVar(CCVars.EmergencyShuttleAutoCallTime));
|
||||
|
||||
// Mono
|
||||
var query = EntityQueryEnumerator<RoundEndTimeRuleComponent, ActiveGameRuleComponent>();
|
||||
while (query.MoveNext(out _, out var endTimeComp, out _))
|
||||
{
|
||||
mins = endTimeComp.EndAt;
|
||||
}
|
||||
|
||||
if (mins.TotalSeconds != 0 && _gameTiming.CurTime - AutoCallStartTime > mins)
|
||||
{
|
||||
if (!_shuttle.EmergencyShuttleArrived && ExpectedCountdownEnd is null)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace Content.Server.RoundEnd;
|
||||
|
||||
/// <summary>
|
||||
/// If a gamerule with this component is present, override the roundend time to the time set in it.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class RoundEndTimeRuleComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public TimeSpan EndAt;
|
||||
}
|
||||
@@ -105,6 +105,24 @@ namespace Content.Server.Shuttles.Components
|
||||
/// </summar>
|
||||
[DataField]
|
||||
public Vector2 LastThrust = Vector2.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Multiplier to angular thrust. Set depending on pilot.
|
||||
/// </summary
|
||||
[ViewVariables]
|
||||
public float AngularMultiplier = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Multiplier to linear thrust. Set depending on pilot.
|
||||
/// </summary
|
||||
[ViewVariables]
|
||||
public float AccelerationMultiplier = 1f;
|
||||
|
||||
/// <summary>
|
||||
/// Multiplier to max velocity. Set depending on pilot.
|
||||
/// </summary
|
||||
[ViewVariables]
|
||||
public float MaxVelMultiplier = 1f;
|
||||
// </Mono>
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Content.Server.Shuttles.Components
|
||||
public string MachinePartThrust = "Capacitor";
|
||||
|
||||
[DataField("partRatingThrustMultiplier")]
|
||||
public float PartRatingThrustMultiplier = 1.15f; // Frontier - PR #1292 1.5f<1.15f
|
||||
public float PartRatingThrustMultiplier = 1.25f; // Mono: 1.15->1.25 (frontier: 1.5->1.15)
|
||||
|
||||
/// <summary>
|
||||
/// Frontier - Amount of charge this needs from an APC per second to function.
|
||||
|
||||
@@ -202,7 +202,6 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
|
||||
{
|
||||
DockingInterfaceState? dockState = null;
|
||||
UpdateState(uid, ref dockState);
|
||||
_shuttle.NfSetPowered(uid, component, args.Powered); // Frontier
|
||||
|
||||
// Handle job slots when power changes
|
||||
HandleJobSlotsOnPowerChange(uid, component, args.Powered);
|
||||
|
||||
@@ -225,16 +225,19 @@ public sealed partial class ShuttleSystem
|
||||
var inelasticVel = totalInertia / (ourMass + otherMass);
|
||||
|
||||
// Mono Edit - partial credit to https://github.com/Sector-Crescent/Hullrot/pull/692
|
||||
//ShipShieldedComp is removed when shields are broken, only reduces energy delivered when shields are active. ShipShieldsSystem ln 256.
|
||||
//ShipShieldedComp is removed when shields are broken, reduces both energies when shields are active. ShipShieldsSystem ln 256.
|
||||
float shieldFactor = 1f;
|
||||
if (TryComp<ShipShieldedComponent>(args.OurEntity, out var ShipShieldedComponent) //Our ship collision resistance
|
||||
&& TryComp<ShipShieldEmitterComponent>(ShipShieldedComponent.Source, out var ShipShieldEmitterComponent)
|
||||
)
|
||||
toUsEnergy *= ShipShieldEmitterComponent.CollisionResistanceMultiplier;
|
||||
shieldFactor *= ShipShieldEmitterComponent.CollisionResistanceMultiplier;
|
||||
|
||||
if (TryComp<ShipShieldedComponent>(args.OtherEntity, out var OtherShipShieldedComponent) //Other ship collision resistance
|
||||
&& TryComp<ShipShieldEmitterComponent>(OtherShipShieldedComponent.Source, out var OtherShipShieldEmitterComponent)
|
||||
)
|
||||
toOtherEnergy *= OtherShipShieldEmitterComponent.CollisionResistanceMultiplier;
|
||||
shieldFactor *= OtherShipShieldEmitterComponent.CollisionResistanceMultiplier;
|
||||
toUsEnergy *= shieldFactor;
|
||||
toOtherEnergy *= shieldFactor;
|
||||
// Mono Edit end
|
||||
|
||||
DoGridImpact((args.OurEntity, ourGrid, ourXform, ourBody), args.OurFixture, inelasticVel, ourVelocity, ourTile, ourTiles, toUsEnergy);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Server.Inventory;
|
||||
using Content.Server.Radio.Components;
|
||||
using Content.Shared._CorvaxNext.Silicons.Borgs.Components;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Silicons.Borgs;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
@@ -28,6 +29,23 @@ public sealed class BorgSwitchableTypeSystem : SharedBorgSwitchableTypeSystem
|
||||
if (TryComp(ent, out ActiveRadioComponent? activeRadio))
|
||||
activeRadio.Channels = [.. radioChannels];
|
||||
|
||||
// Corvax-Next-AiRemoteControl-Start
|
||||
if (TryComp(ent, out AiRemoteControllerComponent? aiRemoteComp))
|
||||
{
|
||||
if (TryComp(aiRemoteComp.AiHolder, out IntrinsicRadioTransmitterComponent? stationAiTransmitter) && transmitter != null)
|
||||
{
|
||||
aiRemoteComp.PreviouslyTransmitterChannels = [.. radioChannels];
|
||||
transmitter.Channels = [.. stationAiTransmitter.Channels];
|
||||
}
|
||||
|
||||
if (TryComp(aiRemoteComp.AiHolder, out ActiveRadioComponent? stationAiActiveRadio) && activeRadio != null)
|
||||
{
|
||||
aiRemoteComp.PreviouslyActiveRadioChannels = [.. radioChannels];
|
||||
activeRadio.Channels = [.. stationAiActiveRadio.Channels];
|
||||
}
|
||||
}
|
||||
// Corvax-Next-AiRemoteControl-End
|
||||
|
||||
// Borg transponder for the robotics console
|
||||
if (TryComp(ent, out BorgTransponderComponent? transponder))
|
||||
{
|
||||
|
||||
@@ -6,7 +6,9 @@ using Content.Shared.Silicons.Borgs.Components;
|
||||
using Content.Shared.DeviceNetwork.Components;
|
||||
using Content.Shared.DeviceNetwork.Events;
|
||||
using Content.Shared.Emag.Systems;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
using Content.Shared._CorvaxNext.Silicons.Borgs.Components; // Corvax-Next-AiRemoteControl
|
||||
|
||||
namespace Content.Server.Silicons.Borgs;
|
||||
|
||||
@@ -47,7 +49,8 @@ public sealed partial class BorgSystem
|
||||
charge,
|
||||
chassis.ModuleCount,
|
||||
hasBrain,
|
||||
canDisable);
|
||||
canDisable,
|
||||
HasComp<AiRemoteControllerComponent>(uid)); // Corvax-Next-AiRemoteControl
|
||||
|
||||
var payload = new NetworkPayload()
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@ using Content.Server.DeviceNetwork.Systems;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Server.Hands.Systems;
|
||||
using Content.Server.PowerCell;
|
||||
using Content.Shared._CorvaxNext.Silicons.Borgs.Components;
|
||||
using Content.Shared.UserInterface;
|
||||
using Content.Shared.Access;
|
||||
using Content.Shared.Access.Components;
|
||||
@@ -25,6 +26,8 @@ using Content.Shared.PowerCell.Components;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Silicons.Borgs;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Content.Shared.Silicons.StationAi;
|
||||
using Content.Shared.StationAi;
|
||||
using Content.Shared.Throwing;
|
||||
using Content.Shared.Whitelist;
|
||||
using Content.Shared.Wires;
|
||||
@@ -144,6 +147,21 @@ public sealed partial class BorgSystem : SharedBorgSystem
|
||||
args.Handled = true;
|
||||
UpdateUI(uid, component);
|
||||
}
|
||||
|
||||
// Corvax-Next-AiRemoteControl-Start
|
||||
if (component.BrainEntity == null && HasComp<AiRemoteBrainComponent>(used) &&
|
||||
_whitelistSystem.IsWhitelistPassOrNull(component.BrainWhitelist, used))
|
||||
{
|
||||
EnsureComp<AiRemoteControllerComponent>(uid);
|
||||
_container.Insert(used, component.BrainContainer);
|
||||
_adminLog.Add(LogType.Action, LogImpact.Medium,
|
||||
$"{ToPrettyString(args.User):player} installed ai remote brain {ToPrettyString(used)} into borg {ToPrettyString(uid)}");
|
||||
args.Handled = true;
|
||||
BorgActivate(uid, component);
|
||||
|
||||
UpdateUI(uid, component);
|
||||
}
|
||||
// Corvax-Next-AiRemoteControl-End
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -178,6 +196,15 @@ public sealed partial class BorgSystem : SharedBorgSystem
|
||||
{
|
||||
_mind.TransferTo(mindId, args.Entity, mind: mind);
|
||||
}
|
||||
|
||||
// Corvax-Next-AiRemoteControl-Start
|
||||
if (HasComp<AiRemoteBrainComponent>(args.Entity))
|
||||
{
|
||||
BorgDeactivate(uid, component);
|
||||
RemComp<AiRemoteControllerComponent>(uid);
|
||||
RemComp<StationAiVisionComponent>(uid);
|
||||
}
|
||||
// Corvax-Next-AiRemoteControl-End
|
||||
}
|
||||
|
||||
private void OnMindAdded(EntityUid uid, BorgChassisComponent component, MindAddedMessage args)
|
||||
|
||||
@@ -4,6 +4,7 @@ using Content.Server.Chat.Managers;
|
||||
using Content.Server.Radio.Components;
|
||||
using Content.Server.Roles;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Shared._CorvaxNext.Silicons.Borgs.Components; // Corvax-Next-AiRemoteControl
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Emag.Systems;
|
||||
@@ -13,6 +14,9 @@ using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Silicons.Laws;
|
||||
using Content.Shared.Silicons.Laws.Components;
|
||||
using Content.Shared.Silicons.StationAi; // Corvax-Next-AiRemoteControl
|
||||
using Content.Shared.Tag; // Corvax-Next-AiRemoteControl
|
||||
using Content.Shared.Wires;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
@@ -33,6 +37,7 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
|
||||
[Dependency] private readonly StationSystem _station = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
|
||||
[Dependency] private readonly EmagSystem _emag = default!;
|
||||
[Dependency] private readonly TagSystem _tagSystem = default!; // Corvax-Next-AiRemoteControl
|
||||
[Dependency] private readonly IMapManager _map = default!; // Mono - Law update is grid-only now.
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -63,6 +68,12 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
|
||||
if (!TryComp<ActorComponent>(uid, out var actor))
|
||||
return;
|
||||
|
||||
// Corvax-Next-AiRemoteControl-Start
|
||||
if (HasComp<AiRemoteControllerComponent>(uid)
|
||||
|| _tagSystem.HasTag(uid, "StationAi")) // skip a law's notification for remotable and AI
|
||||
return;
|
||||
// Corvax-Next-AiRemoteControl-End
|
||||
|
||||
var msg = Loc.GetString("laws-notify");
|
||||
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", msg));
|
||||
_chatManager.ChatMessageToOne(ChatChannel.Server, msg, wrappedMessage, default, false, actor.PlayerSession.Channel, colorOverride: Color.FromHex("#5ed7aa"));
|
||||
@@ -155,6 +166,11 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
|
||||
if (component.Lawset == null)
|
||||
component.Lawset = GetLawset(component.Laws);
|
||||
|
||||
// Corvax-Next-AiRemoteControl-Start
|
||||
if (HasComp<AiRemoteControllerComponent>(uid)) // You can't emag controllable entities
|
||||
return;
|
||||
// Corvax-Next-AiRemoteControl-End
|
||||
|
||||
// Show the silicon has been subverted.
|
||||
component.Subverted = true;
|
||||
|
||||
@@ -281,7 +297,7 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
|
||||
/// <summary>
|
||||
/// Set the laws of a silicon entity while notifying the player.
|
||||
/// </summary>
|
||||
public void SetLaws(List<SiliconLaw> newLaws, EntityUid target, SoundSpecifier? cue = null)
|
||||
public void SetLaws(List<SiliconLaw> newLaws, EntityUid target, SoundSpecifier? cue = null, bool silent = false) // AiRemoteControl - silent
|
||||
{
|
||||
if (!TryComp<SiliconLawProviderComponent>(target, out var component))
|
||||
return;
|
||||
@@ -290,7 +306,8 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
|
||||
component.Lawset = new SiliconLawset();
|
||||
|
||||
component.Lawset.Laws = newLaws;
|
||||
NotifyLawsChanged(target, cue);
|
||||
if (!silent) // AiRemoteControl - silent
|
||||
NotifyLawsChanged(target, cue);
|
||||
}
|
||||
|
||||
protected override void OnUpdaterInsert(Entity<SiliconLawUpdaterComponent> ent, ref EntInsertedIntoContainerMessage args)
|
||||
@@ -312,8 +329,16 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
|
||||
continue;
|
||||
|
||||
SetLaws(lawset, update, provider.LawUploadSound);
|
||||
// Mono edit end
|
||||
// Corvax-Next-AiRemoteControl-Start
|
||||
if (TryComp<StationAiHeldComponent>(update, out var stationAiHeldComp)
|
||||
&& stationAiHeldComp.CurrentConnectedEntity != null
|
||||
&& HasComp<SiliconLawProviderComponent>(stationAiHeldComp.CurrentConnectedEntity))
|
||||
{
|
||||
SetLaws(lawset, stationAiHeldComp.CurrentConnectedEntity.Value, provider.LawUploadSound);
|
||||
}
|
||||
// Corvax-Next-AiRemoteControl-End
|
||||
}
|
||||
// Mono edit end
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,6 +69,12 @@ public sealed partial class StationEventComponent : Component
|
||||
[DataField]
|
||||
public ProtoId<RadioChannelPrototype> EndRadioAnnouncementChannel = "Supply"; // Frontier
|
||||
|
||||
/// <summary>
|
||||
/// Mono - sender of start/warn/end announcements.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public LocId? AnnounceSender = null;
|
||||
|
||||
/// <summary>
|
||||
/// In minutes, when is the first round time this event can start
|
||||
/// </summary>
|
||||
|
||||
@@ -50,7 +50,7 @@ public abstract class StationEventSystem<T> : GameRuleSystem<T> where T : ICompo
|
||||
Filter allPlayersInGame = Filter.Empty().AddWhere(GameTicker.UserHasJoinedGame);
|
||||
|
||||
if (stationEvent.StartAnnouncement != null)
|
||||
ChatSystem.DispatchFilteredAnnouncement(allPlayersInGame, Loc.GetString(stationEvent.StartAnnouncement), playSound: false, colorOverride: stationEvent.StartAnnouncementColor);
|
||||
ChatSystem.DispatchFilteredAnnouncement(allPlayersInGame, Loc.GetString(stationEvent.StartAnnouncement), sender: stationEvent.AnnounceSender is { } send ? Loc.GetString(send) : null, playSound: false, colorOverride: stationEvent.StartAnnouncementColor);
|
||||
|
||||
// Frontier
|
||||
if (stationEvent.StartRadioAnnouncement != null)
|
||||
@@ -97,7 +97,7 @@ public abstract class StationEventSystem<T> : GameRuleSystem<T> where T : ICompo
|
||||
Filter allPlayersInGame = Filter.Empty().AddWhere(GameTicker.UserHasJoinedGame);
|
||||
|
||||
if (stationEvent.EndAnnouncement != null)
|
||||
ChatSystem.DispatchFilteredAnnouncement(allPlayersInGame, Loc.GetString(stationEvent.EndAnnouncement), playSound: false, colorOverride: stationEvent.EndAnnouncementColor);
|
||||
ChatSystem.DispatchFilteredAnnouncement(allPlayersInGame, Loc.GetString(stationEvent.EndAnnouncement), sender: stationEvent.AnnounceSender is { } send ? Loc.GetString(send) : null, playSound: false, colorOverride: stationEvent.EndAnnouncementColor);
|
||||
|
||||
// Frontier: radio announcements
|
||||
if (stationEvent.EndRadioAnnouncement != null)
|
||||
@@ -139,7 +139,7 @@ public abstract class StationEventSystem<T> : GameRuleSystem<T> where T : ICompo
|
||||
{
|
||||
Filter allPlayersInGame = Filter.Empty().AddWhere(GameTicker.UserHasJoinedGame); // we don't want to send to players who aren't in game (i.e. in the lobby)
|
||||
if (stationEvent.WarningAnnouncement != null)
|
||||
ChatSystem.DispatchFilteredAnnouncement(allPlayersInGame, Loc.GetString(stationEvent.WarningAnnouncement), playSound: false, colorOverride: stationEvent.WarningAnnouncementColor);
|
||||
ChatSystem.DispatchFilteredAnnouncement(allPlayersInGame, Loc.GetString(stationEvent.WarningAnnouncement), sender: stationEvent.AnnounceSender is { } send ? Loc.GetString(send) : null, playSound: false, colorOverride: stationEvent.WarningAnnouncementColor);
|
||||
if (stationEvent.WarningRadioAnnouncement != null)
|
||||
{
|
||||
var message = Loc.GetString(stationEvent.WarningRadioAnnouncement);
|
||||
|
||||
@@ -58,7 +58,6 @@ namespace Content.Server.VendingMachines
|
||||
[Dependency] private readonly BankSystem _bankSystem = default!; // Frontier
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!; // Frontier
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!; // Frontier
|
||||
[Dependency] private readonly ContrabandTurnInSystem _contraband = default!; // Frontier
|
||||
[Dependency] private readonly StackSystem _stack = default!; // Frontier
|
||||
[Dependency] private readonly VendingMachinePurchaseSystem _vendingPurchase = default!; // Mono
|
||||
|
||||
@@ -347,11 +346,11 @@ namespace Content.Server.VendingMachines
|
||||
if (TryComp<MarketModifierComponent>(component.Owner, out var modifier))
|
||||
price *= modifier.Mod;
|
||||
|
||||
var totalPrice = (int) price;
|
||||
var totalPrice = component.RequiresCash ? (int) price : 0;
|
||||
|
||||
// If any price has a vendor price, explicitly use its value - higher OR lower, over others.
|
||||
var priceVend = _pricing.GetEstimatedVendPrice(proto);
|
||||
if (priceVend > 0.0) // if vending price exists, overwrite it.
|
||||
if (priceVend > 0.0 && component.RequiresCash) // if vending price exists, overwrite it.
|
||||
totalPrice = (int) priceVend;
|
||||
|
||||
if (IsAuthorized(uid, sender, component))
|
||||
@@ -545,8 +544,6 @@ namespace Content.Server.VendingMachines
|
||||
|
||||
var ent = Spawn(vendComponent.NextItemToEject, spawnCoordinates);
|
||||
|
||||
_contraband.ClearContrabandValue(ent); // Frontier
|
||||
|
||||
// Mono: Track vending machine purchases for pricing modifications
|
||||
// Only track if this was a paid purchase (not a random eject or force eject)
|
||||
if (!forceEject && vendComponent.LastPurchasePrice.HasValue)
|
||||
|
||||
@@ -233,7 +233,9 @@ namespace Content.Server.Voting.Managers
|
||||
|
||||
foreach (var (k, v) in presets)
|
||||
{
|
||||
options.Options.Add((Loc.GetString(v), k));
|
||||
options.Options.Add((Loc.GetString(v.Name), k));
|
||||
if (v.Weight != 1f)
|
||||
options.Weights.Add(k, v.Weight);
|
||||
}
|
||||
|
||||
WirePresetVoteInitiator(options, initiator);
|
||||
@@ -247,13 +249,13 @@ namespace Content.Server.Voting.Managers
|
||||
{
|
||||
picked = (string) _random.Pick(args.Winners);
|
||||
_chatManager.DispatchServerAnnouncement(
|
||||
Loc.GetString("ui-vote-gamemode-tie", ("picked", Loc.GetString(presets[picked]))));
|
||||
Loc.GetString("ui-vote-gamemode-tie", ("picked", Loc.GetString(presets[picked].Name))));
|
||||
}
|
||||
else
|
||||
{
|
||||
picked = (string) args.Winner;
|
||||
_chatManager.DispatchServerAnnouncement(
|
||||
Loc.GetString("ui-vote-gamemode-win", ("winner", Loc.GetString(presets[picked]))));
|
||||
Loc.GetString("ui-vote-gamemode-win", ("winner", Loc.GetString(presets[picked].Name))));
|
||||
}
|
||||
_adminLogger.Add(LogType.Vote, LogImpact.Medium, $"Preset vote finished: {picked}");
|
||||
var ticker = _entityManager.EntitySysManager.GetEntitySystem<GameTicker>();
|
||||
@@ -585,9 +587,9 @@ namespace Content.Server.Voting.Managers
|
||||
DirtyCanCallVoteAll();
|
||||
}
|
||||
|
||||
private Dictionary<string, string> GetGamePresets()
|
||||
private Dictionary<string, (string Name, float Weight)> GetGamePresets() // Mono - add weight
|
||||
{
|
||||
var presets = new Dictionary<string, string>();
|
||||
var presets = new Dictionary<string, (string, float)>();
|
||||
|
||||
foreach (var preset in _prototypeManager.EnumeratePrototypes<GamePresetPrototype>())
|
||||
{
|
||||
@@ -600,7 +602,7 @@ namespace Content.Server.Voting.Managers
|
||||
if(_playerManager.PlayerCount > (preset.MaxPlayers ?? int.MaxValue))
|
||||
continue;
|
||||
|
||||
presets[preset.ID] = preset.ModeTitle;
|
||||
presets[preset.ID] = (preset.ModeTitle, preset.Weight);
|
||||
}
|
||||
return presets;
|
||||
}
|
||||
|
||||
@@ -206,7 +206,8 @@ namespace Content.Server.Voting.Managers
|
||||
{
|
||||
var id = _nextVoteId++;
|
||||
|
||||
var entries = options.Options.Select(o => new VoteEntry(o.data, o.text)).ToArray();
|
||||
// Mono - added weight
|
||||
var entries = options.Options.Select(o => new VoteEntry(o.data, o.text, options.Weights.TryGetValue(o.data, out var weight) ? weight : 1f)).ToArray();
|
||||
|
||||
var start = _timing.RealTime;
|
||||
var end = start + options.Duration;
|
||||
@@ -286,11 +287,12 @@ namespace Content.Server.Voting.Managers
|
||||
msg.DisplayVotes = true;
|
||||
}
|
||||
|
||||
msg.Options = new (ushort votes, string name)[v.Entries.Length];
|
||||
msg.Options = new (ushort votes, string name, ushort realVotes)[v.Entries.Length];
|
||||
for (var i = 0; i < msg.Options.Length; i++)
|
||||
{
|
||||
ref var entry = ref v.Entries[i];
|
||||
msg.Options[i] = (msg.DisplayVotes ? (ushort) entry.Votes : (ushort) 0, entry.Text);
|
||||
var votes = msg.DisplayVotes ? (ushort) entry.Votes : (ushort) 0; // Mono
|
||||
msg.Options[i] = (votes, entry.Text, (ushort) (votes * entry.Weight)); // Mono - account for weight
|
||||
}
|
||||
|
||||
player.Channel.SendMessage(msg);
|
||||
@@ -394,7 +396,7 @@ namespace Content.Server.Voting.Managers
|
||||
|
||||
// Find winner or stalemate.
|
||||
var winners = v.Entries
|
||||
.GroupBy(e => e.Votes)
|
||||
.GroupBy(e => e.Votes * e.Weight) // Mono - add weight
|
||||
.OrderByDescending(g => g.Key)
|
||||
.First()
|
||||
.Select(e => e.Data)
|
||||
@@ -536,12 +538,15 @@ namespace Content.Server.Voting.Managers
|
||||
public object Data;
|
||||
public string Text;
|
||||
public int Votes;
|
||||
public float Weight; // Mono
|
||||
|
||||
public VoteEntry(object data, string text)
|
||||
public VoteEntry(object data, string text,
|
||||
float weight = 1f) // Mono
|
||||
{
|
||||
Data = data;
|
||||
Text = text;
|
||||
Votes = 0;
|
||||
Weight = weight; // Mono
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,9 @@ namespace Content.Server.Voting
|
||||
/// </summary>
|
||||
public List<(string text, object data)> Options { get; set; } = new();
|
||||
|
||||
// Mono - this is evil but allows seamless integration with the code
|
||||
public Dictionary<object, float> Weights = new();
|
||||
|
||||
/// <summary>
|
||||
/// Which sessions may send a vote. Used when only a subset of players should be able to vote. Defaults to all.
|
||||
/// </summary>
|
||||
@@ -50,7 +53,7 @@ namespace Content.Server.Voting
|
||||
public bool DisplayVotes = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the vote should have an entity attached to it, to be used for things like letting ghosts follow it.
|
||||
/// Whether the vote should have an entity attached to it, to be used for things like letting ghosts follow it.
|
||||
/// </summary>
|
||||
public NetEntity? TargetEntity = null;
|
||||
|
||||
|
||||
@@ -165,7 +165,8 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
CreateAndFireProjectiles(ent.Value, newAmmo, offset);
|
||||
|
||||
break;
|
||||
case HitscanAmmoComponent:
|
||||
|
||||
case HitscanAmmoComponent hitscanammo:
|
||||
if (ent == null)
|
||||
break;
|
||||
|
||||
@@ -179,10 +180,13 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
};
|
||||
RaiseLocalEvent(ent.Value, ref hitscanEv);
|
||||
|
||||
Del(ent);
|
||||
|
||||
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
|
||||
// Mono start
|
||||
Spawn(hitscanammo.CasingPrototype, fromEnt);
|
||||
Del(ent);
|
||||
// Mono end
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
|
||||
// SPDX-FileCopyrightText: 2025 ImHoks <142083149+ImHoks@users.noreply.github.com>
|
||||
// SPDX-FileCopyrightText: 2025 ImHoks <imhokzzzz@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2025 KillanGenifer <killangenifer@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2025 gluesniffler <159397573+gluesniffler@users.noreply.github.com>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
using Content.Server.Radio.Components;
|
||||
using Content.Server.Silicons.Laws;
|
||||
using Content.Shared._CorvaxNext.Silicons.Borgs;
|
||||
using Content.Shared._CorvaxNext.Silicons.Borgs.Components;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Silicons.Laws.Components;
|
||||
using Content.Shared.Silicons.StationAi;
|
||||
using Content.Shared.StationAi;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Map; // Mono
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Player;
|
||||
using System.Threading.Tasks.Dataflow;
|
||||
|
||||
namespace Content.Server._CorvaxNext.Silicons.Borgs;
|
||||
|
||||
public sealed class AiRemoteControlSystem : SharedAiRemoteControlSystem
|
||||
{
|
||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||
[Dependency] private readonly SiliconLawSystem _lawSystem = default!;
|
||||
[Dependency] private readonly SharedStationAiSystem _stationAiSystem = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
|
||||
|
||||
[Dependency] private readonly IMapManager _map = default!; // Mono
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<AiRemoteControllerComponent, ReturnMindIntoAiEvent>(OnReturnMindIntoAi);
|
||||
SubscribeLocalEvent<AiRemoteControllerComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<AiRemoteControllerComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<AiRemoteControllerComponent, GetVerbsEvent<AlternativeVerb>>(OnGetVerbs);
|
||||
SubscribeLocalEvent<StationAiHeldComponent, AiRemoteControllerComponent.RemoteDeviceActionMessage>(OnUiRemoteAction);
|
||||
SubscribeLocalEvent<StationAiHeldComponent, ToggleRemoteDevicesScreenEvent>(OnToggleRemoteDevicesScreen);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<AiRemoteControllerComponent> entity, ref MapInitEvent args)
|
||||
{
|
||||
var visionComp = EnsureComp<StationAiVisionComponent>(entity.Owner);
|
||||
EntityUid? actionEnt = null;
|
||||
|
||||
_actions.AddAction(entity.Owner, ref actionEnt, entity.Comp.BackToAiAction);
|
||||
|
||||
if (actionEnt != null)
|
||||
entity.Comp.BackToAiActionEntity = actionEnt.Value;
|
||||
}
|
||||
|
||||
private void OnShutdown(Entity<AiRemoteControllerComponent> entity, ref ComponentShutdown args)
|
||||
{
|
||||
_actions.RemoveAction(entity.Owner, entity.Comp.BackToAiActionEntity);
|
||||
|
||||
var backArgs = new ReturnMindIntoAiEvent();
|
||||
backArgs.Performer = entity;
|
||||
|
||||
if (TryComp(entity, out IntrinsicRadioTransmitterComponent? transmitter)
|
||||
&& entity.Comp.PreviouslyTransmitterChannels != null)
|
||||
transmitter.Channels = [.. entity.Comp.PreviouslyTransmitterChannels];
|
||||
|
||||
if (TryComp(entity, out ActiveRadioComponent? activeRadio)
|
||||
&& entity.Comp.PreviouslyActiveRadioChannels != null)
|
||||
activeRadio.Channels = [.. entity.Comp.PreviouslyActiveRadioChannels];
|
||||
|
||||
ReturnMindIntoAi(entity);
|
||||
}
|
||||
|
||||
private void OnGetVerbs(Entity<AiRemoteControllerComponent> entity, ref GetVerbsEvent<AlternativeVerb> args)
|
||||
{
|
||||
var user = args.User;
|
||||
|
||||
if (!TryComp<StationAiHeldComponent>(user, out var stationAiHeldComp))
|
||||
return;
|
||||
|
||||
var verb = new AlternativeVerb
|
||||
{
|
||||
Text = Loc.GetString("ai-remote-control"),
|
||||
Act = () => AiTakeControl(user, entity)
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
private void OnReturnMindIntoAi(Entity<AiRemoteControllerComponent> entity, ref ReturnMindIntoAiEvent args) =>
|
||||
ReturnMindIntoAi(entity);
|
||||
|
||||
public void AiTakeControl(EntityUid ai, EntityUid entity)
|
||||
{
|
||||
if (!_mind.TryGetMind(ai, out var mindId, out var mind))
|
||||
return;
|
||||
|
||||
if (!TryComp<StationAiHeldComponent>(ai, out var stationAiHeldComp))
|
||||
return;
|
||||
|
||||
if (!TryComp<AiRemoteControllerComponent>(entity, out var aiRemoteComp))
|
||||
return;
|
||||
|
||||
if (!_map.TryFindGridAt(Transform(ai).MapPosition, out var grid, out var _) || Transform(entity).GridUid != grid)
|
||||
return; // Mono no controlling borgs outside the ai's grid.
|
||||
|
||||
if (TryComp(entity, out IntrinsicRadioTransmitterComponent? transmitter))
|
||||
{
|
||||
aiRemoteComp.PreviouslyTransmitterChannels = [.. transmitter.Channels];
|
||||
|
||||
if (TryComp(ai, out IntrinsicRadioTransmitterComponent? stationAiTransmitter))
|
||||
transmitter.Channels = [.. stationAiTransmitter.Channels];
|
||||
}
|
||||
|
||||
if (TryComp(entity, out ActiveRadioComponent? activeRadio))
|
||||
{
|
||||
aiRemoteComp.PreviouslyActiveRadioChannels = [.. activeRadio.Channels];
|
||||
|
||||
if (TryComp(ai, out ActiveRadioComponent? stationAiActiveRadio))
|
||||
activeRadio.Channels = [.. stationAiActiveRadio.Channels];
|
||||
}
|
||||
|
||||
_mind.ControlMob(ai, entity);
|
||||
aiRemoteComp.AiHolder = ai;
|
||||
aiRemoteComp.LinkedMind = mindId;
|
||||
|
||||
stationAiHeldComp.CurrentConnectedEntity = entity;
|
||||
|
||||
if (!_stationAiSystem.TryGetCore(ai, out var stationAiCore))
|
||||
return;
|
||||
|
||||
_stationAiSystem.SwitchRemoteEntityMode(stationAiCore, false);
|
||||
|
||||
RewriteLaws(ai, entity);
|
||||
}
|
||||
|
||||
private void OnToggleRemoteDevicesScreen(EntityUid uid, StationAiHeldComponent component, ToggleRemoteDevicesScreenEvent args)
|
||||
{
|
||||
if (args.Handled || !TryComp<ActorComponent>(uid, out var actor))
|
||||
return;
|
||||
args.Handled = true;
|
||||
|
||||
_userInterface.TryToggleUi(uid, RemoteDeviceUiKey.Key, actor.PlayerSession);
|
||||
|
||||
var aiGrid = Transform(uid).GridUid; // Mono
|
||||
var query = EntityManager.EntityQueryEnumerator<AiRemoteControllerComponent>();
|
||||
var remoteDevices = new List<RemoteDevicesData>();
|
||||
|
||||
while (query.MoveNext(out var queryUid, out var comp))
|
||||
{
|
||||
if (Transform(queryUid).GridUid != aiGrid)
|
||||
continue;
|
||||
var data = new RemoteDevicesData
|
||||
{
|
||||
NetEntityUid = GetNetEntity(queryUid),
|
||||
DisplayName = Comp<MetaDataComponent>(queryUid).EntityName
|
||||
};
|
||||
|
||||
remoteDevices.Add(data);
|
||||
}
|
||||
|
||||
var state = new RemoteDevicesBuiState(remoteDevices);
|
||||
_userInterface.SetUiState(uid, RemoteDeviceUiKey.Key, state);
|
||||
}
|
||||
|
||||
private void OnUiRemoteAction(EntityUid uid, StationAiHeldComponent component, AiRemoteControllerComponent.RemoteDeviceActionMessage msg)
|
||||
{
|
||||
if (msg.RemoteAction == null)
|
||||
return;
|
||||
|
||||
var target = GetEntity(msg.RemoteAction?.Target);
|
||||
|
||||
if (!HasComp<AiRemoteControllerComponent>(target))
|
||||
return;
|
||||
|
||||
switch (msg.RemoteAction?.ActionType)
|
||||
{
|
||||
case RemoteDeviceActionEvent.RemoteDeviceActionType.MoveToDevice:
|
||||
if (!_stationAiSystem.TryGetCore(uid, out var stationAiCore)
|
||||
|| stationAiCore.Comp?.RemoteEntity == null)
|
||||
return;
|
||||
_xformSystem.SetCoordinates(stationAiCore.Comp.RemoteEntity.Value, Transform(target.Value).Coordinates);
|
||||
break;
|
||||
|
||||
case RemoteDeviceActionEvent.RemoteDeviceActionType.TakeControl:
|
||||
AiTakeControl(uid, target.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void RewriteLaws(EntityUid from, EntityUid to)
|
||||
{
|
||||
if (!TryComp<SiliconLawProviderComponent>(from, out var fromLawsComp))
|
||||
return;
|
||||
|
||||
if (!TryComp<SiliconLawProviderComponent>(to, out var toLawsComp))
|
||||
return;
|
||||
|
||||
if (fromLawsComp.Lawset == null)
|
||||
return;
|
||||
|
||||
var fromLaws = _lawSystem.GetLaws(from);
|
||||
_lawSystem.SetLaws(fromLaws.Laws, to, silent: true);
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ public sealed class JobWhitelistsCommand : LocalizedCommands
|
||||
{
|
||||
shell.WriteLine(Loc.GetString("cmd-ban-invalid-arguments"));
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
var located = await _locator.LookupIdByNameOrIdAsync(args[0]);
|
||||
|
||||
+12
@@ -1,3 +1,7 @@
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._EinsteinEngines.Silicon.Death;
|
||||
|
||||
/// <summary>
|
||||
@@ -14,4 +18,12 @@ public sealed partial class SiliconDownOnDeadComponent : Component
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public bool Dead;
|
||||
|
||||
/// <summary>
|
||||
/// Mono - applies modifier when silicon is out of charge.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public ProtoId<DamageModifierSetPrototype>? ModifierOnDead = "IPCWeakened";
|
||||
|
||||
public ProtoId<DamageModifierSetPrototype>? OriginalModifier;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Content.Shared.Bed.Sleep;
|
||||
using Content.Server._EinsteinEngines.Silicon.Charge;
|
||||
using Content.Server._EinsteinEngines.Power.Components;
|
||||
using Content.Server.Humanoid;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems; //Monolith IPC rework
|
||||
@@ -15,7 +16,7 @@ public sealed class SiliconDeathSystem : EntitySystem
|
||||
[Dependency] private readonly SleepingSystem _sleep = default!;
|
||||
[Dependency] private readonly SiliconChargeSystem _silicon = default!;
|
||||
[Dependency] private readonly HumanoidAppearanceSystem _humanoidAppearanceSystem = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!; //Monolith IPC rework
|
||||
[Dependency] private readonly DamageableSystem _damage = default!; // mono
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -46,16 +47,15 @@ public sealed class SiliconDeathSystem : EntitySystem
|
||||
var deadEvent = new SiliconChargeDyingEvent(uid, batteryComp, batteryUid);
|
||||
RaiseLocalEvent(uid, deadEvent);
|
||||
|
||||
if (deadEvent.Cancelled)
|
||||
if (deadEvent.Cancelled
|
||||
|| siliconDeadComp.ModifierOnDead == null
|
||||
|| !TryComp<DamageableComponent>(uid, out var damageComp))
|
||||
return;
|
||||
|
||||
/*EntityManager.EnsureComponent<SleepingComponent>(uid); Monolith IPC rework edit start
|
||||
EntityManager.EnsureComponent<ForcedSleepingComponent>(uid);*/
|
||||
|
||||
if(!TryComp<HandsComponent>(uid, out var handsComp))
|
||||
return;
|
||||
_hands.RemoveHands(uid, handsComp); // edit end
|
||||
|
||||
siliconDeadComp.OriginalModifier = damageComp.DamageModifierSetId;
|
||||
_damage.SetDamageModifierSetId(uid, siliconDeadComp.ModifierOnDead.Value);
|
||||
|
||||
if (TryComp(uid, out HumanoidAppearanceComponent? humanoidAppearanceComponent))
|
||||
{
|
||||
@@ -73,8 +73,8 @@ public sealed class SiliconDeathSystem : EntitySystem
|
||||
/*RemComp<ForcedSleepingComponent>(uid); Monolith IPC rework edit start
|
||||
_sleep.TryWaking(uid, true, null);*/
|
||||
|
||||
_hands.AddHand(uid, "right hand", HandLocation.Right);
|
||||
_hands.AddHand(uid, "left hand", HandLocation.Left); // edit end
|
||||
if (siliconDeadComp.OriginalModifier != null)
|
||||
_damage.SetDamageModifierSetId(uid, siliconDeadComp.OriginalModifier.Value);
|
||||
|
||||
siliconDeadComp.Dead = false;
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
using System.Linq;
|
||||
using Content.Server._Mono.Grid;
|
||||
using Content.Server.Administration;
|
||||
using Content.Shared._Mono.Grid;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server._Mono.Administration.Commands;
|
||||
|
||||
/// <summary>
|
||||
/// Command that allows you to apply grid modifiers to existing grids
|
||||
/// </summary>
|
||||
[AdminCommand(AdminFlags.Admin)]
|
||||
public sealed class AddGridModifier : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
|
||||
public string Command => "addgridmodifier";
|
||||
public string Description => "Applies grid modification to chosen grid.";
|
||||
public string Help => $"Usage: {Command} <gridUid> <modification...>";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
|
||||
if (args.Length < 2)
|
||||
shell.WriteLine($"Not enough arguments.\n{Help}");
|
||||
|
||||
if (!NetEntity.TryParse(args[0], out var uidNet) || !_entManager.TryGetEntity(uidNet, out var uid))
|
||||
{
|
||||
shell.WriteLine($"Invalid entity id.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_entManager.TryGetComponent(uid, out MapGridComponent? map))
|
||||
{
|
||||
shell.WriteLine($"Entity is not a grid.");
|
||||
return;
|
||||
}
|
||||
|
||||
List<ProtoId<GridModificationPrototype>> modifiers = [];
|
||||
var gridMod = _entManager.System<GridModifierSystem>();
|
||||
|
||||
foreach (var mod in args.Skip(1))
|
||||
{
|
||||
modifiers.Add(mod);
|
||||
}
|
||||
|
||||
gridMod.ModifyGrid(uid.Value, modifiers);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user