From e35d4becd276a0163a5759d1b18bd7dd74c35e9a Mon Sep 17 00:00:00 2001 From: Mona Hmiza Date: Fri, 8 Mar 2024 19:38:53 +0300 Subject: [PATCH 01/13] =?UTF-8?q?=D0=91=D0=B0=D0=B7=D0=B0,=20=D0=BE=D1=81?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D0=B0,=20=D1=84=D1=83=D0=BD=D0=B4=D0=B0?= =?UTF-8?q?=D0=BC=D0=B5=D0=BD=D1=82,=20=D0=B3=D0=B5=D0=BD=D1=88=D1=82?= =?UTF-8?q?=D0=B0=D0=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Strongest matches foundation infrastructure source Strong matches authority backbone basis core essence essential evidence fundamental groundwork heart key origin principal principle root underpinning Weak matches chief constituent important part primary element --- .../_Miracle/GameRules/Violence/ViolenceRuleComponent.cs | 7 +++++++ .../_Miracle/GameRules/Violence/ViolenceRuleSystem.cs | 8 ++++++++ Resources/Prototypes/_Miracle/GameRules/game_presets.yml | 9 +++++++++ Resources/Prototypes/_Miracle/GameRules/roundstart.yml | 6 ++++++ 4 files changed, 30 insertions(+) create mode 100644 Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs create mode 100644 Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs create mode 100644 Resources/Prototypes/_Miracle/GameRules/game_presets.yml create mode 100644 Resources/Prototypes/_Miracle/GameRules/roundstart.yml diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs new file mode 100644 index 00000000000..c5595bb5724 --- /dev/null +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs @@ -0,0 +1,7 @@ +namespace Content.Server._Miracle.GameRules; + +[RegisterComponent] +[Access(typeof(ViolenceRuleSystem))] +public sealed partial class ViolenceRuleComponent : Component +{ +} diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs new file mode 100644 index 00000000000..a9577b36cdd --- /dev/null +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs @@ -0,0 +1,8 @@ +using Content.Server.GameTicking.Rules; + +namespace Content.Server._Miracle.GameRules; + +public sealed class ViolenceRuleSystem : GameRuleSystem +{ + +} diff --git a/Resources/Prototypes/_Miracle/GameRules/game_presets.yml b/Resources/Prototypes/_Miracle/GameRules/game_presets.yml new file mode 100644 index 00000000000..04ec17e02ec --- /dev/null +++ b/Resources/Prototypes/_Miracle/GameRules/game_presets.yml @@ -0,0 +1,9 @@ +- type: gamePreset + id: Violence + alias: + - violence + name: Violence + description: Violence + showInVote: false + rules: + - Violence diff --git a/Resources/Prototypes/_Miracle/GameRules/roundstart.yml b/Resources/Prototypes/_Miracle/GameRules/roundstart.yml new file mode 100644 index 00000000000..2f4f0faec0e --- /dev/null +++ b/Resources/Prototypes/_Miracle/GameRules/roundstart.yml @@ -0,0 +1,6 @@ +- type: entity + id: Violence + parent: BaseGameRule + noSpawn: true + components: + - type: ViolenceRule From 9f9e25cc1c0dbef2676aca705d681aa571b36383 Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Sat, 30 Mar 2024 17:30:19 +0300 Subject: [PATCH 02/13] unclean gamemode --- Content.Server/Points/PointSystem.cs | 78 +++++++++ .../Components/SpawnPointComponent.cs | 7 + .../EntitySystems/SpawnPointSystem.cs | 6 +- .../Station/Systems/StationSpawningSystem.cs | 16 +- .../ViolenceParticipatorComponent.cs | 14 ++ .../Violence/ViolenceRuleComponent.cs | 62 ++++++- .../GameRules/Violence/ViolenceRuleSystem.cs | 151 ++++++++++++++++++ .../Points/PointManagerComponent.cs | 13 ++ Content.Shared/Points/SharedPointSystem.cs | 35 ++++ 9 files changed, 375 insertions(+), 7 deletions(-) create mode 100644 Content.Server/_Miracle/Components/ViolenceParticipatorComponent.cs diff --git a/Content.Server/Points/PointSystem.cs b/Content.Server/Points/PointSystem.cs index b5f94d097ed..7a7b7e541a0 100644 --- a/Content.Server/Points/PointSystem.cs +++ b/Content.Server/Points/PointSystem.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Server._Miracle.GameRules; using Content.Shared.FixedPoint; using Content.Shared.Points; using JetBrains.Annotations; @@ -86,4 +87,81 @@ public override FormattedMessage GetScoreboard(EntityUid uid, PointManagerCompon return msg; } + + // WD EDIT START. Violence gamemode team points + + /// + /// Adds the specified point value to a player. + /// + [PublicAPI] + public void AdjustTeamPointValue(ushort team, FixedPoint2 value, EntityUid uid, PointManagerComponent? component) + { + if (!Resolve(uid, ref component)) + return; + + if (!component.TeamPoints.TryGetValue(team, out var current)) + current = 0; + + SetTeamPointValue(team, current + value, uid, component); + } + + /// + /// Sets the amount of points for a player + /// + [PublicAPI] + public void SetTeamPointValue(ushort team, FixedPoint2 value, EntityUid uid, PointManagerComponent? component) + { + if (!Resolve(uid, ref component)) + return; + + if (component.TeamPoints.TryGetValue(team, out var current) && current == value) + return; + + component.TeamPoints[team] = value; + component.TeamScoreboard = GetTeamScoreboard(uid, component); + Dirty(uid, component); + + var ev = new TeamPointChangedEvent(team, value); + RaiseLocalEvent(uid, ref ev, true); + } + + /// + /// Gets the amount of points for a given player + /// + [PublicAPI] + public FixedPoint2 GetTeamPointValue(ushort team, EntityUid uid, PointManagerComponent? component) + { + if (!Resolve(uid, ref component)) + return FixedPoint2.Zero; + + return component.TeamPoints.TryGetValue(team, out var value) + ? value + : FixedPoint2.Zero; + } + + /// + public override FormattedMessage GetTeamScoreboard(EntityUid uid, PointManagerComponent? component = null) + { + var msg = new FormattedMessage(); + + if (!Resolve(uid, ref component)) + return msg; + + var orderedPlayers = component.Points.OrderByDescending(p => p.Value).ToList(); + var place = 1; + foreach (var (id, points) in orderedPlayers) + { + if (!_player.TryGetPlayerData(id, out var data)) + continue; + + msg.AddMarkup(Loc.GetString("point-scoreboard-list", + ("place", place), + ("name", data.UserName), + ("points", points.Int()))); + msg.PushNewline(); + place++; + } + + return msg; + } } diff --git a/Content.Server/Spawners/Components/SpawnPointComponent.cs b/Content.Server/Spawners/Components/SpawnPointComponent.cs index 5a86175a1d2..afa3ee2b224 100644 --- a/Content.Server/Spawners/Components/SpawnPointComponent.cs +++ b/Content.Server/Spawners/Components/SpawnPointComponent.cs @@ -16,6 +16,13 @@ public sealed partial class SpawnPointComponent : Component [DataField("spawn_type")] public SpawnPointType SpawnType { get; private set; } = SpawnPointType.Unset; + /// + /// WD EDIT. This is needed for Violence gamemode for team spawners. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("teamId")] + public ushort? TeamId = null; + public JobPrototype? Job => string.IsNullOrEmpty(_jobId) ? null : _prototypeManager.Index(_jobId); public override string ToString() diff --git a/Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs b/Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs index b98e04844b9..221ff56cea7 100644 --- a/Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs +++ b/Content.Server/Spawners/EntitySystems/SpawnPointSystem.cs @@ -34,14 +34,16 @@ private void OnPlayerSpawning(PlayerSpawningEvent args) if (_gameTicker.RunLevel == GameRunLevel.InRound && spawnPoint.SpawnType == SpawnPointType.LateJoin) { - possiblePositions.Add(xform.Coordinates); + if (args.Team == spawnPoint.TeamId) // WD EDIT. Violence gamemode spawns needed here + possiblePositions.Add(xform.Coordinates); } if (_gameTicker.RunLevel != GameRunLevel.InRound && spawnPoint.SpawnType == SpawnPointType.Job && (args.Job == null || spawnPoint.Job?.ID == args.Job.Prototype)) { - possiblePositions.Add(xform.Coordinates); + if (args.Team == spawnPoint.TeamId) // WD EDIT. + possiblePositions.Add(xform.Coordinates); } } diff --git a/Content.Server/Station/Systems/StationSpawningSystem.cs b/Content.Server/Station/Systems/StationSpawningSystem.cs index 557d0d63b68..71fbe350c27 100644 --- a/Content.Server/Station/Systems/StationSpawningSystem.cs +++ b/Content.Server/Station/Systems/StationSpawningSystem.cs @@ -67,23 +67,24 @@ public override void Initialize() } /// - /// Attempts to spawn a player character onto the given station. + /// Attempts to spawn a player character onto the given station. WD EDIT /// /// Station to spawn onto. /// The job to assign, if any. /// The character profile to use, if any. /// Resolve pattern, the station spawning component for the station. + /// Player's team for Violence GameRule. WD EDIT; /// The resulting player character, if any. /// Thrown when the given station is not a station. /// /// This only spawns the character, and does none of the mind-related setup you'd need for it to be playable. /// - public EntityUid? SpawnPlayerCharacterOnStation(EntityUid? station, JobComponent? job, HumanoidCharacterProfile? profile, StationSpawningComponent? stationSpawning = null) + public EntityUid? SpawnPlayerCharacterOnStation(EntityUid? station, JobComponent? job, HumanoidCharacterProfile? profile, StationSpawningComponent? stationSpawning = null, ushort? team = null) { if (station != null && !Resolve(station.Value, ref stationSpawning)) throw new ArgumentException("Tried to use a non-station entity as a station!", nameof(station)); - var ev = new PlayerSpawningEvent(job, profile, station); + var ev = new PlayerSpawningEvent(job, profile, station, team); if (station != null && profile != null) { @@ -326,10 +327,17 @@ public sealed class PlayerSpawningEvent : EntityEventArgs /// public readonly EntityUid? Station; - public PlayerSpawningEvent(JobComponent? job, HumanoidCharacterProfile? humanoidCharacterProfile, EntityUid? station) + /// + /// Player's team for Violence GameRule. WD EDIT + /// + public readonly ushort? Team; + + + public PlayerSpawningEvent(JobComponent? job, HumanoidCharacterProfile? humanoidCharacterProfile, EntityUid? station, ushort? team) { Job = job; HumanoidCharacterProfile = humanoidCharacterProfile; Station = station; + Team = team; //WD EDIT } } diff --git a/Content.Server/_Miracle/Components/ViolenceParticipatorComponent.cs b/Content.Server/_Miracle/Components/ViolenceParticipatorComponent.cs new file mode 100644 index 00000000000..3e5434812a8 --- /dev/null +++ b/Content.Server/_Miracle/Components/ViolenceParticipatorComponent.cs @@ -0,0 +1,14 @@ +namespace Content.Server._Miracle.Components; + +/// +/// This is used for... +/// +[RegisterComponent] +public sealed partial class ViolenceParticipatorComponent : Component +{ + /// + /// List of factions in this gamemode. + /// + [DataField] + public EntityUid? MatchUid { get; private set; } = null; +} diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs index c5595bb5724..76e5505a021 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs @@ -1,7 +1,67 @@ -namespace Content.Server._Miracle.GameRules; +using Content.Shared.FixedPoint; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; + +namespace Content.Server._Miracle.GameRules; [RegisterComponent] [Access(typeof(ViolenceRuleSystem))] public sealed partial class ViolenceRuleComponent : Component { + /// + /// Min players needed for Violence gamerule to start. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int MinPlayers = 2; + + /// + /// How long until the round restarts + /// + [DataField("restartDelay"), ViewVariables(VVAccess.ReadWrite)] + public TimeSpan RestartDelay = TimeSpan.FromSeconds(10f); + + /// + /// Time until automatic match end. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan? MatchDuration = null; + + /// + /// Time until automatic match end. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan? MatchStartTime = null; + + /// + /// List of teams in this gamemode. + /// + [DataField("teams")] + public IReadOnlyList Teams { get; private set; } = Array.Empty(); + + /// + /// List of scores in this gamemode. + /// + [DataField("scores")] + public Dictionary Scores { get; private set; } = new Dictionary(); + + /// + /// Stored, for some reason. + /// + [DataField("victor")] + public ushort? Victor; + + /// + /// The number of points a player has to get to win. + /// + [DataField("pointCap"), ViewVariables(VVAccess.ReadWrite)] + public FixedPoint2 PointCap = 100; + + /// + /// Dictionary of a players and their teams + /// + [DataField("teamMembers")] + public Dictionary TeamMembers { get; private set; } = new Dictionary(); } diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs index a9577b36cdd..ebfb251d4fe 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs @@ -1,8 +1,159 @@ using Content.Server.GameTicking.Rules; +using System.Linq; +using Content.Server._Miracle.Components; +using Content.Server.Antag; +using Content.Server.GameTicking; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.RoundEnd; +using Content.Shared.Mobs.Systems; +using Robust.Shared.Map; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; +using Content.Server.Station.Systems; +using Robust.Server.Player; +using Robust.Shared.Utility; +using Content.Server.KillTracking; +using Content.Server.Mind; +using Content.Server.Points; +using Content.Shared.Points; +using Robust.Shared.Random; namespace Content.Server._Miracle.GameRules; +// TODO: отредачить pointsystem и sharedpointsystem чтобы оно считало очки команд - сделано? +// TODO: дать экшен или предмет на выход из матча? - удалять человека из RespawnTrackerComponent.Players +// TODO: инициализация команд (просто добавить в прототип геймрула), функция для смены команды? вставить куда-нибудь EnsureTeam из PointSystem? +// TODO: прототипы геймрула, аплинка и startingGear, gameMapPool, сама карта +// TODO: мб дать в startingGear аплинк, который будет не просто давать предметы, но и сразу их надевать на космонавтика +// TODO: нормальный RoundEndTextAppend и TeamScoreboard + public sealed class ViolenceRuleSystem : GameRuleSystem { + [Dependency] private readonly RoundEndSystem _roundEnd = default!; + [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly PointSystem _point = default!; + [Dependency] private readonly RespawnRuleSystem _respawn = default!; + [Dependency] private readonly StationSpawningSystem _stationSpawning = default!; + [Dependency] private readonly IRobustRandom _robustRandom = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnBeforeSpawn); + //SubscribeLocalEvent(OnPlayerSpawning); + SubscribeLocalEvent(OnSpawnComplete); + SubscribeLocalEvent(OnKillReported); + SubscribeLocalEvent(OnPointChanged); + SubscribeLocalEvent(OnRoundEndTextAppend); + } + + private void OnBeforeSpawn(PlayerBeforeSpawnEvent ev) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var ruleComponent, out var tracker, out var point, out var rule)) + { + if (!GameTicker.IsGameRuleActive(uid, rule)) + continue; + + var newMind = _mind.CreateMind(ev.Player.UserId, ev.Profile.Name); + _mind.SetUserId(newMind, ev.Player.UserId); + + // Assign player to a team, if he has none + if (!ruleComponent.TeamMembers.TryGetValue(ev.Player, out var team)) + ruleComponent.TeamMembers.Add(ev.Player, ruleComponent.Teams[_robustRandom.Next(0, ruleComponent.Teams.Count())]); + + var mobMaybe = _stationSpawning.SpawnPlayerCharacterOnStation(ev.Station, null, ev.Profile, null, team); // make it spawn on specified team spawnpoints + DebugTools.AssertNotNull(mobMaybe); + var mob = mobMaybe!.Value; + + _mind.TransferTo(newMind, mob); + // Should get startingGear from the client here. Setting default startingGear if none or invalid is passed. + // Also different startingGear for different teams. A dictionary of teams and startingGear in ViolenceRuleComponent? + //SetOutfitCommand.SetOutfit(mob, ruleComponent.Gear, EntityManager); + EnsureComp(mob); + _respawn.AddToTracker(ev.Player.UserId, uid, tracker); + + _point.EnsurePlayer(ev.Player.UserId, uid, point); + + ev.Handled = true; + break; + } + } + + private void OnSpawnComplete(PlayerSpawnCompleteEvent ev) + { + EnsureComp(ev.Mob); + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out _, out var tracker, out var rule)) + { + if (!GameTicker.IsGameRuleActive(uid, rule)) + continue; + _respawn.AddToTracker(ev.Mob, uid, tracker); + } + } + + private void OnKillReported(ref KillReportedEvent ev) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var ruleComponent, out var point, out var rule)) + { + if (!GameTicker.IsGameRuleActive(uid, rule)) + continue; + + + // YOU SUICIDED OR GOT THROWN INTO LAVA! + // WHAT A GIANT FUCKING NERD! LAUGH NOW! + if (ev.Primary is not KillPlayerSource player) + { + _point.AdjustPointValue(ev.Entity, -1, uid, point); + // -1 point to the nerd's team? + continue; + } + + + // adjust team points here + _point.AdjustPointValue(player.PlayerId, 1, uid, point); + + + if (ev.Assist is KillPlayerSource assist && ruleComponent.Victor == null) + _point.AdjustPointValue(assist.PlayerId, 0.5, uid, point); + + // I dont know if we will have reward spawns or any direct rewards for players + //var spawns = EntitySpawnCollection.GetSpawns(ruleComponent.RewardSpawns).Cast().ToList(); + //EntityManager.SpawnEntities(Transform(ev.Entity).MapPosition, spawns); + } + } + + private void OnPointChanged(EntityUid uid, ViolenceRuleComponent component, ref PlayerPointChangedEvent args) + { + if (component.Victor != null) + return; + + if (args.Points < component.PointCap) + return; + + //component.Victor = args.Player; // should assign a team as victor probably? + _roundEnd.EndRound(component.RestartDelay); + } + + private void OnRoundEndTextAppend(RoundEndTextAppendEvent ev) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var ruleComponent, out var point, out var rule)) + { + if (!GameTicker.IsGameRuleAdded(uid, rule)) + continue; + /* + if (ruleComponent.Victor != null && _player.TryGetPlayerData(ruleComponent.Victor.Value, out var data)) // get the team data here + { + ev.AddLine(Loc.GetString("point-scoreboard-winner", ("player", data.UserName))); + ev.AddLine(""); + } + */ + ev.AddLine(Loc.GetString("point-scoreboard-header")); // edit this, probably + ev.AddLine(new FormattedMessage(point.Scoreboard).ToMarkup()); + } + } } diff --git a/Content.Shared/Points/PointManagerComponent.cs b/Content.Shared/Points/PointManagerComponent.cs index 6c5bf6b0fa3..e39b6b88ec2 100644 --- a/Content.Shared/Points/PointManagerComponent.cs +++ b/Content.Shared/Points/PointManagerComponent.cs @@ -18,9 +18,22 @@ public sealed partial class PointManagerComponent : Component [DataField, AutoNetworkedField] public Dictionary Points = new(); + /// + /// WHITE EDIT. A dictionary for team points, used for Violence gamemode. + /// + [DataField, AutoNetworkedField] + public Dictionary TeamPoints = new(); + + /// /// A text-only version of the scoreboard used by the client. /// [DataField, AutoNetworkedField] public FormattedMessage Scoreboard = new(); + + /// + /// A text-only version of the scoreboard used by the client. + /// + [DataField, AutoNetworkedField] + public FormattedMessage TeamScoreboard = new(); } diff --git a/Content.Shared/Points/SharedPointSystem.cs b/Content.Shared/Points/SharedPointSystem.cs index 978f4bca42e..36bedade155 100644 --- a/Content.Shared/Points/SharedPointSystem.cs +++ b/Content.Shared/Points/SharedPointSystem.cs @@ -75,6 +75,33 @@ public virtual FormattedMessage GetScoreboard(EntityUid uid, PointManagerCompone { return new FormattedMessage(); } + + // WD EDIT START. Team points for Violence gamemode + + /// + /// Ensures that a player is being tracked by the PointManager, giving them a default score of 0. + /// + public void EnsureTeam(ushort team, EntityUid uid, PointManagerComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + if (component.TeamPoints.ContainsKey(team)) + return; + + component.TeamPoints.Add(team, FixedPoint2.Zero); + } + + /// + /// Returns a formatted message containing a ranking of all the teams and their scores. + /// I don't even know if i should leave this here. Why do you even need PointSystem AND SharedPointSystem? + /// + public virtual FormattedMessage GetTeamScoreboard(EntityUid uid, PointManagerComponent? component = null) + { + return new FormattedMessage(); + } + + // WD EDIT END } /// @@ -84,3 +111,11 @@ public virtual FormattedMessage GetScoreboard(EntityUid uid, PointManagerCompone /// [ByRefEvent] public readonly record struct PlayerPointChangedEvent(NetUserId Player, FixedPoint2 Points); + +/// +/// WD EDIT. Event raised on the point manager entity and broadcasted whenever a team's points change. +/// +/// +/// +[ByRefEvent] +public readonly record struct TeamPointChangedEvent(ushort Team, FixedPoint2 Points); From c92c142e5d2764ef13632026c8826784bdb398f8 Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Sun, 31 Mar 2024 01:44:31 +0300 Subject: [PATCH 03/13] simple TODO and cleanup left --- Content.Server/Points/PointSystem.cs | 4 +- .../GameRules/Violence/SwitchTeamCommand.cs | 42 +++++++++ .../Violence/ViolenceRuleComponent.cs | 3 +- .../GameRules/Violence/ViolenceRuleSystem.cs | 92 ++++++++++++++----- 4 files changed, 115 insertions(+), 26 deletions(-) create mode 100644 Content.Server/_Miracle/GameRules/Violence/SwitchTeamCommand.cs diff --git a/Content.Server/Points/PointSystem.cs b/Content.Server/Points/PointSystem.cs index 7a7b7e541a0..ba1d06b6451 100644 --- a/Content.Server/Points/PointSystem.cs +++ b/Content.Server/Points/PointSystem.cs @@ -100,7 +100,7 @@ public void AdjustTeamPointValue(ushort team, FixedPoint2 value, EntityUid uid, return; if (!component.TeamPoints.TryGetValue(team, out var current)) - current = 0; + current = FixedPoint2.Zero; SetTeamPointValue(team, current + value, uid, component); } @@ -139,7 +139,7 @@ public FixedPoint2 GetTeamPointValue(ushort team, EntityUid uid, PointManagerCom : FixedPoint2.Zero; } - /// + // Ignore this method, I will finish it later myself. public override FormattedMessage GetTeamScoreboard(EntityUid uid, PointManagerComponent? component = null) { var msg = new FormattedMessage(); diff --git a/Content.Server/_Miracle/GameRules/Violence/SwitchTeamCommand.cs b/Content.Server/_Miracle/GameRules/Violence/SwitchTeamCommand.cs new file mode 100644 index 00000000000..679672022ca --- /dev/null +++ b/Content.Server/_Miracle/GameRules/Violence/SwitchTeamCommand.cs @@ -0,0 +1,42 @@ +using Robust.Shared.Console; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using System; +using Robust.Shared.Network; + +// This was written with copilot, beware. +namespace Content.Server._Miracle.GameRules +{ + class SwitchTeamCommand : IConsoleCommand + { + public string Command => "switchteam"; + public string Description => "Switches the player's team."; + public string Help => "switchteam "; + + public void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length != 2) + { + shell.WriteLine("Expected exactly 2 arguments."); + return; + } + + if (!Guid.TryParse(args[0], out var guid)) + { + shell.WriteLine($"Invalid player ID: {args[0]}"); + return; + } + + var playerId = new NetUserId(guid); + + if (!ushort.TryParse(args[1], out var newTeamId)) + { + shell.WriteLine($"Invalid team ID: {args[1]}"); + return; + } + + var violenceRuleSystem = IoCManager.Resolve().GetEntitySystem(); + violenceRuleSystem.SwitchTeam(playerId, newTeamId); + } + } +} diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs index 76e5505a021..dfb34a1dc47 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs @@ -1,4 +1,5 @@ using Content.Shared.FixedPoint; +using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; @@ -63,5 +64,5 @@ public sealed partial class ViolenceRuleComponent : Component /// Dictionary of a players and their teams /// [DataField("teamMembers")] - public Dictionary TeamMembers { get; private set; } = new Dictionary(); + public Dictionary TeamMembers { get; private set; } = new Dictionary(); } diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs index ebfb251d4fe..ef456138e3a 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs @@ -1,31 +1,27 @@ using Content.Server.GameTicking.Rules; using System.Linq; using Content.Server._Miracle.Components; -using Content.Server.Antag; using Content.Server.GameTicking; using Content.Server.GameTicking.Rules.Components; using Content.Server.RoundEnd; -using Content.Shared.Mobs.Systems; -using Robust.Shared.Map; -using Robust.Shared.Prototypes; -using Robust.Shared.Timing; using Content.Server.Station.Systems; -using Robust.Server.Player; using Robust.Shared.Utility; using Content.Server.KillTracking; using Content.Server.Mind; using Content.Server.Points; using Content.Shared.Points; +using Robust.Shared.Network; +using Robust.Shared.Player; using Robust.Shared.Random; namespace Content.Server._Miracle.GameRules; -// TODO: отредачить pointsystem и sharedpointsystem чтобы оно считало очки команд - сделано? -// TODO: дать экшен или предмет на выход из матча? - удалять человека из RespawnTrackerComponent.Players -// TODO: инициализация команд (просто добавить в прототип геймрула), функция для смены команды? вставить куда-нибудь EnsureTeam из PointSystem? -// TODO: прототипы геймрула, аплинка и startingGear, gameMapPool, сама карта -// TODO: мб дать в startingGear аплинк, который будет не просто давать предметы, но и сразу их надевать на космонавтика -// TODO: нормальный RoundEndTextAppend и TeamScoreboard +// TODO: edit pointsystem and sharedpointsystem so that it correctly manages team points - осталось только тестить +// TODO: give player a button to switch teams +// TODO: use EnsureTeam from PointSystem? +// TODO: prototypes of gamerule, uplink and startingGear, gameMapPool, the map itself +// TODO: maybe include and uplink in startingGear that wont only give items, but equip them instantly +// TODO: proper RoundEndTextAppend and TeamScoreboard public sealed class ViolenceRuleSystem : GameRuleSystem { @@ -44,7 +40,7 @@ public override void Initialize() //SubscribeLocalEvent(OnPlayerSpawning); SubscribeLocalEvent(OnSpawnComplete); SubscribeLocalEvent(OnKillReported); - SubscribeLocalEvent(OnPointChanged); + SubscribeLocalEvent(OnPointChanged); SubscribeLocalEvent(OnRoundEndTextAppend); } @@ -60,8 +56,8 @@ private void OnBeforeSpawn(PlayerBeforeSpawnEvent ev) _mind.SetUserId(newMind, ev.Player.UserId); // Assign player to a team, if he has none - if (!ruleComponent.TeamMembers.TryGetValue(ev.Player, out var team)) - ruleComponent.TeamMembers.Add(ev.Player, ruleComponent.Teams[_robustRandom.Next(0, ruleComponent.Teams.Count())]); + if (!ruleComponent.TeamMembers.TryGetValue(ev.Player.UserId, out var team)) + ruleComponent.TeamMembers.Add(ev.Player.UserId, ruleComponent.Teams[_robustRandom.Next(0, ruleComponent.Teams.Count())]); var mobMaybe = _stationSpawning.SpawnPlayerCharacterOnStation(ev.Station, null, ev.Profile, null, team); // make it spawn on specified team spawnpoints DebugTools.AssertNotNull(mobMaybe); @@ -101,23 +97,35 @@ private void OnKillReported(ref KillReportedEvent ev) if (!GameTicker.IsGameRuleActive(uid, rule)) continue; - + var team = GetEntitiesTeam(ev.Entity, ruleComponent); // YOU SUICIDED OR GOT THROWN INTO LAVA! // WHAT A GIANT FUCKING NERD! LAUGH NOW! if (ev.Primary is not KillPlayerSource player) { - _point.AdjustPointValue(ev.Entity, -1, uid, point); + _point.AdjustPointValue(ev.Entity, -1, uid, point); // krill issue penalty // -1 point to the nerd's team? continue; } + if (ev.Primary is KillPlayerSource killer && ruleComponent.TeamMembers.TryGetValue(killer.PlayerId, out var suckersTeam)) + { + if (team != suckersTeam && team != null) + { + _point.AdjustTeamPointValue(team.Value, 1, uid, point); + _point.AdjustPointValue(killer.PlayerId, 1, uid, point); + } + } - // adjust team points here - _point.AdjustPointValue(player.PlayerId, 1, uid, point); + if (ev.Assist is KillPlayerSource assist && ruleComponent.Victor == null && ruleComponent.TeamMembers.TryGetValue(assist.PlayerId, out var shitTeam)) + { + if (team != shitTeam && team != null) + { + _point.AdjustTeamPointValue(team.Value, 0.5, uid, point); + _point.AdjustPointValue(assist.PlayerId, 0.5, uid, point); + } - if (ev.Assist is KillPlayerSource assist && ruleComponent.Victor == null) - _point.AdjustPointValue(assist.PlayerId, 0.5, uid, point); + } // I dont know if we will have reward spawns or any direct rewards for players //var spawns = EntitySpawnCollection.GetSpawns(ruleComponent.RewardSpawns).Cast().ToList(); @@ -125,7 +133,7 @@ private void OnKillReported(ref KillReportedEvent ev) } } - private void OnPointChanged(EntityUid uid, ViolenceRuleComponent component, ref PlayerPointChangedEvent args) + private void OnPointChanged(EntityUid uid, ViolenceRuleComponent component, ref TeamPointChangedEvent args) { if (component.Victor != null) return; @@ -133,10 +141,48 @@ private void OnPointChanged(EntityUid uid, ViolenceRuleComponent component, ref if (args.Points < component.PointCap) return; - //component.Victor = args.Player; // should assign a team as victor probably? + component.Victor = args.Team; _roundEnd.EndRound(component.RestartDelay); } + private ushort? GetEntitiesTeam(EntityUid uid, ViolenceRuleComponent comp) + { + ActorComponent? actor = null; + if (!Resolve(uid, ref actor, false)) + return null; + + if (!comp.TeamMembers.TryGetValue(actor.PlayerSession.UserId, out var team)) + return null; + + return team; + } + + // Do we even need that? + public void SwitchTeam(NetUserId playerId, ushort newTeamId) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var ruleComponent, out var rule)) + { + if (!GameTicker.IsGameRuleActive(uid, rule)) + continue; + + if (!ruleComponent.Teams.Contains(newTeamId)) + continue; + + if (ruleComponent.TeamMembers[playerId] == newTeamId) + return; + + var currentTeamId = ruleComponent.TeamMembers[playerId]; + var currentTeamCount = ruleComponent.TeamMembers.Values.Count(teamId => teamId == currentTeamId); + var newTeamCount = ruleComponent.TeamMembers.Values.Count(teamId => teamId == newTeamId); + + if (newTeamCount >= currentTeamCount) + return; + + ruleComponent.TeamMembers[playerId] = newTeamId; + } + } + private void OnRoundEndTextAppend(RoundEndTextAppendEvent ev) { var query = EntityQueryEnumerator(); From c56db1b33cb3c8f9653f03377fe3f31a60bf1ab0 Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Sun, 31 Mar 2024 18:14:26 +0300 Subject: [PATCH 04/13] coding --- .../Violence/ViolenceRuleComponent.cs | 37 ++- .../GameRules/Violence/ViolenceRuleSystem.cs | 236 +++++++++++++++++- 2 files changed, 263 insertions(+), 10 deletions(-) diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs index dfb34a1dc47..797f69fbdb0 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs @@ -1,4 +1,5 @@ using Content.Shared.FixedPoint; +using Robust.Shared.Map; using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Prototypes; @@ -13,11 +14,17 @@ namespace Content.Server._Miracle.GameRules; public sealed partial class ViolenceRuleComponent : Component { /// - /// Min players needed for Violence gamerule to start. + /// Min players needed for Violence round to start. /// [DataField, ViewVariables(VVAccess.ReadWrite)] public int MinPlayers = 2; + /// + /// Max players needed for Violence round. + /// + [DataField("maxPlayers"), ViewVariables(VVAccess.ReadWrite)] + public Dictionary MaxPlayers = new Dictionary(); + /// /// How long until the round restarts /// @@ -45,13 +52,13 @@ public sealed partial class ViolenceRuleComponent : Component /// /// List of scores in this gamemode. /// - [DataField("scores")] + [DataField("scores"), ViewVariables(VVAccess.ReadWrite)] public Dictionary Scores { get; private set; } = new Dictionary(); /// /// Stored, for some reason. /// - [DataField("victor")] + [DataField("victor"), ViewVariables(VVAccess.ReadWrite)] public ushort? Victor; /// @@ -63,6 +70,28 @@ public sealed partial class ViolenceRuleComponent : Component /// /// Dictionary of a players and their teams /// - [DataField("teamMembers")] + [DataField("teamMembers"), ViewVariables(VVAccess.ReadWrite)] public Dictionary TeamMembers { get; private set; } = new Dictionary(); + + /// + /// Pool of maps for this set of teams + /// + [DataField("mapPool"), ViewVariables(VVAccess.ReadWrite)] + public string MapPool; + + /// + /// EntityUid of current map. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public MapId? CurrentMap = null; + + [DataField("roundState"), ViewVariables(VVAccess.ReadWrite)] + public RoundState RoundState { get; set; } = RoundState.NotInProgress; +} + +public enum RoundState +{ + Starting, + InProgress, + NotInProgress } diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs index ef456138e3a..5695b47be2b 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs @@ -7,11 +7,19 @@ using Content.Server.Station.Systems; using Robust.Shared.Utility; using Content.Server.KillTracking; +using Content.Server.Maps; using Content.Server.Mind; using Content.Server.Points; +using Content.Shared.Database; +using Content.Shared.Mind; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; using Content.Shared.Points; +using Robust.Shared.Console; +using Robust.Shared.Map; using Robust.Shared.Network; using Robust.Shared.Player; +using Robust.Shared.Prototypes; using Robust.Shared.Random; namespace Content.Server._Miracle.GameRules; @@ -23,6 +31,15 @@ namespace Content.Server._Miracle.GameRules; // TODO: maybe include and uplink in startingGear that wont only give items, but equip them instantly // TODO: proper RoundEndTextAppend and TeamScoreboard +// todo to implement round, one gamerule entity - one instance and one active round +// TODO: make a menu to join the round, switch teams, leave the round +// TODO: properly start and end the round, make a small delay before the new round starts, catch MobStateChangedEvent and make the round stop, if only one team is alive +// TODO: make ViolenceRoundStartingEvent +// TODO: votekick? +// TODO: make a way to change the map and Teams in the ViolenceGameRuleComponent after victory + +// TODO: maybe add a way to start the match after the round starts, so that it can be used on usual servers. + public sealed class ViolenceRuleSystem : GameRuleSystem { [Dependency] private readonly RoundEndSystem _roundEnd = default!; @@ -31,19 +48,36 @@ public sealed class ViolenceRuleSystem : GameRuleSystem [Dependency] private readonly RespawnRuleSystem _respawn = default!; [Dependency] private readonly StationSpawningSystem _stationSpawning = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!; + [Dependency] private readonly IConsoleHost _consoleHost = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly ILogManager _logManager = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly GameTicker _gameTicker = default!; + + private ISawmill _sawmill = default!; + + private string _defaultViolenceRule = "Violence"; public override void Initialize() { base.Initialize(); + _sawmill = _logManager.GetSawmill("violence"); + //_sawmillReplays = _logManager.GetSawmill("violence.replays"); + SubscribeLocalEvent(OnBeforeSpawn); //SubscribeLocalEvent(OnPlayerSpawning); SubscribeLocalEvent(OnSpawnComplete); SubscribeLocalEvent(OnKillReported); SubscribeLocalEvent(OnPointChanged); SubscribeLocalEvent(OnRoundEndTextAppend); + //SubscribeLocalEvent(OnMobStateChanged); } + /// + /// This method handles roundstart player spawning. After roundstart, players will be spawned by RespawnRuleSystem. + /// + /// private void OnBeforeSpawn(PlayerBeforeSpawnEvent ev) { var query = EntityQueryEnumerator(); @@ -52,14 +86,21 @@ private void OnBeforeSpawn(PlayerBeforeSpawnEvent ev) if (!GameTicker.IsGameRuleActive(uid, rule)) continue; - var newMind = _mind.CreateMind(ev.Player.UserId, ev.Profile.Name); - _mind.SetUserId(newMind, ev.Player.UserId); + var teamList = RoundEligibleToJoin(ruleComponent); + if (teamList.Count == 0) + { + continue; + } - // Assign player to a team, if he has none if (!ruleComponent.TeamMembers.TryGetValue(ev.Player.UserId, out var team)) - ruleComponent.TeamMembers.Add(ev.Player.UserId, ruleComponent.Teams[_robustRandom.Next(0, ruleComponent.Teams.Count())]); + { + ruleComponent.TeamMembers.Add(ev.Player.UserId, teamList[_robustRandom.Next(0, teamList.Count())]); + } - var mobMaybe = _stationSpawning.SpawnPlayerCharacterOnStation(ev.Station, null, ev.Profile, null, team); // make it spawn on specified team spawnpoints + var newMind = _mind.CreateMind(ev.Player.UserId, ev.Profile.Name); + _mind.SetUserId(newMind, ev.Player.UserId); + + var mobMaybe = _stationSpawning.SpawnPlayerCharacterOnStation(ev.Station, null, ev.Profile, null, team); DebugTools.AssertNotNull(mobMaybe); var mob = mobMaybe!.Value; @@ -89,6 +130,45 @@ private void OnSpawnComplete(PlayerSpawnCompleteEvent ev) } } + public bool StartRound(ViolenceRuleComponent comp) + { + if (comp.RoundState != RoundState.NotInProgress) + { + // probably assert here? + return false; + } + + if (!comp.CurrentMap.HasValue) + { + comp.CurrentMap = _mapManager.CreateMap(); + + if (!comp.CurrentMap.HasValue) + return false; // i give up. or maybe load default map? + + _mapManager.AddUninitializedMap(comp.CurrentMap.Value); + + _prototypeManager.TryIndex(comp.MapPool, out var extractedMapPool); + if (extractedMapPool == null) + return false; + + var mapPrototype = extractedMapPool.Maps.ElementAt(_robustRandom.Next(extractedMapPool.Maps.Count)); + _prototypeManager.TryIndex(mapPrototype, out var map); + + if (map != null) + { + _gameTicker.LoadGameMap(map, comp.CurrentMap.Value, null); + } + else + { + return false; + } + } + + // TODO: spawn players and give them gear here + + return true; + } + private void OnKillReported(ref KillReportedEvent ev) { var query = EntityQueryEnumerator(); @@ -142,9 +222,59 @@ private void OnPointChanged(EntityUid uid, ViolenceRuleComponent component, ref return; component.Victor = args.Team; - _roundEnd.EndRound(component.RestartDelay); + //somehow end round here? + //_roundEnd.EndRound(component.RestartDelay); // since there can be multiple Violence instances, we dont need to end the global round + } + + /// + /// Tries to join the round + /// + /// + /// + /// + /// + /// + public bool JoinRound(NetUserId player, ViolenceRuleComponent comp, ushort? preferredTeam = null, bool anyTeam = false) + { + // Check if the player is already a member of a team + if (comp.TeamMembers.ContainsKey(player)) + { + return false; + } + + var eligibleTeams = RoundEligibleToJoin(comp); + // If there are no eligible teams, return false + if (eligibleTeams.Count == 0) + { + return false; + } + + // If a team is specified, check if it's eligible for joining + if (preferredTeam.HasValue) + { + if (eligibleTeams.Contains(preferredTeam.Value)) + { + comp.TeamMembers.Add(player, preferredTeam.Value); + return true; + } + + if (!anyTeam) + return false; + } + + // Randomly select a team from the eligible teams and add the player to it + var selectedTeam = eligibleTeams[_robustRandom.Next(0, eligibleTeams.Count)]; + comp.TeamMembers.Add(player, selectedTeam); + + return true; } + /// + /// Get a player's team from EntityUid and ViolenceRuleComponent + /// + /// + /// + /// private ushort? GetEntitiesTeam(EntityUid uid, ViolenceRuleComponent comp) { ActorComponent? actor = null; @@ -157,6 +287,34 @@ private void OnPointChanged(EntityUid uid, ViolenceRuleComponent component, ref return team; } + public int GetAliveTeamMembersCount(ushort teamId, ViolenceRuleComponent comp) + { + var alive = 0; + + foreach (var (player, team) in comp.TeamMembers) + { + if (team == teamId) + { + var mind = _mind.GetMind(player); + if (mind == null || TryComp(mind, out var mindComponent) || mindComponent == null) + continue; + + if (mindComponent.OwnedEntity == null) + continue; + + if (!TryComp(mindComponent.OwnedEntity.Value, out var mobStateComponent)) + continue; + + if (mobStateComponent.CurrentState != MobState.Alive) + continue; + + alive++; + } + } + + return alive; + } + // Do we even need that? public void SwitchTeam(NetUserId playerId, ushort newTeamId) { @@ -183,6 +341,72 @@ public void SwitchTeam(NetUserId playerId, ushort newTeamId) } } + /// + /// Create new instance of the rule entity. TODO: limit the number of these + /// + /// + /// + public ViolenceRuleComponent? CreateNewInstance(string? ruleId) + { + if (ruleId == null) + ruleId = _defaultViolenceRule; + + var ruleEntity = Spawn(ruleId, MapCoordinates.Nullspace); + if (!TryComp(ruleEntity, out var comp)) + { + EntityManager.DeleteEntity(ruleEntity); + return null; + } + + _sawmill.Info($"Added game rule {ToPrettyString(ruleEntity)}"); + var ev = new GameRuleAddedEvent(ruleEntity, ruleId); + RaiseLocalEvent(ruleEntity, ref ev, true); + + return comp; + } + + /// + /// Delete the instance of the rule entity. TODO: make it automatic, if the rule doesn't have any players + /// + /// + /// + public bool DeleteInstance(EntityUid uid) + { + if (!TryComp(uid, out var comp)) + { + return false; + } + + if (comp.CurrentMap.HasValue) + _mapManager.DeleteMap(comp.CurrentMap.Value); + + EntityManager.DeleteEntity(uid); + + return true; + } + + + + public List RoundEligibleToJoin(ViolenceRuleComponent comp) + { + var teamList = new List(); + + foreach (var team in comp.Teams) + { + var teamCount = comp.TeamMembers.Values.Count(teamId => teamId == team); + + if (comp.MaxPlayers.TryGetValue(team, out var maxPlayers)) + { + if (teamCount < maxPlayers) + { + teamList.Add(team); + } + } + } + + return teamList; + } + private void OnRoundEndTextAppend(RoundEndTextAppendEvent ev) { var query = EntityQueryEnumerator(); From fe70ad9257183189a52dc4f9ae138714fe5897e3 Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Mon, 1 Apr 2024 17:21:39 +0300 Subject: [PATCH 05/13] StartRound, EndRound. Getting there --- .../Violence/ViolenceRuleComponent.cs | 36 +++++++ .../GameRules/Violence/ViolenceRuleSystem.cs | 94 +++++++++++++++---- 2 files changed, 113 insertions(+), 17 deletions(-) diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs index 797f69fbdb0..da92384cc6a 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs @@ -67,12 +67,48 @@ public sealed partial class ViolenceRuleComponent : Component [DataField("pointCap"), ViewVariables(VVAccess.ReadWrite)] public FixedPoint2 PointCap = 100; + /// + /// Time when current round ends. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan RoundEndTime = TimeSpan.FromMinutes(5); + + /// + /// Time between final kill of the round and actual round end. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan RoundEndDelay = TimeSpan.FromSeconds(10); + + /// + /// Time when new round starts. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan RoundStartTime = TimeSpan.Zero; + + /// + /// Time between player's spawns and round start. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan RoundStartDelay = TimeSpan.FromSeconds(10); + + /// + /// The duration of a round. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan RoundDuration = TimeSpan.FromMinutes(5); + /// /// Dictionary of a players and their teams /// [DataField("teamMembers"), ViewVariables(VVAccess.ReadWrite)] public Dictionary TeamMembers { get; private set; } = new Dictionary(); + /// + /// Dictionary of a players and their money. + /// + [DataField("money"), ViewVariables(VVAccess.ReadWrite)] + public Dictionary Money { get; private set; } = new Dictionary(); + /// /// Pool of maps for this set of teams /// diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs index 5695b47be2b..be4cd155e48 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs @@ -21,6 +21,7 @@ using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Robust.Shared.Timing; namespace Content.Server._Miracle.GameRules; @@ -32,11 +33,13 @@ namespace Content.Server._Miracle.GameRules; // TODO: proper RoundEndTextAppend and TeamScoreboard // todo to implement round, one gamerule entity - one instance and one active round -// TODO: make a menu to join the round, switch teams, leave the round -// TODO: properly start and end the round, make a small delay before the new round starts, catch MobStateChangedEvent and make the round stop, if only one team is alive +// TODO: make a menu to join the round, switch teams, leave the round - мб сделает валтос +// TODO: properly start and end the round, make a small delay before the new round starts +// TODO: catch MobStateChangedEvent and make the round stop, if only one team is alive // TODO: make ViolenceRoundStartingEvent // TODO: votekick? // TODO: make a way to change the map and Teams in the ViolenceGameRuleComponent after victory +// TODO: respawns may suck // TODO: maybe add a way to start the match after the round starts, so that it can be used on usual servers. @@ -53,10 +56,13 @@ public sealed class ViolenceRuleSystem : GameRuleSystem [Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly GameTicker _gameTicker = default!; + [Dependency] private readonly IGameTiming _timing = default!; private ISawmill _sawmill = default!; - private string _defaultViolenceRule = "Violence"; + private List _activeRules = new List(); + + private readonly string _defaultViolenceRule = "Violence"; public override void Initialize() { @@ -70,10 +76,38 @@ public override void Initialize() SubscribeLocalEvent(OnSpawnComplete); SubscribeLocalEvent(OnKillReported); SubscribeLocalEvent(OnPointChanged); - SubscribeLocalEvent(OnRoundEndTextAppend); + //SubscribeLocalEvent(OnRoundEndTextAppend); //SubscribeLocalEvent(OnMobStateChanged); } + protected override void ActiveTick(EntityUid uid, ViolenceRuleComponent comp, GameRuleComponent gameRule, + float frameTime) + { + base.ActiveTick(uid, comp, gameRule, frameTime); + + switch (comp.RoundState) + { + case RoundState.InProgress: + if (_timing.CurTime >= comp.RoundEndTime) + { + EndRound(comp); + } + break; + + case RoundState.Starting: + if (_timing.CurTime >= comp.RoundStartTime) + { + StartRound(comp); + } + break; + + case RoundState.NotInProgress: + // dunno + + break; + } + } + /// /// This method handles roundstart player spawning. After roundstart, players will be spawned by RespawnRuleSystem. /// @@ -86,7 +120,7 @@ private void OnBeforeSpawn(PlayerBeforeSpawnEvent ev) if (!GameTicker.IsGameRuleActive(uid, rule)) continue; - var teamList = RoundEligibleToJoin(ruleComponent); + var teamList = TeamsEligibleToJoin(ruleComponent); if (teamList.Count == 0) { continue; @@ -127,6 +161,8 @@ private void OnSpawnComplete(PlayerSpawnCompleteEvent ev) if (!GameTicker.IsGameRuleActive(uid, rule)) continue; _respawn.AddToTracker(ev.Mob, uid, tracker); + + // TODO: add money or equip to the player here } } @@ -169,6 +205,24 @@ public bool StartRound(ViolenceRuleComponent comp) return true; } + public bool EndRound(ViolenceRuleComponent comp) + { + if (comp.RoundState == RoundState.NotInProgress) + { + // probably assert here? + return false; + } + + comp.RoundState = RoundState.NotInProgress; + + if (comp.CurrentMap.HasValue) + _mapManager.DeleteMap(comp.CurrentMap.Value); + + // TODO: give rewards if appropriate here + return true; + } + + private void OnKillReported(ref KillReportedEvent ev) { var query = EntityQueryEnumerator(); @@ -222,8 +276,7 @@ private void OnPointChanged(EntityUid uid, ViolenceRuleComponent component, ref return; component.Victor = args.Team; - //somehow end round here? - //_roundEnd.EndRound(component.RestartDelay); // since there can be multiple Violence instances, we dont need to end the global round + // DoRoundEndBehavior(uid);??? } /// @@ -242,7 +295,7 @@ public bool JoinRound(NetUserId player, ViolenceRuleComponent comp, ushort? pref return false; } - var eligibleTeams = RoundEligibleToJoin(comp); + var eligibleTeams = TeamsEligibleToJoin(comp); // If there are no eligible teams, return false if (eligibleTeams.Count == 0) { @@ -351,16 +404,16 @@ public void SwitchTeam(NetUserId playerId, ushort newTeamId) if (ruleId == null) ruleId = _defaultViolenceRule; - var ruleEntity = Spawn(ruleId, MapCoordinates.Nullspace); + var ruleEntity = _gameTicker.AddGameRule(ruleId); + if (!TryComp(ruleEntity, out var comp)) { EntityManager.DeleteEntity(ruleEntity); return null; } - _sawmill.Info($"Added game rule {ToPrettyString(ruleEntity)}"); - var ev = new GameRuleAddedEvent(ruleEntity, ruleId); - RaiseLocalEvent(ruleEntity, ref ev, true); + _gameTicker.StartGameRule(ruleEntity); + _activeRules.Add(ruleEntity); return comp; } @@ -369,7 +422,7 @@ public void SwitchTeam(NetUserId playerId, ushort newTeamId) /// Delete the instance of the rule entity. TODO: make it automatic, if the rule doesn't have any players /// /// - /// + /// true if was deleted successfully public bool DeleteInstance(EntityUid uid) { if (!TryComp(uid, out var comp)) @@ -377,17 +430,22 @@ public bool DeleteInstance(EntityUid uid) return false; } + comp.RoundState = RoundState.NotInProgress; + + // TODO: maybe delete all players from the round if deleting the map fucks that up? + if (comp.CurrentMap.HasValue) _mapManager.DeleteMap(comp.CurrentMap.Value); - EntityManager.DeleteEntity(uid); + _gameTicker.EndGameRule(uid); + _activeRules.Remove(uid); return true; } - public List RoundEligibleToJoin(ViolenceRuleComponent comp) + public List TeamsEligibleToJoin(ViolenceRuleComponent comp) { var teamList = new List(); @@ -407,6 +465,7 @@ public List RoundEligibleToJoin(ViolenceRuleComponent comp) return teamList; } + /* private void OnRoundEndTextAppend(RoundEndTextAppendEvent ev) { var query = EntityQueryEnumerator(); @@ -415,15 +474,16 @@ private void OnRoundEndTextAppend(RoundEndTextAppendEvent ev) if (!GameTicker.IsGameRuleAdded(uid, rule)) continue; - /* + if (ruleComponent.Victor != null && _player.TryGetPlayerData(ruleComponent.Victor.Value, out var data)) // get the team data here { ev.AddLine(Loc.GetString("point-scoreboard-winner", ("player", data.UserName))); ev.AddLine(""); } - */ + ev.AddLine(Loc.GetString("point-scoreboard-header")); // edit this, probably ev.AddLine(new FormattedMessage(point.Scoreboard).ToMarkup()); } } + */ } From cf6d480387c34464cc6e74ed7a9ca4e87de1de82 Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Mon, 1 Apr 2024 22:53:48 +0300 Subject: [PATCH 06/13] yaica commit --- .../Violence/ViolenceRuleComponent.cs | 2 +- .../GameRules/Violence/ViolenceRuleSystem.cs | 79 ++++++++++++++++--- 2 files changed, 69 insertions(+), 12 deletions(-) diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs index da92384cc6a..6558402c4df 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs @@ -65,7 +65,7 @@ public sealed partial class ViolenceRuleComponent : Component /// The number of points a player has to get to win. /// [DataField("pointCap"), ViewVariables(VVAccess.ReadWrite)] - public FixedPoint2 PointCap = 100; + public FixedPoint2 PointCap = 5; /// /// Time when current round ends. diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs index be4cd155e48..33461699363 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs @@ -25,23 +25,18 @@ namespace Content.Server._Miracle.GameRules; -// TODO: edit pointsystem and sharedpointsystem so that it correctly manages team points - осталось только тестить -// TODO: give player a button to switch teams -// TODO: use EnsureTeam from PointSystem? -// TODO: prototypes of gamerule, uplink and startingGear, gameMapPool, the map itself -// TODO: maybe include and uplink in startingGear that wont only give items, but equip them instantly -// TODO: proper RoundEndTextAppend and TeamScoreboard - // todo to implement round, one gamerule entity - one instance and one active round -// TODO: make a menu to join the round, switch teams, leave the round - мб сделает валтос // TODO: properly start and end the round, make a small delay before the new round starts // TODO: catch MobStateChangedEvent and make the round stop, if only one team is alive // TODO: make ViolenceRoundStartingEvent // TODO: votekick? // TODO: make a way to change the map and Teams in the ViolenceGameRuleComponent after victory // TODO: respawns may suck - -// TODO: maybe add a way to start the match after the round starts, so that it can be used on usual servers. +// TODO: buying equipment and saving equipment between rounds +// TODO: scoreboard +// TODO: prototypes of gamerule, uplink and startingGear, gameMapPool, the map itself +// TODO: use EnsureTeam from PointSystem? +// TODO: make a menu to join the round, switch teams, leave the round - мб сделает валтос public sealed class ViolenceRuleSystem : GameRuleSystem { @@ -77,7 +72,7 @@ public override void Initialize() SubscribeLocalEvent(OnKillReported); SubscribeLocalEvent(OnPointChanged); //SubscribeLocalEvent(OnRoundEndTextAppend); - //SubscribeLocalEvent(OnMobStateChanged); + SubscribeLocalEvent(OnMobStateChanged); } protected override void ActiveTick(EntityUid uid, ViolenceRuleComponent comp, GameRuleComponent gameRule, @@ -114,6 +109,10 @@ protected override void ActiveTick(EntityUid uid, ViolenceRuleComponent comp, Ga /// private void OnBeforeSpawn(PlayerBeforeSpawnEvent ev) { + /* + if (ev.LateJoin) // this will allow this gamerule to be added to usual rounds. maybe + return; + */ var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var ruleComponent, out var tracker, out var point, out var rule)) { @@ -166,6 +165,32 @@ private void OnSpawnComplete(PlayerSpawnCompleteEvent ev) } } + private void OnMobStateChanged(MobStateChangedEvent ev) + { + if (ev.NewMobState != MobState.Dead) + return; + + if (!TryComp(ev.Target, out var actor)) + return; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var ruleComponent, out var point, out var rule)) + { + if (!GameTicker.IsGameRuleActive(uid, rule)) + continue; + + var team = GetEntitiesTeam(ev.Target, ruleComponent); + if (team == null) + continue; + + var alive = GetAliveTeamMembersCount(team.Value, ruleComponent); + if (alive == 0) + { + //Check for round end + } + } + } + public bool StartRound(ViolenceRuleComponent comp) { if (comp.RoundState != RoundState.NotInProgress) @@ -205,6 +230,12 @@ public bool StartRound(ViolenceRuleComponent comp) return true; } + public bool DoRoundEndBehavior(ViolenceRuleComponent comp) + { + // wait for comp.RoundEndDelay + return EndRound(comp); + } + public bool EndRound(ViolenceRuleComponent comp) { if (comp.RoundState == RoundState.NotInProgress) @@ -368,6 +399,32 @@ public int GetAliveTeamMembersCount(ushort teamId, ViolenceRuleComponent comp) return alive; } + /// + /// Round ends only if players of one single team are alive. + /// + /// + /// + public bool CheckForRoundEnd(ViolenceRuleComponent comp) + { + var aliveTeams = new List(); + + foreach (var team in comp.Teams) + { + if (GetAliveTeamMembersCount(team, comp) > 0) + { + aliveTeams.Add(team); + } + } + + if (aliveTeams.Count == 1) + { + comp.Victor = aliveTeams[0]; + return true; + } + + return false; + } + // Do we even need that? public void SwitchTeam(NetUserId playerId, ushort newTeamId) { From 372bf42d590697688c232f5fcc9f58b6ec96ae0f Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Tue, 2 Apr 2024 17:07:55 +0300 Subject: [PATCH 07/13] small things make the game --- .../GameRules/Violence/ViolenceRuleSystem.cs | 53 ++++++++++++++++--- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs index 33461699363..ed51e8feff1 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs @@ -11,10 +11,13 @@ using Content.Server.Mind; using Content.Server.Points; using Content.Shared.Database; +using Content.Shared.Hands.Components; +using Content.Shared.Inventory; using Content.Shared.Mind; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Points; +using Robust.Server.Player; using Robust.Shared.Console; using Robust.Shared.Map; using Robust.Shared.Network; @@ -25,7 +28,6 @@ namespace Content.Server._Miracle.GameRules; -// todo to implement round, one gamerule entity - one instance and one active round // TODO: properly start and end the round, make a small delay before the new round starts // TODO: catch MobStateChangedEvent and make the round stop, if only one team is alive // TODO: make ViolenceRoundStartingEvent @@ -52,6 +54,7 @@ public sealed class ViolenceRuleSystem : GameRuleSystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly GameTicker _gameTicker = default!; [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; private ISawmill _sawmill = default!; @@ -90,16 +93,15 @@ protected override void ActiveTick(EntityUid uid, ViolenceRuleComponent comp, Ga break; case RoundState.Starting: + + break; + + case RoundState.NotInProgress: if (_timing.CurTime >= comp.RoundStartTime) { StartRound(comp); } break; - - case RoundState.NotInProgress: - // dunno - - break; } } @@ -186,7 +188,8 @@ private void OnMobStateChanged(MobStateChangedEvent ev) var alive = GetAliveTeamMembersCount(team.Value, ruleComponent); if (alive == 0) { - //Check for round end + if (CheckForRoundEnd(ruleComponent)) + DoRoundEndBehavior(ruleComponent); } } } @@ -232,6 +235,39 @@ public bool StartRound(ViolenceRuleComponent comp) public bool DoRoundEndBehavior(ViolenceRuleComponent comp) { + // announce the winner + foreach (var (player, team) in comp.TeamMembers) + { + if (team != comp.Victor) + continue; + + if (!_playerManager.SessionsDict.TryGetValue(player, out var session)) + continue; + + if (session.AttachedEntity != null && + TryComp(session.AttachedEntity, out var mobState) && + mobState.CurrentState == MobState.Alive) + { + if (TryComp(session.AttachedEntity, out var hands)) + { + foreach (var (name, hand) in hands.Hands) + { + if (hand.HeldEntity != null) + { + // save it + } + } + } + if (TryComp(session.AttachedEntity, out var inv)) + { + // save items in inventory + } + + } + + + + } // wait for comp.RoundEndDelay return EndRound(comp); } @@ -249,6 +285,7 @@ public bool EndRound(ViolenceRuleComponent comp) if (comp.CurrentMap.HasValue) _mapManager.DeleteMap(comp.CurrentMap.Value); + comp.RoundStartTime = _timing.CurTime + comp.RoundStartDelay; // TODO: give rewards if appropriate here return true; } @@ -303,7 +340,7 @@ private void OnPointChanged(EntityUid uid, ViolenceRuleComponent component, ref if (component.Victor != null) return; - if (args.Points < component.PointCap) + if (args.Points <= component.PointCap) return; component.Victor = args.Team; From ce999b8c3a646c7cc0fa15c9da1ccaf715726513 Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Wed, 3 Apr 2024 18:57:58 +0300 Subject: [PATCH 08/13] =?UTF-8?q?=D0=BD=D0=B0=D1=81=D0=B8=D0=BB=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=B3=D0=B5=D0=B9=D0=BC=D0=B8=D0=BD=D0=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Violence/ViolenceRuleComponent.cs | 28 ++- .../GameRules/Violence/ViolenceRuleSystem.cs | 179 ++++++++++++++---- 2 files changed, 169 insertions(+), 38 deletions(-) diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs index 6558402c4df..e3fa39b55fd 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs @@ -52,14 +52,21 @@ public sealed partial class ViolenceRuleComponent : Component /// /// List of scores in this gamemode. /// - [DataField("scores"), ViewVariables(VVAccess.ReadWrite)] - public Dictionary Scores { get; private set; } = new Dictionary(); + [DataField("teamScores"), ViewVariables(VVAccess.ReadWrite)] + public Dictionary TeamScores { get; private set; } = new Dictionary(); + + List kd = new List(new int[2]); + /// + /// Stores number of kills and deaths for each player. Indexes: 0 for kills, 1 for assists, 2 for deaths. + /// + [DataField("killsDeaths"), ViewVariables(VVAccess.ReadWrite)] + public Dictionary> KillDeaths { get; private set; } = new Dictionary>(); /// /// Stored, for some reason. /// - [DataField("victor"), ViewVariables(VVAccess.ReadWrite)] - public ushort? Victor; + [DataField("matchVictor"), ViewVariables(VVAccess.ReadWrite)] + public ushort? MatchVictor; /// /// The number of points a player has to get to win. @@ -109,6 +116,19 @@ public sealed partial class ViolenceRuleComponent : Component [DataField("money"), ViewVariables(VVAccess.ReadWrite)] public Dictionary Money { get; private set; } = new Dictionary(); + /// + /// Reward for remaining alive at the end of the round. + /// + [DataField("aliveReward"), ViewVariables(VVAccess.ReadWrite)] + public int AliveReward { get; private set; } = 300; + + /// + /// Reward for remaining alive at the end of the round. + /// + [DataField("killReward"), ViewVariables(VVAccess.ReadWrite)] + public int KillReward { get; private set; } = 300; + + /// /// Pool of maps for this set of teams /// diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs index ed51e8feff1..9b2accc4b0c 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs @@ -17,6 +17,7 @@ using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Points; +using Content.Shared.Preferences; using Robust.Server.Player; using Robust.Shared.Console; using Robust.Shared.Map; @@ -28,17 +29,13 @@ namespace Content.Server._Miracle.GameRules; -// TODO: properly start and end the round, make a small delay before the new round starts -// TODO: catch MobStateChangedEvent and make the round stop, if only one team is alive -// TODO: make ViolenceRoundStartingEvent -// TODO: votekick? -// TODO: make a way to change the map and Teams in the ViolenceGameRuleComponent after victory // TODO: respawns may suck +// TODO: recheck matchflow and roundflow +// TODO: finish StartRound and OnSpawnComplete // TODO: buying equipment and saving equipment between rounds -// TODO: scoreboard // TODO: prototypes of gamerule, uplink and startingGear, gameMapPool, the map itself // TODO: use EnsureTeam from PointSystem? -// TODO: make a menu to join the round, switch teams, leave the round - мб сделает валтос +// TODO: make a menu to join the round, switch teams, leave the round, get scoreboard - мб сделает валтос public sealed class ViolenceRuleSystem : GameRuleSystem { @@ -99,7 +96,7 @@ protected override void ActiveTick(EntityUid uid, ViolenceRuleComponent comp, Ga case RoundState.NotInProgress: if (_timing.CurTime >= comp.RoundStartTime) { - StartRound(comp); + StartRound(uid, comp); } break; } @@ -153,6 +150,10 @@ private void OnBeforeSpawn(PlayerBeforeSpawnEvent ev) } } + /// + /// TODO: think about it. i think it is not needed. respawns are handled by StartRound anyway + /// + /// private void OnSpawnComplete(PlayerSpawnCompleteEvent ev) { EnsureComp(ev.Mob); @@ -181,7 +182,7 @@ private void OnMobStateChanged(MobStateChangedEvent ev) if (!GameTicker.IsGameRuleActive(uid, rule)) continue; - var team = GetEntitiesTeam(ev.Target, ruleComponent); + var team = GetPlayersTeam(actor.PlayerSession.UserId, ruleComponent); if (team == null) continue; @@ -194,7 +195,7 @@ private void OnMobStateChanged(MobStateChangedEvent ev) } } - public bool StartRound(ViolenceRuleComponent comp) + public bool StartRound(EntityUid uid, ViolenceRuleComponent comp) { if (comp.RoundState != RoundState.NotInProgress) { @@ -228,7 +229,50 @@ public bool StartRound(ViolenceRuleComponent comp) } } - // TODO: spawn players and give them gear here + // TODO: recheck the rest of this code block + // spawning players + foreach (var (playerId, teamId) in comp.TeamMembers) + { + var player = _playerManager.GetSessionById(playerId); + if (player == null) + continue; + + var profile = _gameTicker.GetPlayerProfile(player); + + var newMind = _mind.CreateMind(playerId, profile.Name); + _mind.SetUserId(newMind, playerId); + + if (!comp.CurrentMap.HasValue) + return false; + var station = _mapManager.GetMapEntityId(comp.CurrentMap.Value); + + var mobMaybe = _stationSpawning.SpawnPlayerCharacterOnStation(station, null, profile, null, teamId); + DebugTools.AssertNotNull(mobMaybe); + var mob = mobMaybe!.Value; + + _mind.TransferTo(newMind, mob); + // Should get startingGear from the client here. Setting default startingGear if none or invalid is passed. + // Also different startingGear for different teams. A dictionary of teams and startingGear in ViolenceRuleComponent? + //SetOutfitCommand.SetOutfit(mob, ruleComponent.Gear, EntityManager); + EnsureComp(mob); + + if (TryComp(uid, out var tracker)) + { + _respawn.AddToTracker(playerId, uid, tracker); + } + else + { + _respawn.AddToTracker(playerId, uid); + } + + _point.EnsurePlayer(playerId, uid); + + DebugTools.AssertNotNull(mobMaybe); + } + + // Set round state and end time + comp.RoundState = RoundState.InProgress; + comp.RoundEndTime = _timing.CurTime + comp.RoundDuration; return true; } @@ -238,12 +282,13 @@ public bool DoRoundEndBehavior(ViolenceRuleComponent comp) // announce the winner foreach (var (player, team) in comp.TeamMembers) { - if (team != comp.Victor) + if (team != comp.MatchVictor) continue; if (!_playerManager.SessionsDict.TryGetValue(player, out var session)) continue; + if (session.AttachedEntity != null && TryComp(session.AttachedEntity, out var mobState) && mobState.CurrentState == MobState.Alive) @@ -263,6 +308,15 @@ public bool DoRoundEndBehavior(ViolenceRuleComponent comp) // save items in inventory } + // ummm TODO: test this + if (comp.Money.ContainsKey(session.UserId)) + { + comp.Money[session.UserId] += comp.AliveReward; + } + else + { + comp.Money.TryAdd(session.UserId, comp.AliveReward); + } } @@ -286,7 +340,6 @@ public bool EndRound(ViolenceRuleComponent comp) _mapManager.DeleteMap(comp.CurrentMap.Value); comp.RoundStartTime = _timing.CurTime + comp.RoundStartDelay; - // TODO: give rewards if appropriate here return true; } @@ -299,7 +352,26 @@ private void OnKillReported(ref KillReportedEvent ev) if (!GameTicker.IsGameRuleActive(uid, rule)) continue; - var team = GetEntitiesTeam(ev.Entity, ruleComponent); + ActorComponent? actor = null; + if (!Resolve(uid, ref actor, false)) + return; + + var team = GetPlayersTeam(actor.PlayerSession.UserId, ruleComponent); + if (team == null) + return; + + if (!ruleComponent.KillDeaths.TryGetValue(actor.PlayerSession.UserId, out var targetKd)) + { + targetKd = new List(); + targetKd.Add(0); + targetKd.Add(0); + targetKd.Add(0); + + ruleComponent.KillDeaths.Add(actor.PlayerSession.UserId, targetKd); + } + + targetKd[2]++; + // YOU SUICIDED OR GOT THROWN INTO LAVA! // WHAT A GIANT FUCKING NERD! LAUGH NOW! if (ev.Primary is not KillPlayerSource player) @@ -309,24 +381,41 @@ private void OnKillReported(ref KillReportedEvent ev) continue; } - if (ev.Primary is KillPlayerSource killer && ruleComponent.TeamMembers.TryGetValue(killer.PlayerId, out var suckersTeam)) + if (ev.Primary is KillPlayerSource killer && ruleComponent.TeamMembers.TryGetValue(killer.PlayerId, out var killerTeam)) { - if (team != suckersTeam && team != null) + if (team != killerTeam) { - _point.AdjustTeamPointValue(team.Value, 1, uid, point); - _point.AdjustPointValue(killer.PlayerId, 1, uid, point); + if (!ruleComponent.KillDeaths.TryGetValue(killer.PlayerId, out var killerKd)) + { + killerKd = new List(); + killerKd.Add(0); + killerKd.Add(0); + killerKd.Add(0); + + ruleComponent.KillDeaths.Add(actor.PlayerSession.UserId, killerKd); + } + + killerKd[0]++; } } - if (ev.Assist is KillPlayerSource assist && ruleComponent.Victor == null && ruleComponent.TeamMembers.TryGetValue(assist.PlayerId, out var shitTeam)) + if (ev.Assist is KillPlayerSource assist && ruleComponent.MatchVictor == null && ruleComponent.TeamMembers.TryGetValue(assist.PlayerId, out var assistTeam)) { - if (team != shitTeam && team != null) + if (team != assistTeam) { - _point.AdjustTeamPointValue(team.Value, 0.5, uid, point); - _point.AdjustPointValue(assist.PlayerId, 0.5, uid, point); - } + if (!ruleComponent.KillDeaths.TryGetValue(assist.PlayerId, out var assistKd)) + { + assistKd = new List(); + assistKd.Add(0); + assistKd.Add(0); + assistKd.Add(0); + + ruleComponent.KillDeaths.Add(actor.PlayerSession.UserId, assistKd); + } + assistKd[1]++; + } } // I dont know if we will have reward spawns or any direct rewards for players @@ -337,16 +426,37 @@ private void OnKillReported(ref KillReportedEvent ev) private void OnPointChanged(EntityUid uid, ViolenceRuleComponent component, ref TeamPointChangedEvent args) { - if (component.Victor != null) + if (component.MatchVictor != null) return; if (args.Points <= component.PointCap) return; - component.Victor = args.Team; + DoRoundEndBehavior(component); + + component.MatchVictor = args.Team; + _gameTicker.EndGameRule(uid); + if (ActiveMatches() == 0) + { + // maybe wait a bit before ending the round? + _roundEnd.DoRoundEndBehavior(RoundEndBehavior.InstantEnd, TimeSpan.FromSeconds(10)); + } // DoRoundEndBehavior(uid);??? } + private int ActiveMatches() + { + int count = 0; + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var violence, out var rule)) + { + if (GameTicker.IsGameRuleActive(uid, rule)) + count++; + } + + return count; + } + /// /// Tries to join the round /// @@ -396,13 +506,9 @@ public bool JoinRound(NetUserId player, ViolenceRuleComponent comp, ushort? pref /// /// /// - private ushort? GetEntitiesTeam(EntityUid uid, ViolenceRuleComponent comp) + private ushort? GetPlayersTeam(NetUserId userId, ViolenceRuleComponent comp) { - ActorComponent? actor = null; - if (!Resolve(uid, ref actor, false)) - return null; - - if (!comp.TeamMembers.TryGetValue(actor.PlayerSession.UserId, out var team)) + if (!comp.TeamMembers.TryGetValue(userId, out var team)) return null; return team; @@ -455,7 +561,12 @@ public bool CheckForRoundEnd(ViolenceRuleComponent comp) if (aliveTeams.Count == 1) { - comp.Victor = aliveTeams[0]; + comp.MatchVictor = aliveTeams[0]; + return true; + } + + if (aliveTeams.Count == 0) + { return true; } @@ -513,7 +624,7 @@ public void SwitchTeam(NetUserId playerId, ushort newTeamId) } /// - /// Delete the instance of the rule entity. TODO: make it automatic, if the rule doesn't have any players + /// Delete the instance of the rule entity. /// /// /// true if was deleted successfully @@ -569,7 +680,7 @@ private void OnRoundEndTextAppend(RoundEndTextAppendEvent ev) continue; - if (ruleComponent.Victor != null && _player.TryGetPlayerData(ruleComponent.Victor.Value, out var data)) // get the team data here + if (ruleComponent.MatchVictor != null && _player.TryGetPlayerData(ruleComponent.MatchVictor.Value, out var data)) // get the team data here { ev.AddLine(Loc.GetString("point-scoreboard-winner", ("player", data.UserName))); ev.AddLine(""); From 06ac5d0e513b49ff282129aeb3ecca2d897f48d4 Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Thu, 4 Apr 2024 17:24:04 +0300 Subject: [PATCH 09/13] saving equip between rounds --- .../Polymorph/Systems/PolymorphSystem.Map.cs | 4 +- .../Violence/ViolenceRuleComponent.cs | 13 ++++ .../GameRules/Violence/ViolenceRuleSystem.cs | 75 +++++++++++++++---- 3 files changed, 75 insertions(+), 17 deletions(-) diff --git a/Content.Server/Polymorph/Systems/PolymorphSystem.Map.cs b/Content.Server/Polymorph/Systems/PolymorphSystem.Map.cs index 120e04eeb08..3f9f690dd2a 100644 --- a/Content.Server/Polymorph/Systems/PolymorphSystem.Map.cs +++ b/Content.Server/Polymorph/Systems/PolymorphSystem.Map.cs @@ -24,9 +24,9 @@ private void OnRoundRestart(RoundRestartCleanupEvent _) /// /// Used internally to ensure a paused map that is - /// stores polymorphed entities. + /// stores polymorphed entities. WD EDIT to make it public /// - private void EnsurePausedMap() + public void EnsurePausedMap() { if (PausedMap != null && Exists(PausedMap)) return; diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs index e3fa39b55fd..4dd292ede2d 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs @@ -1,4 +1,5 @@ using Content.Shared.FixedPoint; +using Content.Shared.Inventory; using Robust.Shared.Map; using Robust.Shared.Network; using Robust.Shared.Player; @@ -116,6 +117,18 @@ public sealed partial class ViolenceRuleComponent : Component [DataField("money"), ViewVariables(VVAccess.ReadWrite)] public Dictionary Money { get; private set; } = new Dictionary(); + /// + /// Dictionary of a players and lists of their equipment. + /// + [DataField("savedEquip"), ViewVariables(VVAccess.ReadWrite)] + public Dictionary> SavedEquip { get; private set; } = new Dictionary>(); + + /// + /// This tries to save the slots of player's equip from the last round. + /// + [DataField("equipSlots"), ViewVariables(VVAccess.ReadWrite)] + public Dictionary EquipSlots { get; private set; } = new Dictionary(); + /// /// Reward for remaining alive at the end of the round. /// diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs index 9b2accc4b0c..ab9707f872f 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs @@ -3,6 +3,7 @@ using Content.Server._Miracle.Components; using Content.Server.GameTicking; using Content.Server.GameTicking.Rules.Components; +using Content.Server.Inventory; using Content.Server.RoundEnd; using Content.Server.Station.Systems; using Robust.Shared.Utility; @@ -10,14 +11,17 @@ using Content.Server.Maps; using Content.Server.Mind; using Content.Server.Points; +using Content.Server.Polymorph.Systems; using Content.Shared.Database; using Content.Shared.Hands.Components; +using Content.Shared.Hands.EntitySystems; using Content.Shared.Inventory; using Content.Shared.Mind; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Points; using Content.Shared.Preferences; +using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Shared.Console; using Robust.Shared.Map; @@ -31,10 +35,8 @@ namespace Content.Server._Miracle.GameRules; // TODO: respawns may suck // TODO: recheck matchflow and roundflow -// TODO: finish StartRound and OnSpawnComplete -// TODO: buying equipment and saving equipment between rounds +// TODO: buying equipment // TODO: prototypes of gamerule, uplink and startingGear, gameMapPool, the map itself -// TODO: use EnsureTeam from PointSystem? // TODO: make a menu to join the round, switch teams, leave the round, get scoreboard - мб сделает валтос public sealed class ViolenceRuleSystem : GameRuleSystem @@ -52,6 +54,10 @@ public sealed class ViolenceRuleSystem : GameRuleSystem [Dependency] private readonly GameTicker _gameTicker = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly PolymorphSystem _polymorphSystem = default!; + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly ServerInventorySystem _inventory = default!; private ISawmill _sawmill = default!; @@ -158,13 +164,35 @@ private void OnSpawnComplete(PlayerSpawnCompleteEvent ev) { EnsureComp(ev.Mob); var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out _, out var tracker, out var rule)) + while (query.MoveNext(out var uid, out var violence, out var tracker, out var rule)) { if (!GameTicker.IsGameRuleActive(uid, rule)) continue; - _respawn.AddToTracker(ev.Mob, uid, tracker); - // TODO: add money or equip to the player here + if (violence.TeamMembers.ContainsKey(ev.Player.UserId)) + _respawn.AddToTracker(ev.Mob, uid, tracker); + + if (violence.SavedEquip.TryGetValue(ev.Player.UserId, out var equip)) + { + foreach (var equipId in equip) + { + if (violence.EquipSlots.TryGetValue(equipId, out var slot)) + { + _transform.SetParent(equipId, ev.Station); + // TODO: teleport equipId right under the player + if (slot == "hand") + { + _hands.TryPickupAnyHand(ev.Mob, equipId, checkActionBlocker: false); + } + else + { + _inventory.TryEquip(ev.Mob, equipId, slot); + } + } + } + equip.Clear(); + } + } } @@ -229,7 +257,7 @@ public bool StartRound(EntityUid uid, ViolenceRuleComponent comp) } } - // TODO: recheck the rest of this code block + // TODO: recheck the rest of this method // spawning players foreach (var (playerId, teamId) in comp.TeamMembers) { @@ -289,23 +317,40 @@ public bool DoRoundEndBehavior(ViolenceRuleComponent comp) continue; + _polymorphSystem.EnsurePausedMap(); + if (session.AttachedEntity != null && TryComp(session.AttachedEntity, out var mobState) && mobState.CurrentState == MobState.Alive) { - if (TryComp(session.AttachedEntity, out var hands)) + if (_polymorphSystem.PausedMap != null) { - foreach (var (name, hand) in hands.Hands) + if (!comp.SavedEquip.ContainsKey(session.UserId)) + { + comp.SavedEquip.Add(session.UserId, new List()); + } + + if (TryComp(session.AttachedEntity, out var hands)) { - if (hand.HeldEntity != null) + foreach (var hand in _hands.EnumerateHeld(session.AttachedEntity.Value)) { - // save it + _hands.TryDrop(session.AttachedEntity.Value, hand, checkActionBlocker: false); + _transform.SetParent(hand, Transform(hand), _polymorphSystem.PausedMap.Value); + comp.SavedEquip[session.UserId].Add(hand); + comp.EquipSlots.Add(hand, "hand"); + } + } + if (TryComp(session.AttachedEntity, out var inv)) + { + var enumerator = new InventorySystem.InventorySlotEnumerator(inv); + while (enumerator.NextItem(out var item, out var slot)) + { + _inventory.TryUnequip(session.AttachedEntity.Value, slot.Name, true, true); + _transform.SetParent(item, Transform(item), _polymorphSystem.PausedMap.Value); + comp.SavedEquip[session.UserId].Add(item); + comp.EquipSlots.Add(item, slot.Name); } } - } - if (TryComp(session.AttachedEntity, out var inv)) - { - // save items in inventory } // ummm TODO: test this From f7696e7294192493abe36d51bd413d9e986edd19 Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Mon, 15 Apr 2024 09:49:20 +0300 Subject: [PATCH 10/13] buying equipment --- .../Violence/ViolenceRuleComponent.cs | 6 ++ .../GameRules/Violence/ViolenceRuleSystem.cs | 55 ++++++++++++++++++- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs index 4dd292ede2d..0f9d9d83b42 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs @@ -117,6 +117,12 @@ public sealed partial class ViolenceRuleComponent : Component [DataField("money"), ViewVariables(VVAccess.ReadWrite)] public Dictionary Money { get; private set; } = new Dictionary(); + /// + /// Dictionary of teams and dictionaries of items and their prices. Shop[team][item].Price = price of said item. Shop[team][item].Slot - slot of the said item. Item itself is prototypeID + /// + [DataField("shop"), ViewVariables(VVAccess.ReadWrite)] + public Dictionary> Shop { get; private set; } = new Dictionary>(); + /// /// Dictionary of a players and lists of their equipment. /// diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs index ab9707f872f..0d043fba717 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs @@ -35,7 +35,7 @@ namespace Content.Server._Miracle.GameRules; // TODO: respawns may suck // TODO: recheck matchflow and roundflow -// TODO: buying equipment +// TODO: test buying equipment // TODO: prototypes of gamerule, uplink and startingGear, gameMapPool, the map itself // TODO: make a menu to join the round, switch teams, leave the round, get scoreboard - мб сделает валтос @@ -182,7 +182,7 @@ private void OnSpawnComplete(PlayerSpawnCompleteEvent ev) // TODO: teleport equipId right under the player if (slot == "hand") { - _hands.TryPickupAnyHand(ev.Mob, equipId, checkActionBlocker: false); + _hands.PickupOrDrop(ev.Mob, equipId); } else { @@ -192,7 +192,6 @@ private void OnSpawnComplete(PlayerSpawnCompleteEvent ev) } equip.Clear(); } - } } @@ -489,6 +488,47 @@ private void OnPointChanged(EntityUid uid, ViolenceRuleComponent component, ref // DoRoundEndBehavior(uid);??? } + public bool TryBuyItem(NetUserId player, ViolenceRuleComponent comp, string item) + { + if (!comp.TeamMembers.TryGetValue(player, out var team)) + return false; + + if (!comp.Shop.TryGetValue(team, out var shop)) + return false; + + if (!shop.TryGetValue(item, out var listing)) + return false; + + if (!comp.Money.TryGetValue(player, out var money)) + return false; + + + if (money >= listing.Price) + { + if (!_prototypeManager.TryIndex(item, out var proto)) + return false; + + comp.Money[player] -= listing.Price; + + if (!_playerManager.GetSessionById(player).AttachedEntity.HasValue) + { + + } + + _polymorphSystem.EnsurePausedMap(); + var equip = EntityManager.SpawnEntity(item, new EntityCoordinates(_polymorphSystem.PausedMap!.Value, 0, 0)); + + if (!comp.SavedEquip.ContainsKey(player)) + { + comp.SavedEquip.Add(player, new List()); + } + comp.SavedEquip[player].Add(equip); + comp.EquipSlots.Add(equip, listing.Slot); + } + + return false; + } + private int ActiveMatches() { int count = 0; @@ -545,6 +585,15 @@ public bool JoinRound(NetUserId player, ViolenceRuleComponent comp, ushort? pref return true; } + public bool LeaveRound(NetUserId player, ViolenceRuleComponent comp) + { + if (!comp.TeamMembers.ContainsKey(player)) + return false; + + comp.TeamMembers.Remove(player); + return true; + } + /// /// Get a player's team from EntityUid and ViolenceRuleComponent /// From 2ff3c8772bb8dd6f9a1cf6106aceb5089086e632 Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Mon, 15 Apr 2024 13:28:12 +0300 Subject: [PATCH 11/13] SwitchTeamCommand done, some cleanup --- .../GameRules/Violence/SwitchTeamCommand.cs | 68 ++++++++++--------- .../Violence/ViolenceRuleComponent.cs | 7 +- .../GameRules/Violence/ViolenceRuleSystem.cs | 26 +++---- 3 files changed, 50 insertions(+), 51 deletions(-) diff --git a/Content.Server/_Miracle/GameRules/Violence/SwitchTeamCommand.cs b/Content.Server/_Miracle/GameRules/Violence/SwitchTeamCommand.cs index 679672022ca..a3e430fc75e 100644 --- a/Content.Server/_Miracle/GameRules/Violence/SwitchTeamCommand.cs +++ b/Content.Server/_Miracle/GameRules/Violence/SwitchTeamCommand.cs @@ -1,42 +1,46 @@ +using Content.Server.Administration; +using Content.Shared.Administration; using Robust.Shared.Console; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using System; using Robust.Shared.Network; -// This was written with copilot, beware. -namespace Content.Server._Miracle.GameRules +// TODO: test permissions +namespace Content.Server._Miracle.GameRules.Violence; + +[AdminCommand(AdminFlags.Admin)] +internal class SwitchTeamCommand : IConsoleCommand { - class SwitchTeamCommand : IConsoleCommand + [Dependency] private readonly IPlayerLocator _locator = default!; + + public string Command => "switchteam"; + public string Description => "Switches the player's team."; + public string Help => "switchteam "; + + public async void Execute(IConsoleShell shell, string argStr, string[] args) { - public string Command => "switchteam"; - public string Description => "Switches the player's team."; - public string Help => "switchteam "; + if (args.Length != 2) + { + shell.WriteLine("Expected exactly 2 arguments."); + return; + } + + var target = args[0]; + var located = await _locator.LookupIdByNameOrIdAsync(target); + var player = shell.Player; - public void Execute(IConsoleShell shell, string argStr, string[] args) + if (player == null) { - if (args.Length != 2) - { - shell.WriteLine("Expected exactly 2 arguments."); - return; - } - - if (!Guid.TryParse(args[0], out var guid)) - { - shell.WriteLine($"Invalid player ID: {args[0]}"); - return; - } - - var playerId = new NetUserId(guid); - - if (!ushort.TryParse(args[1], out var newTeamId)) - { - shell.WriteLine($"Invalid team ID: {args[1]}"); - return; - } - - var violenceRuleSystem = IoCManager.Resolve().GetEntitySystem(); - violenceRuleSystem.SwitchTeam(playerId, newTeamId); + shell.WriteLine("Player not found."); + return; } + + if (!ushort.TryParse(args[1], out var newTeamId)) + { + shell.WriteLine($"Invalid team ID: {args[1]}"); + return; + } + + var violenceRuleSystem = IoCManager.Resolve().GetEntitySystem(); + violenceRuleSystem.SwitchTeam(player.UserId, newTeamId); } } + diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs index 0f9d9d83b42..26f2fcf1d93 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs @@ -1,14 +1,9 @@ using Content.Shared.FixedPoint; -using Content.Shared.Inventory; using Robust.Shared.Map; using Robust.Shared.Network; -using Robust.Shared.Player; -using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; -namespace Content.Server._Miracle.GameRules; +namespace Content.Server._Miracle.GameRules.Violence; [RegisterComponent] [Access(typeof(ViolenceRuleSystem))] diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs index 0d043fba717..643d50baff5 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs @@ -1,18 +1,15 @@ -using Content.Server.GameTicking.Rules; -using System.Linq; -using Content.Server._Miracle.Components; +using System.Linq; using Content.Server.GameTicking; +using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; using Content.Server.Inventory; using Content.Server.RoundEnd; using Content.Server.Station.Systems; -using Robust.Shared.Utility; using Content.Server.KillTracking; using Content.Server.Maps; using Content.Server.Mind; using Content.Server.Points; using Content.Server.Polymorph.Systems; -using Content.Shared.Database; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Inventory; @@ -20,9 +17,9 @@ using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Points; -using Content.Shared.Preferences; using Robust.Server.GameObjects; using Robust.Server.Player; +using Robust.Shared.Utility; using Robust.Shared.Console; using Robust.Shared.Map; using Robust.Shared.Network; @@ -31,9 +28,10 @@ using Robust.Shared.Random; using Robust.Shared.Timing; -namespace Content.Server._Miracle.GameRules; +namespace Content.Server._Miracle.GameRules.Violence; // TODO: respawns may suck +// TODO: ViolencePreset, that sets mappool, teams, shop, rewards, startinggear, pointcap, RoundDuration and possibly more! // TODO: recheck matchflow and roundflow // TODO: test buying equipment // TODO: prototypes of gamerule, uplink and startingGear, gameMapPool, the map itself @@ -179,7 +177,6 @@ private void OnSpawnComplete(PlayerSpawnCompleteEvent ev) if (violence.EquipSlots.TryGetValue(equipId, out var slot)) { _transform.SetParent(equipId, ev.Station); - // TODO: teleport equipId right under the player if (slot == "hand") { _hands.PickupOrDrop(ev.Mob, equipId); @@ -204,7 +201,7 @@ private void OnMobStateChanged(MobStateChangedEvent ev) return; var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var ruleComponent, out var point, out var rule)) + while (query.MoveNext(out var uid, out var ruleComponent, out _, out var rule)) { if (!GameTicker.IsGameRuleActive(uid, rule)) continue; @@ -261,8 +258,6 @@ public bool StartRound(EntityUid uid, ViolenceRuleComponent comp) foreach (var (playerId, teamId) in comp.TeamMembers) { var player = _playerManager.GetSessionById(playerId); - if (player == null) - continue; var profile = _gameTicker.GetPlayerProfile(player); @@ -676,9 +671,12 @@ public void SwitchTeam(NetUserId playerId, ushort newTeamId) if (!GameTicker.IsGameRuleActive(uid, rule)) continue; - if (!ruleComponent.Teams.Contains(newTeamId)) + if (!ruleComponent.TeamMembers.ContainsKey(playerId)) continue; + if (!ruleComponent.Teams.Contains(newTeamId)) + return; + if (ruleComponent.TeamMembers[playerId] == newTeamId) return; @@ -686,10 +684,12 @@ public void SwitchTeam(NetUserId playerId, ushort newTeamId) var currentTeamCount = ruleComponent.TeamMembers.Values.Count(teamId => teamId == currentTeamId); var newTeamCount = ruleComponent.TeamMembers.Values.Count(teamId => teamId == newTeamId); - if (newTeamCount >= currentTeamCount) + if (newTeamCount >= currentTeamCount * 1.2) return; ruleComponent.TeamMembers[playerId] = newTeamId; + + // TODO: maybe clear equipment and money? } } From 6db6ed412e6a3cc14bc5c0f0c89cb9068fafddb2 Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Mon, 15 Apr 2024 14:54:01 +0300 Subject: [PATCH 12/13] fix retardation --- .../Violence/ViolenceRuleComponent.cs | 12 +++++- .../GameRules/Violence/ViolenceRuleSystem.cs | 43 ++++++++++++++----- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs index 26f2fcf1d93..6895bb7bd8b 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs @@ -137,11 +137,21 @@ public sealed partial class ViolenceRuleComponent : Component public int AliveReward { get; private set; } = 300; /// - /// Reward for remaining alive at the end of the round. + /// Reward for a kill. /// [DataField("killReward"), ViewVariables(VVAccess.ReadWrite)] public int KillReward { get; private set; } = 300; + /// + /// Reward for an assist. + /// + [DataField("assistReward"), ViewVariables(VVAccess.ReadWrite)] + public int AssistReward { get; private set; } = 100; + + + [DataField("skillIssuePenalty"), ViewVariables(VVAccess.ReadWrite)] + public int SkillIssuePenalty { get; private set; } = 300; + /// /// Pool of maps for this set of teams diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs index 643d50baff5..f01405c8189 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs @@ -385,19 +385,19 @@ public bool EndRound(ViolenceRuleComponent comp) private void OnKillReported(ref KillReportedEvent ev) { + ActorComponent? actor = null; + if (!Resolve(ev.Entity, ref actor, false)) + return; + var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var ruleComponent, out var point, out var rule)) { if (!GameTicker.IsGameRuleActive(uid, rule)) continue; - ActorComponent? actor = null; - if (!Resolve(uid, ref actor, false)) - return; - var team = GetPlayersTeam(actor.PlayerSession.UserId, ruleComponent); if (team == null) - return; + continue; if (!ruleComponent.KillDeaths.TryGetValue(actor.PlayerSession.UserId, out var targetKd)) { @@ -408,15 +408,17 @@ private void OnKillReported(ref KillReportedEvent ev) ruleComponent.KillDeaths.Add(actor.PlayerSession.UserId, targetKd); } + ruleComponent.KillDeaths[actor.PlayerSession.UserId][2]++; - targetKd[2]++; // YOU SUICIDED OR GOT THROWN INTO LAVA! // WHAT A GIANT FUCKING NERD! LAUGH NOW! if (ev.Primary is not KillPlayerSource player) { - _point.AdjustPointValue(ev.Entity, -1, uid, point); // krill issue penalty - // -1 point to the nerd's team? + if (!ruleComponent.Money.ContainsKey(actor.PlayerSession.UserId)) + ruleComponent.Money.TryAdd(actor.PlayerSession.UserId, 0); + + ruleComponent.Money[actor.PlayerSession.UserId] -= ruleComponent.SkillIssuePenalty; continue; } @@ -434,7 +436,12 @@ private void OnKillReported(ref KillReportedEvent ev) ruleComponent.KillDeaths.Add(actor.PlayerSession.UserId, killerKd); } - killerKd[0]++; + ruleComponent.KillDeaths[killer.PlayerId][0]++; + + if (!ruleComponent.Money.ContainsKey(killer.PlayerId)) + ruleComponent.Money.TryAdd(killer.PlayerId, 0); + + ruleComponent.Money[killer.PlayerId] += ruleComponent.KillReward; } } @@ -453,7 +460,12 @@ private void OnKillReported(ref KillReportedEvent ev) ruleComponent.KillDeaths.Add(actor.PlayerSession.UserId, assistKd); } - assistKd[1]++; + ruleComponent.KillDeaths[assist.PlayerId][1]++; + + if (!ruleComponent.Money.ContainsKey(assist.PlayerId)) + ruleComponent.Money.TryAdd(assist.PlayerId, 0); + + ruleComponent.Money[assist.PlayerId] += ruleComponent.AssistReward; } } @@ -480,9 +492,15 @@ private void OnPointChanged(EntityUid uid, ViolenceRuleComponent component, ref // maybe wait a bit before ending the round? _roundEnd.DoRoundEndBehavior(RoundEndBehavior.InstantEnd, TimeSpan.FromSeconds(10)); } - // DoRoundEndBehavior(uid);??? } + /// + /// I will rewrite this to not require component input if needed. In fact, TODO + /// + /// + /// + /// + /// public bool TryBuyItem(NetUserId player, ViolenceRuleComponent comp, string item) { if (!comp.TeamMembers.TryGetValue(player, out var team)) @@ -638,6 +656,9 @@ public int GetAliveTeamMembersCount(ushort teamId, ViolenceRuleComponent comp) /// public bool CheckForRoundEnd(ViolenceRuleComponent comp) { + if (comp.RoundState != RoundState.InProgress) + return false; + var aliveTeams = new List(); foreach (var team in comp.Teams) From 0e76d37af558af1f50843353153bd9172155eb4e Mon Sep 17 00:00:00 2001 From: melano <92106367+melanoTurbo@users.noreply.github.com> Date: Wed, 17 Apr 2024 15:04:08 +0300 Subject: [PATCH 13/13] debug map + prototypes --- .../Violence/ViolenceRuleComponent.cs | 5 +- .../GameRules/Violence/ViolenceRuleSystem.cs | 12 +- Resources/Maps/_Miracle/violenceTestMap.yml | 1433 +++++++++++++++++ .../_Miracle/GameRules/game_presets.yml | 9 - .../_Miracle/GameRules/roundstart.yml | 6 - .../_Miracle/Violence/game_presets.yml | 1 + .../_Miracle/Violence/prototypes.yml | 93 ++ .../Prototypes/_Miracle/Violence/testmap.yml | 27 + 8 files changed, 1566 insertions(+), 20 deletions(-) create mode 100644 Resources/Maps/_Miracle/violenceTestMap.yml delete mode 100644 Resources/Prototypes/_Miracle/GameRules/game_presets.yml delete mode 100644 Resources/Prototypes/_Miracle/GameRules/roundstart.yml create mode 100644 Resources/Prototypes/_Miracle/Violence/game_presets.yml create mode 100644 Resources/Prototypes/_Miracle/Violence/prototypes.yml create mode 100644 Resources/Prototypes/_Miracle/Violence/testmap.yml diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs index 6895bb7bd8b..1aeb83b30e6 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleComponent.cs @@ -97,7 +97,7 @@ public sealed partial class ViolenceRuleComponent : Component /// /// The duration of a round. /// - [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + [DataField("roundDuration", customTypeSerializer: typeof(TimeOffsetSerializer))] public TimeSpan RoundDuration = TimeSpan.FromMinutes(5); /// @@ -149,6 +149,9 @@ public sealed partial class ViolenceRuleComponent : Component public int AssistReward { get; private set; } = 100; + /// + /// Penalty for suicide or being thrown in lava or something. + /// [DataField("skillIssuePenalty"), ViewVariables(VVAccess.ReadWrite)] public int SkillIssuePenalty { get; private set; } = 300; diff --git a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs index f01405c8189..956e9f735dc 100644 --- a/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs +++ b/Content.Server/_Miracle/GameRules/Violence/ViolenceRuleSystem.cs @@ -31,11 +31,12 @@ namespace Content.Server._Miracle.GameRules.Violence; // TODO: respawns may suck -// TODO: ViolencePreset, that sets mappool, teams, shop, rewards, startinggear, pointcap, RoundDuration and possibly more! -// TODO: recheck matchflow and roundflow +// TODO: ViolencePreset, that sets MapPool, teams, Shop, rewards, StartingGear, PointCap, RoundDuration and possibly more! +// TODO: recheck match flow and round flow // TODO: test buying equipment -// TODO: prototypes of gamerule, uplink and startingGear, gameMapPool, the map itself +// TODO: prototypes of gameRule, uplink? and startingGear, gameMapPool, the map itself // TODO: make a menu to join the round, switch teams, leave the round, get scoreboard - мб сделает валтос +// TODO: localize kill callouts public sealed class ViolenceRuleSystem : GameRuleSystem { @@ -257,7 +258,10 @@ public bool StartRound(EntityUid uid, ViolenceRuleComponent comp) // spawning players foreach (var (playerId, teamId) in comp.TeamMembers) { - var player = _playerManager.GetSessionById(playerId); + _playerManager.TryGetSessionById(playerId, out var player); + + if (player == null) + continue; var profile = _gameTicker.GetPlayerProfile(player); diff --git a/Resources/Maps/_Miracle/violenceTestMap.yml b/Resources/Maps/_Miracle/violenceTestMap.yml new file mode 100644 index 00000000000..5fa9f845032 --- /dev/null +++ b/Resources/Maps/_Miracle/violenceTestMap.yml @@ -0,0 +1,1433 @@ +meta: + format: 6 + postmapinit: false +tilemap: + 0: Space + 81: FloorReinforced + 130: Lattice +entities: +- proto: "" + entities: + - uid: 1 + components: + - type: MetaData + name: map 1337 + - type: Transform + - type: Map + - type: PhysicsMap + - type: GridTree + - type: MovedGrids + - type: Broadphase + - type: OccluderTree + - type: LoadedMap + - uid: 2 + components: + - type: MetaData + name: grid + - type: Transform + pos: -8.028967,2.9101562 + parent: 1 + - type: MapGrid + chunks: + 0,0: + ind: 0,0 + tiles: UQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + version: 6 + 0,-1: + ind: 0,-1 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAA + version: 6 + 1,0: + ind: 1,0 + tiles: UQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + version: 6 + 1,-1: + ind: 1,-1 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + version: 6 + -1,-1: + ind: -1,-1 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + version: 6 + - type: Broadphase + - type: Physics + bodyStatus: InAir + angularDamping: 0.05 + linearDamping: 0.05 + fixedRotation: False + bodyType: Dynamic + - type: Fixtures + fixtures: {} + - type: OccluderTree + - type: SpreaderGrid + - type: Shuttle + - type: GridPathfinding + - type: Gravity + gravityShakeSound: !type:SoundPathSpecifier + path: /Audio/Effects/alert.ogg + - type: DecalGrid + chunkCollection: + version: 2 + nodes: + - node: + color: '#FFFFFFFF' + id: 1 + decals: + 9: 6.9687967,-1.2083154 + - node: + color: '#FFFFFFFF' + id: 2 + decals: + 3: 9.988901,-0.9758549 + - node: + angle: 1.5707963267948966 rad + color: '#FFFFFFFF' + id: ArrowsGreyscale + decals: + 5: 8.767031,-3.1789799 + 6: 6.062023,-2.2180424 + 8: 8.945265,-2.1242924 + - node: + color: '#FFFFFFFF' + id: disk + decals: + 0: 11.031746,-3.1004572 + 1: 11.031746,-3.1004572 + - type: GridAtmosphere + version: 2 + data: + tiles: + 0,0: + 0: 255 + 1,0: + 0: 255 + 1,-2: + 0: 65535 + 1,-1: + 0: 65535 + 2,0: + 0: 255 + 3,0: + 0: 255 + 0,-3: + 0: 65280 + 0,-2: + 0: 65535 + 0,-1: + 0: 65535 + 1,-3: + 0: 65280 + 2,-3: + 0: 65280 + 2,-2: + 0: 65535 + 2,-1: + 0: 65535 + 3,-3: + 0: 65280 + 3,-2: + 0: 65535 + 3,-1: + 0: 65535 + 4,0: + 0: 17 + 4,-3: + 0: 4352 + 4,-2: + 0: 4369 + 4,-1: + 0: 4369 + -1,-2: + 0: 61440 + -1,-1: + 0: 4095 + uniqueMixes: + - volume: 2500 + temperature: 293.15 + moles: + - 21.824879 + - 82.10312 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + chunkSize: 4 + - type: GasTileOverlay + - type: RadiationGridResistance + - uid: 182 + components: + - type: MetaData + name: grid + - type: Transform + pos: -8.028967,1.9101562 + parent: 1 + - type: MapGrid + chunks: + 0,0: + ind: 0,0 + tiles: ggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + version: 6 + - type: Broadphase + - type: Physics + bodyStatus: InAir + angularDamping: 0.05 + linearDamping: 0.05 + fixedRotation: False + bodyType: Dynamic + - type: Fixtures + fixtures: {} + - type: OccluderTree + - type: SpreaderGrid + - type: Shuttle + - type: GridPathfinding + - type: Gravity + gravityShakeSound: !type:SoundPathSpecifier + path: /Audio/Effects/alert.ogg + - type: DecalGrid + chunkCollection: + version: 2 + nodes: [] + - uid: 183 + components: + - type: MetaData + name: grid + - type: Transform + pos: -8.028967,0.91015625 + parent: 1 + - type: MapGrid + chunks: + 0,0: + ind: 0,0 + tiles: ggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + version: 6 + - type: Broadphase + - type: Physics + bodyStatus: InAir + angularDamping: 0.05 + linearDamping: 0.05 + fixedRotation: False + bodyType: Dynamic + - type: Fixtures + fixtures: {} + - type: OccluderTree + - type: SpreaderGrid + - type: Shuttle + - type: GridPathfinding + - type: Gravity + gravityShakeSound: !type:SoundPathSpecifier + path: /Audio/Effects/alert.ogg + - type: DecalGrid + chunkCollection: + version: 2 + nodes: [] + - uid: 184 + components: + - type: MetaData + name: grid + - type: Transform + pos: -8.028967,-0.08984375 + parent: 1 + - type: MapGrid + chunks: + 0,0: + ind: 0,0 + tiles: ggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + version: 6 + - type: Broadphase + - type: Physics + bodyStatus: InAir + angularDamping: 0.05 + linearDamping: 0.05 + fixedRotation: False + bodyType: Dynamic + - type: Fixtures + fixtures: {} + - type: OccluderTree + - type: SpreaderGrid + - type: Shuttle + - type: GridPathfinding + - type: Gravity + gravityShakeSound: !type:SoundPathSpecifier + path: /Audio/Effects/alert.ogg + - type: DecalGrid + chunkCollection: + version: 2 + nodes: [] + - uid: 185 + components: + - type: MetaData + name: grid + - type: Transform + pos: -8.028967,-1.0898438 + parent: 1 + - type: MapGrid + chunks: + 0,0: + ind: 0,0 + tiles: ggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + version: 6 + - type: Broadphase + - type: Physics + bodyStatus: InAir + angularDamping: 0.05 + linearDamping: 0.05 + fixedRotation: False + bodyType: Dynamic + - type: Fixtures + fixtures: {} + - type: OccluderTree + - type: SpreaderGrid + - type: Shuttle + - type: GridPathfinding + - type: Gravity + gravityShakeSound: !type:SoundPathSpecifier + path: /Audio/Effects/alert.ogg + - type: DecalGrid + chunkCollection: + version: 2 + nodes: [] + - uid: 186 + components: + - type: MetaData + name: grid + - type: Transform + pos: -8.028967,-2.0898438 + parent: 1 + - type: MapGrid + chunks: + 0,0: + ind: 0,0 + tiles: ggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + version: 6 + - type: Broadphase + - type: Physics + bodyStatus: InAir + angularDamping: 0.05 + linearDamping: 0.05 + fixedRotation: False + bodyType: Dynamic + - type: Fixtures + fixtures: {} + - type: OccluderTree + - type: SpreaderGrid + - type: Shuttle + - type: GridPathfinding + - type: Gravity + gravityShakeSound: !type:SoundPathSpecifier + path: /Audio/Effects/alert.ogg + - type: DecalGrid + chunkCollection: + version: 2 + nodes: [] + - uid: 187 + components: + - type: MetaData + name: grid + - type: Transform + pos: -8.028967,-3.0898438 + parent: 1 + - type: MapGrid + chunks: + 0,0: + ind: 0,0 + tiles: ggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + version: 6 + - type: Broadphase + - type: Physics + bodyStatus: InAir + angularDamping: 0.05 + linearDamping: 0.05 + fixedRotation: False + bodyType: Dynamic + - type: Fixtures + fixtures: {} + - type: OccluderTree + - type: SpreaderGrid + - type: Shuttle + - type: GridPathfinding + - type: Gravity + gravityShakeSound: !type:SoundPathSpecifier + path: /Audio/Effects/alert.ogg + - type: DecalGrid + chunkCollection: + version: 2 + nodes: [] + - uid: 188 + components: + - type: MetaData + name: grid + - type: Transform + pos: -8.028967,-4.0898438 + parent: 1 + - type: MapGrid + chunks: + 0,0: + ind: 0,0 + tiles: ggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + version: 6 + - type: Broadphase + - type: Physics + bodyStatus: InAir + angularDamping: 0.05 + linearDamping: 0.05 + fixedRotation: False + bodyType: Dynamic + - type: Fixtures + fixtures: {} + - type: OccluderTree + - type: SpreaderGrid + - type: Shuttle + - type: GridPathfinding + - type: Gravity + gravityShakeSound: !type:SoundPathSpecifier + path: /Audio/Effects/alert.ogg + - type: DecalGrid + chunkCollection: + version: 2 + nodes: [] + - uid: 189 + components: + - type: MetaData + name: grid + - type: Transform + pos: -8.028967,-5.0898438 + parent: 1 + - type: MapGrid + chunks: + 0,0: + ind: 0,0 + tiles: ggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + version: 6 + - type: Broadphase + - type: Physics + bodyStatus: InAir + angularDamping: 0.05 + linearDamping: 0.05 + fixedRotation: False + bodyType: Dynamic + - type: Fixtures + fixtures: {} + - type: OccluderTree + - type: SpreaderGrid + - type: Shuttle + - type: GridPathfinding + - type: Gravity + gravityShakeSound: !type:SoundPathSpecifier + path: /Audio/Effects/alert.ogg + - type: DecalGrid + chunkCollection: + version: 2 + nodes: [] +- proto: AdminHypo + entities: + - uid: 175 + components: + - type: Transform + pos: 5.215679,0.5063157 + parent: 2 +- proto: AirlockGlass + entities: + - uid: 178 + components: + - type: Transform + pos: 1.5,-0.5 + parent: 2 +- proto: AlwaysPoweredWallLight + entities: + - uid: 3 + components: + - type: Transform + anchored: False + pos: 3.5,0.5 + parent: 2 + - uid: 4 + components: + - type: Transform + pos: 15.5,0.5 + parent: 2 + - uid: 5 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 15.5,-4.5 + parent: 2 + - uid: 6 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 14.5,-8.5 + parent: 2 + - uid: 7 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 9.5,-8.5 + parent: 2 + - uid: 8 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 3.5,-8.5 + parent: 2 + - uid: 9 + components: + - type: Transform + anchored: False + rot: 1.5707963267948966 rad + pos: 1.5,-1.5 + parent: 2 + - uid: 10 + components: + - type: Transform + anchored: False + pos: 9.5,0.5 + parent: 2 +- proto: APCHyperCapacity + entities: + - uid: 11 + components: + - type: Transform + pos: -0.5,-2.5 + parent: 2 +- proto: BaseUplinkRadioDebug + entities: + - uid: 12 + components: + - type: Transform + pos: 12.542767,-5.162384 + parent: 2 +- proto: CableApcExtension + entities: + - uid: 13 + components: + - type: Transform + pos: -0.5,-2.5 + parent: 2 + - uid: 14 + components: + - type: Transform + pos: -0.5,-2.5 + parent: 2 + - uid: 15 + components: + - type: Transform + pos: -1.5,-2.5 + parent: 2 + - uid: 16 + components: + - type: Transform + pos: 0.5,-2.5 + parent: 2 + - uid: 17 + components: + - type: Transform + pos: 4.5,-7.5 + parent: 2 + - uid: 18 + components: + - type: Transform + pos: 7.5,-7.5 + parent: 2 + - uid: 19 + components: + - type: Transform + pos: 9.5,-7.5 + parent: 2 + - uid: 20 + components: + - type: Transform + pos: 10.5,-3.5 + parent: 2 + - uid: 21 + components: + - type: Transform + pos: 10.5,-2.5 + parent: 2 + - uid: 22 + components: + - type: Transform + pos: 11.5,-2.5 + parent: 2 + - uid: 23 + components: + - type: Transform + pos: 12.5,-2.5 + parent: 2 + - uid: 24 + components: + - type: Transform + pos: 13.5,-2.5 + parent: 2 + - uid: 25 + components: + - type: Transform + pos: 14.5,-2.5 + parent: 2 + - uid: 26 + components: + - type: Transform + pos: 2.5,-7.5 + parent: 2 + - uid: 27 + components: + - type: Transform + pos: 14.5,-7.5 + parent: 2 + - uid: 28 + components: + - type: Transform + pos: 11.5,-7.5 + parent: 2 + - uid: 29 + components: + - type: Transform + pos: 13.5,-7.5 + parent: 2 + - uid: 30 + components: + - type: Transform + pos: 12.5,-7.5 + parent: 2 + - uid: 31 + components: + - type: Transform + pos: 3.5,-7.5 + parent: 2 + - uid: 32 + components: + - type: Transform + pos: 6.5,-7.5 + parent: 2 + - uid: 33 + components: + - type: Transform + pos: 8.5,-7.5 + parent: 2 + - uid: 34 + components: + - type: Transform + pos: 10.5,-7.5 + parent: 2 + - uid: 35 + components: + - type: Transform + pos: 5.5,-7.5 + parent: 2 + - uid: 36 + components: + - type: Transform + pos: 1.5,-2.5 + parent: 2 + - uid: 37 + components: + - type: Transform + pos: 0.5,-2.5 + parent: 2 + - uid: 38 + components: + - type: Transform + pos: 2.5,-2.5 + parent: 2 + - uid: 39 + components: + - type: Transform + pos: 8.5,-2.5 + parent: 2 + - uid: 40 + components: + - type: Transform + pos: 4.5,-2.5 + parent: 2 + - uid: 41 + components: + - type: Transform + pos: 6.5,-2.5 + parent: 2 + - uid: 42 + components: + - type: Transform + pos: 3.5,-2.5 + parent: 2 + - uid: 43 + components: + - type: Transform + pos: 5.5,-2.5 + parent: 2 + - uid: 44 + components: + - type: Transform + pos: 9.5,-2.5 + parent: 2 + - uid: 45 + components: + - type: Transform + pos: 10.5,-5.5 + parent: 2 + - uid: 46 + components: + - type: Transform + pos: 7.5,-2.5 + parent: 2 + - uid: 47 + components: + - type: Transform + pos: 10.5,-6.5 + parent: 2 + - uid: 48 + components: + - type: Transform + pos: 10.5,-4.5 + parent: 2 +- proto: CableHV + entities: + - uid: 49 + components: + - type: Transform + pos: -2.5,-2.5 + parent: 2 + - uid: 50 + components: + - type: Transform + pos: -1.5,-2.5 + parent: 2 +- proto: CableMV + entities: + - uid: 51 + components: + - type: Transform + pos: 5.5,-2.5 + parent: 2 + - uid: 52 + components: + - type: Transform + pos: 6.5,-2.5 + parent: 2 + - uid: 53 + components: + - type: Transform + pos: 8.5,-2.5 + parent: 2 + - uid: 54 + components: + - type: Transform + pos: 7.5,-2.5 + parent: 2 + - uid: 55 + components: + - type: Transform + pos: 3.5,-2.5 + parent: 2 + - uid: 56 + components: + - type: Transform + pos: 1.5,-2.5 + parent: 2 + - uid: 57 + components: + - type: Transform + pos: 4.5,-2.5 + parent: 2 + - uid: 58 + components: + - type: Transform + pos: -1.5,-2.5 + parent: 2 + - uid: 59 + components: + - type: Transform + pos: -0.5,-2.5 + parent: 2 + - uid: 60 + components: + - type: Transform + pos: 0.5,-2.5 + parent: 2 + - uid: 61 + components: + - type: Transform + pos: 15.5,-2.5 + parent: 2 + - uid: 62 + components: + - type: Transform + pos: 2.5,-2.5 + parent: 2 + - uid: 63 + components: + - type: Transform + pos: 0.5,-2.5 + parent: 2 + - uid: 64 + components: + - type: Transform + pos: 10.5,-2.5 + parent: 2 + - uid: 65 + components: + - type: Transform + pos: 11.5,-2.5 + parent: 2 + - uid: 66 + components: + - type: Transform + pos: 12.5,-2.5 + parent: 2 + - uid: 67 + components: + - type: Transform + pos: 13.5,-2.5 + parent: 2 + - uid: 68 + components: + - type: Transform + pos: 14.5,-2.5 + parent: 2 + - uid: 69 + components: + - type: Transform + pos: 9.5,-2.5 + parent: 2 +- proto: DebugGenerator + entities: + - uid: 70 + components: + - type: Transform + pos: -2.5,-2.5 + parent: 2 + - uid: 71 + components: + - type: Transform + pos: -2.5,-2.5 + parent: 2 +- proto: EmagUnlimited + entities: + - uid: 168 + components: + - type: Transform + pos: 2.6720915,-2.5398998 + parent: 2 +- proto: FloorChasmEntity + entities: + - uid: 169 + components: + - type: Transform + pos: 1.5,-4.5 + parent: 2 + - uid: 170 + components: + - type: Transform + pos: 1.5,-5.5 + parent: 2 + - uid: 171 + components: + - type: Transform + pos: 1.5,-6.5 + parent: 2 + - uid: 172 + components: + - type: Transform + pos: 1.5,-7.5 + parent: 2 +- proto: GravityGeneratorMini + entities: + - uid: 173 + components: + - type: Transform + pos: -0.5,-3.5 + parent: 2 +- proto: HardlightSpearImplanter + entities: + - uid: 72 + components: + - type: Transform + pos: 11.990374,-5.15222 + parent: 2 +- proto: MeleeDebugGib + entities: + - uid: 73 + components: + - type: Transform + pos: 10.240486,-5.3331795 + parent: 2 +- proto: RCDExperimental + entities: + - uid: 174 + components: + - type: Transform + pos: 4.5301466,0.42327738 + parent: 2 +- proto: Recycler + entities: + - uid: 167 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 1.5,-2.5 + parent: 2 +- proto: SpawnPointLatejoinViolenceOne + entities: + - uid: 181 + components: + - type: Transform + pos: 7.5,-2.5 + parent: 2 +- proto: SpawnPointLatejoinViolenceTwo + entities: + - uid: 74 + components: + - type: Transform + pos: 10.5,-2.5 + parent: 2 +- proto: SpawnPointViolenceOne + entities: + - uid: 180 + components: + - type: Transform + pos: 7.5,-1.5 + parent: 2 +- proto: SpawnPointViolenceTwo + entities: + - uid: 75 + components: + - type: Transform + pos: 10.5,-1.5 + parent: 2 +- proto: SubstationBasic + entities: + - uid: 76 + components: + - type: Transform + pos: -1.5,-2.5 + parent: 2 + - uid: 77 + components: + - type: Transform + pos: -1.5,-2.5 + parent: 2 +- proto: TablePlasmaGlass + entities: + - uid: 78 + components: + - type: Transform + pos: 9.5,-5.5 + parent: 2 + - uid: 79 + components: + - type: Transform + pos: 7.5,-5.5 + parent: 2 + - uid: 80 + components: + - type: Transform + pos: 8.5,-5.5 + parent: 2 + - uid: 81 + components: + - type: Transform + pos: 6.5,-5.5 + parent: 2 + - uid: 82 + components: + - type: Transform + pos: 12.5,-5.5 + parent: 2 + - uid: 83 + components: + - type: Transform + pos: 11.5,-5.5 + parent: 2 + - uid: 84 + components: + - type: Transform + pos: 10.5,-5.5 + parent: 2 +- proto: TargetClown + entities: + - uid: 85 + components: + - type: Transform + pos: 15.5,-2.5 + parent: 2 +- proto: TargetStrange + entities: + - uid: 86 + components: + - type: Transform + pos: 15.5,-3.5 + parent: 2 +- proto: TargetSyndicate + entities: + - uid: 87 + components: + - type: Transform + pos: 15.5,-1.5 + parent: 2 +- proto: WallPlastitaniumIndestructible + entities: + - uid: 88 + components: + - type: Transform + pos: 11.5,-9.5 + parent: 2 + - uid: 89 + components: + - type: Transform + pos: 0.5,-8.5 + parent: 2 + - uid: 90 + components: + - type: Transform + pos: 0.5,-9.5 + parent: 2 + - uid: 91 + components: + - type: Transform + pos: 0.5,1.5 + parent: 2 + - uid: 92 + components: + - type: Transform + pos: 0.5,0.5 + parent: 2 + - uid: 93 + components: + - type: Transform + pos: 0.5,-0.5 + parent: 2 + - uid: 94 + components: + - type: Transform + pos: 0.5,-1.5 + parent: 2 + - uid: 95 + components: + - type: Transform + pos: 0.5,-2.5 + parent: 2 + - uid: 96 + components: + - type: Transform + pos: 0.5,-3.5 + parent: 2 + - uid: 97 + components: + - type: Transform + pos: 0.5,-4.5 + parent: 2 + - uid: 98 + components: + - type: Transform + pos: 0.5,-5.5 + parent: 2 + - uid: 99 + components: + - type: Transform + pos: 0.5,-6.5 + parent: 2 + - uid: 100 + components: + - type: Transform + pos: 0.5,-7.5 + parent: 2 + - uid: 101 + components: + - type: Transform + pos: 12.5,-9.5 + parent: 2 + - uid: 102 + components: + - type: Transform + pos: 13.5,-9.5 + parent: 2 + - uid: 103 + components: + - type: Transform + pos: 14.5,-9.5 + parent: 2 + - uid: 104 + components: + - type: Transform + pos: 15.5,-9.5 + parent: 2 + - uid: 105 + components: + - type: Transform + pos: 16.5,-9.5 + parent: 2 + - uid: 106 + components: + - type: Transform + pos: 1.5,-9.5 + parent: 2 + - uid: 107 + components: + - type: Transform + pos: 2.5,-9.5 + parent: 2 + - uid: 108 + components: + - type: Transform + pos: 3.5,-9.5 + parent: 2 + - uid: 109 + components: + - type: Transform + pos: 4.5,-9.5 + parent: 2 + - uid: 110 + components: + - type: Transform + pos: 5.5,-9.5 + parent: 2 + - uid: 111 + components: + - type: Transform + pos: 6.5,-9.5 + parent: 2 + - uid: 112 + components: + - type: Transform + pos: 7.5,-9.5 + parent: 2 + - uid: 113 + components: + - type: Transform + pos: 8.5,-9.5 + parent: 2 + - uid: 114 + components: + - type: Transform + pos: 9.5,-9.5 + parent: 2 + - uid: 115 + components: + - type: Transform + pos: 10.5,-9.5 + parent: 2 + - uid: 116 + components: + - type: Transform + pos: 16.5,1.5 + parent: 2 + - uid: 117 + components: + - type: Transform + pos: 16.5,-8.5 + parent: 2 + - uid: 118 + components: + - type: Transform + pos: 16.5,-7.5 + parent: 2 + - uid: 119 + components: + - type: Transform + pos: 16.5,-6.5 + parent: 2 + - uid: 120 + components: + - type: Transform + pos: 16.5,-5.5 + parent: 2 + - uid: 121 + components: + - type: Transform + pos: 16.5,-4.5 + parent: 2 + - uid: 122 + components: + - type: Transform + pos: 16.5,-3.5 + parent: 2 + - uid: 123 + components: + - type: Transform + pos: 16.5,-2.5 + parent: 2 + - uid: 124 + components: + - type: Transform + pos: 16.5,-1.5 + parent: 2 + - uid: 125 + components: + - type: Transform + pos: 16.5,-0.5 + parent: 2 + - uid: 126 + components: + - type: Transform + pos: 16.5,0.5 + parent: 2 + - uid: 127 + components: + - type: Transform + pos: 6.5,1.5 + parent: 2 + - uid: 128 + components: + - type: Transform + pos: 5.5,1.5 + parent: 2 + - uid: 129 + components: + - type: Transform + pos: 4.5,1.5 + parent: 2 + - uid: 130 + components: + - type: Transform + pos: 3.5,1.5 + parent: 2 + - uid: 131 + components: + - type: Transform + pos: 2.5,1.5 + parent: 2 + - uid: 132 + components: + - type: Transform + pos: 1.5,1.5 + parent: 2 + - uid: 133 + components: + - type: Transform + pos: 15.5,1.5 + parent: 2 + - uid: 134 + components: + - type: Transform + pos: 14.5,1.5 + parent: 2 + - uid: 135 + components: + - type: Transform + pos: 13.5,1.5 + parent: 2 + - uid: 136 + components: + - type: Transform + pos: 12.5,1.5 + parent: 2 + - uid: 137 + components: + - type: Transform + pos: 11.5,1.5 + parent: 2 + - uid: 138 + components: + - type: Transform + pos: 10.5,1.5 + parent: 2 + - uid: 139 + components: + - type: Transform + pos: 9.5,1.5 + parent: 2 + - uid: 140 + components: + - type: Transform + pos: 8.5,1.5 + parent: 2 + - uid: 141 + components: + - type: Transform + pos: 7.5,1.5 + parent: 2 + - uid: 142 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -0.5,-1.5 + parent: 2 + - uid: 143 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -1.5,-1.5 + parent: 2 + - uid: 144 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -2.5,-1.5 + parent: 2 + - uid: 145 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,-1.5 + parent: 2 + - uid: 146 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,-2.5 + parent: 2 + - uid: 147 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,-3.5 + parent: 2 + - uid: 148 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,-4.5 + parent: 2 + - uid: 149 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -2.5,-4.5 + parent: 2 + - uid: 150 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -1.5,-4.5 + parent: 2 + - uid: 151 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -0.5,-4.5 + parent: 2 +- proto: WallShuttle + entities: + - uid: 176 + components: + - type: Transform + pos: 2.5,-0.5 + parent: 2 + - uid: 177 + components: + - type: Transform + pos: 2.5,0.5 + parent: 2 +- proto: WeaponAntiqueLaser + entities: + - uid: 161 + components: + - type: Transform + pos: 7.7074156,-5.3778133 + parent: 2 + - uid: 162 + components: + - type: Transform + pos: 7.7074156,-5.3778133 + parent: 2 +- proto: WeaponLightMachineGunL6 + entities: + - uid: 152 + components: + - type: Transform + pos: 10.812881,-5.185898 + parent: 2 +- proto: WeaponMinigun + entities: + - uid: 153 + components: + - type: Transform + pos: 11.87611,-5.377356 + parent: 2 +- proto: WeaponPistolDebug + entities: + - uid: 157 + components: + - type: Transform + pos: 9.910168,-5.310079 + parent: 2 +- proto: WeaponPistolN1984 + entities: + - uid: 154 + components: + - type: Transform + pos: 10.672256,-5.5374603 + parent: 2 +- proto: WeaponPoweredCrossbow + entities: + - uid: 160 + components: + - type: Transform + pos: 8.176166,-5.541876 + parent: 2 +- proto: WeaponPulseRifle + entities: + - uid: 155 + components: + - type: Transform + pos: 12.50983,-5.482825 + parent: 2 +- proto: WeaponShotgunBulldog + entities: + - uid: 159 + components: + - type: Transform + pos: 8.575228,-5.320423 + parent: 2 +- proto: WeaponStaffHealing + entities: + - uid: 166 + components: + - type: Transform + pos: 6.2598753,-5.3296022 + parent: 2 +- proto: WeaponSubMachineGunC20r + entities: + - uid: 156 + components: + - type: Transform + pos: 11.4912,-5.3998337 + parent: 2 +- proto: WeaponTeslaGun + entities: + - uid: 158 + components: + - type: Transform + pos: 9.212797,-5.488595 + parent: 2 +- proto: WeaponTurretHostile + entities: + - uid: 179 + components: + - type: Transform + pos: 1.5,0.5 + parent: 2 +- proto: WeaponWandDeath + entities: + - uid: 163 + components: + - type: Transform + pos: 7.1136885,-5.448126 + parent: 2 +- proto: WeaponWandFireball + entities: + - uid: 164 + components: + - type: Transform + pos: 6.9261885,-5.307501 + parent: 2 +- proto: WeaponWandPolymorphCarp + entities: + - uid: 165 + components: + - type: Transform + pos: 6.5980635,-5.354376 + parent: 2 +... diff --git a/Resources/Prototypes/_Miracle/GameRules/game_presets.yml b/Resources/Prototypes/_Miracle/GameRules/game_presets.yml deleted file mode 100644 index 04ec17e02ec..00000000000 --- a/Resources/Prototypes/_Miracle/GameRules/game_presets.yml +++ /dev/null @@ -1,9 +0,0 @@ -- type: gamePreset - id: Violence - alias: - - violence - name: Violence - description: Violence - showInVote: false - rules: - - Violence diff --git a/Resources/Prototypes/_Miracle/GameRules/roundstart.yml b/Resources/Prototypes/_Miracle/GameRules/roundstart.yml deleted file mode 100644 index 2f4f0faec0e..00000000000 --- a/Resources/Prototypes/_Miracle/GameRules/roundstart.yml +++ /dev/null @@ -1,6 +0,0 @@ -- type: entity - id: Violence - parent: BaseGameRule - noSpawn: true - components: - - type: ViolenceRule diff --git a/Resources/Prototypes/_Miracle/Violence/game_presets.yml b/Resources/Prototypes/_Miracle/Violence/game_presets.yml new file mode 100644 index 00000000000..e02abfc9b0e --- /dev/null +++ b/Resources/Prototypes/_Miracle/Violence/game_presets.yml @@ -0,0 +1 @@ + diff --git a/Resources/Prototypes/_Miracle/Violence/prototypes.yml b/Resources/Prototypes/_Miracle/Violence/prototypes.yml new file mode 100644 index 00000000000..4cf03b2380c --- /dev/null +++ b/Resources/Prototypes/_Miracle/Violence/prototypes.yml @@ -0,0 +1,93 @@ +- type: gamePreset + id: ViolenceDebug + alias: + - violenceTest + - violenceDebug + name: Violence + description: Violence + showInVote: false + rules: + - ViolenceTestRule + +- type: entity + id: ViolenceTestRule + parent: BaseGameRule + noSpawn: true + components: + - type: ViolenceRule + teams: + shop: + pointCap: 5 + roundDuration: 5 # timespan issues? + killReward: 300 + assistReward: 100 + aliveReward: 300 + skillIssuePenalty: 300 # penalty for suicide or being thrown in lava + mapPool: ViolenceTestMapPool + +- type: gameMapPool + id: ViolenceTestMapPool + maps: + - ViolenceTestMap + +- type: entity + id: SpawnPointViolenceOne + parent: SpawnPointJobBase + name: violence team 1 spawner + components: + - type: SpawnPoint + job_id: ViolenceJob + teamId: 1 + - type: Sprite # TODO: edit this + layers: + - state: green + - state: qm + +- type: entity + name: violence latejoin spawn point (team 1) + id: SpawnPointLatejoinViolenceOne + parent: SpawnPointJobBase + components: + - type: Sprite + state: green + - type: SpawnPoint + spawn_type: LateJoin + teamId: 1 + +- type: entity + id: SpawnPointViolenceTwo + parent: SpawnPointJobBase + name: violence team 2 spawner + components: + - type: SpawnPoint + job_id: ViolenceJob + teamId: 2 + - type: Sprite # TODO: edit this + layers: + - state: green + - state: qm + +- type: entity + name: violence latejoin spawn point (team 2) + id: SpawnPointLatejoinViolenceTwo + parent: SpawnPointJobBase + components: + - type: Sprite + state: green + - type: SpawnPoint + spawn_type: LateJoin + teamId: 2 + +- type: job + id: ViolenceJob + name: job-name-violence + description: job-description-violence + playTimeTracker: JobViolence + startingGear: CentcomGear + icon: "JobIconPassenger" + canBeAntag: false + access: + - Maintenance + +- type: playTimeTracker + id: JobViolence diff --git a/Resources/Prototypes/_Miracle/Violence/testmap.yml b/Resources/Prototypes/_Miracle/Violence/testmap.yml new file mode 100644 index 00000000000..355d85221f7 --- /dev/null +++ b/Resources/Prototypes/_Miracle/Violence/testmap.yml @@ -0,0 +1,27 @@ +- type: gameMap + id: ViolenceTestMap + mapName: Meteor Arena + mapPath: /Maps/_Miracle/violenceTestMap.yml + minPlayers: 0 + stations: + Arena: + stationProto: ViolenceStation + components: + - type: StationNameSetup + mapNameTemplate: "Violence" + - type: StationJobs # maybe exclude this + overflowJobs: + - ViolenceJob + availableJobs: + ViolenceJob: [ -1, -1 ] + +- type: entity + id: ViolenceStation + parent: + - BaseStation + - BaseStationJobsSpawning + - BaseStationRecords + - BaseStationNanotrasen + noSpawn: true + components: + - type: Transform