Cherrypicks 3 (#382)
* Mobs burn to ashes on excessive heat damage (#26971) * mobs burn to ashes on excessive heat damage * remove comment, remove random lines I didn't mean to add * combine code into behavior * clean unused * fix namespace * drop next to * fix spawn entities behavior spawning entities outside container * fix burning to ash not working on all mobs (#27158) * add ghostnado button to warp menu (#27556) * add ghostnado button to warp menu * translator ops --------- Co-authored-by: deltanedas <@deltanedas:kde.org> * Make arguments and parameters wrap to one variable per line (#27766) * Fix ghosts getting spawned in nullspace (#27617) * Add tests for ghost spawn position * Make ghosts spawn immediately * Format mind system * Move ghost spawning to GhostSystem * Spawn ghost on grid or map This fixes the ghosts being attached the parent entity instead of the grid. * Move logging out of the ghost system * Make round start observer spawn using GhostSystem * Move GameTicker ghost spawning to GhostSystem Moved the more robust character name selection code over. Moved the TimeOfDeath code over. Added canReturn logic. * Add overrides and default for ghost spawn coordinates * Add warning log to ghost spawn fail * Clean up test * Dont spawn ghost on map delete * Minor changes to the role test * Fix role test failing to spawn ghost It was failing the map check due to using Nullspace * Fix ghost tests when running in parallel Not sure what happened, but it seems to be because they were running simultaneously and overwriting values. * Clean up ghost tests * Test that map deletion does not spawn ghosts * Spawn ghost on the next available map * Disallow spawning on deleted maps * Fix map deletion ghost test * Cleanup --------- Co-authored-by: Whisper <121047731+QuietlyWhisper@users.noreply.github.com> Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com> Co-authored-by: ShadowCommander <shadowjjt@gmail.com>
This commit is contained in:
@@ -283,40 +283,18 @@ namespace Content.Server.GameTicking
|
||||
}
|
||||
}
|
||||
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
var coords = _transform.GetMoverCoordinates(position, xformQuery);
|
||||
|
||||
var ghost = Spawn(ObserverPrototypeName, coords);
|
||||
|
||||
// Try setting the ghost entity name to either the character name or the player name.
|
||||
// If all else fails, it'll default to the default entity prototype name, "observer".
|
||||
// However, that should rarely happen.
|
||||
if (!string.IsNullOrWhiteSpace(mind.CharacterName))
|
||||
_metaData.SetEntityName(ghost, mind.CharacterName);
|
||||
else if (!string.IsNullOrWhiteSpace(mind.Session?.Name))
|
||||
_metaData.SetEntityName(ghost, mind.Session.Name);
|
||||
|
||||
var ghostComponent = Comp<GhostComponent>(ghost);
|
||||
|
||||
if (mind.TimeOfDeath.HasValue)
|
||||
{
|
||||
_ghost.SetTimeOfDeath(ghost, mind.TimeOfDeath!.Value, ghostComponent);
|
||||
}
|
||||
var ghost = _ghost.SpawnGhost((mindId, mind), position, canReturn);
|
||||
if (ghost == null)
|
||||
return false;
|
||||
|
||||
if (playerEntity != null)
|
||||
_adminLogger.Add(LogType.Mind, $"{EntityManager.ToPrettyString(playerEntity.Value):player} ghosted{(!canReturn ? " (non-returnable)" : "")}");
|
||||
|
||||
_ghost.SetCanReturnToBody(ghostComponent, canReturn);
|
||||
|
||||
if (canReturn)
|
||||
_mind.Visit(mindId, ghost, mind);
|
||||
else
|
||||
_mind.TransferTo(mindId, ghost, mind: mind);
|
||||
|
||||
var player = mind.Session;
|
||||
var userId = player?.UserId;
|
||||
if (userId.HasValue && !_ghostSystem._deathTime.TryGetValue(userId.Value, out _))
|
||||
_ghostSystem._deathTime[userId.Value] = _gameTiming.CurTime;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ using Content.Server.Speech.Components;
|
||||
using Content.Server.Station.Components;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Players;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Preferences;
|
||||
@@ -59,7 +60,9 @@ namespace Content.Server.GameTicking
|
||||
return spawnableStations;
|
||||
}
|
||||
|
||||
private void SpawnPlayers(List<ICommonSession> readyPlayers, Dictionary<NetUserId, HumanoidCharacterProfile> profiles, bool force)
|
||||
private void SpawnPlayers(List<ICommonSession> readyPlayers,
|
||||
Dictionary<NetUserId, HumanoidCharacterProfile> profiles,
|
||||
bool force)
|
||||
{
|
||||
// Allow game rules to spawn players by themselves if needed. (For example, nuke ops or wizard)
|
||||
RaiseLocalEvent(new RulePlayerSpawningEvent(readyPlayers, profiles, force));
|
||||
@@ -120,10 +123,17 @@ namespace Content.Server.GameTicking
|
||||
RefreshLateJoinAllowed();
|
||||
|
||||
// Allow rules to add roles to players who have been spawned in. (For example, on-station traitors)
|
||||
RaiseLocalEvent(new RulePlayerJobsAssignedEvent(assignedJobs.Keys.Select(x => _playerManager.GetSessionById(x)).ToArray(), profiles, force));
|
||||
RaiseLocalEvent(new RulePlayerJobsAssignedEvent(
|
||||
assignedJobs.Keys.Select(x => _playerManager.GetSessionById(x)).ToArray(),
|
||||
profiles,
|
||||
force));
|
||||
}
|
||||
|
||||
private void SpawnPlayer(ICommonSession player, EntityUid station, string? jobId = null, bool lateJoin = true, bool silent = false)
|
||||
private void SpawnPlayer(ICommonSession player,
|
||||
EntityUid station,
|
||||
string? jobId = null,
|
||||
bool lateJoin = true,
|
||||
bool silent = false)
|
||||
{
|
||||
var character = GetPlayerProfile(player);
|
||||
|
||||
@@ -136,7 +146,12 @@ namespace Content.Server.GameTicking
|
||||
SpawnPlayer(player, character, station, jobId, lateJoin, silent);
|
||||
}
|
||||
|
||||
private void SpawnPlayer(ICommonSession player, HumanoidCharacterProfile character, EntityUid station, string? jobId = null, bool lateJoin = true, bool silent = false)
|
||||
private void SpawnPlayer(ICommonSession player,
|
||||
HumanoidCharacterProfile character,
|
||||
EntityUid station,
|
||||
string? jobId = null,
|
||||
bool lateJoin = true,
|
||||
bool silent = false)
|
||||
{
|
||||
// Can't spawn players with a dummy ticker!
|
||||
if (DummyTicker)
|
||||
@@ -244,7 +259,9 @@ namespace Content.Server.GameTicking
|
||||
restrictedRoles.UnionWith(jobBans);
|
||||
|
||||
// Pick best job best on prefs.
|
||||
jobId ??= _stationJobs.PickBestAvailableJobWithPriority(station, character.JobPriorities, true,
|
||||
jobId ??= _stationJobs.PickBestAvailableJobWithPriority(station,
|
||||
character.JobPriorities,
|
||||
true,
|
||||
restrictedRoles);
|
||||
// If no job available, stay in lobby, or if no lobby spawn as observer
|
||||
if (jobId is null)
|
||||
@@ -253,7 +270,9 @@ namespace Content.Server.GameTicking
|
||||
{
|
||||
JoinAsObserver(player);
|
||||
}
|
||||
_chatManager.DispatchServerMessage(player, Loc.GetString("game-ticker-player-no-jobs-available-when-joining"));
|
||||
|
||||
_chatManager.DispatchServerMessage(player,
|
||||
Loc.GetString("game-ticker-player-no-jobs-available-when-joining"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -267,7 +286,7 @@ namespace Content.Server.GameTicking
|
||||
_mind.SetUserId(newMind, data.UserId);
|
||||
|
||||
var jobPrototype = _prototypeManager.Index<JobPrototype>(jobId);
|
||||
var job = new JobComponent { Prototype = jobId };
|
||||
var job = new JobComponent {Prototype = jobId};
|
||||
_roles.MindAddRole(newMind, job, silent: silent);
|
||||
var jobName = _jobs.MindTryGetJobName(newMind);
|
||||
|
||||
@@ -311,12 +330,11 @@ namespace Content.Server.GameTicking
|
||||
if (lateJoin && !silent)
|
||||
{
|
||||
_chatSystem.DispatchStationAnnouncement(station,
|
||||
Loc.GetString(
|
||||
"latejoin-arrival-announcement",
|
||||
("character", MetaData(mob).EntityName),
|
||||
Loc.GetString("latejoin-arrival-announcement",
|
||||
("character", MetaData(mob).EntityName),
|
||||
("gender", character.Gender), // WD-EDIT
|
||||
("job", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(jobName))
|
||||
), Loc.GetString("latejoin-arrival-sender"),
|
||||
("job", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(jobName))),
|
||||
Loc.GetString("latejoin-arrival-sender"),
|
||||
playDefaultSound: false);
|
||||
}
|
||||
|
||||
@@ -328,14 +346,17 @@ namespace Content.Server.GameTicking
|
||||
_stationJobs.TryAssignJob(station, jobPrototype, player.UserId);
|
||||
|
||||
if (lateJoin)
|
||||
_adminLogger.Add(LogType.LateJoin, LogImpact.Medium, $"Player {player.Name} late joined as {character.Name:characterName} on station {Name(station):stationName} with {ToPrettyString(mob):entity} as a {jobName:jobName}.");
|
||||
_adminLogger.Add(LogType.LateJoin,
|
||||
LogImpact.Medium,
|
||||
$"Player {player.Name} late joined as {character.Name:characterName} on station {Name(station):stationName} with {ToPrettyString(mob):entity} as a {jobName:jobName}.");
|
||||
else
|
||||
_adminLogger.Add(LogType.RoundStartJoin, LogImpact.Medium, $"Player {player.Name} joined as {character.Name:characterName} on station {Name(station):stationName} with {ToPrettyString(mob):entity} as a {jobName:jobName}.");
|
||||
_adminLogger.Add(LogType.RoundStartJoin,
|
||||
LogImpact.Medium,
|
||||
$"Player {player.Name} joined as {character.Name:characterName} on station {Name(station):stationName} with {ToPrettyString(mob):entity} as a {jobName:jobName}.");
|
||||
|
||||
// Make sure they're aware of extended access.
|
||||
if (Comp<StationJobsComponent>(station).ExtendedAccess
|
||||
&& (jobPrototype.ExtendedAccess.Count > 0
|
||||
|| jobPrototype.ExtendedAccessGroups.Count > 0))
|
||||
&& (jobPrototype.ExtendedAccess.Count > 0 || jobPrototype.ExtendedAccessGroups.Count > 0))
|
||||
{
|
||||
_chatManager.DispatchServerMessage(player, Loc.GetString("job-greet-crew-shortages"));
|
||||
}
|
||||
@@ -357,14 +378,20 @@ namespace Content.Server.GameTicking
|
||||
}
|
||||
else
|
||||
{
|
||||
_chatManager.DispatchServerMessage(player, Loc.GetString("latejoin-arrivals-direction-time",
|
||||
("time", $"{arrival:mm\\:ss}")));
|
||||
_chatManager.DispatchServerMessage(player,
|
||||
Loc.GetString("latejoin-arrivals-direction-time", ("time", $"{arrival:mm\\:ss}")));
|
||||
}
|
||||
}
|
||||
|
||||
// We raise this event directed to the mob, but also broadcast it so game rules can do something now.
|
||||
PlayersJoinedRoundNormally++;
|
||||
var aev = new PlayerSpawnCompleteEvent(mob, player, jobId, lateJoin, PlayersJoinedRoundNormally, station, character);
|
||||
var aev = new PlayerSpawnCompleteEvent(mob,
|
||||
player,
|
||||
jobId,
|
||||
lateJoin,
|
||||
PlayersJoinedRoundNormally,
|
||||
station,
|
||||
character);
|
||||
RaiseLocalEvent(mob, aev, true);
|
||||
}
|
||||
|
||||
@@ -485,32 +512,24 @@ namespace Content.Server.GameTicking
|
||||
if (DummyTicker)
|
||||
return;
|
||||
|
||||
var mind = player.GetMind();
|
||||
Entity<MindComponent?>? mind = player.GetMind();
|
||||
if (mind == null)
|
||||
{
|
||||
mind = _mind.CreateMind(player.UserId);
|
||||
var name = GetPlayerProfile(player).Name;
|
||||
var (mindId, mindComp) = _mind.CreateMind(player.UserId, name);
|
||||
mind = (mindId, mindComp);
|
||||
_mind.SetUserId(mind.Value, player.UserId);
|
||||
_roles.MindAddRole(mind.Value, new ObserverRoleComponent());
|
||||
}
|
||||
|
||||
var name = GetPlayerProfile(player).Name;
|
||||
|
||||
var ghost = SpawnObserverMob();
|
||||
_metaData.SetEntityName(ghost, name);
|
||||
_ghost.SetCanReturnToBody(ghost, false);
|
||||
_mind.TransferTo(mind.Value, ghost);
|
||||
_adminLogger.Add(LogType.LateJoin, LogImpact.Low, $"{player.Name} late joined the round as an Observer with {ToPrettyString(ghost):entity}.");
|
||||
var ghost = _ghost.SpawnGhost(mind.Value);
|
||||
_adminLogger.Add(LogType.LateJoin,
|
||||
LogImpact.Low,
|
||||
$"{player.Name} late joined the round as an Observer with {ToPrettyString(ghost):entity}.");
|
||||
}
|
||||
|
||||
#region Mob Spawning Helpers
|
||||
private EntityUid SpawnObserverMob()
|
||||
{
|
||||
var coordinates = GetObserverSpawnPoint();
|
||||
return EntityManager.SpawnEntity(ObserverPrototypeName, coordinates);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Spawn Points
|
||||
|
||||
public EntityCoordinates GetObserverSpawnPoint()
|
||||
{
|
||||
_possiblePositions.Clear();
|
||||
@@ -531,8 +550,7 @@ namespace Content.Server.GameTicking
|
||||
var query = AllEntityQuery<MapGridComponent>();
|
||||
while (query.MoveNext(out var uid, out var grid))
|
||||
{
|
||||
if (!metaQuery.TryGetComponent(uid, out var meta) ||
|
||||
meta.EntityPaused)
|
||||
if (!metaQuery.TryGetComponent(uid, out var meta) || meta.EntityPaused || TerminatingOrDeleted(uid))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -553,8 +571,7 @@ namespace Content.Server.GameTicking
|
||||
{
|
||||
var gridXform = Transform(gridUid);
|
||||
|
||||
return new EntityCoordinates(gridUid,
|
||||
gridXform.InvWorldMatrix.Transform(toMap.Position));
|
||||
return new EntityCoordinates(gridUid, gridXform.InvWorldMatrix.Transform(toMap.Position));
|
||||
}
|
||||
|
||||
return spawn;
|
||||
@@ -570,8 +587,9 @@ namespace Content.Server.GameTicking
|
||||
{
|
||||
var mapUid = _mapManager.GetMapEntityId(map);
|
||||
|
||||
if (!metaQuery.TryGetComponent(mapUid, out var meta) ||
|
||||
meta.EntityPaused)
|
||||
if (!metaQuery.TryGetComponent(mapUid, out var meta)
|
||||
|| meta.EntityPaused
|
||||
|| TerminatingOrDeleted(mapUid))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -584,6 +602,7 @@ namespace Content.Server.GameTicking
|
||||
_sawmill.Warning("Found no observer spawn points!");
|
||||
return EntityCoordinates.Invalid;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -601,7 +620,11 @@ namespace Content.Server.GameTicking
|
||||
public bool LateJoin { get; }
|
||||
public EntityUid Station { get; }
|
||||
|
||||
public PlayerBeforeSpawnEvent(ICommonSession player, HumanoidCharacterProfile profile, string? jobId, bool lateJoin, EntityUid station)
|
||||
public PlayerBeforeSpawnEvent(ICommonSession player,
|
||||
HumanoidCharacterProfile profile,
|
||||
string? jobId,
|
||||
bool lateJoin,
|
||||
EntityUid station)
|
||||
{
|
||||
Player = player;
|
||||
Profile = profile;
|
||||
@@ -629,7 +652,13 @@ namespace Content.Server.GameTicking
|
||||
// Ex. If this is the 27th person to join, this will be 27.
|
||||
public int JoinOrder { get; }
|
||||
|
||||
public PlayerSpawnCompleteEvent(EntityUid mob, ICommonSession player, string? jobId, bool lateJoin, int joinOrder, EntityUid station, HumanoidCharacterProfile profile)
|
||||
public PlayerSpawnCompleteEvent(EntityUid mob,
|
||||
ICommonSession player,
|
||||
string? jobId,
|
||||
bool lateJoin,
|
||||
int joinOrder,
|
||||
EntityUid station,
|
||||
HumanoidCharacterProfile profile)
|
||||
{
|
||||
Mob = mob;
|
||||
Player = player;
|
||||
|
||||
Reference in New Issue
Block a user