Merge remote-tracking branch 'upstream/master' into develop

This commit is contained in:
EvilFactory
2024-12-11 10:44:53 -03:00
257 changed files with 4793 additions and 1653 deletions
@@ -656,6 +656,16 @@ namespace Barotrauma
private readonly List<ItemSpawnInfo> spawnItems;
/// <summary>
/// If set, the character targeted by this effect will send the corresponding localized text that has the specified identifier in the chat.
/// </summary>
private readonly Identifier forceSayIdentifier = Identifier.Empty;
/// <summary>
/// If set to true, the character targeted by this effect's "forcesay" command will send their message in the radio.
/// </summary>
private readonly bool forceSayInRadio;
/// <summary>
/// If enabled, one of the items this effect is configured to spawn is selected randomly, as opposed to spawning all of them.
/// </summary>
@@ -1190,6 +1200,10 @@ namespace Barotrauma
animationsToTrigger ??= new List<AnimLoadInfo>();
animationsToTrigger.Add(new AnimLoadInfo(animType, file, priority, expectedSpeciesNames.ToImmutableArray()));
break;
case "forcesay":
forceSayIdentifier = subElement.GetAttributeIdentifier("message", Identifier.Empty);
forceSayInRadio = subElement.GetAttributeBool("sayinradio", false);
break;
}
}
@@ -1981,6 +1995,27 @@ namespace Barotrauma
TryTriggerAnimation(target, entity);
if (!forceSayIdentifier.IsEmpty)
{
LocalizedString messageToSay = TextManager.Get(forceSayIdentifier).Fallback(forceSayIdentifier.Value);
if (!messageToSay.IsNullOrEmpty() && target is Character targetCharacter && targetCharacter.SpeechImpediment < 100.0f && !targetCharacter.IsDead)
{
ChatMessageType messageType = ChatMessageType.Default;
bool canUseRadio = ChatMessage.CanUseRadio(targetCharacter, out WifiComponent radio);
if (canUseRadio && forceSayInRadio)
{
messageType = ChatMessageType.Radio;
}
#if SERVER
GameMain.Server?.SendChatMessage(messageToSay.Value, messageType, senderClient: null, targetCharacter);
#elif CLIENT
AIChatMessage message = new AIChatMessage(messageToSay.Value, messageType);
targetCharacter.SendSinglePlayerMessage(message, canUseRadio, radio);
#endif
}
}
if (isNotClient)
{
// these effects do not need to be run clientside, as they are replicated from server to clients anyway
@@ -2112,20 +2147,37 @@ namespace Barotrauma
foreach (CharacterSpawnInfo characterSpawnInfo in spawnCharacters)
{
var characters = new List<Character>();
CharacterTeamType? inheritedTeam = null;
if (characterSpawnInfo.InheritTeam)
{
bool isPvP = GameMain.GameSession?.GameMode?.Preset == GameModePreset.PvP;
inheritedTeam = entity switch
{
Character c => c.TeamID,
Item it => it.GetRootInventoryOwner() is Character owner ? owner.TeamID : GetTeamFromSubmarine(it),
MapEntity e => GetTeamFromSubmarine(e),
_ => null
// Default to Team1, when we can't deduce the team (for example when spawning outside the sub AND character inventory).
} ?? (isPvP ? CharacterTeamType.None : CharacterTeamType.Team1);
CharacterTeamType? GetTeamFromSubmarine(MapEntity e)
{
if (e.Submarine == null) { return null; }
// Don't allow team FriendlyNPC in outposts, because if you buy a spawner item (such as husk container) from the store and choose to get it immediately, it will be spawned in the outpost.
return !isPvP && e.Submarine.Info.IsOutpost && e.Submarine.TeamID == CharacterTeamType.FriendlyNPC ?
CharacterTeamType.Team1 : e.Submarine.TeamID;
}
}
for (int i = 0; i < characterSpawnInfo.Count; i++)
{
Entity.Spawner.AddCharacterToSpawnQueue(characterSpawnInfo.SpeciesName, position + Rand.Vector(characterSpawnInfo.Spread, Rand.RandSync.Unsynced) + characterSpawnInfo.Offset,
onSpawn: newCharacter =>
{
if (characterSpawnInfo.InheritTeam)
if (inheritedTeam.HasValue)
{
newCharacter.TeamID = entity switch
{
Character c => c.TeamID,
Item it => it.GetRootInventoryOwner() is Character owner ? owner.TeamID : it.Submarine?.TeamID ?? newCharacter.TeamID,
MapEntity e => e.Submarine?.TeamID ?? newCharacter.TeamID,
_ => newCharacter.TeamID
};
newCharacter.SetOriginalTeamAndChangeTeam(inheritedTeam.Value, processImmediately: true);
}
if (characterSpawnInfo.TotalMaxCount > 0)
{
@@ -2462,16 +2514,14 @@ namespace Barotrauma
{
ignoredBodies = user?.AnimController.Limbs.Where(l => !l.IsSevered).Select(l => l.body.FarseerBody).ToList();
}
float damageMultiplier = 1f;
if (sourceEntity is Limb attackLimb)
if (entity is Character character && sourceEntity is Limb { attack: Attack attack })
{
damageMultiplier = attackLimb.attack?.DamageMultiplier ?? 1.0f;
attack.ResetDamageMultiplier();
attack.DamageMultiplier *= 1.0f + character.GetStatValue(StatTypes.NaturalRangedAttackMultiplier);
damageMultiplier = attack.DamageMultiplier;
}
projectile.Shoot(user, spawnPos, spawnPos, rotation,
ignoredBodies: ignoredBodies, createNetworkEvent: true, damageMultiplier: damageMultiplier);
projectile.Shoot(user, spawnPos, spawnPos, rotation, ignoredBodies: ignoredBodies, createNetworkEvent: true, damageMultiplier: damageMultiplier);
projectile.Item.Submarine = projectile.LaunchSub = sourceEntity?.Submarine;
}
else if (newItem.body != null)
@@ -2827,18 +2877,21 @@ namespace Barotrauma
if (limb == null) { return; }
foreach (Affliction limbAffliction in limb.character.CharacterHealth.GetAllAfflictions())
{
if (result.Afflictions != null && result.Afflictions.Any(a => a.Prefab == limbAffliction.Prefab) &&
if (result.Afflictions != null &&
/* "affliction" is the affliction directly defined in the status effect (e.g. "5 internal damage (per second / per frame / however the effect is defined to run)"),
* "result" is how much we actually applied of that affliction right now (taking into account the elapsed time, resistances and such) */
result.Afflictions.FirstOrDefault(a => a.Prefab == limbAffliction.Prefab) is Affliction resultAffliction &&
(!affliction.Prefab.LimbSpecific || limb.character.CharacterHealth.GetAfflictionLimb(affliction) == limb))
{
if (type == ActionType.OnUse || type == ActionType.OnSuccess)
{
limbAffliction.AppliedAsSuccessfulTreatmentTime = Timing.TotalTime;
limb.character.TryAdjustHealerSkill(user, affliction: affliction);
limb.character.TryAdjustHealerSkill(user, affliction: resultAffliction);
}
else if (type == ActionType.OnFailure)
{
limbAffliction.AppliedAsFailedTreatmentTime = Timing.TotalTime;
limb.character.TryAdjustHealerSkill(user, affliction: affliction);
limb.character.TryAdjustHealerSkill(user, affliction: resultAffliction);
}
}
}