1982 lines
52 KiB
C#
1982 lines
52 KiB
C#
|
using System;
|
||
|
using System.Collections;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Collections.ObjectModel;
|
||
|
using System.Linq;
|
||
|
using EntityStates;
|
||
|
using EntityStates.GummyClone;
|
||
|
using RoR2.CharacterAI;
|
||
|
using RoR2.Items;
|
||
|
using RoR2.Stats;
|
||
|
using UnityEngine;
|
||
|
using UnityEngine.Events;
|
||
|
using UnityEngine.Networking;
|
||
|
using UnityEngine.ResourceManagement.AsyncOperations;
|
||
|
using UnityEngine.Serialization;
|
||
|
|
||
|
namespace RoR2;
|
||
|
|
||
|
[RequireComponent(typeof(MinionOwnership))]
|
||
|
[RequireComponent(typeof(Inventory))]
|
||
|
[DisallowMultipleComponent]
|
||
|
public class CharacterMaster : NetworkBehaviour
|
||
|
{
|
||
|
private static class CommonAssets
|
||
|
{
|
||
|
public static GameObject goldOnStageStartEffect;
|
||
|
|
||
|
public static void Resolve()
|
||
|
{
|
||
|
AsyncOperationHandle<GameObject> asyncOperationHandle = LegacyResourcesAPI.LoadAsync<GameObject>("Prefabs/Effects/GoldOnStageStartCoinGain");
|
||
|
asyncOperationHandle.Completed += delegate(AsyncOperationHandle<GameObject> x)
|
||
|
{
|
||
|
goldOnStageStartEffect = x.Result;
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public enum SEEKER_REVIVE_STATUS
|
||
|
{
|
||
|
UNUSED,
|
||
|
USED,
|
||
|
BURNED
|
||
|
}
|
||
|
|
||
|
[Tooltip("This is assigned to the prefab automatically by MasterCatalog at runtime. Do not set this value manually.")]
|
||
|
[HideInInspector]
|
||
|
[SerializeField]
|
||
|
private int _masterIndex;
|
||
|
|
||
|
[Tooltip("The prefab of this character's body.")]
|
||
|
public GameObject bodyPrefab;
|
||
|
|
||
|
[Tooltip("Whether or not to spawn the body at the position of this manager object as soon as Start runs.")]
|
||
|
public bool spawnOnStart;
|
||
|
|
||
|
[Tooltip("The team of the body.")]
|
||
|
[FormerlySerializedAs("teamIndex")]
|
||
|
[SerializeField]
|
||
|
private TeamIndex _teamIndex;
|
||
|
|
||
|
public UnityEvent onBodyDeath;
|
||
|
|
||
|
[Tooltip("Whether or not to destroy this master when the body dies.")]
|
||
|
public bool destroyOnBodyDeath = true;
|
||
|
|
||
|
private static List<CharacterMaster> instancesList;
|
||
|
|
||
|
private static ReadOnlyCollection<CharacterMaster> _readOnlyInstancesList;
|
||
|
|
||
|
private BaseAI[] aiComponents;
|
||
|
|
||
|
private const uint bodyDirtyBit = 1u;
|
||
|
|
||
|
private const uint moneyDirtyBit = 2u;
|
||
|
|
||
|
private const uint survivalTimeDirtyBit = 4u;
|
||
|
|
||
|
private const uint teamDirtyBit = 8u;
|
||
|
|
||
|
private const uint loadoutDirtyBit = 16u;
|
||
|
|
||
|
private const uint miscFlagsDirtyBit = 32u;
|
||
|
|
||
|
private const uint voidCoinsDirtyBit = 64u;
|
||
|
|
||
|
private const uint allDirtyBits = 127u;
|
||
|
|
||
|
public readonly Loadout loadout = new Loadout();
|
||
|
|
||
|
private NetworkInstanceId _bodyInstanceId = NetworkInstanceId.Invalid;
|
||
|
|
||
|
private GameObject resolvedBodyInstance;
|
||
|
|
||
|
private bool bodyResolved;
|
||
|
|
||
|
private ulong beadExperience;
|
||
|
|
||
|
private int numberOfBeadStatsGained;
|
||
|
|
||
|
private uint oldBeadLevel = 1u;
|
||
|
|
||
|
private uint newBeadLevel;
|
||
|
|
||
|
private ulong beadXPNeededForCurrentLevel = 20uL;
|
||
|
|
||
|
private uint _money;
|
||
|
|
||
|
public uint lssMoneyExp;
|
||
|
|
||
|
private uint _voidCoins;
|
||
|
|
||
|
public bool isBoss;
|
||
|
|
||
|
private Xoroshiro128Plus cloverVoidRng;
|
||
|
|
||
|
[NonSerialized]
|
||
|
private List<DeployableInfo> deployablesList;
|
||
|
|
||
|
public bool preventGameOver = true;
|
||
|
|
||
|
private Vector3 deathAimVector = Vector3.zero;
|
||
|
|
||
|
private bool killedByUnsafeArea;
|
||
|
|
||
|
private const float respawnDelayDuration = 2f;
|
||
|
|
||
|
private float _internalSurvivalTime;
|
||
|
|
||
|
private BodyIndex killerBodyIndex = BodyIndex.None;
|
||
|
|
||
|
private bool preventRespawnUntilNextStageServer;
|
||
|
|
||
|
private SEEKER_REVIVE_STATUS seekerUsedRevive;
|
||
|
|
||
|
[HideInInspector]
|
||
|
public GameObject devotionInventoryPrefab;
|
||
|
|
||
|
[HideInInspector]
|
||
|
public DevotionInventoryController devotionInventoryUpdaterRef;
|
||
|
|
||
|
[HideInInspector]
|
||
|
public ItemIndex devotionItem;
|
||
|
|
||
|
[HideInInspector]
|
||
|
public int devotedEvolutionTracker;
|
||
|
|
||
|
[HideInInspector]
|
||
|
public bool isDevotedMinion;
|
||
|
|
||
|
[HideInInspector]
|
||
|
public Interactor summonerRef;
|
||
|
|
||
|
private bool godMode;
|
||
|
|
||
|
private uint lostBodyToDeathFlag = 1u;
|
||
|
|
||
|
private uint _miscFlags;
|
||
|
|
||
|
private static int kRpcRpcOnPlayerBodyDamaged;
|
||
|
|
||
|
private static int kCmdCmdRespawn;
|
||
|
|
||
|
private static int kRpcRpcSetSeekerRevive;
|
||
|
|
||
|
public MasterCatalog.MasterIndex masterIndex
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return (MasterCatalog.MasterIndex)_masterIndex;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
_masterIndex = (int)value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public NetworkIdentity networkIdentity { get; private set; }
|
||
|
|
||
|
public bool hasEffectiveAuthority { get; private set; }
|
||
|
|
||
|
public TeamIndex teamIndex
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return _teamIndex;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
if (_teamIndex != value)
|
||
|
{
|
||
|
_teamIndex = value;
|
||
|
if (NetworkServer.active)
|
||
|
{
|
||
|
SetDirtyBit(8u);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static ReadOnlyCollection<CharacterMaster> readOnlyInstancesList => _readOnlyInstancesList;
|
||
|
|
||
|
public Inventory inventory { get; private set; }
|
||
|
|
||
|
public PlayerCharacterMasterController playerCharacterMasterController { get; private set; }
|
||
|
|
||
|
public PlayerStatsComponent playerStatsComponent { get; private set; }
|
||
|
|
||
|
public MinionOwnership minionOwnership { get; private set; }
|
||
|
|
||
|
private NetworkInstanceId bodyInstanceId
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return _bodyInstanceId;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
if (!(value == _bodyInstanceId))
|
||
|
{
|
||
|
SetDirtyBit(1u);
|
||
|
_bodyInstanceId = value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public BodyIndex backupBodyIndex { get; private set; }
|
||
|
|
||
|
private GameObject bodyInstanceObject
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (!bodyResolved)
|
||
|
{
|
||
|
resolvedBodyInstance = Util.FindNetworkObject(bodyInstanceId);
|
||
|
if ((bool)resolvedBodyInstance)
|
||
|
{
|
||
|
bodyResolved = true;
|
||
|
StoreBackupBodyIndex();
|
||
|
}
|
||
|
}
|
||
|
return resolvedBodyInstance;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
NetworkInstanceId invalid = NetworkInstanceId.Invalid;
|
||
|
resolvedBodyInstance = null;
|
||
|
bodyResolved = true;
|
||
|
if ((bool)value)
|
||
|
{
|
||
|
NetworkIdentity component = value.GetComponent<NetworkIdentity>();
|
||
|
if ((bool)component)
|
||
|
{
|
||
|
invalid = component.netId;
|
||
|
resolvedBodyInstance = value;
|
||
|
StoreBackupBodyIndex();
|
||
|
}
|
||
|
}
|
||
|
bodyInstanceId = invalid;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public uint money
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return _money;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
if (value != _money)
|
||
|
{
|
||
|
SetDirtyBit(2u);
|
||
|
_money = value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public uint voidCoins
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return _voidCoins;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
if (value != _voidCoins)
|
||
|
{
|
||
|
SetDirtyBit(64u);
|
||
|
_voidCoins = value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public float luck { get; set; }
|
||
|
|
||
|
public bool hasBody => bodyInstanceObject;
|
||
|
|
||
|
public Vector3 deathFootPosition { get; private set; } = Vector3.zero;
|
||
|
|
||
|
|
||
|
private float internalSurvivalTime
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return _internalSurvivalTime;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
if (value != _internalSurvivalTime)
|
||
|
{
|
||
|
SetDirtyBit(4u);
|
||
|
_internalSurvivalTime = value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public float currentLifeStopwatch
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (internalSurvivalTime <= 0f)
|
||
|
{
|
||
|
return 0f - internalSurvivalTime;
|
||
|
}
|
||
|
if ((bool)Run.instance)
|
||
|
{
|
||
|
return Run.instance.GetRunStopwatch() - internalSurvivalTime;
|
||
|
}
|
||
|
return 0f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private uint miscFlags
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return _miscFlags;
|
||
|
}
|
||
|
set
|
||
|
{
|
||
|
if (value != _miscFlags)
|
||
|
{
|
||
|
_miscFlags = value;
|
||
|
if (NetworkServer.active)
|
||
|
{
|
||
|
SetDirtyBit(32u);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool lostBodyToDeath
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return (miscFlags & lostBodyToDeathFlag) != 0;
|
||
|
}
|
||
|
private set
|
||
|
{
|
||
|
if (value)
|
||
|
{
|
||
|
miscFlags |= lostBodyToDeathFlag;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
miscFlags &= ~lostBodyToDeathFlag;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static event Action<CharacterMaster> onStartGlobal;
|
||
|
|
||
|
public static event Action<CharacterMaster> onCharacterMasterDiscovered;
|
||
|
|
||
|
public static event Action<CharacterMaster> onCharacterMasterLost;
|
||
|
|
||
|
public event Action<CharacterBody> onBodyStart;
|
||
|
|
||
|
public event Action<CharacterBody> onBodyDestroyed;
|
||
|
|
||
|
public event Action<PlayerCharacterMasterController> onPlayerBodyDamaged;
|
||
|
|
||
|
private void UpdateAuthority()
|
||
|
{
|
||
|
hasEffectiveAuthority = Util.HasEffectiveAuthority(networkIdentity);
|
||
|
}
|
||
|
|
||
|
public override void OnStartAuthority()
|
||
|
{
|
||
|
base.OnStartAuthority();
|
||
|
UpdateAuthority();
|
||
|
}
|
||
|
|
||
|
public override void OnStopAuthority()
|
||
|
{
|
||
|
UpdateAuthority();
|
||
|
base.OnStopAuthority();
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
public void SetLoadoutServer(Loadout newLoadout)
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'System.Void RoR2.CharacterMaster::SetLoadoutServer(RoR2.Loadout)' called on client");
|
||
|
return;
|
||
|
}
|
||
|
newLoadout.Copy(loadout);
|
||
|
SetDirtyBit(16u);
|
||
|
}
|
||
|
|
||
|
private void StoreBackupBodyIndex()
|
||
|
{
|
||
|
if ((bool)resolvedBodyInstance)
|
||
|
{
|
||
|
CharacterBody component = resolvedBodyInstance.GetComponent<CharacterBody>();
|
||
|
if ((bool)component)
|
||
|
{
|
||
|
backupBodyIndex = component.bodyIndex;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void OnSyncBodyInstanceId(NetworkInstanceId value)
|
||
|
{
|
||
|
resolvedBodyInstance = null;
|
||
|
bodyResolved = value == NetworkInstanceId.Invalid;
|
||
|
_bodyInstanceId = value;
|
||
|
}
|
||
|
|
||
|
public void TrackBeadExperience(ulong amount)
|
||
|
{
|
||
|
if (!inventory)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
int itemCount = inventory.GetItemCount(DLC2Content.Items.ExtraStatsOnLevelUp);
|
||
|
if (itemCount <= 0)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
ulong num = amount * (ulong)(0.25f * (float)(itemCount - 1));
|
||
|
ulong num2 = amount * (ulong)(0.025f * (float)numberOfBeadStatsGained);
|
||
|
if (num2 >= num + amount)
|
||
|
{
|
||
|
num2 = (ulong)(0.8f * (float)(num + amount));
|
||
|
}
|
||
|
beadExperience += amount + num - num2;
|
||
|
newBeadLevel = TeamManager.instance.FindLevelForBeadExperience(beadExperience);
|
||
|
if (beadExperience >= beadXPNeededForCurrentLevel)
|
||
|
{
|
||
|
CharacterBody body = GetBody();
|
||
|
body.SetBuffCount(DLC2Content.Buffs.ExtraStatsOnLevelUpBuff.buffIndex, (int)oldBeadLevel);
|
||
|
float num3 = body.GetBuffCount(DLC2Content.Buffs.ExtraStatsOnLevelUpBuff);
|
||
|
float num4 = 1f;
|
||
|
switch (body.hullClassification)
|
||
|
{
|
||
|
case HullClassification.Golem:
|
||
|
num4 = 2f;
|
||
|
break;
|
||
|
case HullClassification.BeetleQueen:
|
||
|
num4 = 3f;
|
||
|
break;
|
||
|
}
|
||
|
num4 += num3 / (num3 * 0.25f + 1f);
|
||
|
EffectManager.SpawnEffect(LegacyResourcesAPI.Load<GameObject>("Prefabs/Effects/ExtraStatsOnLevelUpEffect"), new EffectData
|
||
|
{
|
||
|
origin = body.footPosition,
|
||
|
scale = num4
|
||
|
}, transmit: true);
|
||
|
Util.PlaySound("Play_item_proc_extraStatsOnLevelUp", body.gameObject);
|
||
|
oldBeadLevel = newBeadLevel;
|
||
|
beadXPNeededForCurrentLevel = TeamManager.GetExperienceForLevel(oldBeadLevel + 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void OnBeadReset(bool gainedStats)
|
||
|
{
|
||
|
if (gainedStats)
|
||
|
{
|
||
|
numberOfBeadStatsGained += (int)oldBeadLevel;
|
||
|
}
|
||
|
oldBeadLevel = 1u;
|
||
|
beadExperience = 0uL;
|
||
|
beadXPNeededForCurrentLevel = 20uL;
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
public void GiveExperience(ulong amount)
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'System.Void RoR2.CharacterMaster::GiveExperience(System.UInt64)' called on client");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TeamManager.instance.GiveTeamExperience(teamIndex, amount);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void GiveMoney(uint amount)
|
||
|
{
|
||
|
int itemCount = inventory.GetItemCount(DLC2Content.Items.OnLevelUpFreeUnlock);
|
||
|
int num = TeamManager.LongstandingSolitudesInParty();
|
||
|
if (itemCount > 0)
|
||
|
{
|
||
|
double num2 = TeamManager.GetExperienceForLevel((uint)GetBody().level + 1);
|
||
|
uint difficultyScaledCost = (uint)Run.instance.GetDifficultyScaledCost(25, Run.instance.difficultyCoefficient);
|
||
|
lssMoneyExp += amount;
|
||
|
if ((float)lssMoneyExp >= (float)(difficultyScaledCost * num) + GetBody().level)
|
||
|
{
|
||
|
GiveExperience((ulong)(num2 / (double)(10 + (itemCount - 1) * 15)));
|
||
|
lssMoneyExp = 0u;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
money += amount;
|
||
|
StatManager.OnGoldCollected(this, amount);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void GiveVoidCoins(uint amount)
|
||
|
{
|
||
|
voidCoins += amount;
|
||
|
}
|
||
|
|
||
|
public int GetDeployableSameSlotLimit(DeployableSlot slot)
|
||
|
{
|
||
|
int result = 0;
|
||
|
int num = 1;
|
||
|
if (RunArtifactManager.instance.IsArtifactEnabled(RoR2Content.Artifacts.swarmsArtifactDef))
|
||
|
{
|
||
|
num = 2;
|
||
|
}
|
||
|
switch (slot)
|
||
|
{
|
||
|
case DeployableSlot.EngiMine:
|
||
|
result = 4;
|
||
|
if ((bool)bodyInstanceObject)
|
||
|
{
|
||
|
result = bodyInstanceObject.GetComponent<SkillLocator>().secondary.maxStock;
|
||
|
}
|
||
|
break;
|
||
|
case DeployableSlot.EngiTurret:
|
||
|
result = ((inventory.GetItemCount(DLC1Content.Items.EquipmentMagazineVoid) <= 0) ? 2 : 3);
|
||
|
break;
|
||
|
case DeployableSlot.BeetleGuardAlly:
|
||
|
result = inventory.GetItemCount(RoR2Content.Items.BeetleGland) * num;
|
||
|
break;
|
||
|
case DeployableSlot.EngiBubbleShield:
|
||
|
result = 1;
|
||
|
break;
|
||
|
case DeployableSlot.LoaderPylon:
|
||
|
result = 3;
|
||
|
break;
|
||
|
case DeployableSlot.EngiSpiderMine:
|
||
|
result = 4;
|
||
|
if ((bool)bodyInstanceObject)
|
||
|
{
|
||
|
result = bodyInstanceObject.GetComponent<SkillLocator>().secondary.maxStock;
|
||
|
}
|
||
|
break;
|
||
|
case DeployableSlot.RoboBallMini:
|
||
|
result = 3;
|
||
|
break;
|
||
|
case DeployableSlot.ParentPodAlly:
|
||
|
result = inventory.GetItemCount(JunkContent.Items.Incubator) * num;
|
||
|
break;
|
||
|
case DeployableSlot.ParentAlly:
|
||
|
result = inventory.GetItemCount(JunkContent.Items.Incubator) * num;
|
||
|
break;
|
||
|
case DeployableSlot.PowerWard:
|
||
|
result = 1;
|
||
|
break;
|
||
|
case DeployableSlot.CrippleWard:
|
||
|
result = 5;
|
||
|
break;
|
||
|
case DeployableSlot.DeathProjectile:
|
||
|
result = 3;
|
||
|
break;
|
||
|
case DeployableSlot.RoboBallRedBuddy:
|
||
|
case DeployableSlot.RoboBallGreenBuddy:
|
||
|
result = num;
|
||
|
break;
|
||
|
case DeployableSlot.GummyClone:
|
||
|
result = 3;
|
||
|
break;
|
||
|
case DeployableSlot.LunarSunBomb:
|
||
|
result = LunarSunBehavior.GetMaxProjectiles(inventory);
|
||
|
break;
|
||
|
case DeployableSlot.VendingMachine:
|
||
|
result = 1;
|
||
|
break;
|
||
|
case DeployableSlot.VoidMegaCrabItem:
|
||
|
result = VoidMegaCrabItemBehavior.GetMaxProjectiles(inventory);
|
||
|
break;
|
||
|
case DeployableSlot.DroneWeaponsDrone:
|
||
|
result = 1;
|
||
|
break;
|
||
|
case DeployableSlot.MinorConstructOnKill:
|
||
|
result = inventory.GetItemCount(DLC1Content.Items.MinorConstructOnKill) * 4;
|
||
|
break;
|
||
|
case DeployableSlot.CaptainSupplyDrop:
|
||
|
result = 2;
|
||
|
break;
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
public void AddDeployable(Deployable deployable, DeployableSlot slot)
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'System.Void RoR2.CharacterMaster::AddDeployable(RoR2.Deployable,RoR2.DeployableSlot)' called on client");
|
||
|
return;
|
||
|
}
|
||
|
if ((bool)deployable.ownerMaster)
|
||
|
{
|
||
|
Debug.LogErrorFormat("Attempted to add deployable {0} which already belongs to master {1} to master {2}.", deployable.gameObject, deployable.ownerMaster.gameObject, base.gameObject);
|
||
|
}
|
||
|
if (deployablesList == null)
|
||
|
{
|
||
|
deployablesList = new List<DeployableInfo>();
|
||
|
}
|
||
|
int num = 0;
|
||
|
int deployableSameSlotLimit = GetDeployableSameSlotLimit(slot);
|
||
|
for (int num2 = deployablesList.Count - 1; num2 >= 0; num2--)
|
||
|
{
|
||
|
if (deployablesList[num2].slot == slot)
|
||
|
{
|
||
|
num++;
|
||
|
if (num >= deployableSameSlotLimit)
|
||
|
{
|
||
|
Deployable deployable2 = deployablesList[num2].deployable;
|
||
|
deployablesList.RemoveAt(num2);
|
||
|
deployable2.ownerMaster = null;
|
||
|
deployable2.onUndeploy.Invoke();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
deployablesList.Add(new DeployableInfo
|
||
|
{
|
||
|
deployable = deployable,
|
||
|
slot = slot
|
||
|
});
|
||
|
deployable.ownerMaster = this;
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
public int GetDeployableCount(DeployableSlot slot)
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'System.Int32 RoR2.CharacterMaster::GetDeployableCount(RoR2.DeployableSlot)' called on client");
|
||
|
return 0;
|
||
|
}
|
||
|
if (deployablesList == null)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
int num = 0;
|
||
|
for (int num2 = deployablesList.Count - 1; num2 >= 0; num2--)
|
||
|
{
|
||
|
if (deployablesList[num2].slot == slot)
|
||
|
{
|
||
|
num++;
|
||
|
}
|
||
|
}
|
||
|
return num;
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
public bool IsDeployableLimited(DeployableSlot slot)
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'System.Boolean RoR2.CharacterMaster::IsDeployableLimited(RoR2.DeployableSlot)' called on client");
|
||
|
return false;
|
||
|
}
|
||
|
return GetDeployableCount(slot) >= GetDeployableSameSlotLimit(slot);
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
public void RemoveDeployable(Deployable deployable)
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'System.Void RoR2.CharacterMaster::RemoveDeployable(RoR2.Deployable)' called on client");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (deployablesList == null || deployable.ownerMaster != this)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
for (int num = deployablesList.Count - 1; num >= 0; num--)
|
||
|
{
|
||
|
if (deployablesList[num].deployable == deployable)
|
||
|
{
|
||
|
deployablesList.RemoveAt(num);
|
||
|
}
|
||
|
}
|
||
|
deployable.ownerMaster = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
public bool IsDeployableSlotAvailable(DeployableSlot deployableSlot)
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'System.Boolean RoR2.CharacterMaster::IsDeployableSlotAvailable(RoR2.DeployableSlot)' called on client");
|
||
|
return false;
|
||
|
}
|
||
|
return GetDeployableCount(deployableSlot) < GetDeployableSameSlotLimit(deployableSlot);
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
public CharacterBody SpawnBody(Vector3 position, Quaternion rotation)
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'RoR2.CharacterBody RoR2.CharacterMaster::SpawnBody(UnityEngine.Vector3,UnityEngine.Quaternion)' called on client");
|
||
|
return null;
|
||
|
}
|
||
|
if ((bool)bodyInstanceObject)
|
||
|
{
|
||
|
Debug.LogError("Character cannot have more than one body at this time.");
|
||
|
return null;
|
||
|
}
|
||
|
if (!bodyPrefab)
|
||
|
{
|
||
|
Debug.LogErrorFormat("Attempted to spawn body of character master {0} with no body prefab.", base.gameObject);
|
||
|
}
|
||
|
if (!bodyPrefab.GetComponent<CharacterBody>())
|
||
|
{
|
||
|
Debug.LogErrorFormat("Attempted to spawn body of character master {0} with a body prefab that has no {1} component attached.", base.gameObject, typeof(CharacterBody).Name);
|
||
|
}
|
||
|
bool flag = bodyPrefab.GetComponent<CharacterDirection>();
|
||
|
GameObject gameObject = UnityEngine.Object.Instantiate(bodyPrefab, position, flag ? Quaternion.identity : rotation);
|
||
|
CharacterBody component = gameObject.GetComponent<CharacterBody>();
|
||
|
component.masterObject = base.gameObject;
|
||
|
component.teamComponent.teamIndex = teamIndex;
|
||
|
component.SetLoadoutServer(loadout);
|
||
|
if (flag)
|
||
|
{
|
||
|
CharacterDirection component2 = gameObject.GetComponent<CharacterDirection>();
|
||
|
float y = rotation.eulerAngles.y;
|
||
|
component2.yaw = y;
|
||
|
}
|
||
|
NetworkConnection clientAuthorityOwner = GetComponent<NetworkIdentity>().clientAuthorityOwner;
|
||
|
if (clientAuthorityOwner != null)
|
||
|
{
|
||
|
clientAuthorityOwner.isReady = true;
|
||
|
NetworkServer.SpawnWithClientAuthority(gameObject, clientAuthorityOwner);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
NetworkServer.Spawn(gameObject);
|
||
|
}
|
||
|
bodyInstanceObject = gameObject;
|
||
|
Run.instance.OnServerCharacterBodySpawned(component);
|
||
|
return component;
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
public void DestroyBody()
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'System.Void RoR2.CharacterMaster::DestroyBody()' called on client");
|
||
|
}
|
||
|
else if ((bool)bodyInstanceObject)
|
||
|
{
|
||
|
CharacterBody body = GetBody();
|
||
|
UnityEngine.Object.Destroy(bodyInstanceObject);
|
||
|
OnBodyDestroyed(body);
|
||
|
bodyInstanceObject = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public GameObject GetBodyObject()
|
||
|
{
|
||
|
return bodyInstanceObject;
|
||
|
}
|
||
|
|
||
|
public CharacterBody GetBody()
|
||
|
{
|
||
|
GameObject bodyObject = GetBodyObject();
|
||
|
if (!bodyObject)
|
||
|
{
|
||
|
return null;
|
||
|
}
|
||
|
return bodyObject.GetComponent<CharacterBody>();
|
||
|
}
|
||
|
|
||
|
private void Awake()
|
||
|
{
|
||
|
networkIdentity = GetComponent<NetworkIdentity>();
|
||
|
inventory = GetComponent<Inventory>();
|
||
|
aiComponents = (NetworkServer.active ? GetComponents<BaseAI>() : Array.Empty<BaseAI>());
|
||
|
playerCharacterMasterController = GetComponent<PlayerCharacterMasterController>();
|
||
|
playerStatsComponent = GetComponent<PlayerStatsComponent>();
|
||
|
minionOwnership = GetComponent<MinionOwnership>();
|
||
|
inventory.onInventoryChanged += OnInventoryChanged;
|
||
|
inventory.onItemAddedClient += OnItemAddedClient;
|
||
|
inventory.onEquipmentExternalRestockServer += OnInventoryEquipmentExternalRestockServer;
|
||
|
OnInventoryChanged();
|
||
|
Stage.onServerStageBegin += OnServerStageBegin;
|
||
|
}
|
||
|
|
||
|
private void OnItemAddedClient(ItemIndex itemIndex)
|
||
|
{
|
||
|
StartCoroutine(HighlightNewItem(itemIndex));
|
||
|
}
|
||
|
|
||
|
private IEnumerator HighlightNewItem(ItemIndex itemIndex)
|
||
|
{
|
||
|
yield return new WaitForSeconds(0.05f);
|
||
|
GameObject bodyObject = GetBodyObject();
|
||
|
if (!bodyObject)
|
||
|
{
|
||
|
yield break;
|
||
|
}
|
||
|
ModelLocator component = bodyObject.GetComponent<ModelLocator>();
|
||
|
if (!component)
|
||
|
{
|
||
|
yield break;
|
||
|
}
|
||
|
Transform modelTransform = component.modelTransform;
|
||
|
if ((bool)modelTransform)
|
||
|
{
|
||
|
CharacterModel component2 = modelTransform.GetComponent<CharacterModel>();
|
||
|
if ((bool)component2)
|
||
|
{
|
||
|
component2.HighlightItemDisplay(itemIndex);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void Start()
|
||
|
{
|
||
|
UpdateAuthority();
|
||
|
if (NetworkServer.active && spawnOnStart && !bodyInstanceObject)
|
||
|
{
|
||
|
SpawnBodyHere();
|
||
|
}
|
||
|
CharacterMaster.onStartGlobal?.Invoke(this);
|
||
|
}
|
||
|
|
||
|
public void SetAIUpdateFrequency(bool updateEveryFrame)
|
||
|
{
|
||
|
BaseAI[] array = aiComponents;
|
||
|
for (int i = 0; i < array.Length; i++)
|
||
|
{
|
||
|
array[i].forceUpdateEveryFrame = updateEveryFrame;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void OnInventoryChanged()
|
||
|
{
|
||
|
luck = 0f;
|
||
|
luck += inventory.GetItemCount(RoR2Content.Items.Clover);
|
||
|
luck -= inventory.GetItemCount(RoR2Content.Items.LunarBadLuck);
|
||
|
if (NetworkServer.active && (bool)inventory)
|
||
|
{
|
||
|
CharacterBody body = GetBody();
|
||
|
if ((bool)body && body.bodyIndex != BodyCatalog.SpecialCases.HereticBody() && inventory.GetItemCount(RoR2Content.Items.LunarPrimaryReplacement.itemIndex) > 0 && inventory.GetItemCount(RoR2Content.Items.LunarSecondaryReplacement.itemIndex) > 0 && inventory.GetItemCount(RoR2Content.Items.LunarSpecialReplacement.itemIndex) > 0 && inventory.GetItemCount(RoR2Content.Items.LunarUtilityReplacement.itemIndex) > 0)
|
||
|
{
|
||
|
TransformBody("HereticBody");
|
||
|
}
|
||
|
if (inventory.GetItemCount(DLC2Content.Items.OnLevelUpFreeUnlock) > 0 && money != 0)
|
||
|
{
|
||
|
GiveMoney(money);
|
||
|
money = 0u;
|
||
|
}
|
||
|
}
|
||
|
SetUpGummyClone();
|
||
|
}
|
||
|
|
||
|
private void OnInventoryEquipmentExternalRestockServer()
|
||
|
{
|
||
|
CharacterBody body = GetBody();
|
||
|
if ((bool)body)
|
||
|
{
|
||
|
EffectData effectData = new EffectData();
|
||
|
effectData.origin = body.corePosition;
|
||
|
effectData.SetNetworkedObjectReference(body.gameObject);
|
||
|
EffectManager.SpawnEffect(LegacyResourcesAPI.Load<GameObject>("Prefabs/Effects/EquipmentRestockEffect"), effectData, transmit: true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
public void SpawnBodyHere()
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'System.Void RoR2.CharacterMaster::SpawnBodyHere()' called on client");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SpawnBody(base.transform.position, base.transform.rotation);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void OnEnable()
|
||
|
{
|
||
|
instancesList.Add(this);
|
||
|
CharacterMaster.onCharacterMasterDiscovered?.Invoke(this);
|
||
|
}
|
||
|
|
||
|
private void OnDisable()
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
CharacterMaster.onCharacterMasterLost?.Invoke(this);
|
||
|
}
|
||
|
catch (Exception message)
|
||
|
{
|
||
|
Debug.LogError(message);
|
||
|
}
|
||
|
instancesList.Remove(this);
|
||
|
}
|
||
|
|
||
|
private void OnDestroy()
|
||
|
{
|
||
|
if (isBoss)
|
||
|
{
|
||
|
isBoss = false;
|
||
|
}
|
||
|
Stage.onServerStageBegin -= OnServerStageBegin;
|
||
|
}
|
||
|
|
||
|
public void OnBodyStart(CharacterBody body)
|
||
|
{
|
||
|
if (NetworkServer.active)
|
||
|
{
|
||
|
lostBodyToDeath = false;
|
||
|
}
|
||
|
preventGameOver = true;
|
||
|
killerBodyIndex = BodyIndex.None;
|
||
|
killedByUnsafeArea = false;
|
||
|
body.RecalculateStats();
|
||
|
if (NetworkServer.active)
|
||
|
{
|
||
|
BaseAI[] array = aiComponents;
|
||
|
for (int i = 0; i < array.Length; i++)
|
||
|
{
|
||
|
array[i].OnBodyStart(body);
|
||
|
}
|
||
|
}
|
||
|
if ((bool)playerCharacterMasterController)
|
||
|
{
|
||
|
if ((bool)playerCharacterMasterController.networkUserObject)
|
||
|
{
|
||
|
_ = playerCharacterMasterController.networkUserObject.GetComponent<NetworkIdentity>().isLocalPlayer;
|
||
|
}
|
||
|
playerCharacterMasterController.OnBodyStart();
|
||
|
}
|
||
|
if (inventory.GetItemCount(RoR2Content.Items.Ghost) > 0)
|
||
|
{
|
||
|
Util.PlaySound("Play_item_proc_ghostOnKill", body.gameObject);
|
||
|
}
|
||
|
if (NetworkServer.active)
|
||
|
{
|
||
|
HealthComponent healthComponent = body.healthComponent;
|
||
|
if ((bool)healthComponent)
|
||
|
{
|
||
|
if (teamIndex == TeamIndex.Player && Run.instance.selectedDifficulty >= DifficultyIndex.Eclipse1)
|
||
|
{
|
||
|
healthComponent.Networkhealth = healthComponent.fullHealth * 0.5f;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
healthComponent.Networkhealth = healthComponent.fullHealth;
|
||
|
}
|
||
|
}
|
||
|
UpdateBodyGodMode();
|
||
|
StartLifeStopwatch();
|
||
|
}
|
||
|
SetUpGummyClone();
|
||
|
this.onBodyStart?.Invoke(body);
|
||
|
if (inventory.GetItemCount(DLC2Content.Items.ExtraStatsOnLevelUp) > 0)
|
||
|
{
|
||
|
uint num = TeamManager.instance.FindLevelForBeadExperience(beadExperience);
|
||
|
body.SetBuffCount(DLC2Content.Buffs.ExtraStatsOnLevelUpBuff.buffIndex, (int)(num - 1));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
public bool IsExtraLifePendingServer()
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'System.Boolean RoR2.CharacterMaster::IsExtraLifePendingServer()' called on client");
|
||
|
return false;
|
||
|
}
|
||
|
if (!IsInvoking("RespawnExtraLife") && !IsInvoking("RespawnExtraLifeVoid") && !IsInvoking("RespawnExtraLifeShrine"))
|
||
|
{
|
||
|
return IsInvoking("RespawnExtraLifeHealAndRevive");
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
public bool IsDeadAndOutOfLivesServer()
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'System.Boolean RoR2.CharacterMaster::IsDeadAndOutOfLivesServer()' called on client");
|
||
|
return false;
|
||
|
}
|
||
|
CharacterBody body = GetBody();
|
||
|
if (body != null)
|
||
|
{
|
||
|
if (body.healthComponent.alive)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
if (body.HasBuff(DLC2Content.Buffs.ExtraLifeBuff))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
bool flag = false;
|
||
|
if (body.equipmentSlot != null && body.equipmentSlot.equipmentIndex == DLC2Content.Equipment.HealAndRevive.equipmentIndex)
|
||
|
{
|
||
|
flag = true;
|
||
|
}
|
||
|
if (flag)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return inventory.GetItemCount(RoR2Content.Items.ExtraLife) <= 0 && inventory.GetItemCount(DLC1Content.Items.ExtraLifeVoid) <= 0 && !IsExtraLifePendingServer();
|
||
|
}
|
||
|
|
||
|
public void OnBodyDeath(CharacterBody body)
|
||
|
{
|
||
|
if (NetworkServer.active)
|
||
|
{
|
||
|
lostBodyToDeath = true;
|
||
|
deathFootPosition = body.footPosition;
|
||
|
BaseAI[] array = aiComponents;
|
||
|
for (int i = 0; i < array.Length; i++)
|
||
|
{
|
||
|
array[i].OnBodyDeath(body);
|
||
|
}
|
||
|
if ((bool)playerCharacterMasterController)
|
||
|
{
|
||
|
playerCharacterMasterController.OnBodyDeath();
|
||
|
}
|
||
|
if (body.HasBuff(DLC2Content.Buffs.ExtraLifeBuff))
|
||
|
{
|
||
|
body.RemoveBuff(DLC2Content.Buffs.ExtraLifeBuff);
|
||
|
Invoke("RespawnExtraLifeShrine", 2f);
|
||
|
Invoke("PlayExtraLifeSFX", 1f);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bool flag = false;
|
||
|
if ((bool)body.equipmentSlot && body.equipmentSlot.equipmentIndex == DLC2Content.Equipment.HealAndRevive.equipmentIndex)
|
||
|
{
|
||
|
flag = true;
|
||
|
}
|
||
|
if (flag)
|
||
|
{
|
||
|
CharacterMasterNotificationQueue.SendTransformNotification(this, inventory.currentEquipmentIndex, DLC2Content.Equipment.HealAndReviveConsumed.equipmentIndex, CharacterMasterNotificationQueue.TransformationType.Default);
|
||
|
inventory.SetEquipmentIndex(DLC2Content.Equipment.HealAndReviveConsumed.equipmentIndex);
|
||
|
Invoke("RespawnExtraLifeHealAndRevive", 2f);
|
||
|
Invoke("PlayExtraLifeSFX", 1f);
|
||
|
}
|
||
|
else if (inventory.GetItemCount(RoR2Content.Items.ExtraLife) > 0)
|
||
|
{
|
||
|
inventory.RemoveItem(RoR2Content.Items.ExtraLife);
|
||
|
Invoke("RespawnExtraLife", 2f);
|
||
|
Invoke("PlayExtraLifeSFX", 1f);
|
||
|
}
|
||
|
else if (inventory.GetItemCount(DLC1Content.Items.ExtraLifeVoid) > 0)
|
||
|
{
|
||
|
inventory.RemoveItem(DLC1Content.Items.ExtraLifeVoid);
|
||
|
Invoke("RespawnExtraLifeVoid", 2f);
|
||
|
Invoke("PlayExtraLifeVoidSFX", 1f);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (destroyOnBodyDeath)
|
||
|
{
|
||
|
UnityEngine.Object.Destroy(base.gameObject, 1f);
|
||
|
}
|
||
|
preventGameOver = false;
|
||
|
preventRespawnUntilNextStageServer = true;
|
||
|
}
|
||
|
}
|
||
|
if (TryGetComponent<DevotedLemurianController>(out var component))
|
||
|
{
|
||
|
component.OnDevotedBodyDead();
|
||
|
}
|
||
|
if ((bool)devotionInventoryPrefab)
|
||
|
{
|
||
|
devotionInventoryPrefab.GetComponent<Inventory>().RemoveItem(devotionItem, devotedEvolutionTracker + 1);
|
||
|
devotionInventoryUpdaterRef.DropScrapOnDeath(devotionItem, body);
|
||
|
devotionInventoryUpdaterRef.UpdateAllMinions();
|
||
|
}
|
||
|
ResetLifeStopwatch();
|
||
|
}
|
||
|
onBodyDeath?.Invoke();
|
||
|
}
|
||
|
|
||
|
public void TrueKill()
|
||
|
{
|
||
|
TrueKill(null, null, DamageType.Generic);
|
||
|
}
|
||
|
|
||
|
public void TrueKill(GameObject killerOverride = null, GameObject inflictorOverride = null, DamageTypeCombo damageTypeOverride = default(DamageTypeCombo))
|
||
|
{
|
||
|
int itemCount = inventory.GetItemCount(RoR2Content.Items.ExtraLife);
|
||
|
if (itemCount > 0)
|
||
|
{
|
||
|
inventory.ResetItem(RoR2Content.Items.ExtraLife);
|
||
|
inventory.GiveItem(RoR2Content.Items.ExtraLifeConsumed, itemCount);
|
||
|
CharacterMasterNotificationQueue.SendTransformNotification(this, RoR2Content.Items.ExtraLife.itemIndex, RoR2Content.Items.ExtraLifeConsumed.itemIndex, CharacterMasterNotificationQueue.TransformationType.Default);
|
||
|
}
|
||
|
if (inventory.GetItemCount(DLC1Content.Items.ExtraLifeVoid) > 0)
|
||
|
{
|
||
|
inventory.ResetItem(DLC1Content.Items.ExtraLifeVoid);
|
||
|
inventory.GiveItem(DLC1Content.Items.ExtraLifeVoidConsumed, itemCount);
|
||
|
CharacterMasterNotificationQueue.SendTransformNotification(this, DLC1Content.Items.ExtraLifeVoid.itemIndex, DLC1Content.Items.ExtraLifeVoidConsumed.itemIndex, CharacterMasterNotificationQueue.TransformationType.Default);
|
||
|
}
|
||
|
if (GetBody().HasBuff(DLC2Content.Buffs.ExtraLifeBuff))
|
||
|
{
|
||
|
GetBody().RemoveBuff(DLC2Content.Buffs.ExtraLifeBuff);
|
||
|
}
|
||
|
bool flag = false;
|
||
|
if ((bool)GetBody().equipmentSlot && GetBody().equipmentSlot.equipmentIndex == DLC2Content.Equipment.HealAndRevive.equipmentIndex)
|
||
|
{
|
||
|
flag = true;
|
||
|
}
|
||
|
if (flag)
|
||
|
{
|
||
|
CharacterMasterNotificationQueue.SendTransformNotification(this, inventory.currentEquipmentIndex, DLC2Content.Equipment.HealAndReviveConsumed.equipmentIndex, CharacterMasterNotificationQueue.TransformationType.Default);
|
||
|
inventory.SetEquipmentIndex(DLC2Content.Equipment.HealAndReviveConsumed.equipmentIndex);
|
||
|
}
|
||
|
CancelInvoke("RespawnExtraLife");
|
||
|
CancelInvoke("PlayExtraLifeSFX");
|
||
|
CancelInvoke("RespawnExtraLifeVoid");
|
||
|
CancelInvoke("PlayExtraLifeVoidSFX");
|
||
|
CancelInvoke("RespawnExtraLifeShrine");
|
||
|
CharacterBody body = GetBody();
|
||
|
if ((bool)body)
|
||
|
{
|
||
|
body.healthComponent.Suicide(killerOverride, inflictorOverride, damageTypeOverride);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void PlayExtraLifeSFX()
|
||
|
{
|
||
|
GameObject gameObject = bodyInstanceObject;
|
||
|
if ((bool)gameObject)
|
||
|
{
|
||
|
Util.PlaySound("Play_item_proc_extraLife", gameObject);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void PlayExtraLifeVoidSFX()
|
||
|
{
|
||
|
GameObject gameObject = bodyInstanceObject;
|
||
|
if ((bool)gameObject)
|
||
|
{
|
||
|
Util.PlaySound("Play_item_void_extraLife", gameObject);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void RespawnExtraLife()
|
||
|
{
|
||
|
inventory.GiveItem(RoR2Content.Items.ExtraLifeConsumed);
|
||
|
CharacterMasterNotificationQueue.SendTransformNotification(this, RoR2Content.Items.ExtraLife.itemIndex, RoR2Content.Items.ExtraLifeConsumed.itemIndex, CharacterMasterNotificationQueue.TransformationType.Default);
|
||
|
Vector3 vector = deathFootPosition;
|
||
|
if (killedByUnsafeArea)
|
||
|
{
|
||
|
vector = TeleportHelper.FindSafeTeleportDestination(deathFootPosition, bodyPrefab.GetComponent<CharacterBody>(), RoR2Application.rng) ?? deathFootPosition;
|
||
|
}
|
||
|
Respawn(vector, Quaternion.Euler(0f, UnityEngine.Random.Range(0f, 360f), 0f), wasRevivedMidStage: true);
|
||
|
GetBody().AddTimedBuff(RoR2Content.Buffs.Immune, 3f);
|
||
|
if (NetworkServer.active)
|
||
|
{
|
||
|
inventory.SetEquipmentDisabled(_active: false);
|
||
|
}
|
||
|
GameObject gameObject = LegacyResourcesAPI.Load<GameObject>("Prefabs/Effects/HippoRezEffect");
|
||
|
if ((bool)bodyInstanceObject)
|
||
|
{
|
||
|
EntityStateMachine[] components = bodyInstanceObject.GetComponents<EntityStateMachine>();
|
||
|
foreach (EntityStateMachine obj in components)
|
||
|
{
|
||
|
obj.initialStateType = obj.mainStateType;
|
||
|
}
|
||
|
if ((bool)gameObject)
|
||
|
{
|
||
|
EffectManager.SpawnEffect(gameObject, new EffectData
|
||
|
{
|
||
|
origin = vector,
|
||
|
rotation = bodyInstanceObject.transform.rotation
|
||
|
}, transmit: true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void RespawnExtraLifeVoid()
|
||
|
{
|
||
|
inventory.GiveItem(DLC1Content.Items.ExtraLifeVoidConsumed);
|
||
|
CharacterMasterNotificationQueue.SendTransformNotification(this, DLC1Content.Items.ExtraLifeVoid.itemIndex, DLC1Content.Items.ExtraLifeVoidConsumed.itemIndex, CharacterMasterNotificationQueue.TransformationType.Default);
|
||
|
Vector3 vector = deathFootPosition;
|
||
|
if (killedByUnsafeArea)
|
||
|
{
|
||
|
vector = TeleportHelper.FindSafeTeleportDestination(deathFootPosition, bodyPrefab.GetComponent<CharacterBody>(), RoR2Application.rng) ?? deathFootPosition;
|
||
|
}
|
||
|
Respawn(vector, Quaternion.Euler(0f, UnityEngine.Random.Range(0f, 360f), 0f), wasRevivedMidStage: true);
|
||
|
GetBody().AddTimedBuff(RoR2Content.Buffs.Immune, 3f);
|
||
|
if (NetworkServer.active)
|
||
|
{
|
||
|
inventory.SetEquipmentDisabled(_active: false);
|
||
|
}
|
||
|
if ((bool)bodyInstanceObject)
|
||
|
{
|
||
|
EntityStateMachine[] components = bodyInstanceObject.GetComponents<EntityStateMachine>();
|
||
|
foreach (EntityStateMachine obj in components)
|
||
|
{
|
||
|
obj.initialStateType = obj.mainStateType;
|
||
|
}
|
||
|
if ((bool)ExtraLifeVoidManager.rezEffectPrefab)
|
||
|
{
|
||
|
EffectManager.SpawnEffect(ExtraLifeVoidManager.rezEffectPrefab, new EffectData
|
||
|
{
|
||
|
origin = vector,
|
||
|
rotation = bodyInstanceObject.transform.rotation
|
||
|
}, transmit: true);
|
||
|
}
|
||
|
}
|
||
|
foreach (ContagiousItemManager.TransformationInfo transformationInfo in ContagiousItemManager.transformationInfos)
|
||
|
{
|
||
|
ContagiousItemManager.TryForceReplacement(inventory, transformationInfo.originalItem);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void RespawnExtraLifeShrine()
|
||
|
{
|
||
|
Vector3 vector = deathFootPosition;
|
||
|
if (killedByUnsafeArea)
|
||
|
{
|
||
|
vector = TeleportHelper.FindSafeTeleportDestination(deathFootPosition, bodyPrefab.GetComponent<CharacterBody>(), RoR2Application.rng) ?? deathFootPosition;
|
||
|
}
|
||
|
Respawn(vector, Quaternion.Euler(0f, UnityEngine.Random.Range(0f, 360f), 0f), wasRevivedMidStage: true);
|
||
|
GetBody().AddTimedBuff(RoR2Content.Buffs.Immune, 3f);
|
||
|
GameObject gameObject = LegacyResourcesAPI.Load<GameObject>("Prefabs/Effects/fxHealAndReviveGold");
|
||
|
if ((bool)bodyInstanceObject)
|
||
|
{
|
||
|
EntityStateMachine[] components = bodyInstanceObject.GetComponents<EntityStateMachine>();
|
||
|
foreach (EntityStateMachine obj in components)
|
||
|
{
|
||
|
obj.initialStateType = obj.mainStateType;
|
||
|
}
|
||
|
if ((bool)gameObject)
|
||
|
{
|
||
|
EffectManager.SpawnEffect(gameObject, new EffectData
|
||
|
{
|
||
|
origin = vector,
|
||
|
rotation = bodyInstanceObject.transform.rotation
|
||
|
}, transmit: true);
|
||
|
Util.PlaySound("Play_item_use_healAndRevive_activate", base.gameObject);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void RespawnExtraLifeHealAndRevive()
|
||
|
{
|
||
|
Vector3 vector = deathFootPosition;
|
||
|
if (killedByUnsafeArea)
|
||
|
{
|
||
|
vector = TeleportHelper.FindSafeTeleportDestination(deathFootPosition, bodyPrefab.GetComponent<CharacterBody>(), RoR2Application.rng) ?? deathFootPosition;
|
||
|
}
|
||
|
Respawn(vector, Quaternion.Euler(0f, UnityEngine.Random.Range(0f, 360f), 0f), wasRevivedMidStage: true);
|
||
|
GetBody().AddTimedBuff(RoR2Content.Buffs.Immune, 3f);
|
||
|
GameObject gameObject = LegacyResourcesAPI.Load<GameObject>("Prefabs/Effects/fxHealAndReviveGold");
|
||
|
if ((bool)bodyInstanceObject)
|
||
|
{
|
||
|
EntityStateMachine[] components = bodyInstanceObject.GetComponents<EntityStateMachine>();
|
||
|
foreach (EntityStateMachine obj in components)
|
||
|
{
|
||
|
obj.initialStateType = obj.mainStateType;
|
||
|
}
|
||
|
if ((bool)gameObject)
|
||
|
{
|
||
|
EffectManager.SpawnEffect(gameObject, new EffectData
|
||
|
{
|
||
|
origin = vector,
|
||
|
rotation = bodyInstanceObject.transform.rotation
|
||
|
}, transmit: true);
|
||
|
Util.PlaySound("Play_item_use_healAndRevive_activate", base.gameObject);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void OnBodyDamaged(DamageReport damageReport)
|
||
|
{
|
||
|
BaseAI[] array = aiComponents;
|
||
|
for (int i = 0; i < array.Length; i++)
|
||
|
{
|
||
|
array[i].OnBodyDamaged(damageReport);
|
||
|
}
|
||
|
if ((bool)playerCharacterMasterController)
|
||
|
{
|
||
|
HandleOnPlayerBodyDamaged();
|
||
|
CallRpcOnPlayerBodyDamaged();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[ClientRpc]
|
||
|
private void RpcOnPlayerBodyDamaged()
|
||
|
{
|
||
|
HandleOnPlayerBodyDamaged();
|
||
|
}
|
||
|
|
||
|
private void HandleOnPlayerBodyDamaged()
|
||
|
{
|
||
|
this.onPlayerBodyDamaged?.Invoke(playerCharacterMasterController);
|
||
|
}
|
||
|
|
||
|
public void OnBodyDestroyed(CharacterBody characterBody)
|
||
|
{
|
||
|
if ((object)characterBody != GetBody())
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
if (NetworkServer.active)
|
||
|
{
|
||
|
BaseAI[] array = aiComponents;
|
||
|
for (int i = 0; i < array.Length; i++)
|
||
|
{
|
||
|
array[i].OnBodyDestroyed(characterBody);
|
||
|
}
|
||
|
PauseLifeStopwatch();
|
||
|
}
|
||
|
this.onBodyDestroyed?.Invoke(characterBody);
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
private void StartLifeStopwatch()
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'System.Void RoR2.CharacterMaster::StartLifeStopwatch()' called on client");
|
||
|
}
|
||
|
else if (!(internalSurvivalTime > 0f))
|
||
|
{
|
||
|
internalSurvivalTime = Run.instance.GetRunStopwatch() - currentLifeStopwatch;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
private void PauseLifeStopwatch()
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'System.Void RoR2.CharacterMaster::PauseLifeStopwatch()' called on client");
|
||
|
}
|
||
|
else if (!(internalSurvivalTime <= 0f))
|
||
|
{
|
||
|
internalSurvivalTime = 0f - currentLifeStopwatch;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
private void ResetLifeStopwatch()
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'System.Void RoR2.CharacterMaster::ResetLifeStopwatch()' called on client");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
internalSurvivalTime = 0f;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
public BodyIndex GetKillerBodyIndex()
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'RoR2.BodyIndex RoR2.CharacterMaster::GetKillerBodyIndex()' called on client");
|
||
|
return default(BodyIndex);
|
||
|
}
|
||
|
return killerBodyIndex;
|
||
|
}
|
||
|
|
||
|
[InitDuringStartup]
|
||
|
private static void Init()
|
||
|
{
|
||
|
GlobalEventManager.onCharacterDeathGlobal += delegate(DamageReport damageReport)
|
||
|
{
|
||
|
CharacterMaster victimMaster = damageReport.victimMaster;
|
||
|
if ((bool)victimMaster)
|
||
|
{
|
||
|
victimMaster.killerBodyIndex = BodyCatalog.FindBodyIndex(damageReport.damageInfo.attacker);
|
||
|
victimMaster.killedByUnsafeArea = (bool)damageReport.damageInfo.inflictor && (bool)damageReport.damageInfo.inflictor.GetComponent<MapZone>();
|
||
|
}
|
||
|
};
|
||
|
Stage.onServerStageBegin += delegate
|
||
|
{
|
||
|
foreach (CharacterMaster instances in instancesList)
|
||
|
{
|
||
|
instances.preventRespawnUntilNextStageServer = false;
|
||
|
}
|
||
|
};
|
||
|
CommonAssets.Resolve();
|
||
|
}
|
||
|
|
||
|
[Command]
|
||
|
public void CmdRespawn(string bodyName)
|
||
|
{
|
||
|
if (preventRespawnUntilNextStageServer)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
if (!string.IsNullOrEmpty(bodyName))
|
||
|
{
|
||
|
bodyPrefab = BodyCatalog.FindBodyPrefab(bodyName);
|
||
|
if (!bodyPrefab)
|
||
|
{
|
||
|
Debug.LogError("CmdRespawn failed to find bodyPrefab for name '" + bodyName + "'.");
|
||
|
}
|
||
|
}
|
||
|
if ((bool)Stage.instance)
|
||
|
{
|
||
|
Stage.instance.RespawnCharacter(this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
public void TransformBody(string bodyName)
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'System.Void RoR2.CharacterMaster::TransformBody(System.String)' called on client");
|
||
|
}
|
||
|
else if (!string.IsNullOrEmpty(bodyName))
|
||
|
{
|
||
|
bodyPrefab = BodyCatalog.FindBodyPrefab(bodyName);
|
||
|
if (bodyPrefab != null)
|
||
|
{
|
||
|
Transform component = bodyInstanceObject.GetComponent<Transform>();
|
||
|
Vector3 position = component.position;
|
||
|
Quaternion rotation = component.rotation;
|
||
|
DestroyBody();
|
||
|
CharacterBody component2 = bodyPrefab.GetComponent<CharacterBody>();
|
||
|
if ((bool)component2)
|
||
|
{
|
||
|
position = CalculateSafeGroundPosition(position, component2);
|
||
|
SpawnBody(position, rotation).GetComponent<HereticInitialStateHelper>()?.PrepareTransformation();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Debug.LogErrorFormat("Trying to respawn as object {0} who has no Character Body!", bodyPrefab);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Debug.LogError("Can't TransformBody because there's no prefab for body named '" + bodyName + "'");
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Debug.LogError("Can't TransformBody with null or empty body name.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
private void OnServerStageBegin(Stage stage)
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'System.Void RoR2.CharacterMaster::OnServerStageBegin(RoR2.Stage)' called on client");
|
||
|
return;
|
||
|
}
|
||
|
TryCloverVoidUpgrades();
|
||
|
TryRegenerateScrap();
|
||
|
TrySaleStar();
|
||
|
}
|
||
|
|
||
|
[ClientRpc]
|
||
|
public void RpcSetSeekerRevive(SEEKER_REVIVE_STATUS s)
|
||
|
{
|
||
|
seekerUsedRevive = s;
|
||
|
}
|
||
|
|
||
|
public SEEKER_REVIVE_STATUS getSeekerUsedRevive()
|
||
|
{
|
||
|
return seekerUsedRevive;
|
||
|
}
|
||
|
|
||
|
private void TryRegenerateScrap()
|
||
|
{
|
||
|
int itemCount = inventory.GetItemCount(DLC1Content.Items.RegeneratingScrapConsumed);
|
||
|
if (itemCount > 0)
|
||
|
{
|
||
|
inventory.RemoveItem(DLC1Content.Items.RegeneratingScrapConsumed, itemCount);
|
||
|
inventory.GiveItem(DLC1Content.Items.RegeneratingScrap, itemCount);
|
||
|
CharacterMasterNotificationQueue.SendTransformNotification(this, DLC1Content.Items.RegeneratingScrapConsumed.itemIndex, DLC1Content.Items.RegeneratingScrap.itemIndex, CharacterMasterNotificationQueue.TransformationType.RegeneratingScrapRegen);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void TrySaleStar()
|
||
|
{
|
||
|
int itemCount = inventory.GetItemCount(DLC2Content.Items.LowerPricedChestsConsumed);
|
||
|
if (itemCount > 0)
|
||
|
{
|
||
|
inventory.RemoveItem(DLC2Content.Items.LowerPricedChestsConsumed, itemCount);
|
||
|
inventory.GiveItem(DLC2Content.Items.LowerPricedChests, itemCount);
|
||
|
CharacterMasterNotificationQueue.SendTransformNotification(this, DLC2Content.Items.LowerPricedChestsConsumed.itemIndex, DLC2Content.Items.LowerPricedChests.itemIndex, CharacterMasterNotificationQueue.TransformationType.SaleStarRegen);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void TryTeleportOnLowHealthRegen()
|
||
|
{
|
||
|
int itemCount = inventory.GetItemCount(DLC2Content.Items.TeleportOnLowHealthConsumed);
|
||
|
if (itemCount > 0)
|
||
|
{
|
||
|
inventory.RemoveItem(DLC2Content.Items.TeleportOnLowHealthConsumed, itemCount);
|
||
|
inventory.GiveItem(DLC2Content.Items.TeleportOnLowHealth, itemCount);
|
||
|
CharacterMasterNotificationQueue.SendTransformNotification(this, DLC2Content.Items.TeleportOnLowHealthConsumed.itemIndex, DLC2Content.Items.TeleportOnLowHealth.itemIndex, CharacterMasterNotificationQueue.TransformationType.TeleportOnLowHealthRegen);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
public void TrackDevotionItem(ItemIndex itemIndex, Interactor summoner, GameObject devotedInventoryRef, DevotionInventoryController devotionInventoryUpdater)
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'System.Void RoR2.CharacterMaster::TrackDevotionItem(RoR2.ItemIndex,RoR2.Interactor,UnityEngine.GameObject,RoR2.DevotionInventoryController)' called on client");
|
||
|
return;
|
||
|
}
|
||
|
_ = summoner == null;
|
||
|
devotionItem = itemIndex;
|
||
|
summonerRef = summoner;
|
||
|
devotionInventoryPrefab = devotedInventoryRef;
|
||
|
isDevotedMinion = true;
|
||
|
devotionInventoryUpdaterRef = devotionInventoryUpdater;
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
private void TryCloverVoidUpgrades()
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'System.Void RoR2.CharacterMaster::TryCloverVoidUpgrades()' called on client");
|
||
|
return;
|
||
|
}
|
||
|
if (cloverVoidRng == null)
|
||
|
{
|
||
|
cloverVoidRng = new Xoroshiro128Plus(Run.instance.seed);
|
||
|
}
|
||
|
int itemCount = inventory.GetItemCount(DLC1Content.Items.CloverVoid);
|
||
|
List<PickupIndex> list = new List<PickupIndex>(Run.instance.availableTier2DropList);
|
||
|
List<PickupIndex> list2 = new List<PickupIndex>(Run.instance.availableTier3DropList);
|
||
|
List<ItemIndex> list3 = new List<ItemIndex>(inventory.itemAcquisitionOrder);
|
||
|
Util.ShuffleList(list3, cloverVoidRng);
|
||
|
int num = itemCount * 3;
|
||
|
int num2 = 0;
|
||
|
int num3 = 0;
|
||
|
while (num2 < num && num3 < list3.Count)
|
||
|
{
|
||
|
ItemDef startingItemDef = ItemCatalog.GetItemDef(list3[num3]);
|
||
|
ItemDef itemDef = null;
|
||
|
List<PickupIndex> list4 = null;
|
||
|
switch (startingItemDef.tier)
|
||
|
{
|
||
|
case ItemTier.Tier1:
|
||
|
list4 = list;
|
||
|
break;
|
||
|
case ItemTier.Tier2:
|
||
|
list4 = list2;
|
||
|
break;
|
||
|
}
|
||
|
if (list4 != null && list4.Count > 0)
|
||
|
{
|
||
|
Util.ShuffleList(list4, cloverVoidRng);
|
||
|
list4.Sort(CompareTags);
|
||
|
itemDef = ItemCatalog.GetItemDef(list4[0].itemIndex);
|
||
|
}
|
||
|
if (itemDef != null)
|
||
|
{
|
||
|
if (inventory.GetItemCount(itemDef.itemIndex) == 0)
|
||
|
{
|
||
|
list3.Add(itemDef.itemIndex);
|
||
|
}
|
||
|
num2++;
|
||
|
int itemCount2 = inventory.GetItemCount(startingItemDef.itemIndex);
|
||
|
inventory.RemoveItem(startingItemDef.itemIndex, itemCount2);
|
||
|
inventory.GiveItem(itemDef.itemIndex, itemCount2);
|
||
|
CharacterMasterNotificationQueue.SendTransformNotification(this, startingItemDef.itemIndex, itemDef.itemIndex, CharacterMasterNotificationQueue.TransformationType.CloverVoid);
|
||
|
}
|
||
|
num3++;
|
||
|
int CompareTags(PickupIndex lhs, PickupIndex rhs)
|
||
|
{
|
||
|
int num4 = 0;
|
||
|
int num5 = 0;
|
||
|
ItemDef itemDef2 = ItemCatalog.GetItemDef(lhs.itemIndex);
|
||
|
ItemDef itemDef3 = ItemCatalog.GetItemDef(rhs.itemIndex);
|
||
|
if (startingItemDef.ContainsTag(ItemTag.Damage))
|
||
|
{
|
||
|
if (itemDef2.ContainsTag(ItemTag.Damage))
|
||
|
{
|
||
|
num4 = 1;
|
||
|
}
|
||
|
if (itemDef3.ContainsTag(ItemTag.Damage))
|
||
|
{
|
||
|
num5 = 1;
|
||
|
}
|
||
|
}
|
||
|
if (startingItemDef.ContainsTag(ItemTag.Healing))
|
||
|
{
|
||
|
if (itemDef2.ContainsTag(ItemTag.Healing))
|
||
|
{
|
||
|
num4 = 1;
|
||
|
}
|
||
|
if (itemDef3.ContainsTag(ItemTag.Healing))
|
||
|
{
|
||
|
num5 = 1;
|
||
|
}
|
||
|
}
|
||
|
if (startingItemDef.ContainsTag(ItemTag.Utility))
|
||
|
{
|
||
|
if (itemDef2.ContainsTag(ItemTag.Utility))
|
||
|
{
|
||
|
num4 = 1;
|
||
|
}
|
||
|
if (itemDef3.ContainsTag(ItemTag.Utility))
|
||
|
{
|
||
|
num5 = 1;
|
||
|
}
|
||
|
}
|
||
|
return num5 - num4;
|
||
|
}
|
||
|
}
|
||
|
if (num2 > 0)
|
||
|
{
|
||
|
GameObject gameObject = bodyInstanceObject;
|
||
|
if ((bool)gameObject)
|
||
|
{
|
||
|
Util.PlaySound("Play_item_proc_extraLife", gameObject);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static GameObject PickRandomSurvivorBodyPrefab(Xoroshiro128Plus rng, NetworkUser networkUser, bool allowHidden)
|
||
|
{
|
||
|
SurvivorDef[] array = SurvivorCatalog.allSurvivorDefs.Where(SurvivorIsUnlockedAndAvailable).ToArray();
|
||
|
return rng.NextElementUniform(array).bodyPrefab;
|
||
|
bool SurvivorIsUnlockedAndAvailable(SurvivorDef survivorDef)
|
||
|
{
|
||
|
if (allowHidden || !survivorDef.hidden)
|
||
|
{
|
||
|
if (!survivorDef.CheckRequiredExpansionEnabled(networkUser))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
UnlockableDef unlockableDef = survivorDef.unlockableDef;
|
||
|
if ((object)unlockableDef != null)
|
||
|
{
|
||
|
return networkUser.unlockables.Contains(unlockableDef);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
[Server]
|
||
|
public CharacterBody Respawn(Vector3 footPosition, Quaternion rotation, bool wasRevivedMidStage = false)
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogWarning("[Server] function 'RoR2.CharacterBody RoR2.CharacterMaster::Respawn(UnityEngine.Vector3,UnityEngine.Quaternion,System.Boolean)' called on client");
|
||
|
return null;
|
||
|
}
|
||
|
if (!wasRevivedMidStage)
|
||
|
{
|
||
|
CallRpcSetSeekerRevive(SEEKER_REVIVE_STATUS.UNUSED);
|
||
|
}
|
||
|
else if (seekerUsedRevive == SEEKER_REVIVE_STATUS.USED)
|
||
|
{
|
||
|
CallRpcSetSeekerRevive(SEEKER_REVIVE_STATUS.BURNED);
|
||
|
}
|
||
|
DestroyBody();
|
||
|
if ((bool)playerCharacterMasterController && RunArtifactManager.instance.IsArtifactEnabled(RoR2Content.Artifacts.randomSurvivorOnRespawnArtifactDef))
|
||
|
{
|
||
|
bodyPrefab = PickRandomSurvivorBodyPrefab(Run.instance.randomSurvivorOnRespawnRng, playerCharacterMasterController.networkUser, allowHidden: false);
|
||
|
}
|
||
|
if ((bool)bodyPrefab)
|
||
|
{
|
||
|
CharacterBody component = bodyPrefab.GetComponent<CharacterBody>();
|
||
|
if ((bool)component)
|
||
|
{
|
||
|
Vector3 position = footPosition;
|
||
|
if (true)
|
||
|
{
|
||
|
position = CalculateSafeGroundPosition(footPosition, component);
|
||
|
}
|
||
|
return SpawnBody(position, rotation);
|
||
|
}
|
||
|
Debug.LogErrorFormat("Trying to respawn as object {0} who has no Character Body!", bodyPrefab);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Debug.LogErrorFormat("CharacterMaster.Respawn failed. {0} does not have a valid body prefab assigned.", base.gameObject.name);
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
private Vector3 CalculateSafeGroundPosition(Vector3 desiredFootPos, CharacterBody body)
|
||
|
{
|
||
|
if ((bool)body)
|
||
|
{
|
||
|
Vector3 result = desiredFootPos;
|
||
|
RaycastHit hitInfo = default(RaycastHit);
|
||
|
Ray ray = new Ray(desiredFootPos + Vector3.up * 2f, Vector3.down);
|
||
|
float maxDistance = 4f;
|
||
|
if (Physics.SphereCast(ray, body.radius, out hitInfo, maxDistance, LayerIndex.world.mask))
|
||
|
{
|
||
|
result.y = ray.origin.y - hitInfo.distance;
|
||
|
}
|
||
|
float bodyPrefabFootOffset = Util.GetBodyPrefabFootOffset(bodyPrefab);
|
||
|
result.y += bodyPrefabFootOffset;
|
||
|
return result;
|
||
|
}
|
||
|
Debug.LogError("Can't calculate safe ground position if the CharacterBody is null");
|
||
|
return desiredFootPos;
|
||
|
}
|
||
|
|
||
|
private void SetUpGummyClone()
|
||
|
{
|
||
|
if (!NetworkServer.active || !inventory || inventory.GetItemCount(DLC1Content.Items.GummyCloneIdentifier.itemIndex) <= 0)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
if (!base.gameObject.GetComponent<MasterSuicideOnTimer>())
|
||
|
{
|
||
|
base.gameObject.AddComponent<MasterSuicideOnTimer>().lifeTimer = 30f;
|
||
|
}
|
||
|
CharacterBody body = GetBody();
|
||
|
if ((bool)body)
|
||
|
{
|
||
|
CharacterDeathBehavior component = body.GetComponent<CharacterDeathBehavior>();
|
||
|
if ((bool)component && component.deathState.stateType != typeof(GummyCloneDeathState))
|
||
|
{
|
||
|
component.deathState = new SerializableEntityStateType(typeof(GummyCloneDeathState));
|
||
|
}
|
||
|
body.portraitIcon = LegacyResourcesAPI.Load<Texture>("Textures/BodyIcons/texGummyCloneBody");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void SetMasterBodyDirtyBit()
|
||
|
{
|
||
|
SetDirtyBit(1u);
|
||
|
}
|
||
|
|
||
|
private void ToggleGod()
|
||
|
{
|
||
|
godMode = !godMode;
|
||
|
UpdateBodyGodMode();
|
||
|
}
|
||
|
|
||
|
private void UpdateBodyGodMode()
|
||
|
{
|
||
|
if ((bool)bodyInstanceObject)
|
||
|
{
|
||
|
HealthComponent component = bodyInstanceObject.GetComponent<HealthComponent>();
|
||
|
if ((bool)component)
|
||
|
{
|
||
|
component.godMode = godMode;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override bool OnSerialize(NetworkWriter writer, bool initialState)
|
||
|
{
|
||
|
uint num = base.syncVarDirtyBits;
|
||
|
if (initialState)
|
||
|
{
|
||
|
num = 127u;
|
||
|
}
|
||
|
bool num2 = (num & 1) != 0;
|
||
|
bool flag = (num & 2) != 0;
|
||
|
bool flag2 = (num & 0x40) != 0;
|
||
|
bool flag3 = (num & 4) != 0;
|
||
|
bool flag4 = (num & 8) != 0;
|
||
|
bool flag5 = (num & 0x10) != 0;
|
||
|
bool flag6 = (num & 0x20) != 0;
|
||
|
writer.Write((byte)num);
|
||
|
if (num2)
|
||
|
{
|
||
|
writer.Write(_bodyInstanceId);
|
||
|
}
|
||
|
if (flag)
|
||
|
{
|
||
|
writer.WritePackedUInt32(_money);
|
||
|
}
|
||
|
if (flag2)
|
||
|
{
|
||
|
writer.WritePackedUInt32(_voidCoins);
|
||
|
}
|
||
|
if (flag3)
|
||
|
{
|
||
|
writer.Write(_internalSurvivalTime);
|
||
|
}
|
||
|
if (flag4)
|
||
|
{
|
||
|
writer.Write(teamIndex);
|
||
|
}
|
||
|
if (flag5)
|
||
|
{
|
||
|
loadout.Serialize(writer);
|
||
|
}
|
||
|
if (flag6)
|
||
|
{
|
||
|
writer.WritePackedUInt32(miscFlags);
|
||
|
}
|
||
|
return num != 0;
|
||
|
}
|
||
|
|
||
|
public override void OnDeserialize(NetworkReader reader, bool initialState)
|
||
|
{
|
||
|
byte num = reader.ReadByte();
|
||
|
bool flag = (num & 1) != 0;
|
||
|
bool flag2 = (num & 2) != 0;
|
||
|
bool flag3 = (num & 0x40) != 0;
|
||
|
bool flag4 = (num & 4) != 0;
|
||
|
bool flag5 = (num & 8) != 0;
|
||
|
bool flag6 = (num & 0x10) != 0;
|
||
|
bool num2 = (num & 0x20) != 0;
|
||
|
if (flag)
|
||
|
{
|
||
|
NetworkInstanceId value = reader.ReadNetworkId();
|
||
|
OnSyncBodyInstanceId(value);
|
||
|
}
|
||
|
if (flag2)
|
||
|
{
|
||
|
_money = reader.ReadPackedUInt32();
|
||
|
}
|
||
|
if (flag3)
|
||
|
{
|
||
|
_voidCoins = reader.ReadPackedUInt32();
|
||
|
}
|
||
|
if (flag4)
|
||
|
{
|
||
|
_internalSurvivalTime = reader.ReadSingle();
|
||
|
}
|
||
|
if (flag5)
|
||
|
{
|
||
|
teamIndex = reader.ReadTeamIndex();
|
||
|
}
|
||
|
if (flag6)
|
||
|
{
|
||
|
loadout.Deserialize(reader);
|
||
|
}
|
||
|
if (num2)
|
||
|
{
|
||
|
miscFlags = reader.ReadPackedUInt32();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static CharacterMaster()
|
||
|
{
|
||
|
instancesList = new List<CharacterMaster>();
|
||
|
_readOnlyInstancesList = new ReadOnlyCollection<CharacterMaster>(instancesList);
|
||
|
kCmdCmdRespawn = 1097984413;
|
||
|
NetworkBehaviour.RegisterCommandDelegate(typeof(CharacterMaster), kCmdCmdRespawn, InvokeCmdCmdRespawn);
|
||
|
kRpcRpcOnPlayerBodyDamaged = -1070481806;
|
||
|
NetworkBehaviour.RegisterRpcDelegate(typeof(CharacterMaster), kRpcRpcOnPlayerBodyDamaged, InvokeRpcRpcOnPlayerBodyDamaged);
|
||
|
kRpcRpcSetSeekerRevive = 458435899;
|
||
|
NetworkBehaviour.RegisterRpcDelegate(typeof(CharacterMaster), kRpcRpcSetSeekerRevive, InvokeRpcRpcSetSeekerRevive);
|
||
|
NetworkCRC.RegisterBehaviour("CharacterMaster", 0);
|
||
|
}
|
||
|
|
||
|
private void UNetVersion()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
protected static void InvokeCmdCmdRespawn(NetworkBehaviour obj, NetworkReader reader)
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogError("Command CmdRespawn called on client.");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
((CharacterMaster)obj).CmdRespawn(reader.ReadString());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void CallCmdRespawn(string bodyName)
|
||
|
{
|
||
|
if (!NetworkClient.active)
|
||
|
{
|
||
|
Debug.LogError("Command function CmdRespawn called on server.");
|
||
|
return;
|
||
|
}
|
||
|
if (base.isServer)
|
||
|
{
|
||
|
CmdRespawn(bodyName);
|
||
|
return;
|
||
|
}
|
||
|
NetworkWriter networkWriter = new NetworkWriter();
|
||
|
networkWriter.Write((short)0);
|
||
|
networkWriter.Write((short)5);
|
||
|
networkWriter.WritePackedUInt32((uint)kCmdCmdRespawn);
|
||
|
networkWriter.Write(GetComponent<NetworkIdentity>().netId);
|
||
|
networkWriter.Write(bodyName);
|
||
|
SendCommandInternal(networkWriter, 0, "CmdRespawn");
|
||
|
}
|
||
|
|
||
|
protected static void InvokeRpcRpcOnPlayerBodyDamaged(NetworkBehaviour obj, NetworkReader reader)
|
||
|
{
|
||
|
if (!NetworkClient.active)
|
||
|
{
|
||
|
Debug.LogError("RPC RpcOnPlayerBodyDamaged called on server.");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
((CharacterMaster)obj).RpcOnPlayerBodyDamaged();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected static void InvokeRpcRpcSetSeekerRevive(NetworkBehaviour obj, NetworkReader reader)
|
||
|
{
|
||
|
if (!NetworkClient.active)
|
||
|
{
|
||
|
Debug.LogError("RPC RpcSetSeekerRevive called on server.");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
((CharacterMaster)obj).RpcSetSeekerRevive((SEEKER_REVIVE_STATUS)reader.ReadInt32());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void CallRpcOnPlayerBodyDamaged()
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogError("RPC Function RpcOnPlayerBodyDamaged called on client.");
|
||
|
return;
|
||
|
}
|
||
|
NetworkWriter networkWriter = new NetworkWriter();
|
||
|
networkWriter.Write((short)0);
|
||
|
networkWriter.Write((short)2);
|
||
|
networkWriter.WritePackedUInt32((uint)kRpcRpcOnPlayerBodyDamaged);
|
||
|
networkWriter.Write(GetComponent<NetworkIdentity>().netId);
|
||
|
SendRPCInternal(networkWriter, 0, "RpcOnPlayerBodyDamaged");
|
||
|
}
|
||
|
|
||
|
public void CallRpcSetSeekerRevive(SEEKER_REVIVE_STATUS s)
|
||
|
{
|
||
|
if (!NetworkServer.active)
|
||
|
{
|
||
|
Debug.LogError("RPC Function RpcSetSeekerRevive called on client.");
|
||
|
return;
|
||
|
}
|
||
|
NetworkWriter networkWriter = new NetworkWriter();
|
||
|
networkWriter.Write((short)0);
|
||
|
networkWriter.Write((short)2);
|
||
|
networkWriter.WritePackedUInt32((uint)kRpcRpcSetSeekerRevive);
|
||
|
networkWriter.Write(GetComponent<NetworkIdentity>().netId);
|
||
|
networkWriter.Write((int)s);
|
||
|
SendRPCInternal(networkWriter, 0, "RpcSetSeekerRevive");
|
||
|
}
|
||
|
|
||
|
public override void PreStartClient()
|
||
|
{
|
||
|
}
|
||
|
}
|