Merge branch 'master' of https://github.com/Regalis11/Barotrauma into develop
This commit is contained in:
@@ -147,13 +147,23 @@ namespace Barotrauma
|
||||
public bool IsRemotePlayer { get; set; }
|
||||
|
||||
public bool IsLocalPlayer => Controlled == this;
|
||||
public bool IsPlayer => Controlled == this || IsRemotePlayer;
|
||||
|
||||
public bool IsPlayer => IsLocalPlayer || IsRemotePlayer;
|
||||
|
||||
/// <summary>
|
||||
/// Is the character player or does it have an active ship command manager (an AI controlled sub)? Bots in the player team are not treated as commanders.
|
||||
/// </summary>
|
||||
public bool IsCommanding => IsPlayer || AIController is HumanAIController { ShipCommandManager.Active: true };
|
||||
|
||||
/// <summary>
|
||||
/// Is the character actively controlled by a human AI?
|
||||
/// </summary>
|
||||
public bool IsBot => !IsPlayer && AIController is HumanAIController { Enabled: true };
|
||||
|
||||
/// <summary>
|
||||
/// Is the character actively controlled by an AI?
|
||||
/// </summary>
|
||||
public bool IsAIControlled => !IsPlayer && AIController is { Enabled: true };
|
||||
public bool IsEscorted { get; set; }
|
||||
public Identifier JobIdentifier => Info?.Job?.Prefab.Identifier ?? Identifier.Empty;
|
||||
|
||||
@@ -381,11 +391,11 @@ namespace Barotrauma
|
||||
|
||||
public bool IsOnPlayerTeam =>
|
||||
teamID == CharacterTeamType.Team1 ||
|
||||
(teamID == CharacterTeamType.Team2 && !IsFriendlyNPCTurnedHostile);
|
||||
(teamID == CharacterTeamType.Team2 && !IsFriendlyNPCTurnedHostile); // Some events use Team2 as a hostile team for NPCs, so we shouldn't treat those to be in any player team. Normally Team2 means a player team in PvP.
|
||||
|
||||
public bool IsOriginallyOnPlayerTeam => originalTeamID == CharacterTeamType.Team1 || originalTeamID == CharacterTeamType.Team2;
|
||||
public bool IsOriginallyOnPlayerTeam => originalTeamID is CharacterTeamType.Team1 or CharacterTeamType.Team2;
|
||||
|
||||
public bool IsFriendlyNPCTurnedHostile => originalTeamID == CharacterTeamType.FriendlyNPC && (teamID == CharacterTeamType.Team2 || teamID == CharacterTeamType.None);
|
||||
public bool IsFriendlyNPCTurnedHostile => originalTeamID == CharacterTeamType.FriendlyNPC && teamID is CharacterTeamType.Team2 or CharacterTeamType.None;
|
||||
|
||||
public bool IsInstigator => CombatAction is { IsInstigator: true };
|
||||
|
||||
@@ -398,6 +408,12 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public bool IsCriminal;
|
||||
|
||||
/// <summary>
|
||||
/// A flag for the guards to remember that the character has used weapons or tools offensively, so that they know to confiscate those.
|
||||
/// Intentionally not set from stealing or fleeing.
|
||||
/// </summary>
|
||||
public bool IsActingOffensively;
|
||||
|
||||
/// <summary>
|
||||
/// Set true only, if the character is turned hostile from an escort mission (See <see cref="EscortMission"/>).
|
||||
/// </summary>
|
||||
@@ -752,11 +768,24 @@ namespace Barotrauma
|
||||
set
|
||||
{
|
||||
if (value == selectedCharacter) { return; }
|
||||
if (selectedCharacter != null) { selectedCharacter.selectedBy = null; }
|
||||
if (selectedCharacter != null) { selectedCharacter.selectedBy = null; }
|
||||
selectedCharacter = value;
|
||||
if (selectedCharacter != null) {selectedCharacter.selectedBy = this; }
|
||||
if (selectedCharacter != null) { selectedCharacter.selectedBy = this; }
|
||||
#if CLIENT
|
||||
CharacterHealth.SetHealthBarVisibility(value == null);
|
||||
|
||||
if (IsLocalPlayer && !GUI.IsUltrawide && GUI.IsHUDScaled)
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
// Scaled HUD on non-ultra-wide -> hide the chatbox, so that it doesn't overlap with the inventory.
|
||||
ChatBox.AutoHideChatBox();
|
||||
}
|
||||
else
|
||||
{
|
||||
ChatBox.ResetChatBoxOpenState();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
bool isServerOrSingleplayer = GameMain.IsSingleplayer || GameMain.NetworkMember is { IsServer: true };
|
||||
CheckTalents(AbilityEffectType.OnLootCharacter, new AbilityCharacterLoot(value));
|
||||
@@ -937,10 +966,7 @@ namespace Barotrauma
|
||||
get { return IsHuman && HasEquippedItem(Tags.HandLockerItem); }
|
||||
}
|
||||
|
||||
public bool IsPet
|
||||
{
|
||||
get { return AIController is EnemyAIController enemyController && enemyController.PetBehavior != null; }
|
||||
}
|
||||
public bool IsPet => Params.IsPet;
|
||||
|
||||
public float Oxygen
|
||||
{
|
||||
@@ -1085,18 +1111,18 @@ namespace Barotrauma
|
||||
}
|
||||
#if CLIENT
|
||||
HintManager.OnSetSelectedItem(this, prevSelectedItem, _selectedItem);
|
||||
if (Controlled == this)
|
||||
if (IsLocalPlayer)
|
||||
{
|
||||
_selectedItem?.GetComponent<Fabricator>()?.RefreshSelectedItem();
|
||||
|
||||
if (_selectedItem == null)
|
||||
{
|
||||
GameMain.GameSession?.CrewManager?.ResetCrewList();
|
||||
}
|
||||
else if (!_selectedItem.IsLadder)
|
||||
if (_selectedItem != null)
|
||||
{
|
||||
GameMain.GameSession?.CrewManager?.AutoHideCrewList();
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMain.GameSession?.CrewManager?.ResetCrewListOpenState();
|
||||
}
|
||||
|
||||
_selectedItem?.GetComponent<CircuitBox>()?.OnViewUpdateProjSpecific();
|
||||
}
|
||||
@@ -1199,7 +1225,7 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
return (SelectedItem == null || SelectedItem.GetComponent<Controller>() is { AllowAiming: true }) && !IsKnockedDown && (!IsRagdolled || AnimController.IsHoldingToRope);
|
||||
return (SelectedItem == null || SelectedItem.GetComponent<Controller>() is { AllowAiming: true }) && !IsKnockedDownOrRagdolled && (!IsRagdolled || AnimController.IsHoldingToRope);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1430,12 +1456,9 @@ namespace Barotrauma
|
||||
//no longer a new hire after spawning (only displayed as a new hire at the end of the outpost round, when the character hasn't spawned yet)
|
||||
Info.IsNewHire = false;
|
||||
}
|
||||
if (characterInfo?.HumanPrefabIds is { } prefabIds &&
|
||||
prefabIds.NpcSetIdentifier != default && prefabIds.NpcIdentifier != default)
|
||||
if (characterInfo?.HumanPrefabIds is { NpcSetIdentifier.IsEmpty: false, NpcIdentifier.IsEmpty: false })
|
||||
{
|
||||
HumanPrefab = NPCSet.Get(
|
||||
characterInfo.HumanPrefabIds.NpcSetIdentifier,
|
||||
characterInfo.HumanPrefabIds.NpcIdentifier);
|
||||
HumanPrefab = characterInfo.HumanPrefab;
|
||||
}
|
||||
|
||||
keys = new Key[Enum.GetNames(typeof(InputType)).Length];
|
||||
@@ -1831,12 +1854,12 @@ namespace Barotrauma
|
||||
if (info == null) { return; }
|
||||
if (info.HumanPrefabIds != default)
|
||||
{
|
||||
var humanPrefab = NPCSet.Get(info.HumanPrefabIds.NpcSetIdentifier, info.HumanPrefabIds.NpcIdentifier);
|
||||
if (humanPrefab == null)
|
||||
var prefab = info.HumanPrefab;
|
||||
if (prefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to give job items for the character \"{Name}\" - could not find human prefab with the id \"{info.HumanPrefabIds.NpcIdentifier}\" from \"{info.HumanPrefabIds.NpcSetIdentifier}\".");
|
||||
}
|
||||
else if (humanPrefab.GiveItems(this, spawnPoint?.Submarine ?? Submarine, spawnPoint))
|
||||
else if (prefab.GiveItems(this, spawnPoint?.Submarine ?? Submarine, spawnPoint))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -1905,23 +1928,9 @@ namespace Barotrauma
|
||||
|
||||
if (skillIdentifier != null)
|
||||
{
|
||||
foreach (Item item in Inventory.AllItems)
|
||||
if (wearableSkillModifiers.TryGetValue(skillIdentifier, out float skillValue))
|
||||
{
|
||||
if (item?.GetComponent<Wearable>() is Wearable wearable &&
|
||||
!Inventory.IsInLimbSlot(item, InvSlotType.Any))
|
||||
{
|
||||
foreach (var allowedSlot in wearable.AllowedSlots)
|
||||
{
|
||||
if (allowedSlot == InvSlotType.Any) { continue; }
|
||||
if (!Inventory.IsInLimbSlot(item, allowedSlot)) { continue; }
|
||||
if (wearable.SkillModifiers.TryGetValue(skillIdentifier, out float skillValue))
|
||||
{
|
||||
skillLevel += skillValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
skillLevel += skillValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2667,7 +2676,7 @@ namespace Barotrauma
|
||||
public bool CanBeDraggedBy(Character character)
|
||||
{
|
||||
if (!IsDraggable) { return false; }
|
||||
return IsKnockedDown || LockHands || (IsPet && character.IsFriendly(this)) || (IsBot && character.TeamID == TeamID);
|
||||
return IsKnockedDownOrRagdolled || LockHands || (IsPet && character.IsOnFriendlyTeam(this)) || (IsBot && character.TeamID == TeamID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -3051,12 +3060,7 @@ namespace Barotrauma
|
||||
|
||||
public void DoInteractionUpdate(float deltaTime, Vector2 mouseSimPos)
|
||||
{
|
||||
bool isLocalPlayer = Controlled == this;
|
||||
|
||||
if (!isLocalPlayer && (this is AICharacter && !IsRemotePlayer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (IsAIControlled) { return; }
|
||||
|
||||
if (DisableInteract)
|
||||
{
|
||||
@@ -3077,7 +3081,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
if (isLocalPlayer)
|
||||
if (IsLocalPlayer)
|
||||
{
|
||||
if (!IsMouseOnUI && (ViewTarget == null || ViewTarget == this) && !DisableFocusingOnEntities)
|
||||
{
|
||||
@@ -3597,23 +3601,25 @@ namespace Barotrauma
|
||||
humanAnimController.Crouching = false;
|
||||
}
|
||||
//ragdolling manually makes the character go through platforms
|
||||
//EXCEPT for clients, they rely on the server telling whether platforms should be ignored or not
|
||||
if (IsRagdolled && GameMain.NetworkMember is not { IsClient: true })
|
||||
//EXCEPT if the character is controlled by the server (i.e. remote player or bot),
|
||||
//in that case the server decides whether platforms should be ignored or not
|
||||
bool isControlledByRemotelyByServer = GameMain.NetworkMember is { IsClient: true } && IsRemotelyControlled;
|
||||
if (IsRagdolled &&
|
||||
!isControlledByRemotelyByServer)
|
||||
{
|
||||
AnimController.IgnorePlatforms = true;
|
||||
}
|
||||
AnimController.ResetPullJoints();
|
||||
SelectedItem = SelectedSecondaryItem = null;
|
||||
SelectedCharacter = null;
|
||||
return;
|
||||
}
|
||||
|
||||
//AI and control stuff
|
||||
|
||||
Control(deltaTime, cam);
|
||||
|
||||
bool isNotControlled = Controlled != this;
|
||||
|
||||
if (isNotControlled && (!(this is AICharacter) || IsRemotePlayer))
|
||||
|
||||
if (IsRemotePlayer)
|
||||
{
|
||||
Vector2 mouseSimPos = ConvertUnits.ToSimUnits(cursorPosition);
|
||||
DoInteractionUpdate(deltaTime, mouseSimPos);
|
||||
@@ -3880,7 +3886,9 @@ namespace Barotrauma
|
||||
|
||||
//don't spawn duffel bags in PvP modes that include respawning, because it can lead to a ton of accumulated items in the sub/outpost
|
||||
bool pvpWithRespawning = GameMain.GameSession?.GameMode is PvPMode && GameMain.NetworkMember?.RespawnManager != null;
|
||||
if (!despawnContainerId.IsEmpty && !pvpWithRespawning)
|
||||
if (!despawnContainerId.IsEmpty && !pvpWithRespawning &&
|
||||
//don't duffelbag disconnected character's items (the items should disappear with the character, and reappear if they rejoin later)
|
||||
CauseOfDeath?.Type != CauseOfDeathType.Disconnected)
|
||||
{
|
||||
var containerPrefab =
|
||||
MapEntityPrefab.FindByIdentifier(despawnContainerId) as ItemPrefab ??
|
||||
@@ -3928,6 +3936,16 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
#if SERVER
|
||||
if (CauseOfDeath?.Type == CauseOfDeathType.Disconnected)
|
||||
{
|
||||
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign mpCampaign)
|
||||
{
|
||||
//refresh campaign data so the disconnected player gets to keep the items that were in their inventory
|
||||
mpCampaign.RefreshCharacterCampaignData(this, refreshHealthData: false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Spawner.AddEntityToRemoveQueue(this);
|
||||
}
|
||||
}
|
||||
@@ -4720,17 +4738,24 @@ namespace Barotrauma
|
||||
healer.Info?.ApplySkillGain(Tags.MedicalItem, medicalGain * SkillSettings.Current.SkillIncreasePerFriendlyHealed);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public bool IsKnockedDownOrRagdolled => (IsRagdolled && !AnimController.IsHangingWithRope) || IsKnockedDown;
|
||||
|
||||
/// <summary>
|
||||
/// Is the character knocked down regardless whether the technical state is dead, unconcious, paralyzed, or stunned.
|
||||
/// With stunning, the parameter uses an one second delay before the character is treated as knocked down. The purpose of this is to ignore minor stunning. If you don't want to to ignore any stun, use the Stun property.
|
||||
/// With stunning, the parameter uses a one-second delay before the character is treated as knocked down. The purpose of this is to ignore minor stunning. If you don't want to to ignore any stun, use the Stun property.
|
||||
/// </summary>
|
||||
public bool IsKnockedDown => (IsRagdolled && !AnimController.IsHangingWithRope) || CharacterHealth.StunTimer > 1.0f || IsIncapacitated;
|
||||
public bool IsKnockedDown => CharacterHealth.StunTimer > 1.0f || IsIncapacitated;
|
||||
|
||||
public void SetStun(float newStun, bool allowStunDecrease = false, bool isNetworkMessage = false)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && !isNetworkMessage) { return; }
|
||||
if (Screen.Selected != GameMain.GameScreen) { return; }
|
||||
if (GodMode)
|
||||
{
|
||||
CharacterHealth.Stun = 0;
|
||||
return;
|
||||
}
|
||||
if (newStun > 0 && Params.Health.StunImmunity)
|
||||
{
|
||||
if (EmpVulnerability <= 0 || CharacterHealth.GetAfflictionStrengthByType(AfflictionPrefab.EMPType, allowLimbAfflictions: false) <= 0)
|
||||
@@ -5008,6 +5033,8 @@ namespace Barotrauma
|
||||
{
|
||||
characterInfo.PermanentlyDead = true;
|
||||
}
|
||||
|
||||
GameMain.GameSession.RefreshAnyOpenPlayerInfo();
|
||||
#endif
|
||||
|
||||
#if SERVER
|
||||
@@ -5016,8 +5043,10 @@ namespace Barotrauma
|
||||
Info.LastRewardDistribution = Option.Some(Wallet.RewardDistribution);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (GameAnalyticsManager.SendUserStatistics && Prefab?.ContentPackage == ContentPackageManager.VanillaCorePackage)
|
||||
//we don't need info of every kill, we can get a good sample size just by logging 5%
|
||||
if (GameAnalyticsManager.SendUserStatistics &&
|
||||
Prefab?.ContentPackage == ContentPackageManager.VanillaCorePackage &&
|
||||
GameAnalyticsManager.ShouldLogRandomSample())
|
||||
{
|
||||
string causeOfDeathStr = causeOfDeathAffliction == null ?
|
||||
causeOfDeath.ToString() : causeOfDeathAffliction.Prefab.Identifier.Value.Replace(" ", "");
|
||||
@@ -5297,6 +5326,7 @@ namespace Barotrauma
|
||||
}
|
||||
#if SERVER
|
||||
newItem.GetComponent<Terminal>()?.SyncHistory();
|
||||
if (newItem.GetComponent<WifiComponent>() is WifiComponent wifiComponent) { newItem.CreateServerEvent(wifiComponent); }
|
||||
if (newItem.GetComponent<GeneticMaterial>() is GeneticMaterial geneticMaterial) { newItem.CreateServerEvent(geneticMaterial); }
|
||||
SyncInGameEditables(newItem);
|
||||
#endif
|
||||
@@ -5511,7 +5541,7 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public bool IsProtectedFromPressure => IsImmuneToPressure || PressureProtection >= (Level.Loaded?.GetRealWorldDepth(WorldPosition.Y) ?? 1.0f);
|
||||
|
||||
public bool IsImmuneToPressure => !NeedsAir || HasAbilityFlag(AbilityFlags.ImmuneToPressure);
|
||||
public bool IsImmuneToPressure => !NeedsAir || HasAbilityFlag(AbilityFlags.ImmuneToPressure) || GodMode;
|
||||
|
||||
#region Talents
|
||||
private readonly List<CharacterTalent> characterTalents = new List<CharacterTalent>();
|
||||
@@ -5772,9 +5802,14 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
private readonly Dictionary<StatTypes, float> wearableStatValues = new Dictionary<StatTypes, float>();
|
||||
|
||||
/// <summary>
|
||||
/// A dictionary with temporary values, updated when the character equips/unequips wearables. Used to reduce unnecessary inventory checking.
|
||||
/// </summary>
|
||||
private readonly Dictionary<Identifier, float> wearableSkillModifiers = new Dictionary<Identifier, float>();
|
||||
|
||||
public float GetStatValue(StatTypes statType, bool includeSaved = true)
|
||||
{
|
||||
if (!IsHuman) { return 0f; }
|
||||
if (Info == null) { return 0f; }
|
||||
|
||||
float statValue = 0f;
|
||||
if (statValues.TryGetValue(statType, out float value))
|
||||
@@ -5785,7 +5820,7 @@ namespace Barotrauma
|
||||
{
|
||||
statValue += CharacterHealth.GetStatValue(statType);
|
||||
}
|
||||
if (Info != null && includeSaved)
|
||||
if (includeSaved)
|
||||
{
|
||||
// could be optimized by instead updating the Character.cs statvalues dictionary whenever the CharacterInfo.cs values change
|
||||
statValue += Info.GetSavedStatValue(statType);
|
||||
@@ -5807,12 +5842,17 @@ namespace Barotrauma
|
||||
|
||||
public void OnWearablesChanged()
|
||||
{
|
||||
HashSet<Wearable> handledWearables = new HashSet<Wearable>();
|
||||
wearableStatValues.Clear();
|
||||
wearableSkillModifiers.Clear();
|
||||
for (int i = 0; i < Inventory.Capacity; i++)
|
||||
{
|
||||
if (Inventory.SlotTypes[i] != InvSlotType.Any && Inventory.SlotTypes[i] != InvSlotType.LeftHand && Inventory.SlotTypes[i] != InvSlotType.RightHand
|
||||
&& Inventory.GetItemAt(i)?.GetComponent<Wearable>() is Wearable wearable)
|
||||
{
|
||||
if (handledWearables.Contains(wearable)) { continue; }
|
||||
handledWearables.Add(wearable);
|
||||
|
||||
foreach (var statValuePair in wearable.WearableStatValues)
|
||||
{
|
||||
if (wearableStatValues.ContainsKey(statValuePair.Key))
|
||||
@@ -5824,6 +5864,17 @@ namespace Barotrauma
|
||||
wearableStatValues.Add(statValuePair.Key, statValuePair.Value);
|
||||
}
|
||||
}
|
||||
foreach (var skillModifier in wearable.SkillModifiers)
|
||||
{
|
||||
if (wearableSkillModifiers.ContainsKey(skillModifier.Key))
|
||||
{
|
||||
wearableSkillModifiers[skillModifier.Key] += skillModifier.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
wearableSkillModifiers.Add(skillModifier.Key, skillModifier.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user