Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ULTRAKILL V1 Survivor v0.2.0
plugins\UltrakillV1.dll
Decompiled an hour ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using EntityStates; using EntityStates.GolemMonster; using HG.BlendableTypes; using KinematicCharacterController; using Microsoft.CodeAnalysis; using On.EntityStates.GolemMonster; using On.RoR2; using On.RoR2.Items; using On.RoR2.Orbs; using R2API; using R2API.Networking; using R2API.Networking.Interfaces; using R2API.Utils; using RoR2; using RoR2.ConVar; using RoR2.Items; using RoR2.Orbs; using RoR2.Projectile; using RoR2.Skills; using TMPro; using UltrakillV1.Components; using UltrakillV1.Hooks; using UltrakillV1.Modules; using UltrakillV1.Modules.AssetSetup; using UltrakillV1.Setup.Pipeline; using UltrakillV1.SkillStates; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.Networking; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("RoR2")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Saidrog")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0")] [assembly: AssemblyProduct("ULTRAKILL V1 Survivor")] [assembly: AssemblyTitle("UltrakillV1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace UltrakillV1 { [BepInPlugin("com.ultrakill.v1survivor", "UltrakillV1Survivor", "0.2.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class UltrakillV1Plugin : BaseUnityPlugin { public const string PluginGUID = "com.ultrakill.v1survivor"; public const string PluginName = "UltrakillV1Survivor"; public const string PluginVersion = "0.2.0"; private bool _setupBroken; public static UltrakillV1Plugin Instance { get; private set; } private void Awake() { Instance = this; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Initializing ULTRAKILL V1 Survivor for Risk of Rain 2..."); ModConfig.Init(((BaseUnityPlugin)this).Config); Tokens.RegisterTokens(); ModDamageTypes.Init(); V1Explosions.Init(); V1SlamLaunch.Init(); V1Network.Init(); try { V1AssetSetup.Init(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); } catch (Exception ex) { _setupBroken = true; ((BaseUnityPlugin)this).Logger.LogError((object)("Asset setup unavailable: " + ex)); } ModAssets.Init(); ModSounds.Init(); ModItems.Init(); Prefabs.Init(); Skills.Init(); BloodHealHooks.Init(); V1NearCameraFade.Init(); V1CameraShake.Init(); CoinChargebackHooks.Init(); BossDropHooks.Init(); ItemCompatHooks.Init(); V1Ragdoll.Init(); StatRecalculationHooks.Init(); V1MovementController.InitHooks(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"ULTRAKILL V1 Survivor successfully initialized! MANKIND IS DEAD. BLOOD IS FUEL. HELL IS FULL."); } private void Update() { ModSounds.Update(); if (_setupBroken) { return; } try { V1AssetSetup.Update(); } catch (Exception ex) { _setupBroken = true; ((BaseUnityPlugin)this).Logger.LogError((object)ex); } } private void LateUpdate() { if (_setupBroken) { return; } try { FolderPicker.UpdateCursor(); } catch (Exception ex) { _setupBroken = true; ((BaseUnityPlugin)this).Logger.LogError((object)ex); } } private void OnGUI() { if (_setupBroken) { return; } try { V1AssetSetup.OnGUI(); } catch (Exception ex) { _setupBroken = true; ((BaseUnityPlugin)this).Logger.LogError((object)ex); } } private void OnDestroy() { V1AudioMixer.Shutdown(); } } } namespace UltrakillV1.SkillStates { public class FeedbackerPunch : BaseSkillState { public static float BaseDuration = 0.4f; public const int MaxStamina = 2; public const float StaminaPerSecond = 1.25f; public const float FistCooldown = 0.25f; public static float DamageCoefficient = 3.5f; public static float PunchRange = 4.5f; private float _duration; private bool _hasAttacked; private ParryZone _parryZone; public override void OnEnter() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_00f6: Unknown result type (might be due to invalid IL or missing references) ((BaseState)this).OnEnter(); _duration = BaseDuration / ((BaseState)this).attackSpeedStat; V1WeaponManager component = ((EntityState)this).GetComponent<V1WeaponManager>(); if (Object.op_Implicit((Object)(object)component)) { component.LastPunchTime = Time.time; } _parryZone = ((EntityState)this).gameObject.GetComponentInChildren<ParryZone>(); if ((Object)(object)_parryZone == (Object)null) { GameObject val = new GameObject("ParryZone"); val.transform.SetParent(((EntityState)this).transform, false); _parryZone = val.AddComponent<ParryZone>(); _parryZone.Owner = ((EntityState)this).gameObject; } _parryZone.IsActive = true; _parryZone.ResetParried(); ModSounds.Play(ModSounds.PunchSwing, ((EntityState)this).gameObject); V1ViewmodelController.TryTrigger(((EntityState)this).gameObject, "Punch"); ((EntityState)this).PlayAnimation("Gesture, Override", "Punch", "Punch.playbackRate", _duration, 0f); if (((EntityState)this).isAuthority) { TryBoostShotgunPellet(((BaseState)this).GetAimRay()); } } private void TryBoostShotgunPellet(Ray aimRay) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) Vector3 center = ((Ray)(ref aimRay)).origin + ((Ray)(ref aimRay)).direction * _parryZone.ParryReach; V1ShotgunPellet v1ShotgunPellet = V1ShotgunPellet.FindPunchable(((EntityState)this).gameObject, center, _parryZone.ParryRadius, ((Ray)(ref aimRay)).origin); if (Object.op_Implicit((Object)(object)v1ShotgunPellet)) { Vector3 point = ((Ray)(ref aimRay)).GetPoint(1000f); Ray val = aimRay; LayerIndex val2 = LayerIndex.world; int num = LayerMask.op_Implicit(((LayerIndex)(ref val2)).mask); val2 = LayerIndex.entityPrecise; RaycastHit val3 = default(RaycastHit); if (Physics.Raycast(val, ref val3, 1000f, num | LayerMask.op_Implicit(((LayerIndex)(ref val2)).mask), (QueryTriggerInteraction)1)) { point = ((RaycastHit)(ref val3)).point; } Vector3 direction = point - v1ShotgunPellet.Position; if (((Vector3)(ref direction)).sqrMagnitude < 0.01f) { direction = ((Ray)(ref aimRay)).direction; } v1ShotgunPellet.Boost(direction, ((BaseState)this).damageStat * 6f, ((BaseState)this).RollCrit()); V1Hitstop component = ((EntityState)this).GetComponent<V1Hitstop>(); if (Object.op_Implicit((Object)(object)component)) { component.ParryFlash(); } ModSounds.Play(ModSounds.PunchHit, ((EntityState)this).gameObject); V1ViewmodelController.TryPlay(((EntityState)this).gameObject, "Hook", 0.065f); } } public override void FixedUpdate() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) ((EntityState)this).FixedUpdate(); Ray aimRay = ((BaseState)this).GetAimRay(); if ((((EntityState)this).isAuthority || NetworkServer.active) && (Object)(object)_parryZone != (Object)null && ((EntityState)this).fixedAge <= _duration * 0.6f) { _parryZone.CheckAndPerformParry(aimRay, NetworkServer.active, ((EntityState)this).isAuthority); } if (!_hasAttacked && ((EntityState)this).fixedAge >= _duration * 0.25f) { _hasAttacked = true; FireMeleeHit(aimRay); } if (((EntityState)this).fixedAge >= _duration && ((EntityState)this).isAuthority) { ((EntityState)this).outer.SetNextStateToMain(); } } private void FireMeleeHit(Ray aimRay) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Expected O, but got Unknown if (!NetworkServer.active) { if (((EntityState)this).isAuthority && Object.op_Implicit((Object)(object)FindPunchTarget(aimRay))) { ModSounds.Play(ModSounds.PunchHit, ((EntityState)this).gameObject); } } else { if (!NetworkServer.active) { return; } BullseyeSearch val = new BullseyeSearch { searchOrigin = ((Ray)(ref aimRay)).origin, searchDirection = ((Ray)(ref aimRay)).direction, minDistanceFilter = 0f, maxDistanceFilter = PunchRange, teamMaskFilter = TeamMask.GetUnprotectedTeams(((EntityState)this).teamComponent.teamIndex), sortMode = (SortMode)1 }; val.RefreshCandidates(); val.FilterOutGameObject(((EntityState)this).gameObject); foreach (HurtBox result in val.GetResults()) { if (Object.op_Implicit((Object)(object)result) && Object.op_Implicit((Object)(object)result.healthComponent)) { DamageInfo val2 = new DamageInfo { attacker = ((EntityState)this).gameObject, inflictor = ((EntityState)this).gameObject, damage = ((BaseState)this).damageStat * DamageCoefficient, crit = ((BaseState)this).RollCrit(), damageType = DamageTypeCombo.op_Implicit((DamageSource)8), position = ((Component)result).transform.position, force = ((Ray)(ref aimRay)).direction * 3000f, procCoefficient = 1f }; result.healthComponent.TakeDamage(val2); GlobalEventManager.instance.OnHitEnemy(val2, ((Component)result.healthComponent).gameObject); ModSounds.Play(ModSounds.PunchHit, ((EntityState)this).gameObject); break; } } } } private HurtBox FindPunchTarget(Ray aimRay) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) BullseyeSearch val = new BullseyeSearch { searchOrigin = ((Ray)(ref aimRay)).origin, searchDirection = ((Ray)(ref aimRay)).direction, minDistanceFilter = 0f, maxDistanceFilter = PunchRange, teamMaskFilter = TeamMask.GetUnprotectedTeams(((EntityState)this).teamComponent.teamIndex), sortMode = (SortMode)1 }; val.RefreshCandidates(); val.FilterOutGameObject(((EntityState)this).gameObject); foreach (HurtBox result in val.GetResults()) { if (Object.op_Implicit((Object)(object)result) && Object.op_Implicit((Object)(object)result.healthComponent)) { return result; } } return null; } public override void OnExit() { if (Object.op_Implicit((Object)(object)_parryZone)) { _parryZone.IsActive = false; } ((EntityState)this).OnExit(); } public override InterruptPriority GetMinimumInterruptPriority() { return (InterruptPriority)1; } } public class NailgunFire : BaseSkillState { public const float UkSpread = 10f; public const float SpawnDistance = 0.52f; private V1Nailgun _nailgun; private float _timer; private bool _wasEmpty; public override void OnEnter() { ((BaseState)this).OnEnter(); _nailgun = ((EntityState)this).GetComponent<V1Nailgun>(); if (Object.op_Implicit((Object)(object)_nailgun)) { _nailgun.Firing = true; } _timer = 0f; } public override void FixedUpdate() { ((EntityState)this).FixedUpdate(); ((BaseState)this).StartAimMode(1f, false); if (!((EntityState)this).isAuthority) { return; } if (!((BaseSkillState)this).IsKeyDownAuthority()) { ((EntityState)this).outer.SetNextStateToMain(); return; } float num = 0.04f / Mathf.Max(0.1f, ((BaseState)this).attackSpeedStat); _timer -= Time.fixedDeltaTime; while (_timer <= 0f) { if (Object.op_Implicit((Object)(object)_nailgun) && _nailgun.Ammo < 1f) { ModSounds.Play(ModSounds.WeaponSwap, ((EntityState)this).gameObject, _wasEmpty ? 0.7f : 1f); _wasEmpty = true; _timer += num * 2f; } else { _wasEmpty = false; FireNail(); _timer += num; } } } private void FireNail() { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)_nailgun)) { _nailgun.Ammo -= 1f; _nailgun.RecordShot(); } ModSounds.Play(ModSounds.NailgunFire, ((EntityState)this).gameObject, Random.Range(0.95f, 1.05f)); V1ViewmodelController.TryTrigger(((EntityState)this).gameObject, "Fire"); V1ViewmodelController.TryUltrakillMuzzleFlash(((EntityState)this).gameObject, ModAssets.UkNailgunMuzzleTemplate, 0.35f, 0.6f, withLight: false, 0.05f); Ray aimRay = ((BaseState)this).GetAimRay(); float num = 3.3333333f; Quaternion val = Quaternion.Euler(Random.Range(0f - num, num), Random.Range(0f - num, num), 0f); Vector3 val2 = Util.QuaternionSafeLookRotation(((Ray)(ref aimRay)).direction) * val * Vector3.forward; Vector3 position = ((Ray)(ref aimRay)).origin + ((Ray)(ref aimRay)).direction * 0.52f; Ray val3 = aimRay; LayerIndex world = LayerIndex.world; if (Physics.Raycast(val3, 0.52f, LayerMask.op_Implicit(((LayerIndex)(ref world)).mask), (QueryTriggerInteraction)1)) { position = ((Ray)(ref aimRay)).origin; } V1Nail.Spawn(((EntityState)this).gameObject, position, val2 * 104f, ((BaseState)this).RollCrit()); } public override void OnExit() { if (Object.op_Implicit((Object)(object)_nailgun)) { _nailgun.Firing = false; } ((EntityState)this).OnExit(); } public override InterruptPriority GetMinimumInterruptPriority() { return (InterruptPriority)1; } } public class NailgunHeatsink : BaseSkillState { public override void OnEnter() { ((BaseState)this).OnEnter(); if (((EntityState)this).isAuthority) { V1Nailgun component = ((EntityState)this).GetComponent<V1Nailgun>(); if (Object.op_Implicit((Object)(object)component) && component.TryBurnOut()) { ModSounds.Play(ModSounds.CoreChargeLoop, ((EntityState)this).gameObject, 1.6f, 0.8f); ModSounds.Play(ModSounds.ShotgunPump1, ((EntityState)this).gameObject, 0.8f); ((EntityState)this).outer.SetNextState((EntityState)(object)new NailgunOverheatFire()); } else { ((EntityState)this).outer.SetNextStateToMain(); } } } public override InterruptPriority GetMinimumInterruptPriority() { return (InterruptPriority)1; } } public class NailgunMagnet : BaseSkillState { public static float BaseDuration = 0.1f; public override void OnEnter() { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) ((BaseState)this).OnEnter(); if (!((EntityState)this).isAuthority) { return; } V1Nailgun component = ((EntityState)this).GetComponent<V1Nailgun>(); if (Object.op_Implicit((Object)(object)component) && component.MagnetCharge < 1f) { ModSounds.Play(ModSounds.WeaponSwap, ((EntityState)this).gameObject, 0.7f); return; } if (Object.op_Implicit((Object)(object)component)) { component.MagnetCharge -= 1f; } ModSounds.Play(ModSounds.MagnetThrow, ((EntityState)this).gameObject); V1ViewmodelController.TryTrigger(((EntityState)this).gameObject, "Fire"); Ray aimRay = ((BaseState)this).GetAimRay(); ((BaseState)this).StartAimMode(aimRay, 2f, false); Vector3 position = ((Ray)(ref aimRay)).origin + ((Ray)(ref aimRay)).direction * 0.52f; Ray val = aimRay; LayerIndex world = LayerIndex.world; if (Physics.Raycast(val, 0.52f, LayerMask.op_Implicit(((LayerIndex)(ref world)).mask), (QueryTriggerInteraction)1)) { position = ((Ray)(ref aimRay)).origin; } V1Magnet.Throw(((EntityState)this).gameObject, position, ((Ray)(ref aimRay)).direction); } public override void FixedUpdate() { ((EntityState)this).FixedUpdate(); if (((EntityState)this).fixedAge >= BaseDuration && ((EntityState)this).isAuthority) { ((EntityState)this).outer.SetNextStateToMain(); } } public override InterruptPriority GetMinimumInterruptPriority() { return (InterruptPriority)1; } } public class NailgunOverheatFire : BaseSkillState { public const float UkSpread = 20f; public const float DamageCoefficient = 0.23689024f; private V1Nailgun _nailgun; private float _timer; public override void OnEnter() { ((BaseState)this).OnEnter(); _nailgun = ((EntityState)this).GetComponent<V1Nailgun>(); _timer = 0f; } public override void FixedUpdate() { ((EntityState)this).FixedUpdate(); ((BaseState)this).StartAimMode(1f, false); if (!((EntityState)this).isAuthority || !Object.op_Implicit((Object)(object)_nailgun)) { return; } bool flag = Object.op_Implicit((Object)(object)((EntityState)this).inputBank) && ((EntityState)this).inputBank.skill1.down; _nailgun.OverheatFiring = flag && !_nailgun.BurnOut; if (!flag && !_nailgun.BurnOut) { ((EntityState)this).outer.SetNextStateToMain(); return; } _timer -= Time.fixedDeltaTime; while (_timer <= 0f) { FireNail(); _timer += _nailgun.OverheatInterval / Mathf.Max(0.1f, ((BaseState)this).attackSpeedStat); } } private void FireNail() { //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) _nailgun.RecordShot(); bool burnOut = _nailgun.BurnOut; bool flag = _nailgun.HeatSinks < 1f && !burnOut; float pitch = (burnOut ? 2f : (flag ? 0.75f : Random.Range(0.95f, 1.05f))); ModSounds.Play(ModSounds.NailgunFire, ((EntityState)this).gameObject, pitch, flag ? 0.4f : 1f); V1ViewmodelController.TryTrigger(((EntityState)this).gameObject, "Fire"); GameObject template = ((burnOut && Object.op_Implicit((Object)(object)ModAssets.UkNailgunMuzzle2Template)) ? ModAssets.UkNailgunMuzzle2Template : ModAssets.UkNailgunMuzzleTemplate); V1ViewmodelController.TryUltrakillMuzzleFlash(((EntityState)this).gameObject, template, 0.35f, 0.6f, withLight: false, 0.05f); Ray aimRay = ((BaseState)this).GetAimRay(); float num = (burnOut ? 40f : 20f) / 3f; Quaternion val = Quaternion.Euler(Random.Range(0f - num, num), Random.Range(0f - num, num), 0f); Vector3 val2 = Util.QuaternionSafeLookRotation(((Ray)(ref aimRay)).direction) * val * Vector3.forward; Vector3 position = ((Ray)(ref aimRay)).origin + ((Ray)(ref aimRay)).direction * 0.52f; Ray val3 = aimRay; LayerIndex world = LayerIndex.world; if (Physics.Raycast(val3, 0.52f, LayerMask.op_Implicit(((LayerIndex)(ref world)).mask), (QueryTriggerInteraction)1)) { position = ((Ray)(ref aimRay)).origin; } V1Nail.Spawn(((EntityState)this).gameObject, position, val2 * 104f, ((BaseState)this).RollCrit(), 0.23689024f, burnOut); } public override void OnExit() { if (Object.op_Implicit((Object)(object)_nailgun)) { _nailgun.OverheatFiring = false; } ((EntityState)this).OnExit(); } public override InterruptPriority GetMinimumInterruptPriority() { if (Object.op_Implicit((Object)(object)_nailgun) && _nailgun.BurnOut) { return (InterruptPriority)7; } return (InterruptPriority)1; } } public class RailcannonFire : BaseSkillState { public const float DamageCoefficient = 20f; public const float Duration = 0.5f; public override void OnEnter() { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) ((BaseState)this).OnEnter(); ModSounds.Play(ModSounds.RailcannonFire, ((EntityState)this).gameObject); V1ViewmodelController.TryTrigger(((EntityState)this).gameObject, "Fire"); if (((EntityState)this).isAuthority) { V1Railcannon component = ((EntityState)this).GetComponent<V1Railcannon>(); if (!Object.op_Implicit((Object)(object)component) || !component.TryFire()) { ((EntityState)this).outer.SetNextStateToMain(); return; } Ray aimRay = ((BaseState)this).GetAimRay(); ((BaseState)this).StartAimMode(aimRay, 2f, false); ((BaseState)this).AddRecoil(-4f, -6f, -1f, 1f); V1Electricity.FireBeam(((EntityState)this).gameObject, ((Ray)(ref aimRay)).origin, ((Ray)(ref aimRay)).direction, 520f, ((BaseState)this).damageStat * 20f, ((BaseState)this).RollCrit(), fromGun: true); } } public override void FixedUpdate() { ((EntityState)this).FixedUpdate(); if (((EntityState)this).isAuthority && ((EntityState)this).fixedAge >= 0.5f) { ((EntityState)this).outer.SetNextStateToMain(); } } public override InterruptPriority GetMinimumInterruptPriority() { return (InterruptPriority)1; } } public class RevolverCoinToss : BaseSkillState { public static float BaseDuration = 0.25f; private float _duration; public override void OnEnter() { ((BaseState)this).OnEnter(); _duration = BaseDuration / ((BaseState)this).attackSpeedStat; if (((EntityState)this).isAuthority) { V1CoinCharges component = ((EntityState)this).GetComponent<V1CoinCharges>(); if (Object.op_Implicit((Object)(object)component) && component.TryConsume()) { TossCoin(); ModSounds.Play(ModSounds.CoinToss, ((EntityState)this).gameObject); V1ViewmodelController.TryTrigger(((EntityState)this).gameObject, "CoinToss"); } else { ModSounds.Play(ModSounds.WeaponSwap, ((EntityState)this).gameObject); } } } private void TossCoin() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)ModAssets.CoinPrefab)) { Ray aimRay = ((BaseState)this).GetAimRay(); Vector3 val = Quaternion.LookRotation(((Ray)(ref aimRay)).direction) * Vector3.up; Vector3 val2 = ((Ray)(ref aimRay)).origin - val * 0.26f; CoinController component = Object.Instantiate<GameObject>(ModAssets.CoinPrefab, val2, Quaternion.identity).GetComponent<CoinController>(); if ((Object)(object)component != (Object)null) { Vector3 playerVelocity = (Object.op_Implicit((Object)(object)((EntityState)this).characterMotor) ? ((EntityState)this).characterMotor.velocity : Vector3.zero); component.Toss(((EntityState)this).gameObject, val2, ((Ray)(ref aimRay)).direction, playerVelocity); } } } public override void FixedUpdate() { ((EntityState)this).FixedUpdate(); if (((EntityState)this).fixedAge >= _duration && ((EntityState)this).isAuthority) { ((EntityState)this).outer.SetNextStateToMain(); } } public override InterruptPriority GetMinimumInterruptPriority() { return (InterruptPriority)1; } } public class RevolverPiercerCharge : BaseSkillState { public static float BaseChargeDuration = 0.5714286f; private const float MinLoopPitch = 0.05f; private float _chargeDuration; private bool _fullyCharged; private V1WeaponManager _weaponManager; private V1ViewmodelController _viewmodel; public override void OnEnter() { ((BaseState)this).OnEnter(); _chargeDuration = BaseChargeDuration / ((BaseState)this).attackSpeedStat; _weaponManager = ((EntityState)this).GetComponent<V1WeaponManager>(); _viewmodel = ((EntityState)this).GetComponent<V1ViewmodelController>(); ModSounds.StartLoop(ModSounds.PiercerChargeLoop, ((EntityState)this).gameObject); ModSounds.SetLoop(ModSounds.PiercerChargeLoop, ((EntityState)this).gameObject, 0.25f, 0.05f); } public override void FixedUpdate() { ((EntityState)this).FixedUpdate(); ((BaseState)this).StartAimMode(2f, false); float num = Mathf.Clamp01(((EntityState)this).fixedAge / _chargeDuration); if (((EntityState)this).isAuthority && Object.op_Implicit((Object)(object)_weaponManager)) { _weaponManager.PiercerCharge = num; } if (Object.op_Implicit((Object)(object)_viewmodel)) { _viewmodel.SetCharge(num); } ModSounds.SetLoop(ModSounds.PiercerChargeLoop, ((EntityState)this).gameObject, 0.25f + num * 0.5f, Mathf.Max(0.05f, num * 0.5f)); if (!_fullyCharged && ((EntityState)this).fixedAge >= _chargeDuration) { _fullyCharged = true; ModSounds.Play(ModSounds.PiercerCharged, ((EntityState)this).gameObject); } if (((EntityState)this).isAuthority && !((BaseSkillState)this).IsKeyDownAuthority()) { if (_fullyCharged) { ((EntityState)this).outer.SetNextState((EntityState)(object)new RevolverPiercerFire { IsCharged = true }); } else { ((EntityState)this).outer.SetNextStateToMain(); } } } public override void OnExit() { ModSounds.StopLoop(ModSounds.PiercerChargeLoop, ((EntityState)this).gameObject); if (Object.op_Implicit((Object)(object)_weaponManager)) { _weaponManager.PiercerCharge = 0f; } if (Object.op_Implicit((Object)(object)_viewmodel)) { _viewmodel.SetCharge(-1f); } ((EntityState)this).OnExit(); } public override InterruptPriority GetMinimumInterruptPriority() { return (InterruptPriority)1; } } public class RevolverPiercerFire : BaseSkillState { public static float BaseDuration = 0.5f; public static float DamageCoefficient = 2.5f; public static float ChargedDamageCoefficient = 7.5f; public bool IsCharged; private float _duration; private DamageSource Source { get { if (IsCharged) { return (DamageSource)2; } return (DamageSource)1; } } public override void OnEnter() { //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) ((BaseState)this).OnEnter(); _duration = BaseDuration / ((BaseState)this).attackSpeedStat; V1WeaponManager component = ((EntityState)this).GetComponent<V1WeaponManager>(); if (((EntityState)this).isAuthority && Object.op_Implicit((Object)(object)component)) { component.LockGun(_duration); if (IsCharged) { component.PiercerBeamCharge = 0f; } } ModSounds.Play(IsCharged ? ModSounds.PiercerChargedFire : ModSounds.PiercerFire, ((EntityState)this).gameObject); if (IsCharged) { V1ViewmodelController component2 = ((EntityState)this).GetComponent<V1ViewmodelController>(); if (Object.op_Implicit((Object)(object)component2)) { component2.ClearCharge(); } } V1ViewmodelController.TryTrigger(((EntityState)this).gameObject, IsCharged ? "FireCharged" : "Fire"); if (!ModAssets.HasUltrakillBeams) { V1ViewmodelController.TryMuzzleFlash(((EntityState)this).gameObject, IsCharged ? ModAssets.PiercerChargedColor : ModAssets.PiercerColor, IsCharged ? 0.35f : 0.18f); } Ray aimRay = ((BaseState)this).GetAimRay(); FireRevolver(aimRay); } private void FireRevolver(Ray aimRay) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Expected O, but got Unknown //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Unknown result type (might be due to invalid IL or missing references) //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Unknown result type (might be due to invalid IL or missing references) //IL_0317: Unknown result type (might be due to invalid IL or missing references) //IL_0322: Unknown result type (might be due to invalid IL or missing references) //IL_032d: Unknown result type (might be due to invalid IL or missing references) //IL_0334: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Expected O, but got Unknown //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Expected O, but got Unknown //IL_034f: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_0358: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) //IL_035d: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) ((BaseState)this).StartAimMode(aimRay, 2f, false); if (!((EntityState)this).isAuthority) { return; } float maxDistance = FirstStopDistance(aimRay, IsCharged); V1CoreEject v1CoreEject = V1CoreEject.FindOnRay(aimRay, maxDistance); float distance; CoinController coinController = CoinController.FindOnRay(aimRay, maxDistance, out distance); float num = (Object.op_Implicit((Object)(object)v1CoreEject) ? Vector3.Distance(((Ray)(ref aimRay)).origin, ((Component)v1CoreEject).transform.position) : float.MaxValue); GameObject val = (IsCharged ? ModAssets.PiercerChargedTracer : ModAssets.PiercerTracer); float num2 = ((BaseState)this).damageStat * (IsCharged ? ChargedDamageCoefficient : DamageCoefficient); bool flag = ((BaseState)this).RollCrit(); List<(V1Magnet, float)> list = V1Magnet.FindOnRay(aimRay, maxDistance); float num3 = ((list.Count > 0) ? list[0].Item2 : float.MaxValue); if (IsCharged) { foreach (var item2 in list) { item2.Item1.Shoot(); } } else if (num3 < Mathf.Min(distance, num)) { V1Magnet item = list[0].Item1; if (Object.op_Implicit((Object)(object)val)) { EffectManager.SpawnEffect(val, new EffectData { origin = item.Center, start = ((Ray)(ref aimRay)).origin }, true); } item.Shoot(); return; } if (Object.op_Implicit((Object)(object)v1CoreEject) && (!Object.op_Implicit((Object)(object)coinController) || num < distance)) { if (IsCharged) { FirePiercingSegment(aimRay, num, num2, flag); } else if (Object.op_Implicit((Object)(object)val)) { EffectManager.SpawnEffect(val, new EffectData { origin = ((Component)v1CoreEject).transform.position, start = ((Ray)(ref aimRay)).origin }, true); } V1Explosions.RequestCoreDetonation(v1CoreEject, ((EntityState)this).gameObject); V1Hitstop component = ((EntityState)this).GetComponent<V1Hitstop>(); if (Object.op_Implicit((Object)(object)component)) { component.ParryFlash(); } return; } if (Object.op_Implicit((Object)(object)coinController)) { if (IsCharged) { FirePiercingSegment(aimRay, distance, num2, flag); } else if (Object.op_Implicit((Object)(object)val)) { EffectManager.SpawnEffect(val, new EffectData { origin = ((Component)coinController).transform.position, start = ((Ray)(ref aimRay)).origin }, true); } coinController.HitByShot(new CoinController.CoinShot { Attacker = ((EntityState)this).gameObject, Kind = (IsCharged ? CoinController.ShotKind.Piercing : CoinController.ShotKind.Revolver), Source = Source, BeamDamage = num2 }); return; } BulletAttack val2 = new BulletAttack { owner = ((EntityState)this).gameObject, weapon = ((EntityState)this).gameObject, origin = ((Ray)(ref aimRay)).origin, aimVector = ((Ray)(ref aimRay)).direction, minSpread = 0f, maxSpread = 0f, damage = num2, damageType = DamageTypeCombo.op_Implicit(Source), force = 800f, tracerEffectPrefab = (IsCharged ? ModAssets.PiercerChargedTracer : ModAssets.PiercerTracer), hitEffectPrefab = LegacyResourcesAPI.Load<GameObject>("Prefabs/Effects/ImpactEffects/HitsparkBandit2Pistol"), isCrit = flag, procCoefficient = 1f, maxDistance = 500f, smartCollision = true, muzzleName = "MuzzleRight" }; LayerMask stopperMask; if (!IsCharged) { stopperMask = BulletAttack.defaultStopperMask; } else { LayerIndex world = LayerIndex.world; stopperMask = ((LayerIndex)(ref world)).mask; } val2.stopperMask = stopperMask; val2.Fire(); } private float FirstStopDistance(Ray aimRay, bool piercing) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) LayerIndex val = LayerIndex.world; int num = LayerMask.op_Implicit(((LayerIndex)(ref val)).mask); int num2; if (!piercing) { val = LayerIndex.entityPrecise; num2 = LayerMask.op_Implicit(((LayerIndex)(ref val)).mask); } else { num2 = 0; } int num3 = num | num2; RaycastHit[] array = Physics.RaycastAll(aimRay, 500f, num3, (QueryTriggerInteraction)2); Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance)); TeamIndex val2 = (TeamIndex)((!Object.op_Implicit((Object)(object)((EntityState)this).teamComponent)) ? 1 : ((int)((EntityState)this).teamComponent.teamIndex)); RaycastHit[] array2 = array; for (int num4 = 0; num4 < array2.Length; num4++) { RaycastHit val3 = array2[num4]; val = LayerIndex.world; if ((LayerMask.op_Implicit(((LayerIndex)(ref val)).mask) & (1 << ((Component)((RaycastHit)(ref val3)).collider).gameObject.layer)) != 0) { if (!((RaycastHit)(ref val3)).collider.isTrigger) { return ((RaycastHit)(ref val3)).distance; } continue; } HurtBox component = ((Component)((RaycastHit)(ref val3)).collider).GetComponent<HurtBox>(); if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.healthComponent) && (Object)(object)((Component)component.healthComponent).gameObject != (Object)(object)((EntityState)this).gameObject && component.teamIndex != val2) { return ((RaycastHit)(ref val3)).distance; } } return 500f; } private void FirePiercingSegment(Ray aimRay, float distance, float damage, bool crit) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) BulletAttack val = new BulletAttack { owner = ((EntityState)this).gameObject, weapon = ((EntityState)this).gameObject, origin = ((Ray)(ref aimRay)).origin, aimVector = ((Ray)(ref aimRay)).direction, minSpread = 0f, maxSpread = 0f, damage = damage, damageType = DamageTypeCombo.op_Implicit(Source), force = 800f, tracerEffectPrefab = ModAssets.PiercerChargedTracer, hitEffectPrefab = LegacyResourcesAPI.Load<GameObject>("Prefabs/Effects/ImpactEffects/HitsparkBandit2Pistol"), isCrit = crit, procCoefficient = 1f, maxDistance = distance, smartCollision = true, muzzleName = "MuzzleRight" }; LayerIndex world = LayerIndex.world; val.stopperMask = ((LayerIndex)(ref world)).mask; val.Fire(); } public override void FixedUpdate() { ((EntityState)this).FixedUpdate(); if (((EntityState)this).fixedAge >= _duration && ((EntityState)this).isAuthority) { ((EntityState)this).outer.SetNextStateToMain(); } } public override InterruptPriority GetMinimumInterruptPriority() { return (InterruptPriority)1; } } public class SawbladeFire : BaseSkillState { public const float AttractorDamage = 1.875f; public const float OverheatDamage = 1.5f; public const float AttractorHits = 3.9f; public const float MaxSpread = 45f; private V1Nailgun _nailgun; private bool _overheat; private float _timer; private bool _wasEmpty; public override void OnEnter() { ((BaseState)this).OnEnter(); _nailgun = ((EntityState)this).GetComponent<V1Nailgun>(); V1WeaponManager component = ((EntityState)this).GetComponent<V1WeaponManager>(); _overheat = Object.op_Implicit((Object)(object)component) && component.CurrentWeapon == V1WeaponManager.WeaponType.SawbladeOverheat; if (Object.op_Implicit((Object)(object)_nailgun)) { if (_overheat) { _nailgun.SawFiring = true; } else { _nailgun.Firing = true; } } _timer = 0f; } public override void FixedUpdate() { ((EntityState)this).FixedUpdate(); ((BaseState)this).StartAimMode(1f, false); if (!((EntityState)this).isAuthority || !Object.op_Implicit((Object)(object)_nailgun)) { return; } if (!((BaseSkillState)this).IsKeyDownAuthority()) { ((EntityState)this).outer.SetNextStateToMain(); return; } _timer -= Time.fixedDeltaTime; while (_timer <= 0f) { float num = _nailgun.SawInterval(_overheat) / Mathf.Max(0.1f, ((BaseState)this).attackSpeedStat); if (!_overheat && _nailgun.Saws < 1f) { ModSounds.Play(ModSounds.WeaponSwap, ((EntityState)this).gameObject, _wasEmpty ? 0.7f : 1f); _wasEmpty = true; _timer += num * 2f; } else { _wasEmpty = false; FireSaw(); _timer += num; } } } private void FireSaw() { //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) _nailgun.RecordShot(); _nailgun.LastSawShot = Time.time; ModSounds.Play(ModSounds.SawFire, ((EntityState)this).gameObject, Random.Range(0.95f, 1.05f)); V1ViewmodelController.TryTrigger(((EntityState)this).gameObject, "Fire"); V1ViewmodelController.TryUltrakillMuzzleFlash(((EntityState)this).gameObject, ModAssets.UkSawMuzzleTemplate, 0.6f, 0.8f, withLight: false); float num = 0f; float hitAmount = 3.9f; GameObject val = ModAssets.UkSawTemplate; float damageCoefficient = 1.875f; if (_overheat) { bool num2 = _nailgun.SawHeatSinks >= 1f; num = (num2 ? Mathf.Lerp(0f, 45f, Mathf.Max(0f, _nailgun.SawHeat - 0.25f)) : 45f); hitAmount = (num2 ? Mathf.Lerp(3f, 1f, _nailgun.SawHeat) : 1f); val = (Object.op_Implicit((Object)(object)ModAssets.UkSawOverheatTemplate) ? ModAssets.UkSawOverheatTemplate : val); damageCoefficient = 1.5f; if (num2) { _nailgun.SawHeat = Mathf.MoveTowards(_nailgun.SawHeat, 1f, 0.125f); } } else { _nailgun.Saws -= 1f; } Ray aimRay = ((BaseState)this).GetAimRay(); float num3 = num / 3f; Quaternion val2 = Quaternion.Euler(Random.Range(0f - num3, num3), Random.Range(0f - num3, num3), 0f); Vector3 val3 = Util.QuaternionSafeLookRotation(((Ray)(ref aimRay)).direction) * val2 * Vector3.forward; Vector3 position = ((Ray)(ref aimRay)).origin + ((Ray)(ref aimRay)).direction * 0.52f; Ray val4 = aimRay; LayerIndex world = LayerIndex.world; if (Physics.Raycast(val4, 0.52f, LayerMask.op_Implicit(((LayerIndex)(ref world)).mask), (QueryTriggerInteraction)1)) { position = ((Ray)(ref aimRay)).origin; } V1Sawblade v1Sawblade = V1Sawblade.Spawn(((EntityState)this).gameObject, val, position, val3 * 104f, ((BaseState)this).RollCrit(), damageCoefficient); v1Sawblade.HitAmount = hitAmount; v1Sawblade.FodderDamageBoost = !_overheat; } public override void OnExit() { if (Object.op_Implicit((Object)(object)_nailgun)) { if (_overheat) { _nailgun.SawFiring = false; } else { _nailgun.Firing = false; } } ((EntityState)this).OnExit(); } public override InterruptPriority GetMinimumInterruptPriority() { return (InterruptPriority)1; } } public class ShotgunBomb : BaseSkillState { public const float DamageCoefficient = 6f; public const float BaseDuration = 2.0566666f; public float Force; private float _duration; public override void OnEnter() { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) ((BaseState)this).OnEnter(); _duration = Mathf.Min(2.0566666f, 1f) / ((BaseState)this).attackSpeedStat; ModSounds.Play(ModSounds.CoreEject, ((EntityState)this).gameObject); V1ViewmodelController.TryTrigger(((EntityState)this).gameObject, "Secondary Fire"); V1ViewmodelController.TryUltrakillMuzzleFlash(((EntityState)this).gameObject, ModAssets.UkShotgunMuzzleTemplate); Ray aimRay = ((BaseState)this).GetAimRay(); ((BaseState)this).StartAimMode(aimRay, 2f, false); if (((EntityState)this).isAuthority) { V1WeaponManager component = ((EntityState)this).GetComponent<V1WeaponManager>(); if (Object.op_Implicit((Object)(object)component)) { component.LastCoreTime = Time.time; component.CoreCharges = Mathf.Max(0f, component.CoreCharges - 1f); component.GunReadyTime = Time.time + _duration; } Vector3 val = ((Ray)(ref aimRay)).direction + Vector3.up * (Force * 0.002f); Vector3 normalized = ((Vector3)(ref val)).normalized; ProjectileManager instance = ProjectileManager.instance; FireProjectileInfo val2 = new FireProjectileInfo { projectilePrefab = (Object.op_Implicit((Object)(object)ModAssets.CoreEjectProjectile) ? ModAssets.CoreEjectProjectile : LegacyResourcesAPI.Load<GameObject>("Prefabs/Projectiles/CommandoGrenadeProjectile")), position = ((Ray)(ref aimRay)).origin + ((Ray)(ref aimRay)).direction * 0.5f, rotation = Util.QuaternionSafeLookRotation(normalized), owner = ((EntityState)this).gameObject, damage = ((BaseState)this).damageStat * 6f, force = 2000f, crit = ((BaseState)this).RollCrit(), damageTypeOverride = DamageTypeCombo.op_Implicit((DamageSource)2) }; ((FireProjectileInfo)(ref val2)).speedOverride = (Force + 10f) * 0.52f; instance.FireProjectile(val2); } } public override void FixedUpdate() { ((EntityState)this).FixedUpdate(); if (((EntityState)this).fixedAge >= _duration && ((EntityState)this).isAuthority) { ((EntityState)this).outer.SetNextStateToMain(); } } public override InterruptPriority GetMinimumInterruptPriority() { return (InterruptPriority)1; } } public class ShotgunCoreCharge : BaseSkillState { public const float MaxForce = 60f; public const float ForcePerSecond = 60f; public const float MinTimeBetweenCores = 0.5f; private const float MinLoopPitch = 0.05f; private float _force; private V1WeaponManager _weapons; public override void OnEnter() { ((BaseState)this).OnEnter(); _weapons = ((EntityState)this).GetComponent<V1WeaponManager>(); if (((EntityState)this).isAuthority && Object.op_Implicit((Object)(object)_weapons) && (Time.time - _weapons.LastCoreTime < 0.5f || _weapons.CoreCharges < 1f)) { ((EntityState)this).outer.SetNextStateToMain(); return; } ModSounds.StartLoop(ModSounds.CoreChargeLoop, ((EntityState)this).gameObject); ModSounds.SetLoop(ModSounds.CoreChargeLoop, ((EntityState)this).gameObject, 1f, 0.05f); } public override void FixedUpdate() { ((EntityState)this).FixedUpdate(); ((BaseState)this).StartAimMode(2f, false); _force = Mathf.MoveTowards(_force, 60f, 60f * Time.fixedDeltaTime * ((BaseState)this).attackSpeedStat); if (((EntityState)this).isAuthority && Object.op_Implicit((Object)(object)_weapons)) { _weapons.CoreCharge = _force / 60f; } ModSounds.SetLoop(ModSounds.CoreChargeLoop, ((EntityState)this).gameObject, 1f, Mathf.Max(0.05f, _force / 60f)); if (((EntityState)this).isAuthority && !((BaseSkillState)this).IsKeyDownAuthority()) { ((EntityState)this).outer.SetNextState((EntityState)(object)new ShotgunBomb { Force = _force }); } } public override void OnExit() { ModSounds.StopLoop(ModSounds.CoreChargeLoop, ((EntityState)this).gameObject); if (((EntityState)this).isAuthority && Object.op_Implicit((Object)(object)_weapons)) { _weapons.CoreCharge = 0f; } ((EntityState)this).OnExit(); } public override InterruptPriority GetMinimumInterruptPriority() { return (InterruptPriority)2; } } public class ShotgunFire : BaseSkillState { public const float PelletDamageCoefficient = 0.6f; public const float PointBlankPellets = 4f; public const float PointBlankRange = 2.08f; public const float BaseSpread = 10f; public const float CoreFireDuration = 1.2120908f; public const float PumpFireDuration = 0.9031818f; public const float OverpumpDistance = 0.52f; public const float OverpumpRadiusMultiplier = 1.5f; public const float OverpumpDamageCoefficient = 6f; public const int CorePellets = 12; public const float PelletProcCoefficient = 0.5f; private static readonly int[] PumpPellets = new int[3] { 10, 16, 24 }; private static readonly float[] PumpSpread = new float[3] { 6.6666665f, 10f, 20f }; public const float AutoPump1Time = 0.40190908f; public const float AutoPump2Time = 0.67736363f; private float _duration; private bool _pumpVariant; private bool _pump1Played; private bool _pump2Played; public const float OverchargeDamagePerLevel = 0.5f; public const float OverchargeRadiusPerLevel = 0.25f; public override void OnEnter() { //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) ((BaseState)this).OnEnter(); V1WeaponManager component = ((EntityState)this).GetComponent<V1WeaponManager>(); bool flag = Object.op_Implicit((Object)(object)component) && component.CurrentWeapon == V1WeaponManager.WeaponType.ShotgunPump; _duration = (flag ? 0.9031818f : 1.2120908f) / ((BaseState)this).attackSpeedStat; _pumpVariant = flag; if (((EntityState)this).isAuthority && Object.op_Implicit((Object)(object)component)) { component.LockGun(_duration); } if (Object.op_Implicit((Object)(object)component)) { component.LastShotgunShot = Time.time; } ModSounds.Play(ModSounds.ShotgunFire, ((EntityState)this).gameObject); V1ViewmodelController.TryTrigger(((EntityState)this).gameObject, flag ? "PumpFire" : "Fire"); V1ViewmodelController.TryUltrakillMuzzleFlash(((EntityState)this).gameObject, ModAssets.UkShotgunMuzzleTemplate); ((BaseState)this).StartAimMode(2f, false); if (((EntityState)this).isAuthority) { Ray aimRay = ((BaseState)this).GetAimRay(); int num = (flag ? component.ShotgunPumps : 0); if (flag) { component.ShotgunPumps = 0; } if (flag && num >= 3) { Overpump(aimRay, num - 3); return; } int pellets = (flag ? PumpPellets[num] : 12); float spread = (flag ? PumpSpread[num] : 10f); FirePellets(aimRay, pellets, spread, 1f); FirePointBlank(aimRay); } } private void FirePellets(Ray aimRay, int pellets, float spread, float damageMultiplier) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) Quaternion val = Util.QuaternionSafeLookRotation(((Ray)(ref aimRay)).direction); float damage = ((BaseState)this).damageStat * 0.6f * damageMultiplier; bool crit = ((BaseState)this).RollCrit(); for (int i = 0; i < pellets; i++) { Quaternion val2 = Quaternion.Euler(Random.Range(0f - spread, spread), Random.Range(0f - spread, spread), Random.Range(0f - spread, spread)); V1ShotgunPellet.Spawn(((EntityState)this).gameObject, ((Ray)(ref aimRay)).origin, val * val2 * Vector3.forward, damage, 0.5f, crit); } } private void FirePointBlank(Ray aimRay) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) BulletAttack val = new BulletAttack { owner = ((EntityState)this).gameObject, weapon = ((EntityState)this).gameObject, origin = ((Ray)(ref aimRay)).origin, aimVector = ((Ray)(ref aimRay)).direction, bulletCount = 1u, radius = 0.6f, damage = ((BaseState)this).damageStat * 0.6f * 4f, damageType = DamageTypeCombo.op_Implicit((DamageSource)1), force = 1500f, isCrit = ((BaseState)this).RollCrit(), procCoefficient = 1f, maxDistance = 2.08f }; LayerIndex world = LayerIndex.world; val.stopperMask = ((LayerIndex)(ref world)).mask; val.smartCollision = true; val.Fire(); } private void Overpump(Ray aimRay, int overcharge) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) float num = 0.52f; Ray val = aimRay; LayerIndex world = LayerIndex.world; RaycastHit val2 = default(RaycastHit); if (Physics.Raycast(val, ref val2, 0.52f, LayerMask.op_Implicit(((LayerIndex)(ref world)).mask), (QueryTriggerInteraction)1)) { num = Mathf.Max(0f, ((RaycastHit)(ref val2)).distance - 0.1f); } V1Explosions.RequestExplosion(((EntityState)this).gameObject, ((Ray)(ref aimRay)).origin + ((Ray)(ref aimRay)).direction * num, 10.5f * (1f + (float)overcharge * 0.25f), ((BaseState)this).damageStat * 6f * (1f + (float)overcharge * 0.5f), ((BaseState)this).RollCrit(), overcharge > 0, 0.5f, (DamageSource)1); } public override void FixedUpdate() { ((EntityState)this).FixedUpdate(); if (_pumpVariant) { float num = ((EntityState)this).fixedAge * ((BaseState)this).attackSpeedStat; if (!_pump1Played && num >= 0.40190908f) { _pump1Played = true; ShotgunPump.PlayPump1(((EntityState)this).gameObject, 0); } if (!_pump2Played && num >= 0.67736363f) { _pump2Played = true; ModSounds.Play(ModSounds.ShotgunPump, ((EntityState)this).gameObject, Random.Range(0.95f, 1.05f)); } } if (((EntityState)this).fixedAge >= _duration && ((EntityState)this).isAuthority) { ((EntityState)this).outer.SetNextStateToMain(); } } public override InterruptPriority GetMinimumInterruptPriority() { return (InterruptPriority)1; } } public class ShotgunPump : BaseSkillState { public const int MaxPumps = 3; public const float BaseDuration = 0.38818184f; public const float Pump1SoundTime = 0.061030302f; public const float Pump2SoundTime = 0.30503032f; private float _duration; private bool _pump1Played; private bool _pump2Played; private int _pumps; public override void OnEnter() { ((BaseState)this).OnEnter(); _duration = 0.38818184f / ((BaseState)this).attackSpeedStat; V1WeaponManager component = ((EntityState)this).GetComponent<V1WeaponManager>(); if (((EntityState)this).isAuthority && Object.op_Implicit((Object)(object)component)) { component.LockGun(_duration); } if (((EntityState)this).isAuthority && Object.op_Implicit((Object)(object)component) && component.ShotgunPumps < component.MaxPumps) { component.ShotgunPumps++; if (component.ShotgunPumps >= 3) { ModSounds.Play(ModSounds.OverpumpBeep, ((EntityState)this).gameObject, 1f + (float)component.Overcharge * 0.15f); } } _pumps = ((!Object.op_Implicit((Object)(object)component)) ? 1 : component.ShotgunPumps); V1ViewmodelController.TryTrigger(((EntityState)this).gameObject, "Pump"); } public override void FixedUpdate() { ((EntityState)this).FixedUpdate(); float num = ((EntityState)this).fixedAge * ((BaseState)this).attackSpeedStat; if (!_pump1Played && num >= 0.061030302f) { _pump1Played = true; PlayPump1(((EntityState)this).gameObject, _pumps); } if (!_pump2Played && num >= 0.30503032f) { _pump2Played = true; ModSounds.Play(ModSounds.ShotgunPump, ((EntityState)this).gameObject, Random.Range(0.95f, 1.05f)); } if (((EntityState)this).fixedAge >= _duration && ((EntityState)this).isAuthority) { ((EntityState)this).outer.SetNextStateToMain(); } } public static void PlayPump1(GameObject source, int pumps) { ModSounds.Play(ModSounds.ShotgunPump1, source, Random.Range(0.95f, 1.05f)); ModSounds.Play(ModSounds.ShotgunPumpCharge, source, 1f + (float)pumps / 5f); } public override InterruptPriority GetMinimumInterruptPriority() { return (InterruptPriority)1; } } public class SuperSaw : BaseSkillState { public const float DamageCoefficient = 2.5f; public const float Hits = 20.9f; public const float Duration = 0.2f; public override void OnEnter() { //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) ((BaseState)this).OnEnter(); if (!((EntityState)this).isAuthority) { return; } V1Nailgun component = ((EntityState)this).GetComponent<V1Nailgun>(); if (!Object.op_Implicit((Object)(object)component) || component.SawHeatSinks < 1f || component.SawHeat < 0.1f) { ModSounds.Play(ModSounds.WeaponSwap, ((EntityState)this).gameObject, 0.7f); return; } int num = Mathf.RoundToInt(component.SawHeat * 3f); component.SawHeatSinks -= 1f; component.SawHeat = 0f; component.LastSawShot = Time.time; ModSounds.Play(ModSounds.SawFire, ((EntityState)this).gameObject, 0.8f, 1.3f); V1ViewmodelController.TryTrigger(((EntityState)this).gameObject, "SuperShoot"); V1ViewmodelController.TryUltrakillMuzzleFlash(((EntityState)this).gameObject, ModAssets.UkSawMuzzle2Template, 0.8f, 1f, withLight: true, 0.12f); ((BaseState)this).AddRecoil(-1.5f, -2f, -0.5f, 0.5f); Ray aimRay = ((BaseState)this).GetAimRay(); ((BaseState)this).StartAimMode(aimRay, 2f, false); Vector3 position = ((Ray)(ref aimRay)).origin + ((Ray)(ref aimRay)).direction * 0.52f; Ray val = aimRay; LayerIndex world = LayerIndex.world; if (Physics.Raycast(val, 0.52f, LayerMask.op_Implicit(((LayerIndex)(ref world)).mask), (QueryTriggerInteraction)1)) { position = ((Ray)(ref aimRay)).origin; } GameObject template = (Object.op_Implicit((Object)(object)ModAssets.UkSawHeatedTemplate) ? ModAssets.UkSawHeatedTemplate : ModAssets.UkSawOverheatTemplate); V1Sawblade v1Sawblade = V1Sawblade.Spawn(((EntityState)this).gameObject, template, position, ((Ray)(ref aimRay)).direction * 104f, ((BaseState)this).RollCrit(), 2.5f); v1Sawblade.HitAmount = 20.9f; v1Sawblade.MultiHitAmount = Mathf.Max(1, num); v1Sawblade.BounceToSurfaceNormal = true; v1Sawblade.Heated = true; v1Sawblade.Source = (DamageSource)2; } public override void FixedUpdate() { ((EntityState)this).FixedUpdate(); if (((EntityState)this).isAuthority && ((EntityState)this).fixedAge >= 0.2f) { ((EntityState)this).outer.SetNextStateToMain(); } } public override InterruptPriority GetMinimumInterruptPriority() { return (InterruptPriority)1; } } public class V1DashState : BaseSkillState { public static float BaseDuration = 0.25f; public override void OnEnter() { ((BaseState)this).OnEnter(); V1MovementController component = ((EntityState)this).GetComponent<V1MovementController>(); if (((EntityState)this).isAuthority && Object.op_Implicit((Object)(object)component)) { component.TryDash(); } } public override void FixedUpdate() { ((EntityState)this).FixedUpdate(); if (((EntityState)this).fixedAge >= BaseDuration && ((EntityState)this).isAuthority) { ((EntityState)this).outer.SetNextStateToMain(); } } public override InterruptPriority GetMinimumInterruptPriority() { return (InterruptPriority)2; } } public class V1MainState : GenericCharacterMain { private V1MovementController _movementController; private V1Hitstop _hitstop; private bool IsFrozen { get { if (Object.op_Implicit((Object)(object)_hitstop)) { return _hitstop.IsFrozen; } return false; } } public override void OnEnter() { ((GenericCharacterMain)this).OnEnter(); _movementController = ((EntityState)this).GetComponent<V1MovementController>(); _hitstop = ((EntityState)this).GetComponent<V1Hitstop>(); } public override void HandleMovements() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (IsFrozen) { if (((BaseCharacterMain)this).hasCharacterMotor) { ((EntityState)this).characterMotor.moveDirection = Vector3.zero; } } else { ((GenericCharacterMain)this).HandleMovements(); } } public override void GatherInputs() { ((GenericCharacterMain)this).GatherInputs(); base.sprintInputReceived = false; } public override void FixedUpdate() { ((GenericCharacterMain)this).FixedUpdate(); if (((EntityState)this).isAuthority && Object.op_Implicit((Object)(object)((EntityState)this).characterBody) && Object.op_Implicit((Object)(object)_movementController)) { ((EntityState)this).characterBody.isSprinting = _movementController.IsSliding; } } public override void ProcessJump() { if (IsFrozen) { base.jumpInputReceived = false; return; } if (base.jumpInputReceived && ((BaseCharacterMain)this).hasCharacterMotor && Object.op_Implicit((Object)(object)_movementController) && ((EntityState)this).isAuthority) { if (_movementController.TryJump() || _movementController.TryWallJump()) { base.jumpInputReceived = false; return; } if (!((EntityState)this).characterMotor.isGrounded && ((EntityState)this).characterMotor.jumpCount == 0) { ((EntityState)this).characterMotor.jumpCount = 1; } } ((GenericCharacterMain)this).ProcessJump(); } } public class V1ParryFreezeState : EntityState { public float Duration = 0.25f; private Animator _animator; private float _animatorSpeed = 1f; private bool _gravityDisabled; public static void Apply(CharacterBody body, float duration) { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown if (!NetworkServer.active || !Object.op_Implicit((Object)(object)body) || body.isPlayerControlled || duration <= 0f) { return; } SetStateOnHurt component = ((Component)body).GetComponent<SetStateOnHurt>(); if (!Object.op_Implicit((Object)(object)component) || !component.canBeStunned || !Object.op_Implicit((Object)(object)component.targetStateMachine)) { return; } component.targetStateMachine.SetInterruptState((EntityState)(object)new V1ParryFreezeState { Duration = duration }, (InterruptPriority)7); if (component.idleStateMachine == null) { return; } EntityStateMachine[] idleStateMachine = component.idleStateMachine; foreach (EntityStateMachine val in idleStateMachine) { if (Object.op_Implicit((Object)(object)val)) { val.SetNextState((EntityState)new Idle()); } } } public override void OnEnter() { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) ((EntityState)this).OnEnter(); _animator = ((EntityState)this).GetModelAnimator(); if (Object.op_Implicit((Object)(object)_animator)) { _animatorSpeed = _animator.speed; _animator.speed = 0f; } if (Object.op_Implicit((Object)(object)((EntityState)this).characterMotor)) { CharacterGravityParameters gravityParameters = ((EntityState)this).characterMotor.gravityParameters; gravityParameters.channeledAntiGravityGranterCount++; ((EntityState)this).characterMotor.gravityParameters = gravityParameters; _gravityDisabled = true; } Hold(); } public override void FixedUpdate() { ((EntityState)this).FixedUpdate(); Hold(); if (((EntityState)this).isAuthority && ((EntityState)this).fixedAge >= Duration) { base.outer.SetNextStateToMain(); } } private void Hold() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)((EntityState)this).characterMotor)) { ((EntityState)this).characterMotor.velocity = Vector3.zero; ((EntityState)this).characterMotor.moveDirection = Vector3.zero; } if (Object.op_Implicit((Object)(object)((EntityState)this).rigidbodyMotor)) { ((EntityState)this).rigidbodyMotor.moveVector = Vector3.zero; } if (Object.op_Implicit((Object)(object)((EntityState)this).rigidbody) && !((EntityState)this).rigidbody.isKinematic) { ((EntityState)this).rigidbody.velocity = Vector3.zero; } } public override void OnExit() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)_animator)) { _animator.speed = _animatorSpeed; } if (_gravityDisabled && Object.op_Implicit((Object)(object)((EntityState)this).characterMotor)) { CharacterGravityParameters gravityParameters = ((EntityState)this).characterMotor.gravityParameters; gravityParameters.channeledAntiGravityGranterCount--; ((EntityState)this).characterMotor.gravityParameters = gravityParameters; } ((EntityState)this).OnExit(); } public override void OnSerialize(NetworkWriter writer) { ((EntityState)this).OnSerialize(writer); writer.Write(Duration); } public override void OnDeserialize(NetworkReader reader) { ((EntityState)this).OnDeserialize(reader); Duration = reader.ReadSingle(); } public override InterruptPriority GetMinimumInterruptPriority() { return (InterruptPriority)7; } } } namespace UltrakillV1.Modules { public static class ModAssets { public static class Names { public const string BodyModel = "mdlV1"; public const string DisplayModel = "mdlV1Display"; public const string Viewmodel = "V1Viewmodel"; public const string CoinModel = "mdlV1Coin"; public const string CoreEjectGhost = "V1CoreEjectGhost"; public const string PiercerTracer = "V1PiercerTracer"; public const string PiercerChargedTracer = "V1PiercerChargedTracer"; public const string CoinRicochetTracer = "V1CoinRicochetTracer"; public const string NailTracer = "V1NailTracer"; public const string SlamShockwave = "V1SlamShockwave"; public const string Portrait = "texV1Icon"; public const string UkRevolverBeam = "ukfx_RevolverBeam"; public const string UkRevolverBeamSuper = "ukfx_RevolverBeamSuper"; public const string UkRicochetBeam = "ukfx_RicochetBeam"; public const string UkCoinFlash = "ukfx_CoinFlash"; public const string UkDodge = "ukfx_Dodge"; public const string UkSlide = "ukfx_Slide"; public const string UkFall = "ukfx_Fall"; public const string UkDustBig = "ukfx_DustBig"; public const string UkShockwave = "ukfx_Shockwave"; public const string UkShotgunMuzzle = "ukfx_ShotgunMuzzle"; public const string UkNailgunMuzzle = "ukfx_NailgunMuzzle"; public const string UkExplosion = "ukfx_Explosion"; public const string UkExplosionSuper = "ukfx_ExplosionSuper"; public const string UkShotgunPellet = "ukfx_ShotgunPellet"; public const string UkBulletSpark = "ukfx_BulletSpark"; public const string UkNail = "ukfx_Nail"; public const string UkMagnet = "ukfx_Magnet"; public const string UkNailHeated = "ukfx_NailHeated"; public const string UkNailgunMuzzle2 = "ukfx_NailgunMuzzle2"; public const string UkRailcannonBeam = "ukfx_RailcannonBeam"; public const string UkSaw = "ukfx_Saw"; public const string UkSawOverheat = "ukfx_SawOverheat"; public const string UkSawHeated = "ukfx_SawHeated"; public const string UkSawMuzzle = "ukfx_SawMuzzle"; public const string UkSawMuzzle2 = "ukfx_SawMuzzle2"; public const string UkSawBounce = "ukfx_SawBounce"; public const string UkSawBreak = "ukfx_SawBreak"; public const string UkRailcannonHit = "ukfx_RailcannonHit"; public const string UkZapImpact = "ukfx_ZapImpact"; } public static AssetBundle MainAssetBundle; public const string BundleName = "ultrakillv1.bundle"; public static readonly Color PiercerColor = new Color(0.25f, 0.9f, 1f); public static readonly Color PiercerChargedColor = new Color(1f, 0.15f, 0.1f); public static readonly Color RicochetColor = new Color(1f, 0.85f, 0.25f); public static readonly Color NailColor = new Color(0.75f, 0.85f, 1f); public static readonly Color CoreEjectColor = new Color(1f, 0.55f, 0.1f); public static readonly Color SlamColor = new Color(1f, 0.8f, 0.2f); public static GameObject BodyModelPrefab; public static GameObject DisplayModelPrefab; public static GameObject ViewmodelPrefab; public static Texture2D PortraitIcon; public static GameObject CoinPrefab; public static GameObject PiercerTracer; public static GameObject PiercerChargedTracer; public static GameObject CoinRicochetTracer; public static GameObject NailTracer; public static GameObject RailcannonTracer; public static GameObject RailcannonRicochetTracer; public static GameObject RailcannonHitEffect; public static GameObject ZapImpactEffect; public static GameObject ElectricArcTracer; public static GameObject SlamShockwaveEffect; public static GameObject CoreEjectProjectile; public const float WorldFxScale = 0.52f; public static GameObject UkDodgeEffect; public static GameObject UkDustEffect; public static GameObject UkShockwaveEffect; public static GameObject UkExplosionEffect; public static GameObject UkExplosionSuperEffect; public static GameObject UkShotgunPelletTemplate; public static GameObject UkBulletSparkEffect; public static GameObject UkNailTemplate; public static GameObject UkMagnetTemplate; public static GameObject UkNailHeatedTemplate; public static GameObject UkNailgunMuzzle2Template; public static GameObject UkSawTemplate; public static GameObject UkSawOverheatTemplate; public static GameObject UkSawHeatedTemplate; public static GameObject UkSawMuzzleTemplate; public static GameObject UkSawMuzzle2Template; public static GameObject UkSawBounceEffect; public static GameObject UkSawBreakEffect; public static GameObject UkSlideTemplate; public static GameObject UkFallTemplate; public static GameObject UkCoinFlashTemplate; public static GameObject UkShotgunMuzzleTemplate; public static GameObject UkNailgunMuzzleTemplate; private static GameObject _ukRevolverBeam; private static GameObject _ukRevolverBeamSuper; private static GameObject _ukRailcannonBeam; private static GameObject _ukRicochetBeam; public static Material V1BluePlatingMaterial; public static Material V1WingsMaterial; private static Shader _hgStandard; private static readonly Dictionary<Material, Material> ConvertedMaterials = new Dictionary<Material, Material>(); public const float CoinVisualDiameter = 0.5f; public static bool HasUltrakillBeams => Object.op_Implicit((Object)(object)_ukRevolverBeam); private static Shader HGStandard { get { if (!Object.op_Implicit((Object)(object)_hgStandard)) { _hgStandard = LoadShader("RoR2/Base/Shaders/HGStandard.shader"); } return _hgStandard; } } public static void Init() { RunStep("LoadAssetBundle", LoadAssetBundle); RunStep("CreateMaterials", CreateMaterials); RunStep("LoadCharacterVisuals", LoadCharacterVisuals); RunStep("LoadUltrakillEffects", LoadUltrakillEffects); RunStep("CreateTracers", CreateTracers); RunStep("CreateCoinPrefab", CreateCoinPrefab); RunStep("CreateSlamShockwave", CreateSlamShockwave); RunStep("CreateCoreEjectProjectile", CreateCoreEjectProjectile); } private static void RunStep(string name, Action step) { try { step(); } catch (Exception ex) { Debug.LogError((object)("[UltrakillV1] Asset step " + name + " failed: " + ex)); } } private static void LoadAssetBundle() { string bundlePath = V1AssetSetup.BundlePath; if (!string.IsNullOrEmpty(bundlePath) && File.Exists(bundlePath)) { MainAssetBundle = AssetBundle.LoadFromFile(bundlePath); if (Object.op_Implicit((Object)(object)MainAssetBundle)) { Debug.Log((object)("[UltrakillV1] Loaded AssetBundle from " + bundlePath)); } else { Debug.LogWarning((object)("[UltrakillV1] Could not load " + bundlePath + ". Using procedural fallbacks.")); } } else { Debug.LogWarning((object)"[UltrakillV1] ULTRAKILL assets not set up yet. Using procedural fallbacks (run the setup from the title screen)."); } } public static T LoadAsset<T>(string name) where T : Object { if (!Object.op_Implicit((Object)(object)MainAssetBundle)) { return default(T); } return MainAssetBundle.LoadAsset<T>(name); } public static GameObject LoadBundlePrefab(string name) { GameObject val = ModAssets.LoadAsset<GameObject>(name); if (Object.op_Implicit((Object)(object)val)) { ConvertMaterials(val); } return val; } private static Shader LoadShader(string address) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) try { return Addressables.LoadAssetAsync<Shader>((object)address).WaitForCompletion(); } catch (Exception ex) { Debug.LogWarning((object)("[UltrakillV1] Could not load shader " + address + ": " + ex.Message)); return null; } } public static void ConvertMaterials(GameObject root) { Renderer[] componentsInChildren = root.GetComponentsInChildren<Renderer>(true); foreach (Renderer val in componentsInChildren) { Material[] sharedMaterials = val.sharedMaterials; for (int j = 0; j < sharedMaterials.Length; j++) { sharedMaterials[j] = ConvertMaterial(sharedMaterials[j]); } val.sharedMaterials = sharedMaterials; } } private static Material ConvertMaterial(Material material) { //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)material) || !Object.op_Implicit((Object)(object)material.shader)) { return material; } if (ConvertedMaterials.TryGetValue(material, out var value)) { return value; } string name = ((Object)material.shader).name; if (name.StartsWith("Stubbed")) { Shader val = LoadShader(name.Substring("Stubbed".Length) + ".shader"); if (Object.op_Implicit((Object)(object)val)) { material.shader = val; } } else if (name == "Standard" && Object.op_Implicit((Object)(object)HGStandard)) { Texture texture = material.GetTexture("_MainTex"); Texture val2 = (material.HasProperty("_BumpMap") ? material.GetTexture("_BumpMap") : null); Tex
plugins\UltrakillV1.Setup.Core.dll
Decompiled an hour ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using AssetsTools.NET; using AssetsTools.NET.Extra; using AssetsTools.NET.Extra.Decompressors.LZ4; using LZ4ps; using Microsoft.CodeAnalysis; using NVorbis; using NVorbis.Ogg; using SevenZip; using SevenZip.Compression.LZ; using SevenZip.Compression.LZMA; using SevenZip.Compression.RangeCoder; using UltrakillV1.Setup.Audio; using UltrakillV1.Setup.Imaging; using UltrakillV1.Setup.Json; using UltrakillV1.Setup.Render; using UltrakillV1.Setup.Unity; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("UltrakillV1.Setup.Core")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("UltrakillV1.Setup.Core")] [assembly: AssemblyTitle("UltrakillV1.Setup.Core")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace UltrakillV1.Setup.Unity { public static class CopyRules { private static readonly HashSet<string> TextureKeep = new HashSet<string> { "m_Name", "m_ForcedFallbackFormat", "m_DownscaleFallback", "m_IsAlphaChannelOptional", "m_IsReadable", "m_IsPreProcessed", "m_IgnoreMasterTextureLimit", "m_IgnoreMipmapLimit", "m_MipmapLimitGroupName", "m_StreamingMipmaps", "m_StreamingMipmapsPriority", "m_TextureSettings", "m_LightmapFormat", "m_ColorSpace", "m_PlatformBlob" }; private static readonly HashSet<string> MeshKeep = new HashSet<string> { "m_Name", "m_IsReadable", "m_KeepVertices", "m_KeepIndices" }; private static readonly HashSet<string> NameOnly = new HashSet<string> { "m_Name" }; public static readonly AssetClassID[] CopiedTypes = new AssetClassID[5] { AssetClassID.Texture2D, AssetClassID.Mesh, AssetClassID.AnimationClip, AssetClassID.Avatar, AssetClassID.AnimatorController }; public static HashSet<string> KeepFor(AssetClassID type) { return type switch { AssetClassID.Texture2D => TextureKeep, AssetClassID.Mesh => MeshKeep, _ => NameOnly, }; } public static void InlineStreamData(AssetClassID type, AssetTypeValueField target, AssetTypeValueField source, Func<string, long, long, byte[]> readResource) { AssetTypeValueField assetTypeValueField = source["m_StreamData"]; if (assetTypeValueField.IsDummy) { return; } long asULong = (long)assetTypeValueField["size"].AsULong; if (asULong > 0) { byte[] bytes = readResource(assetTypeValueField["path"].AsString, (long)assetTypeValueField["offset"].AsULong, asULong); switch (type) { case AssetClassID.Texture2D: FieldCopier.WriteBytes(target["image data"], bytes); break; case AssetClassID.Mesh: FieldCopier.WriteBytes(target["m_VertexData"]["m_DataSize"], bytes); break; } } ClearStream(target); } public static void Downgrade(AssetClassID type, AssetTypeValueField target) { if (type != AssetClassID.Mesh) { return; } foreach (AssetTypeValueField child in target["m_VertexData"]["m_Channels"]["Array"].Children) { child["dimension"].AsByte &= 15; } } public static void ClearStream(AssetTypeValueField target) { AssetTypeValueField assetTypeValueField = target["m_StreamData"]; if (!assetTypeValueField.IsDummy) { assetTypeValueField["offset"].AsULong = 0uL; assetTypeValueField["size"].AsUInt = 0u; assetTypeValueField["path"].AsString = ""; } } } public sealed class ReferenceMap { private readonly Dictionary<(string cab, long pathId), long> _map = new Dictionary<(string, long), long>(); public Func<string, long, long?> Fallback; public void Add(string sourceCab, long sourcePathId, long templatePathId) { _map[(sourceCab.ToLowerInvariant(), sourcePathId)] = templatePathId; } public Func<int, long, long?> For(AssetsFile sourceFile, string sourceCab) { return delegate(int fileId, long pathId) { string text; if (fileId == 0) { text = sourceCab; } else { List<AssetsFileExternal> externals = sourceFile.Metadata.Externals; if (fileId - 1 >= externals.Count) { return (long?)null; } text = Path.GetFileName(externals[fileId - 1].PathName.Replace("archive:/", "").Replace('/', Path.DirectorySeparatorChar)); } long value; return _map.TryGetValue((text.ToLowerInvariant(), pathId), out value) ? new long?(value) : Fallback?.Invoke(text, pathId); }; } } public sealed class FieldCopier { public Func<int, long, long?> RemapPPtr; public HashSet<string> Keep = new HashSet<string>(); public int DroppedFields { get; private set; } public int UnresolvedReferences { get; private set; } public void Copy(AssetTypeValueField target, AssetTypeValueField source) { CopyField(target, source, ""); } private void CopyField(AssetTypeValueField dst, AssetTypeValueField src, string path) { if (path.Length > 0 && Keep.Contains(path)) { return; } if (IsPPtr(dst)) { AssetTypeValueField assetTypeValueField = dst["m_FileID"]; AssetTypeValueField assetTypeValueField2 = dst["m_PathID"]; long asLong = src["m_PathID"].AsLong; if (asLong == 0L) { assetTypeValueField.AsInt = 0; assetTypeValueField2.AsLong = 0L; return; } long? num = RemapPPtr?.Invoke(src["m_FileID"].AsInt, asLong); if (num.HasValue) { assetTypeValueField.AsInt = 0; assetTypeValueField2.AsLong = num.Value; } else { UnresolvedReferences++; } return; } AssetTypeValue value = dst.Value; if (value != null && value.ValueType == AssetValueType.ByteArray) { dst.AsByteArray = ReadBytes(src); return; } if (value != null && value.ValueType == AssetValueType.Array) { CopyArray(dst, src, path); return; } if (value != null && dst.Children.Count == 0) { if (src.Value == null) { DroppedFields++; } else { SetScalar(dst, src.Value); } return; } foreach (AssetTypeValueField child in dst.Children) { AssetTypeValueField assetTypeValueField3 = src[child.FieldName]; if (assetTypeValueField3.IsDummy) { DroppedFields++; } else { CopyField(child, assetTypeValueField3, (path.Length == 0) ? child.FieldName : (path + "." + child.FieldName)); } } } private void CopyArray(AssetTypeValueField dst, AssetTypeValueField src, string path) { AssetTypeValueField assetTypeValueField = ((src.Value != null && src.Value.ValueType == AssetValueType.Array) ? src : src["Array"]); if (assetTypeValueField.IsDummy) { AssetTypeValue value = src.Value; if (value != null && value.ValueType == AssetValueType.ByteArray) { assetTypeValueField = src; } } if (assetTypeValueField.Value != null && assetTypeValueField.Value.ValueType == AssetValueType.ByteArray) { byte[] asByteArray = assetTypeValueField.AsByteArray; List<AssetTypeValueField> list = new List<AssetTypeValueField>(asByteArray.Length); byte[] array = asByteArray; foreach (byte asByte in array) { AssetTypeValueField assetTypeValueField2 = ValueBuilder.DefaultValueFieldFromArrayTemplate(dst); assetTypeValueField2.AsByte = asByte; list.Add(assetTypeValueField2); } dst.Children = list; dst.AsArray = new AssetTypeArrayInfo(list.Count); } else { int count = assetTypeValueField.Children.Count; List<AssetTypeValueField> list2 = new List<AssetTypeValueField>(count); for (int j = 0; j < count; j++) { AssetTypeValueField assetTypeValueField3 = ((j < dst.Children.Count) ? dst.Children[j] : ValueBuilder.DefaultValueFieldFromArrayTemplate(dst)); CopyField(assetTypeValueField3, assetTypeValueField.Children[j], path + "[]"); list2.Add(assetTypeValueField3); } dst.Children = list2; dst.AsArray = new AssetTypeArrayInfo(count); } } public static bool IsPPtr(AssetTypeValueField field) { if (field.TypeName != null && field.TypeName.StartsWith("PPtr<")) { return !field["m_PathID"].IsDummy; } return false; } public static byte[] ReadBytes(AssetTypeValueField field) { if (field.Value != null && field.Value.ValueType == AssetValueType.ByteArray) { return field.AsByteArray; } AssetTypeValueField assetTypeValueField = ((field.Value != null && field.Value.ValueType == AssetValueType.Array) ? field : field["Array"]); if (assetTypeValueField.Value != null && assetTypeValueField.Value.ValueType == AssetValueType.ByteArray) { return assetTypeValueField.AsByteArray; } byte[] array = new byte[assetTypeValueField.Children.Count]; for (int i = 0; i < array.Length; i++) { array[i] = assetTypeValueField.Children[i].AsByte; } return array; } public static void WriteBytes(AssetTypeValueField field, byte[] bytes) { if (field.Value != null && field.Value.ValueType == AssetValueType.ByteArray) { field.AsByteArray = bytes; return; } AssetTypeValueField assetTypeValueField = ((field.Value != null && field.Value.ValueType == AssetValueType.Array) ? field : field["Array"]); if (assetTypeValueField.Value != null && assetTypeValueField.Value.ValueType == AssetValueType.ByteArray) { assetTypeValueField.AsByteArray = bytes; return; } List<AssetTypeValueField> list = new List<AssetTypeValueField>(bytes.Length); foreach (byte asByte in bytes) { AssetTypeValueField assetTypeValueField2 = ValueBuilder.DefaultValueFieldFromArrayTemplate(assetTypeValueField); assetTypeValueField2.AsByte = asByte; list.Add(assetTypeValueField2); } assetTypeValueField.Children = list; assetTypeValueField.AsArray = new AssetTypeArrayInfo(list.Count); } private static void SetScalar(AssetTypeValueField dst, AssetTypeValue src) { object asObject = src.AsObject; if (src.ValueType == AssetValueType.String || dst.Value.ValueType == AssetValueType.String) { dst.Value = src.Clone(); return; } if (dst.Value.ValueType == AssetValueType.Float || dst.Value.ValueType == AssetValueType.Double) { double num = Convert.ToDouble(asObject, CultureInfo.InvariantCulture); if (dst.Value.ValueType == AssetValueType.Float) { dst.AsFloat = (float)num; } else { dst.AsDouble = num; } return; } long num2 = ((asObject is ulong num3) ? ((long)num3) : ((asObject is bool flag) ? (flag ? 1 : 0) : ((asObject is float num4) ? ((long)num4) : ((asObject is double num5) ? ((long)num5) : Convert.ToInt64(asObject, CultureInfo.InvariantCulture))))); switch (dst.Value.ValueType) { case AssetValueType.Bool: dst.AsBool = num2 != 0; break; case AssetValueType.Int8: dst.AsSByte = (sbyte)num2; break; case AssetValueType.UInt8: dst.AsByte = (byte)num2; break; case AssetValueType.Int16: dst.AsShort = (short)num2; break; case AssetValueType.UInt16: dst.AsUShort = (ushort)num2; break; case AssetValueType.Int32: dst.AsInt = (int)num2; break; case AssetValueType.UInt32: dst.AsUInt = (uint)num2; break; case AssetValueType.Int64: dst.AsLong = num2; break; case AssetValueType.UInt64: dst.AsULong = (ulong)num2; break; default: dst.Value = src.Clone(); break; } } public void Clear(AssetTypeValueField target) { ClearField(target, ""); } private void ClearField(AssetTypeValueField f, string path) { if (path.Length > 0 && Keep.Contains(path)) { return; } if (IsPPtr(f)) { f["m_FileID"].AsInt = 0; f["m_PathID"].AsLong = 0L; return; } AssetTypeValue value = f.Value; if (value != null && (f.Children.Count == 0 || value.ValueType == AssetValueType.Array || value.ValueType == AssetValueType.ByteArray)) { switch (value.ValueType) { case AssetValueType.ByteArray: f.AsByteArray = Array.Empty<byte>(); break; case AssetValueType.Array: f.Children = new List<AssetTypeValueField>(); f.AsArray = new AssetTypeArrayInfo(0); break; case AssetValueType.String: f.AsString = ""; break; case AssetValueType.Bool: f.AsBool = false; break; case AssetValueType.Float: f.AsFloat = 0f; break; case AssetValueType.Double: f.AsDouble = 0.0; break; default: f.Value = new AssetTypeValue(value.ValueType, Convert.ChangeType(0, ClrType(value.ValueType))); break; } return; } foreach (AssetTypeValueField child in f.Children) { ClearField(child, (path.Length == 0) ? child.FieldName : (path + "." + child.FieldName)); } } private static Type ClrType(AssetValueType t) { return t switch { AssetValueType.Int8 => typeof(sbyte), AssetValueType.UInt8 => typeof(byte), AssetValueType.Int16 => typeof(short), AssetValueType.UInt16 => typeof(ushort), AssetValueType.Int32 => typeof(int), AssetValueType.UInt32 => typeof(uint), AssetValueType.Int64 => typeof(long), AssetValueType.UInt64 => typeof(ulong), _ => typeof(int), }; } } public sealed class MeshVertexData { public struct Channel { public int Stream; public int Offset; public int Format; public int Dimension; public bool Present => Dimension > 0; } public const int Position = 0; public const int Normal = 1; public const int Tangent = 2; public const int Color = 3; public const int UV0 = 4; public const int BlendWeight = 12; public const int BlendIndices = 13; public readonly int VertexCount; public readonly Channel[] Channels; public readonly int[] StreamOffset; public readonly int[] StreamStride; public byte[] Data; public int RequiredSize { get { int num = 0; for (int i = 0; i < StreamStride.Length; i++) { num = Math.Max(num, StreamOffset[i] + StreamStride[i] * VertexCount); } return num; } } public MeshVertexData(AssetTypeValueField vertexData) { VertexCount = (int)vertexData["m_VertexCount"].AsUInt; List<Channel> list = new List<Channel>(); foreach (AssetTypeValueField child in vertexData["m_Channels"]["Array"].Children) { list.Add(new Channel { Stream = child["stream"].AsByte, Offset = child["offset"].AsByte, Format = child["format"].AsByte, Dimension = (child["dimension"].AsByte & 0xF) }); } Channels = list.ToArray(); int num = 0; Channel[] channels = Channels; for (int i = 0; i < channels.Length; i++) { Channel channel = channels[i]; if (channel.Present) { num = Math.Max(num, channel.Stream + 1); } } StreamStride = new int[num]; StreamOffset = new int[num]; channels = Channels; for (int i = 0; i < channels.Length; i++) { Channel channel2 = channels[i]; if (channel2.Present) { StreamStride[channel2.Stream] = Math.Max(StreamStride[channel2.Stream], channel2.Offset + FormatSize(channel2.Format) * channel2.Dimension); } } int num2 = 0; for (int j = 0; j < num; j++) { num2 = (num2 + 15) & -16; StreamOffset[j] = num2; num2 += StreamStride[j] * VertexCount; } Data = FieldCopier.ReadBytes(vertexData["m_DataSize"]); } public bool Has(int channel) { if (channel < Channels.Length) { return Channels[channel].Present; } return false; } public int Dimension(int channel) { if (!Has(channel)) { return 0; } return Channels[channel].Dimension; } public static int FormatSize(int format) { switch (format) { case 0: case 10: case 11: return 4; case 1: case 4: case 5: case 8: case 9: return 2; default: return 1; } } private int Address(int channel, int vertex, int component) { Channel channel2 = Channels[channel]; return StreamOffset[channel2.Stream] + StreamStride[channel2.Stream] * vertex + channel2.Offset + FormatSize(channel2.Format) * component; } public float Get(int channel, int vertex, int component) { Channel channel2 = Channels[channel]; int num = Address(channel, vertex, component); return channel2.Format switch { 0 => BitConverter.ToSingle(Data, num), 1 => HalfToFloat(BitConverter.ToUInt16(Data, num)), 2 => (float)(int)Data[num] / 255f, 3 => Math.Max((float)(sbyte)Data[num] / 127f, -1f), 4 => (float)(int)BitConverter.ToUInt16(Data, num) / 65535f, 5 => Math.Max((float)BitConverter.ToInt16(Data, num) / 32767f, -1f), 6 => (int)Data[num], 7 => (sbyte)Data[num], 8 => (int)BitConverter.ToUInt16(Data, num), 9 => BitConverter.ToInt16(Data, num), 10 => BitConverter.ToUInt32(Data, num), 11 => BitConverter.ToInt32(Data, num), _ => throw new NotSupportedException("Vertex format " + channel2.Format), }; } public void Set(int channel, int vertex, int component, float value) { Channel channel2 = Channels[channel]; int num = Address(channel, vertex, component); switch (channel2.Format) { case 0: WriteBytes(BitConverter.GetBytes(value), num); break; case 1: WriteBytes(BitConverter.GetBytes(FloatToHalf(value)), num); break; case 2: Data[num] = (byte)Math.Round(Clamp(value, 0f, 1f) * 255f); break; case 3: Data[num] = (byte)(sbyte)Math.Round(Clamp(value, -1f, 1f) * 127f); break; case 4: WriteBytes(BitConverter.GetBytes((ushort)Math.Round(Clamp(value, 0f, 1f) * 65535f)), num); break; case 5: WriteBytes(BitConverter.GetBytes((short)Math.Round(Clamp(value, -1f, 1f) * 32767f)), num); break; case 6: Data[num] = (byte)value; break; case 7: Data[num] = (byte)(sbyte)value; break; case 8: WriteBytes(BitConverter.GetBytes((ushort)value), num); break; case 9: WriteBytes(BitConverter.GetBytes((short)value), num); break; case 10: WriteBytes(BitConverter.GetBytes((uint)value), num); break; case 11: WriteBytes(BitConverter.GetBytes((int)value), num); break; default: throw new NotSupportedException("Vertex format " + channel2.Format); } } private void WriteBytes(byte[] bytes, int at) { Buffer.BlockCopy(bytes, 0, Data, at, bytes.Length); } private static float Clamp(float v, float min, float max) { if (!(v < min)) { if (!(v > max)) { return v; } return max; } return min; } public static float HalfToFloat(ushort h) { int num = (h >> 15) & 1; int num2 = (h >> 10) & 0x1F; int num3 = h & 0x3FF; float num4 = num2 switch { 0 => (float)((double)num3 / 1024.0 * Math.Pow(2.0, -14.0)), 31 => (num3 == 0) ? float.PositiveInfinity : float.NaN, _ => (float)((1.0 + (double)num3 / 1024.0) * Math.Pow(2.0, num2 - 15)), }; if (num != 1) { return num4; } return 0f - num4; } public static ushort FloatToHalf(float f) { int num = BitConverter.ToInt32(BitConverter.GetBytes(f), 0); int num2 = (num >> 16) & 0x8000; int num3 = ((num >> 23) & 0xFF) - 127 + 15; int num4 = num & 0x7FFFFF; if (num3 <= 0) { return (ushort)num2; } if (num3 >= 31) { return (ushort)(num2 | 0x7C00); } return (ushort)(num2 | (num3 << 10) | (num4 >> 13)); } } public static class Signatures { public static string Of(AssetClassID type, AssetTypeValueField f) { switch (type) { case AssetClassID.Mesh: return "v" + f["m_VertexData"]["m_VertexCount"].AsUInt + "/i" + FieldCopier.ReadBytes(f["m_IndexBuffer"]).Length + "/s" + f["m_SubMeshes"]["Array"].Children.Count + "/b" + f["m_BindPose"]["Array"].Children.Count; case AssetClassID.Texture2D: return f["m_Width"].AsInt + "x" + f["m_Height"].AsInt; case AssetClassID.AnimationClip: { AssetTypeValueField assetTypeValueField2 = f["m_MuscleClip"]; return "t" + (assetTypeValueField2["m_StopTime"].AsFloat - assetTypeValueField2["m_StartTime"].AsFloat).ToString("F3", CultureInfo.InvariantCulture) + "/b" + f["m_ClipBindingConstant"]["genericBindings"]["Array"].Children.Count; } case AssetClassID.Avatar: return "n" + f["m_Avatar"]["m_AvatarSkeleton"]["data"]["m_Node"]["Array"].Children.Count + "/t" + f["m_TOS"]["Array"].Children.Count; case AssetClassID.AnimatorController: return "c" + f["m_AnimationClips"]["Array"].Children.Count + "/t" + f["m_TOS"]["Array"].Children.Count; case AssetClassID.AudioClip: return f["m_Channels"].AsInt + "ch/" + f["m_Frequency"].AsInt; case AssetClassID.Sprite: { AssetTypeValueField assetTypeValueField = f["m_RD"]["textureRect"]; return Round(assetTypeValueField["width"].AsFloat) + "x" + Round(assetTypeValueField["height"].AsFloat); } default: return ""; } } private static string Round(float v) { return Math.Round(v).ToString(CultureInfo.InvariantCulture); } } public sealed class SourceBundles : IDisposable { private readonly AssetsManager _manager = new AssetsManager(); private readonly Dictionary<string, BundleFileInstance> _bundles = new Dictionary<string, BundleFileInstance>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, AssetsFileInstance> _files = new Dictionary<string, AssetsFileInstance>(StringComparer.OrdinalIgnoreCase); public string Root { get; } public SourceBundles(string addressablesRoot) { Root = addressablesRoot; } public static string AddressablesFolder(string gameFolder) { return Path.Combine(gameFolder, "ULTRAKILL_Data", "StreamingAssets", "aa", "StandaloneWindows64"); } public string BundlePath(string relative) { string fullPath = Path.GetFullPath(Path.Combine(Root, relative.Replace('/', Path.DirectorySeparatorChar))); string text = Path.GetFullPath(Root).TrimEnd(new char[1] { Path.DirectorySeparatorChar }); char directorySeparatorChar = Path.DirectorySeparatorChar; string value = text + directorySeparatorChar; if (!fullPath.StartsWith(value, StringComparison.OrdinalIgnoreCase)) { throw new InvalidDataException("Bundle path outside the game folder: " + relative); } return fullPath; } public BundleFileInstance Bundle(string relative) { if (!_bundles.TryGetValue(relative, out var value)) { string text = BundlePath(relative); if (!File.Exists(text)) { throw new FileNotFoundException("ULTRAKILL file not found", text); } value = _manager.LoadBundleFile(text, unpackIfPacked: false); _bundles[relative] = value; } return value; } public AssetsFileInstance File0(string relative) { if (_files.TryGetValue(relative, out var value)) { return value; } BundleFileInstance bundleFileInstance = Bundle(relative); for (int i = 0; i < bundleFileInstance.file.BlockAndDirInfo.DirectoryInfos.Count; i++) { if (bundleFileInstance.file.IsAssetsFile(i)) { value = _manager.LoadAssetsFileFromBundle(bundleFileInstance, i); _files[relative] = value; return value; } } throw new InvalidDataException("No serialized file in " + relative); } public string CabName(string relative) { BundleFileInstance bundleFileInstance = Bundle(relative); for (int i = 0; i < bundleFileInstance.file.BlockAndDirInfo.DirectoryInfos.Count; i++) { if (bundleFileInstance.file.IsAssetsFile(i)) { return bundleFileInstance.file.GetFileName(i); } } return null; } public string UnityVersion(string relative) { return File0(relative).file.Metadata.UnityVersion; } public AssetFileInfo Info(string relative, long pathId) { return File0(relative).file.GetAssetInfo(pathId); } public AssetTypeValueField Read(string relative, long pathId) { AssetsFileInstance assetsFileInstance = File0(relative); AssetFileInfo assetInfo = assetsFileInstance.file.GetAssetInfo(pathId); if (assetInfo != null) { return _manager.GetBaseField(assetsFileInstance, assetInfo); } return null; } public AssetTypeValueField Read(string relative, AssetFileInfo info) { return _manager.GetBaseField(File0(relative), info); } public IList<AssetFileInfo> Objects(string relative) { return File0(relative).file.AssetInfos; } public byte[] RawBytes(string relative, AssetFileInfo info) { AssetsFileInstance assetsFileInstance = File0(relative); lock (assetsFileInstance.file.Reader) { assetsFileInstance.file.Reader.Position = info.GetAbsoluteByteOffset(assetsFileInstance.file); return assetsFileInstance.file.Reader.ReadBytes((int)info.ByteSize); } } public byte[] ReadResource(string relative, string archivePath, long offset, long size) { if (size <= 0) { return Array.Empty<byte>(); } string fileName = Path.GetFileName(archivePath.Replace("archive:/", "").Replace('/', Path.DirectorySeparatorChar)); BundleFileInstance bundleFileInstance = Bundle(relative); int fileIndex = bundleFileInstance.file.GetFileIndex(fileName); if (fileIndex < 0) { throw new InvalidDataException("Resource " + fileName + " not in " + relative); } bundleFileInstance.file.GetFileRange(fileIndex, out var offset2, out var length); if (offset < 0 || offset + size > length) { throw new InvalidDataException("Resource range outside " + fileName); } AssetsFileReader dataReader = bundleFileInstance.file.DataReader; lock (dataReader) { dataReader.Position = offset2 + offset; return dataReader.ReadBytes((int)size); } } public static string Sha256(params byte[][] parts) { using SHA256 sHA = SHA256.Create(); foreach (byte[] array in parts) { sHA.TransformBlock(array, 0, array.Length, null, 0); } sHA.TransformFinalBlock(Array.Empty<byte>(), 0, 0); return BitConverter.ToString(sHA.Hash).Replace("-", "").ToLowerInvariant(); } public void Dispose() { _manager.UnloadAll(unloadClassData: true); _bundles.Clear(); _files.Clear(); } } public static class TextureCodec { public const int Alpha8 = 1; public const int ARGB4444 = 2; public const int RGB24 = 3; public const int RGBA32 = 4; public const int ARGB32 = 5; public const int RGB565 = 7; public const int DXT1 = 10; public const int DXT5 = 12; public const int RGBA4444 = 13; public const int BGRA32 = 14; public const int R8 = 63; public static bool CanDecode(int format) { switch (format) { case 1: case 2: case 3: case 4: case 5: case 7: case 10: case 12: case 13: case 14: case 63: return true; default: return false; } } public static RgbaImage Decode(int format, int width, int height, byte[] data) { RgbaImage rgbaImage = new RgbaImage(width, height); float[] pixels = rgbaImage.Pixels; int num = width * height; switch (format) { case 1: { for (int num9 = 0; num9 < num; num9++) { Set(pixels, num9, 1f, 1f, 1f, (float)(int)data[num9] / 255f); } break; } case 63: { for (int l = 0; l < num; l++) { Set(pixels, l, (float)(int)data[l] / 255f, 0f, 0f, 1f); } break; } case 3: { for (int n = 0; n < num; n++) { Set(pixels, n, (float)(int)data[n * 3] / 255f, (float)(int)data[n * 3 + 1] / 255f, (float)(int)data[n * 3 + 2] / 255f, 1f); } break; } case 4: { for (int j = 0; j < num * 4; j++) { pixels[j] = (float)(int)data[j] / 255f; } break; } case 5: { for (int num8 = 0; num8 < num; num8++) { Set(pixels, num8, (float)(int)data[num8 * 4 + 1] / 255f, (float)(int)data[num8 * 4 + 2] / 255f, (float)(int)data[num8 * 4 + 3] / 255f, (float)(int)data[num8 * 4] / 255f); } break; } case 14: { for (int m = 0; m < num; m++) { Set(pixels, m, (float)(int)data[m * 4 + 2] / 255f, (float)(int)data[m * 4 + 1] / 255f, (float)(int)data[m * 4] / 255f, (float)(int)data[m * 4 + 3] / 255f); } break; } case 7: { for (int k = 0; k < num; k++) { int num7 = data[k * 2] | (data[k * 2 + 1] << 8); Set(pixels, k, (float)((num7 >> 11) & 0x1F) / 31f, (float)((num7 >> 5) & 0x3F) / 63f, (float)(num7 & 0x1F) / 31f, 1f); } break; } case 2: case 13: { for (int i = 0; i < num; i++) { int num2 = data[i * 2] | (data[i * 2 + 1] << 8); float num3 = (float)((num2 >> 12) & 0xF) / 15f; float num4 = (float)((num2 >> 8) & 0xF) / 15f; float num5 = (float)((num2 >> 4) & 0xF) / 15f; float num6 = (float)(num2 & 0xF) / 15f; if (format == 2) { Set(pixels, i, num4, num5, num6, num3); } else { Set(pixels, i, num3, num4, num5, num6); } } break; } case 10: case 12: DecodeBlocks(format, width, height, data, pixels); break; default: throw new NotSupportedException("Texture format " + format); } return rgbaImage.FlipVertical(); } private static void Set(float[] p, int i, float r, float g, float b, float a) { p[i * 4] = r; p[i * 4 + 1] = g; p[i * 4 + 2] = b; p[i * 4 + 3] = a; } private static void DecodeBlocks(int format, int width, int height, byte[] data, float[] p) { int num = ((format == 10) ? 8 : 16); int num2 = (width + 3) / 4; int num3 = (height + 3) / 4; float[,] array = new float[4, 4]; float[] array2 = new float[8]; for (int i = 0; i < num3; i++) { for (int j = 0; j < num2; j++) { int num4 = (i * num2 + j) * num; int num5 = num4; ulong num6 = 0uL; if (format == 12) { float num7 = (float)(int)data[num4] / 255f; float num8 = (float)(int)data[num4 + 1] / 255f; array2[0] = num7; array2[1] = num8; if (num7 > num8) { for (int k = 1; k < 7; k++) { array2[k + 1] = ((float)(7 - k) * num7 + (float)k * num8) / 7f; } } else { for (int l = 1; l < 5; l++) { array2[l + 1] = ((float)(5 - l) * num7 + (float)l * num8) / 5f; } array2[6] = 0f; array2[7] = 1f; } for (int m = 0; m < 6; m++) { num6 |= (ulong)data[num4 + 2 + m] << 8 * m; } num5 = num4 + 8; } int num9 = data[num5] | (data[num5 + 1] << 8); int num10 = data[num5 + 2] | (data[num5 + 3] << 8); Rgb565(num9, array, 0); Rgb565(num10, array, 1); bool flag = num9 > num10 || format == 12; for (int n = 0; n < 3; n++) { if (flag) { array[2, n] = (2f * array[0, n] + array[1, n]) / 3f; array[3, n] = (array[0, n] + 2f * array[1, n]) / 3f; } else { array[2, n] = (array[0, n] + array[1, n]) / 2f; array[3, n] = 0f; } } array[0, 3] = (array[1, 3] = (array[2, 3] = 1f)); array[3, 3] = (flag ? 1 : 0); uint num11 = (uint)(data[num5 + 4] | (data[num5 + 5] << 8) | (data[num5 + 6] << 16) | (data[num5 + 7] << 24)); for (int num12 = 0; num12 < 4; num12++) { for (int num13 = 0; num13 < 4; num13++) { int num14 = j * 4 + num13; int num15 = i * 4 + num12; if (num14 < width && num15 < height) { int num16 = num12 * 4 + num13; int num17 = (int)((num11 >> 2 * num16) & 3); int num18 = (num15 * width + num14) * 4; p[num18] = array[num17, 0]; p[num18 + 1] = array[num17, 1]; p[num18 + 2] = array[num17, 2]; p[num18 + 3] = ((format == 12) ? array2[(uint)((num6 >> 3 * num16) & 7)] : array[num17, 3]); } } } } } } private static void Rgb565(int v, float[,] colors, int index) { colors[index, 0] = (float)((v >> 11) & 0x1F) / 31f; colors[index, 1] = (float)((v >> 5) & 0x3F) / 63f; colors[index, 2] = (float)(v & 0x1F) / 31f; } public static RgbaImage Decode(AssetTypeValueField texture) { int asInt = texture["m_TextureFormat"].AsInt; if (!CanDecode(asInt)) { throw new NotSupportedException(string.Format("Texture {0}: format {1} not supported", texture["m_Name"].AsString, asInt)); } return Decode(asInt, texture["m_Width"].AsInt, texture["m_Height"].AsInt, FieldCopier.ReadBytes(texture["image data"])); } public static void Write(AssetTypeValueField texture, RgbaImage image, bool withAlpha = true) { RgbaImage rgbaImage = image.FlipVertical(); int num = image.Width * image.Height; byte[] array; if (withAlpha) { array = rgbaImage.ToRgba32(); } else { array = new byte[num * 3]; for (int i = 0; i < num; i++) { array[i * 3] = RgbaImage.ToByte(rgbaImage.Pixels[i * 4]); array[i * 3 + 1] = RgbaImage.ToByte(rgbaImage.Pixels[i * 4 + 1]); array[i * 3 + 2] = RgbaImage.ToByte(rgbaImage.Pixels[i * 4 + 2]); } } texture["m_Width"].AsInt = image.Width; texture["m_Height"].AsInt = image.Height; texture["m_CompleteImageSize"].AsUInt = (uint)array.Length; texture["m_TextureFormat"].AsInt = (withAlpha ? 4 : 3); texture["m_MipCount"].AsInt = 1; if (!texture["m_ImageCount"].IsDummy) { texture["m_ImageCount"].AsInt = 1; } if (!texture["m_MipsStripped"].IsDummy) { texture["m_MipsStripped"].AsInt = 0; } FieldCopier.WriteBytes(texture["image data"], array); CopyRules.ClearStream(texture); } } } namespace UltrakillV1.Setup.Render { public sealed class PickupIconRenderer { private sealed class Shading { public bool Skip; public RgbaImage Texture; public double ScaleU = 1.0; public double ScaleV = 1.0; public double OffsetU; public double OffsetV; public double R = 1.0; public double G = 1.0; public double B = 1.0; public RgbaImage Emission; public double ER; public double EG; public double EB; public double Cutoff = -1.0; } public const int Size = 256; private const double ModelSize = 1.6; private const double Fov = 30.0; public double Ambient = 0.3; public double Light = 0.9; public double Specular = 0.25; public double AmbientBlue = 0.33; public bool CounterClockwiseFront; public string[] SkipMaterials = new string[0]; private readonly SceneReader _scene; private readonly Dictionary<long, RgbaImage> _textures = new Dictionary<long, RgbaImage>(); public PickupIconRenderer(SceneReader scene) { _scene = scene; } public RgbaImage Render(long rootGameObject, float r, float g, float b) { return Compose(RenderModel(rootGameObject), r, g, b); } public static RgbaImage Compose(RgbaImage gun, float r, float g, float b) { RgbaImage rgbaImage = new RgbaImage(256, 256); float num = 128f; for (int i = 0; i < 256; i++) { for (int j = 0; j < 256; j++) { float num2 = (float)j - num; float num3 = (float)i - num; float num4 = (float)Math.Sqrt(num2 * num2 + num3 * num3) / num; float val = Math.Max(0f, Math.Min(1f, 1f - num4)) * 0.55f; int num5 = gun.Index(j, 255 - i); float num6 = gun.Pixels[num5 + 3]; int num7 = rgbaImage.Index(j, 255 - i); rgbaImage.Pixels[num7] = r + (gun.Pixels[num5] - r) * num6; rgbaImage.Pixels[num7 + 1] = g + (gun.Pixels[num5 + 1] - g) * num6; rgbaImage.Pixels[num7 + 2] = b + (gun.Pixels[num5 + 2] - b) * num6; rgbaImage.Pixels[num7 + 3] = Math.Max(num6, val); } } return rgbaImage; } public RgbaImage RenderModel(long rootGameObject) { (double, double, double) tuple = Normalize(-0.85, 0.35, -0.6); double num = 1.6 / (2.0 * Math.Tan(Math.PI / 12.0)) * 1.15; (double, double, double) eye = (tuple.Item1 * num, tuple.Item2 * num, tuple.Item3 * num); (double, double, double) lightForward = Normalize(0.0 - tuple.Item1, 0.0 - tuple.Item2 - 0.5, 0.0 - tuple.Item3); return RenderDrawables(_scene.Drawables(rootGameObject), eye, (x: 0.0, y: 0.0, z: 0.0), 30.0, lightForward); } public RgbaImage RenderDrawables(IEnumerable<SceneReader.Drawable> drawables, (double x, double y, double z) eye, (double x, double y, double z) target, double fov, (double x, double y, double z) lightForward) { RgbaImage rgbaImage = new RgbaImage(256, 256); double[] array = new double[65536]; for (int i = 0; i < array.Length; i++) { array[i] = double.MaxValue; } (double, double, double) tuple = Normalize(target.x - eye.x, target.y - eye.y, target.z - eye.z); (double, double, double) b = Normalize(Cross((x: 0.0, y: 1.0, z: 0.0), tuple)); (double, double, double) b2 = Cross(tuple, b); lightForward = Normalize(lightForward); (double, double, double) toLight = (0.0 - lightForward.x, 0.0 - lightForward.y, 0.0 - lightForward.z); double num = 1.0 / Math.Tan(fov * 0.5 * Math.PI / 180.0); foreach (SceneReader.Drawable drawable in drawables) { MeshVertexData meshVertexData = new MeshVertexData(drawable.Mesh["m_VertexData"]); int vertexCount = meshVertexData.VertexCount; if (!meshVertexData.Has(0) || meshVertexData.Data.Length < meshVertexData.RequiredSize) { continue; } double[] array2 = new double[vertexCount]; double[] array3 = new double[vertexCount]; double[] array4 = new double[vertexCount]; double[] array5 = new double[vertexCount]; double[] array6 = new double[vertexCount]; double[] array7 = new double[vertexCount]; double[] array8 = new double[vertexCount]; double[] array9 = new double[vertexCount]; Mat4 world = drawable.World; bool flag = world.Determinant3() < 0.0; for (int j = 0; j < vertexCount; j++) { world = drawable.World; (double x, double y, double z) tuple2 = world.Point(meshVertexData.Get(0, j, 0), meshVertexData.Get(0, j, 1), meshVertexData.Get(0, j, 2)); double item = tuple2.x - eye.x; double item2 = tuple2.y - eye.y; double item3 = tuple2.z - eye.z; double num2 = Dot((x: item, y: item2, z: item3), b); double num3 = Dot((x: item, y: item2, z: item3), b2); double num4 = (array4[j] = Dot((x: item, y: item2, z: item3), tuple)); array2[j] = (num2 / num4 * num * 0.5 + 0.5) * 256.0; array3[j] = (0.5 - num3 / num4 * num * 0.5) * 256.0; if (meshVertexData.Has(1)) { world = drawable.World; (double, double, double) tuple3 = world.Vector(meshVertexData.Get(1, j, 0), meshVertexData.Get(1, j, 1), meshVertexData.Get(1, j, 2)); (double, double, double) tuple4 = Normalize(tuple3.Item1, tuple3.Item2, tuple3.Item3); array5[j] = tuple4.Item1; array6[j] = tuple4.Item2; array7[j] = tuple4.Item3; } if (meshVertexData.Has(4)) { array8[j] = meshVertexData.Get(4, j, 0); array9[j] = meshVertexData.Get(4, j, 1); } } byte[] buffer = FieldCopier.ReadBytes(drawable.Mesh["m_IndexBuffer"]); bool flag2 = drawable.Mesh["m_IndexFormat"].AsInt == 1; List<AssetTypeValueField> children = drawable.Mesh["m_SubMeshes"]["Array"].Children; for (int k = 0; k < children.Count; k++) { AssetTypeValueField assetTypeValueField = children[k]; if (assetTypeValueField["topology"].AsInt != 0) { continue; } AssetTypeValueField assetTypeValueField2 = ((k < drawable.Materials.Count) ? drawable.Materials[k] : ((drawable.Materials.Count > 0) ? drawable.Materials[drawable.Materials.Count - 1] : null)); string materialName = ((assetTypeValueField2 == null) ? "" : assetTypeValueField2["m_Name"].AsString); if (Array.Exists(SkipMaterials, (string prefix) => materialName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))) { continue; } Shading shading = ShadingOf(assetTypeValueField2); if (shading.Skip) { continue; } int num5 = (int)assetTypeValueField["firstByte"].AsUInt / (flag2 ? 4 : 2); int asUInt = (int)assetTypeValueField["indexCount"].AsUInt; int asUInt2 = (int)assetTypeValueField["baseVertex"].AsUInt; for (int num6 = 0; num6 + 2 < asUInt; num6 += 3) { int num7 = Index(buffer, num5 + num6, flag2) + asUInt2; int num8 = Index(buffer, num5 + num6 + 1, flag2) + asUInt2; int num9 = Index(buffer, num5 + num6 + 2, flag2) + asUInt2; if (num7 < vertexCount && num8 < vertexCount && num9 < vertexCount && !(array4[num7] <= 0.01) && !(array4[num8] <= 0.01) && !(array4[num9] <= 0.01) && (array2[num8] - array2[num7]) * (array3[num9] - array3[num7]) - (array2[num9] - array2[num7]) * (array3[num8] - array3[num7]) <= 0.0 == (flag ^ CounterClockwiseFront)) { Rasterize(rgbaImage, array, array2, array3, array4, array5, array6, array7, array8, array9, num7, num8, num9, shading, toLight); } } } } return rgbaImage; } private Shading ShadingOf(AssetTypeValueField material) { Shading shading = new Shading(); if (material == null) { return shading; } string text = _scene.ShaderName(material); if (text.StartsWith("Legacy Shaders/Particles")) { shading.Skip = true; return shading; } AssetTypeValueField assetTypeValueField = SceneReader.Property(material, "m_TexEnvs", "_MainTex"); if (assetTypeValueField != null) { shading.Texture = Texture(SceneReader.PathOf(assetTypeValueField["m_Texture"])); shading.ScaleU = assetTypeValueField["m_Scale"]["x"].AsFloat; shading.ScaleV = assetTypeValueField["m_Scale"]["y"].AsFloat; shading.OffsetU = assetTypeValueField["m_Offset"]["x"].AsFloat; shading.OffsetV = assetTypeValueField["m_Offset"]["y"].AsFloat; } AssetTypeValueField assetTypeValueField2 = SceneReader.Property(material, "m_Colors", "_Color"); if (assetTypeValueField2 != null) { shading.R = assetTypeValueField2["r"].AsFloat; shading.G = assetTypeValueField2["g"].AsFloat; shading.B = assetTypeValueField2["b"].AsFloat; } if (_scene.HasKeyword(material, "_EMISSION")) { AssetTypeValueField assetTypeValueField3 = SceneReader.Property(material, "m_Colors", "_EmissionColor"); AssetTypeValueField assetTypeValueField4 = SceneReader.Property(material, "m_TexEnvs", "_EmissionMap"); if (assetTypeValueField3 != null) { shading.ER = assetTypeValueField3["r"].AsFloat; shading.EG = assetTypeValueField3["g"].AsFloat; shading.EB = assetTypeValueField3["b"].AsFloat; } if (assetTypeValueField4 != null) { shading.Emission = Texture(SceneReader.PathOf(assetTypeValueField4["m_Texture"])); } } if (text.Contains("Cutout")) { AssetTypeValueField assetTypeValueField5 = SceneReader.Property(material, "m_Floats", "_Cutoff"); shading.Cutoff = ((assetTypeValueField5 != null) ? ((double)assetTypeValueField5.AsFloat) : 0.5); } return shading; } private RgbaImage Texture(long pathId) { if (pathId == 0L) { return null; } if (_textures.TryGetValue(pathId, out var value)) { return value; } RgbaImage rgbaImage = null; try { AssetTypeValueField assetTypeValueField = _scene.Get(pathId); if (assetTypeValueField != null && TextureCodec.CanDecode(assetTypeValueField["m_TextureFormat"].AsInt) && FieldCopier.ReadBytes(assetTypeValueField["image data"]).Length != 0) { rgbaImage = TextureCodec.Decode(assetTypeValueField); } } catch (NotSupportedException) { } _textures[pathId] = rgbaImage; return rgbaImage; } private void Rasterize(RgbaImage image, double[] depth, double[] sx, double[] sy, double[] sz, double[] nx, double[] ny, double[] nz, double[] u, double[] v, int i0, int i1, int i2, Shading s, (double x, double y, double z) toLight) { int num = Math.Max(0, (int)Math.Floor(Math.Min(sx[i0], Math.Min(sx[i1], sx[i2])))); int num2 = Math.Min(255, (int)Math.Ceiling(Math.Max(sx[i0], Math.Max(sx[i1], sx[i2])))); int num3 = Math.Max(0, (int)Math.Floor(Math.Min(sy[i0], Math.Min(sy[i1], sy[i2])))); int num4 = Math.Min(255, (int)Math.Ceiling(Math.Max(sy[i0], Math.Max(sy[i1], sy[i2])))); double num5 = (sx[i1] - sx[i0]) * (sy[i2] - sy[i0]) - (sx[i2] - sx[i0]) * (sy[i1] - sy[i0]); if (Math.Abs(num5) < 1E-12) { return; } double num6 = 1.0 / sz[i0]; double num7 = 1.0 / sz[i1]; double num8 = 1.0 / sz[i2]; for (int j = num3; j <= num4; j++) { for (int k = num; k <= num2; k++) { double num9 = (double)k + 0.5; double num10 = (double)j + 0.5; double num11 = ((sx[i1] - num9) * (sy[i2] - num10) - (sx[i2] - num9) * (sy[i1] - num10)) / num5; double num12 = ((sx[i2] - num9) * (sy[i0] - num10) - (sx[i0] - num9) * (sy[i2] - num10)) / num5; double num13 = 1.0 - num11 - num12; if (num11 < 0.0 || num12 < 0.0 || num13 < 0.0) { continue; } double num14 = num11 * num6 + num12 * num7 + num13 * num8; double num15 = 1.0 / num14; int num16 = j * 256 + k; if (num15 >= depth[num16]) { continue; } double num17 = num11 * num6 / num14; double num18 = num12 * num7 / num14; double num19 = num13 * num8 / num14; double num20 = num17 * u[i0] + num18 * u[i1] + num19 * u[i2]; double num21 = num17 * v[i0] + num18 * v[i1] + num19 * v[i2]; double num22 = s.R; double num23 = s.G; double num24 = s.B; double num25 = 1.0; if (s.Texture != null) { Sample(s.Texture, num20 * s.ScaleU + s.OffsetU, num21 * s.ScaleV + s.OffsetV, out var r, out var g, out var b, out var a); num22 *= r; num23 *= g; num24 *= b; num25 = a; } if (!(s.Cutoff >= 0.0) || !(num25 < s.Cutoff)) { (double, double, double) a2 = Normalize(num17 * nx[i0] + num18 * nx[i1] + num19 * nx[i2], num17 * ny[i0] + num18 * ny[i1] + num19 * ny[i2], num17 * nz[i0] + num18 * nz[i1] + num19 * nz[i2]); double num26 = Math.Max(0.0, Dot(a2, toLight)); double num27 = Ambient + Light * num26; double num28 = Ambient + Light * num26; double num29 = AmbientBlue + Light * num26; double num30 = Specular * Math.Pow(num26, 8.0) * Light; double num31 = s.ER; double num32 = s.EG; double num33 = s.EB; if (s.Emission != null) { Sample(s.Emission, num20, num21, out var r2, out var g2, out var b2, out var _); num31 *= r2; num32 *= g2; num33 *= b2; } depth[num16] = num15; int num34 = image.Index(k, j); image.Pixels[num34] = (float)Math.Min(1.0, num22 * num27 + num30 + num31); image.Pixels[num34 + 1] = (float)Math.Min(1.0, num23 * num28 + num30 + num32); image.Pixels[num34 + 2] = (float)Math.Min(1.0, num24 * num29 + num30 + num33); image.Pixels[num34 + 3] = 1f; } } } } private static void Sample(RgbaImage tex, double u, double v, out double r, out double g, out double b, out double a) { double num = (u - Math.Floor(u)) * (double)tex.Width - 0.5; double num2 = (1.0 - (v - Math.Floor(v))) * (double)tex.Height - 0.5; int num3 = (int)Math.Floor(num); int num4 = (int)Math.Floor(num2); double num5 = num - (double)num3; double num6 = num2 - (double)num4; r = (g = (b = (a = 0.0))); for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { int x = ((num3 + j) % tex.Width + tex.Width) % tex.Width; int y = ((num4 + i) % tex.Height + tex.Height) % tex.Height; double num7 = ((j == 0) ? (1.0 - num5) : num5) * ((i == 0) ? (1.0 - num6) : num6); int num8 = tex.Index(x, y); r += (double)tex.Pixels[num8] * num7; g += (double)tex.Pixels[num8 + 1] * num7; b += (double)tex.Pixels[num8 + 2] * num7; a += (double)tex.Pixels[num8 + 3] * num7; } } } private static int Index(byte[] buffer, int i, bool wide) { if (!wide) { return BitConverter.ToUInt16(buffer, i * 2); } return BitConverter.ToInt32(buffer, i * 4); } private static (double x, double y, double z) Normalize(double x, double y, double z) { double num = Math.Sqrt(x * x + y * y + z * z); if (!(num < 1E-12)) { return (x: x / num, y: y / num, z: z / num); } return (x: 0.0, y: 0.0, z: 0.0); } private static (double x, double y, double z) Normalize((double x, double y, double z) a) { return Normalize(a.x, a.y, a.z); } private static (double x, double y, double z) Cross((double x, double y, double z) a, (double x, double y, double z) b) { return (x: a.y * b.z - a.z * b.y, y: a.z * b.x - a.x * b.z, z: a.x * b.y - a.y * b.x); } private static double Dot((double x, double y, double z) a, (double x, double y, double z) b) { return a.x * b.x + a.y * b.y + a.z * b.z; } } public sealed class PortraitRenderer { public double TargetY = 1.4; public double Distance = 1.45; public double Yaw = 32.0; public double Pitch = -8.0; public double Fov = 28.0; public (double x, double y, double z) Light = (x: 0.45, y: -0.55, z: -0.7); public string[] SkipMaterials = new string[1] { "V1Wing" }; public RgbaImage Render(SceneReader scene, long rootGameObject, RgbaImage plate, RgbaImage border) { PickupIconRenderer obj = new PickupIconRenderer(scene) { Ambient = 0.35, AmbientBlue = 0.38, Light = 0.95, Specular = 0.3, SkipMaterials = SkipMaterials }; double num = Yaw * Math.PI / 180.0; double num2 = Pitch * Math.PI / 180.0; RgbaImage model = obj.RenderDrawables(target: (x: 0.0, y: TargetY, z: 0.0), eye: (x: Math.Sin(num) * Math.Cos(num2) * Distance, y: TargetY + Math.Sin(num2) * Distance, z: Math.Cos(num) * Math.Cos(num2) * Distance), drawables: scene.Drawables(rootGameObject, includeSkinned: true), fov: Fov, lightForward: Light); return Compose(plate, model, border); } public static RgbaImage Compose(RgbaImage plate, RgbaImage model, RgbaImage border) { RgbaImage rgbaImage = ((plate != null) ? plate.Clone() : RgbaImage.Solid(model.Width, model.Height, 0.06f, 0.07f, 0.1f, 1f)); Mask mask = Mask.AlphaOf(rgbaImage); model = model.Clone(); for (int i = 0; i < mask.Values.Length; i++) { model.Pixels[i * 4 + 3] *= mask.Values[i]; } Mask mask2 = ImageOps.GaussianBlur(ImageOps.MaxFilter(Mask.AlphaOf(model), 9), 6.0); for (int j = 0; j < mask.Values.Length; j++) { mask2.Values[j] *= mask.Values[j]; } ImageOps.AlphaComposite(rgbaImage, ImageOps.Tinted(mask2, 0f, 0f, 0f, 150)); ImageOps.AlphaComposite(rgbaImage, model); if (border != null) { ImageOps.AlphaComposite(rgbaImage, border); } return rgbaImage; } } public struct Mat4 { public double[] M; public double this[int row, int col] { get { return M[col * 4 + row]; } set { M[col * 4 + row] = value; } } public static Mat4 Identity() { Mat4 result = new Mat4 { M = new double[16] }; result.M[0] = (result.M[5] = (result.M[10] = (result.M[15] = 1.0))); return result; } public static Mat4 FromColumnMajor(IList<double> values) { Mat4 result = new Mat4 { M = new double[16] }; for (int i = 0; i < 16; i++) { result.M[i] = values[i]; } return result; } public static Mat4 operator *(Mat4 a, Mat4 b) { Mat4 result = new Mat4 { M = new double[16] }; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { double num = 0.0; for (int k = 0; k < 4; k++) { num += a[i, k] * b[k, j]; } result[i, j] = num; } } return result; } public static Mat4 TRS(double px, double py, double pz, double qx, double qy, double qz, double qw, double sx, double sy, double sz) { Mat4 result = Identity(); double num = qx * qx; double num2 = qy * qy; double num3 = qz * qz; double num4 = qx * qy; double num5 = qx * qz; double num6 = qy * qz; double num7 = qw * qx; double num8 = qw * qy; double num9 = qw * qz; result[0, 0] = (1.0 - 2.0 * (num2 + num3)) * sx; result[0, 1] = 2.0 * (num4 - num9) * sy; result[0, 2] = 2.0 * (num5 + num8) * sz; result[1, 0] = 2.0 * (num4 + num9) * sx; result[1, 1] = (1.0 - 2.0 * (num + num3)) * sy; result[1, 2] = 2.0 * (num6 - num7) * sz; result[2, 0] = 2.0 * (num5 - num8) * sx; result[2, 1] = 2.0 * (num6 + num7) * sy; result[2, 2] = (1.0 - 2.0 * (num + num2)) * sz; result[0, 3] = px; result[1, 3] = py; result[2, 3] = pz; return result; } public (double x, double y, double z) Point(double x, double y, double z) { return (x: this[0, 0] * x + this[0, 1] * y + this[0, 2] * z + this[0, 3], y: this[1, 0] * x + this[1, 1] * y + this[1, 2] * z + this[1, 3], z: this[2, 0] * x + this[2, 1] * y + this[2, 2] * z + this[2, 3]); } public (double x, double y, double z) Vector(double x, double y, double z) { return (x: this[0, 0] * x + this[0, 1] * y + this[0, 2] * z, y: this[1, 0] * x + this[1, 1] * y + this[1, 2] * z, z: this[2, 0] * x + this[2, 1] * y + this[2, 2] * z); } public double Determinant3() { return this[0, 0] * (this[1, 1] * this[2, 2] - this[1, 2] * this[2, 1]) - this[0, 1] * (this[1, 0] * this[2, 2] - this[1, 2] * this[2, 0]) + this[0, 2] * (this[1, 0] * this[2, 1] - this[1, 1] * this[2, 0]); } } public sealed class SceneReader { public struct Drawable { public Mat4 World; public AssetTypeValueField Mesh; public List<AssetTypeValueField> Materials; } private readonly AssetsManager _manager; private readonly AssetsFileInstance _file; private readonly IDictionary<long, AssetTypeValueField> _overrides; private bool _includeSkinned; public SceneReader(AssetsManager manager, AssetsFileInstance file, IDictionary<long, AssetTypeValueField> overrides) { _manager = manager; _file = file; _overrides = overrides; } public AssetTypeValueField Get(long pathId) { if (pathId == 0L) { return null; } if (_overrides != null && _overrides.TryGetValue(pathId, out var value)) { return value; } AssetFileInfo assetInfo = _file.file.GetAssetInfo(pathId); if (assetInfo != null) { return _manager.GetBaseField(_file, assetInfo); } return null; } public int TypeOf(long pathId) { return _file.file.GetAssetInfo(pathId)?.TypeId ?? (-1); } public static long PathOf(AssetTypeValueField pptr) { if (pptr != null && !pptr.IsDummy && pptr["m_FileID"].AsInt == 0) { return pptr["m_PathID"].AsLong; } return 0L; } public long FindRoot(string name) { foreach (AssetFileInfo item in _file.file.GetAssetsOfType(AssetClassID.GameObject)) { AssetTypeValueField assetTypeValueField = Get(item.PathId); if (!(assetTypeValueField["m_Name"].AsString != name)) { long num = Component(assetTypeValueField, 4); if (num != 0L && PathOf(Get(num)["m_Father"]) == 0L) { return item.PathId; } } } return 0L; } public long Component(AssetTypeValueField gameObject, int typeId) { foreach (AssetTypeValueField child in gameObject["m_Component"]["Array"].Children) { long num = PathOf(child["component"]); if (num != 0L && TypeOf(num) == typeId) { return num; } } return 0L; } public List<Drawable> Drawables(long rootGameObject, bool includeSkinned = false) { List<Drawable> result = new List<Drawable>(); AssetTypeValueField gameObject = Get(rootGameObject); _includeSkinned = includeSkinned; Walk(Component(gameObject, 4), Mat4.Identity(), result); return result; } private void Walk(long transformId, Mat4 parent, List<Drawable> result) { AssetTypeValueField assetTypeValueField = Get(transformId); AssetTypeValueField assetTypeValueField2 = Get(PathOf(assetTypeValueField["m_GameObject"])); if (assetTypeValueField2 == null || !assetTypeValueField2["m_IsActive"].AsBool) { return; } AssetTypeValueField assetTypeValueField3 = assetTypeValueField["m_LocalPosition"]; AssetTypeValueField assetTypeValueField4 = assetTypeValueField["m_LocalRotation"]; AssetTypeValueField assetTypeValueField5 = assetTypeValueField["m_LocalScale"]; Mat4 mat = parent * Mat4.TRS(assetTypeValueField3["x"].AsFloat, assetTypeValueField3["y"].AsFloat, assetTypeValueField3["z"].AsFloat, assetTypeValueField4["x"].AsFloat, assetTypeValueField4["y"].AsFloat, assetTypeValueField4["z"].AsFloat, assetTypeValueField4["w"].AsFloat, assetTypeValueField5["x"].AsFloat, assetTypeValueField5["y"].AsFloat, assetTypeValueField5["z"].AsFloat); long num = Component(assetTypeValueField2, 23); long num2 = Component(assetTypeValueField2, 33); if (num != 0L && num2 != 0L) { AssetTypeValueField assetTypeValueField6 = Get(num); AssetTypeValueField assetTypeValueField7 = Get(PathOf(Get(num2)["m_Mesh"])); if (assetTypeValueField6["m_Enabled"].AsBool && assetTypeValueField7 != null) { List<AssetTypeValueField> list = new List<AssetTypeValueField>(); foreach (AssetTypeValueField child in assetTypeValueField6["m_Materials"]["Array"].Children) { list.Add(Get(PathOf(child))); } result.Add(new Drawable { World = mat, Mesh = assetTypeValueField7, Materials = list }); } } long num3 = (_includeSkinned ? Component(assetTypeValueField2, 137) : 0); if (num3 != 0L) { AssetTypeValueField assetTypeValueField8 = Get(num3); AssetTypeValueField assetTypeValueField9 = Get(PathOf(assetTypeValueField8["m_Mesh"])); if (assetTypeValueField8["m_Enabled"].AsBool && assetTypeValueField9 != null) { List<AssetTypeValueField> list2 = new List<AssetTypeValueField>(); foreach (AssetTypeValueField child2 in assetTypeValueField8["m_Materials"]["Array"].Children) { list2.Add(Get(PathOf(child2))); } result.Add(new Drawable { World = mat, Mesh = assetTypeValueField9, Materials = list2 }); } } foreach (AssetTypeValueField child3 in assetTypeValueField["m_Children"]["Array"].Children) { Walk(PathOf(child3), mat, result); } } public static AssetTypeValueField Property(AssetTypeValueField material, string list, string name) { if (material == null) { return null; } foreach (AssetTypeValueField child in material["m_SavedProperties"][list]["Array"].Children) { if (child["first"].AsString == name) { return child["second"]; } } return null; } public bool HasKeyword(AssetTypeValueField material, string keyword) { if (material == null) { return false; } AssetTypeValueField assetTypeValueField = material["m_ShaderKeywords"]; if (!assetTypeValueField.IsDummy && assetTypeValueField.Value != null) { return (" " + assetTypeValueField.AsString + " ").Contains(" " + keyword + " "); } AssetTypeValueField assetTypeValueField2 = material["m_ValidKeywords"]; if (!assetTypeValueField2.IsDummy) { foreach (AssetTypeValueField child in assetTypeValueField2["Array"].Children) { if (child.AsString == keyword) { return true; } } } return false; } public string ShaderName(AssetTypeValueField material) { AssetTypeValueField assetTypeValueField = ((material == null) ? null : Get(PathOf(material["m_Shader"]))); if (assetTypeValueField == null) { return ""; } AssetTypeValueField assetTypeValueField2 = assetTypeValueField["m_ParsedForm"]; if (!assetTypeValueField2.IsDummy) { return assetTypeValueField2["m_Name"].AsString; } return ""; } } } namespace UltrakillV1.Setup.Pipeline { public sealed class BuildProgress { public Action<double, string> Report = delegate { }; public Action<string> Log = delegate { }; public CancellationToken Cancel = CancellationToken.None; } public sealed class BuildResult { public bool Success; public string ErrorKey; public string ErrorDetail; public readonly List<string> Warnings = new List<string>(); public int ChangedSources; public int SkippedOptional; public string GameUnityVersion; } public sealed class SetupException : Exception { public readonly string Key; public SetupException(string key, string message, Exception inner = null) : base(message, inner) { Key = key; } } public sealed class AssetBuilder { private sealed class Resolved { public string Bundle; public AssetFileInfo Info; public AssetTypeValueField Field; } private Recipe _recipe; private SourceBundles _sources; private AssetsManager _manager; private AssetsFileInstance _template; private readonly Dictionary<long, AssetTypeValueField> _overrides = new Dictionary<long, AssetTypeValueField>(); private readonly Dictionary<RecipeStep, Resolved> _resolved = new Dictionary<RecipeStep, Resolved>(); private readonly Dictionary<RecipeStep, Resolved> _resolvedGlow = new Dictionary<RecipeStep, Resolved>(); private BuildProgress _progress; private BuildResult _result; public BuildResult Build(Recipe recipe, string gameFolder, string outputPath, BuildProgress progress) { _recipe = recipe; _progress = progress ?? new BuildProgress(); _result = new BuildResult(); try { Run(gameFolder, outputPath); _result.Success = true; } catch (OperationCanceledException) { _result.ErrorKey = "setup.error.cancelled"; _result.ErrorDetail = "Cancelled"; } catch (SetupException ex2) { _result.ErrorKey = ex2.Key; _result.ErrorDetail = ex2.Message + ((ex2.InnerException != null) ? (" (" + ex2.InnerException.Message + ")") : ""); _progress.Log("ERROR " + _result.ErrorDetail + ((ex2.InnerException != null) ? ("\n" + ex2.InnerException) : "")); } catch (Exception ex3) { _result.ErrorKey = "setup.error.unexpected"; _result.ErrorDetail = ex3.GetType().Name + ": " + ex3.Message; _progress.Log("ERROR " + ex3); } finally { _sources?.Dispose(); _manager?.UnloadAll(unloadClassData: true); _overrides.Clear(); } return _result; } private void Run(string gameFolder, string outputPath) { _progress.Report(0.01, "stage.check"); string text = _recipe.PathOf(_recipe.TemplateFile); if (!File.Exists(text)) { throw new SetupException("setup.error.template", "Template bundle missing: " + text); } string text2 = SourceBundles.Sha256(File.ReadAllBytes(text)); if (!string.IsNullOrEmpty(_recipe.TemplateSha256) && text2 != _recipe.TemplateSha256) { throw new SetupException("setup.error.template", "Template bundle damaged (checksum mismatch)"); } string text3 = SourceBundles.AddressablesFolder(gameFolder); if (!Directory.Exists(text3)) { throw new SetupException("setup.error.gamefiles", "ULTRAKILL data folder not found: " + text3); } _sources = new SourceBundles(text3); foreach (KeyValuePair<string, (string, string)> bundle in _recipe.Bundles) { if (!File.Exists(_sources.BundlePath(bundle.Value.Item1))) { throw new SetupException("setup.error.gamefiles", "ULTRAKILL file missing: " + bundle.Value.Item1); } } try { _result.GameUnityVersion = _sources.UnityVersion(_recipe.Bundles.Values.First().path); } catch (Exception inner) { throw new SetupException("setup.error.gamefiles", "Cannot read ULTRAKILL's files", inner); } if (!string.IsNullOrEmpty(_recipe.GameUnityVersion) && _result.GameUnityVersion != _recipe.GameUnityVersion) { _result.Warnings.Add("ULTRAKILL uses Unity " + _result.GameUnityVersion + ", the recipe was made with " + _recipe.GameUnityVersion); } _manager = new AssetsManager(); BundleFileInstance bundleFileInstance = _manager.LoadBundleFile(text); _template = _manager.LoadAssetsFileFromBundle(bundleFileInstance, 0); ResolveAll(); ReferenceMap referenceMap = new ReferenceMap(); foreach (KeyValuePair<RecipeStep, Resolved> item2 in _resolved) { if (!(item2.Key.Op != "copy")) { referenceMap.Add(_sources.CabName(item2.Value.Bundle), item2.Value.Info.PathId, item2.Key.Target); string item = _recipe.Bundles[item2.Key.Source.Bundle].cab; if (!string.IsNullOrEmpty(item)) { referenceMap.Add(item, item2.Key.Source.PathId, item2.Key.Target); } } } Dictionary<(string type, string name, string sig), long> byDescription = new Dictionary<(string, string, string), long>(); foreach (RecipeStep step in _recipe.Steps) { if (step.Op == "copy" && step.Source != null) { byDescription[(step.Source.Type, step.Source.Name, step.Source.Signature)] = step.Target; } } referenceMap.Fallback = delegate(string cab, long pathId) { foreach (var value2 in _recipe.Bundles.Values) { if (string.Equals(_sources.CabName(value2.path), cab, StringComparison.OrdinalIgnoreCase)) { AssetFileInfo assetFileInfo = _sources.Info(value2.path, pathId); if (assetFileInfo == null) { return (long?)null; } AssetClassID typeId = (AssetClassID)assetFileInfo.TypeId; AssetTypeValueField assetTypeValueField = _sources.Read(value2.path, assetFileInfo); long value; return byDescription.TryGetValue((typeId.ToString(), assetTypeValueField["m_Name"].AsString, Signatures.Of(typeId, assetTypeValueField)), out value) ? new long?(value) : ((long?)null); } } return (long?)null; }; int num = 0; foreach (RecipeStep step2 in _recipe.Steps) { _progress.Cancel.ThrowIfCancellationRequested(); _progress.Report(0.15 + 0.75 * (double)num++ / (double)_recipe.Steps.Count, "stage." + StageOf(step2.Op)); try { Execute(step2, referenceMap); } catch (Exception ex) when (step2.Optional && !(ex is OperationCanceledException)) { _result.SkippedOptional++; _result.Warnings.Add(step2.Op + " " + step2.Label + " skipped: " + ex.Message); _progress.Log($"WARN optional {step2.Op} {step2.Label} failed: {ex}"); } catch (Exception ex2) when (!(ex2 is SetupException) && !(ex2 is OperationCanceledException)) { throw new SetupException("setup.error.convert", step2.Op + " " + step2.Label + " failed", ex2); } } _progress.Report(0.92, "stage.write"); foreach (KeyValuePair<long, AssetTypeValueField> @override in _overrides) { _template.file.GetAssetInfo(@override.Key).SetNewData(@override.Value); } bundleFileInstance.file.BlockAndDirInfo.DirectoryInfos[0].SetNewData(_template.file); string text4 = outputPath + ".raw"; try { Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(outputPath))); using (AssetsFileWriter writer = new AssetsFileWriter(text4)) { bundleFileInstance.file.Write(writer, 0L); } AssetBundleFile assetBundleFile = new AssetBundleFile(); using AssetsFileReader reader = new AssetsFileReader(File.OpenRead(text4)); assetBundleFile.Read(reader); using (AssetsFileWriter writer2 = new AssetsFileWriter(outputPath)) { assetBundleFile.Pack(writer2, AssetBundleCompressionType.LZ4, blockDirAtEnd: false); } assetBundleFile.Close(); } catch (IOException ex3) { throw new SetupException("setup.error.write", "Cannot write the generated bundle: " + ex3.Message, ex3); } finally { try { File.Delete(text4); } catch (IOException) { } } _progress.Log($"Wrote {outputPath} ({new FileInfo(outputPath).Length / 1024} KB); {_result.ChangedSources} source objects differ from the recipe's ULTRAKILL version, {_result.SkippedOptional} optional steps skipped"); } private static string StageOf(string op) { return op switch { "copy" => "copy", "sound" => "sound", "bake" => "mesh", _ => "image", }; } private void ResolveAll() { int num = 0; foreach (RecipeStep step in _recipe.Steps) { _progress.Cancel.ThrowIfCancellationRequested(); if (num++ % 16 == 0) { _progress.Report(0.02 + 0.13 * (double)num / (double)_recipe.Steps.Count, "stage.read"); } try { if (step.Source != null) { _resolved[step] = Resolve(step.Source, step.Op == "copy" || step.Op == "sound"); } if (step.Glow != null) { _resolvedGlow[step] = Resolve(step.Glow, withResource: false); } } catch (SetupException) when (step.Optional) { } } } private Resolved Resolve(SourceRef source, bool withResource) { if (!_recipe.Bundles.TryGetValue(source.Bundle, out (string, string) value)) { throw new SetupException("setup.error.recipe", "Unknown bundle key " + source.Bundle); } AssetClassID assetClassID = (AssetClassID)Enum.Parse(typeof(AssetClassID), source.Type); AssetFileInfo assetFileInfo; try { assetFileInfo = _sources.Info(value.Item1, source.PathId); } catch (Exception inner) { throw new SetupException("setup.error.gamefiles", "Cannot read " + value.Item1, inner); } AssetTypeValueField assetTypeValueField = null; if (assetFileInfo != null && assetFileInfo.TypeId == (int)assetClassID) { assetTypeValueField = _sources.Read(value.Item1, assetFileInfo); if (Signatures.Of(assetClassID, assetTypeValueField) != source.Signature || assetTypeValueField["m_Name"].AsString != source.Name) { assetTypeValueField = null; } } if (assetTypeValueField == null) { foreach (AssetFileInfo item in _sources.File0(value.Item1).file.GetAssetsOfType(assetClassID)) { AssetTypeValueField assetTypeValueField2 = _sources.Read(value.Item1, item); if (assetTypeValueField2["m_Name"].AsString == source.Name && Signatures.Of(assetClassID, assetTypeValueField2) == source.Signature) { assetFileInfo = item; assetTypeValueField = assetTypeValueField2; _result.Warnings.Add($"{source} found at path id {item.PathId}"); break; } } if (assetTypeValueField == null) { throw new SetupException("setup.error.version", $"{source} not found in your ULTRAKILL files (different game version?)"); } } if (!string.IsNullOrEmpty(source.Sha256) && ContentHash(value.Item1, assetFileInfo, assetTypeValueField, withResource) != source.Sha256) { _result.ChangedSources++; _progress.Log($"NOTE {source}: content differs from the recipe's ULTRAKILL version (structure matches, using it)"); } return new Resolved { Bundle = value.Item1, Info = assetFileInfo, Field = assetTypeValueField }; } public static string ContentHash(SourceBundles sources, string bundle, AssetFileInfo info, AssetTypeValueField field, bool withResource) { byte[] array = sources.RawBytes(bundle, info); byte[] array2 = Array.Empty<byte>(); if (withResource) { AssetTypeValueField assetTypeValueField = field["m_StreamData"]; AssetTypeValueField assetTypeValueField2 = field["m_Resource"]; if (!assetTypeValueField.IsDummy && assetTypeValueField["size"].AsULong != 0) { array2 = sources.ReadResource(bundle, assetTypeValueField["path"].AsString, (long)assetTypeValueField["offset"].AsULong, (long)assetTypeValueField["size"].AsULong); } else if (!assetTypeValueField2.IsDummy && assetTypeValueField2["m_Size"].AsULong != 0) { array2 = sources.ReadResource(bundle, assetTypeValueField2["m_Source"].AsString, (long)assetTypeValueField2["m_Offset"].AsULong, (long)assetTypeValueField2["m_Size"].AsULong); } } return SourceBundles.Sha256(array, array2); } private string ContentHash(string bundle, AssetFileInfo info, AssetTypeValueField field, bool withResource) { return ContentHash(_sources, bundle, info, field, withResource); } private Resolved Get(RecipeStep step) { if (!_resolved.TryGetValue(step, out var value)) { throw new SetupException("setup.error.version", "Source of " + step.Label + " not found"); } return value; } private AssetTypeValueField Target(long pathId) { if (_overrides.TryGetValue(pathId, out var value)) { return value; } AssetFileInfo assetInfo = _template.file.GetAssetInfo(pathId); if (assetInfo == null) { throw new SetupException("setup.error.template", "Template object " + pathId + " missing"); } value = _manager.GetBaseField(_template, assetInfo); _overrides[pathId] = value; return value; } private AssetClassID TargetType(long pathId) { return (AssetClassID)_template.file.GetAssetInfo(pathId).TypeId; } private void Execute(RecipeStep step, ReferenceMap references) { switch (step.Op) { case "copy": Copy(step, references); break; case "sound": Sound(step); break; case "emissiveMask": EmissiveMask(step); break; case "skillIcon": SkillIcon(step); break; case "portrait": Portrait(step); break; case "portrait3d": Portrait3D(step); break; case "bake": Bake(step); break; case "pickupIcon": PickupIcon(step); break; default: throw new SetupException("setup.error.recipe", "Unknown recipe step " + step.Op); } } private void Copy(RecipeStep step, ReferenceMap references) { Resolved source = Get(step); AssetClassID type = TargetType(step.Target); AssetTypeValueField target = Target(step.Target); FieldCopier fieldCopier = new FieldCopier { Keep = CopyRules.KeepFor(type), RemapPPtr = references.For(_sources.File0(source.Bundle).file, _sources.CabName(source.Bundle)) }; fieldCopier.Copy(target, source.Field); if (fieldCopier.UnresolvedReferences > 0) { _result.Warnings.Add($"{step.Label}: {fieldCopier.UnresolvedReferences} reference(s) to ULTRAKILL objects the mod doesn't use"); } CopyRules.InlineStreamData(type, target, source.Field, (string path, long offset, long size) => _sources.ReadResource(source.Bundle, path, offset, size)); CopyRules.Downgrade(type, target); } private void Sound(RecipeStep step) { Resolved resolved = Get(step); AssetTypeValueField assetTypeValueField = resolved.Field["m_Resource"]; string value2; byte[] value = FsbVorbisDecoder.DecodeToWav(_sources.ReadResource(resolved.Bundle, assetTypeValueField["m_Source"].AsString, (long)assetTypeValueField["m_Offset"].AsULong, (long)assetTypeValueField["m_Size"].AsULong), (uint crc) => (!_recipe.VorbisSetups.TryGetValue(crc, out value2)) ? null : File.ReadAllBytes(_recipe.PathOf(value2))); Target(step.Target)["m_Script"].Value = new AssetTypeValue(value, asString: true); } private void EmissiveMask(RecipeStep step) { RgbaImage rgbaImage = TextureCodec.Decode(Target(step.Id("from"))); RgbaImage rgbaImage2 = new RgbaImage(rgbaImage.Width, rgbaImage.Height); for (int i = 0; i < rgbaImage.Pixels.Length; i += 4) { int num = RgbaImage.ToByte(rgbaImage.Pixels[i]); int num2 = RgbaImage.ToByte(rgbaImage.Pixels[i + 1]); int num3 = RgbaImage.ToByte(rgbaImage.Pixels[i + 2]); bool flag = num > 180 && num2 > 120 && num3 < 110 && num - num3 > 110; rgbaImage2.Pixels[i] = (flag ? rgbaImage.Pixels[i] : 0f); rgbaImage2.Pixels[i + 1] = (flag ? rgbaImage.Pixels[i + 1] : 0f); rgbaImage2.Pixels[i + 2] = (flag ? rgbaImage.Pixels[i + 2] : 0f); rgbaImage2.Pixels[i + 3] = 1f; } TextureCodec.Write(Target(step.Target), rgbaImage2, withAlpha: false); } private RgbaImage SpriteImage(Resolved sprite) { AssetTypeValueField assetTypeValueField = sprite.Field["m_RD"]; AssetTypeValueField assetTypeValueField2 = assetTypeValueField["texture"]; if (assetTypeValueField2["m_FileID"].AsInt != 0) { throw new SetupException("setup.error.version", "Sprite texture in another file"); } AssetTypeValueField assetTypeValueField3 = _sources.Read(sprite.Bundle, assetTypeValueField2["m_PathID"].AsLong); if (assetTypeValueField3 == null) { throw new SetupException("setup.error.version", "Sprite texture missing"); } AssetTypeValueField assetTypeValueField4 = _sources.Read(sprite.Bundle, assetTypeValueField2["m_PathID"].AsLong); CopyRules.InlineStreamData(AssetClassID.Texture2D, assetTypeValueField4, assetTypeValueField3, (string path, long offset, long size) => _sources.ReadResource(sprite.Bundle, path, offset, size)); RgbaImage rgbaImage = TextureCodec.Decode(assetTypeValueField4); AssetTypeValueField assetTypeValueField5 = assetTypeValueField["textureRect"]; int x = (int)Math.Round(assetTypeValueField5["x"].AsFloat); int x2 = (int)Math.Round(assetTypeValueField5["x"].AsFloat + assetTypeValueField5["width"].AsFloat); int num = (int)Math.Round(assetTypeValueField5["y"].AsFloat); int num2 = (int)Math.Round(assetTypeValueField5["y"].AsFloat + assetTypeValueField5["height"].AsFloat); return ImageOps.Crop(rgbaImage, x, rgbaImage.Height - num2, x2, rgbaImage.Height - num); } private RgbaImage Layer(string file) { if (!string.IsNullOrEmpty(file)) { return RgbaImage.ReadPng(File.ReadAllBytes(_recipe.PathOf(file))); } return null; } private static Mask Fit(Mask alpha, double angle, double box) { Mask mask = ImageOps.Rotate(alpha, angle); (int, int, int, int)? tuple = ImageOps.BoundingBox(mask); if (tuple.HasValue) { mask = ImageOps.Crop(mask, tuple.Value.Item1, tuple.Value.Item2, tuple.Value.Item3, tuple.Value.Item4); } double num = box / (double)Math.Max(mask.Width, mask.Height); return ImageOps.Resize(mask, Math.Max(1, (int)((double)mask.Width * num)), Math.Max(1, (int)((double)mask.Height * num))); } private void SkillIcon(RecipeStep step) { float[] array = step.Color("color"); double angle = step.Num("angle", 32.0); double num = step.Num("box", 212.0); RgbaImage rgbaImage = Layer(step.Str("under")); Mask mask = Mask.AlphaOf(SpriteImage(Get(step))); Mask mask2 = Fit(mask, angle, num); int width = rgbaImage.Width; if (step.Glow != null) { if (!_resolvedGlow.TryGetValue(step, out var value)) { throw new SetupException("setup.error.version", "Glow sprite of " + step.Label + " not found"); } Mask mask3 = Mask.AlphaOf(SpriteImage(value)); Mask mask4 = Fit(mask3, angle, num * (double)Math.Max(mask3.Width, mask3.Height) / (double)Math.Max(mask.Width, mask.Height)); ImageOps.AlphaComposite(rgbaImage, ImageOps.Tinted(ImageOps.GaussianBlur(mask4, 3.0), array[0] / 255f, array[1] / 255f, array[2] / 255f), (width - mask4.Width) / 2, (width - mask4.Height) / 2); } int ox = (width - mask2.Width) / 2; int oy = (width - mask2.Height) / 2; ImageOps.AlphaComposite(rgbaImage, ImageOps.Tinted(ImageOps.MaxFilter(mask2, 7), 0f, 0f, 0f, 200), ox, oy); ImageOps.AlphaComposite(rgbaImage, ImageOps.Tinted(mask2, 1f, 1f, 1f), ox, oy); RgbaImage rgbaImage2 = Layer(step.Str("over")); if (rgbaImage2 != null) { ImageOps.AlphaComposite(rgbaImage, rgbaImage2); } TextureCodec.Write(Target(step.Target), rgbaImage); } private void Portrait(RecipeStep step) { RgbaImage rgbaImage = SpriteImage(Get(step)); int width = rgbaImage.Width; int height = rgbaImage.Height; RgbaImage src = ImageOps.Crop(rgbaImage, (int)((double)width * 0.25), (int)((double)height * 0.12), (int)((double)width * 0.75), (int)((double)height * 0.62)); src = ImageOps.Resize(src, 236, 236); RgbaImage rgbaImage2 = Layer(step.Str("under")); float[] array = step.Color("glow"); ImageOps.AlphaComposite(rgbaImage2, ImageOps.Tinted(ImageOps.GaussianBlur(Mask.AlphaOf(src), 8.0), array[0] / 255f, array[1] / 255f, array[2] / 255f, 160), 10, 14); ImageOps.AlphaComposite(rgbaImage2, src, 10, 14); RgbaImage rgbaImage3 = Layer(step.Str("over")); if (rgbaImage3 != null) { ImageOps.AlphaComposite(rgbaImage2, rgbaImage3); } TextureCodec.Write(Target(step.Target), rgbaImage2); } private void Portrait3D(RecipeStep step) { SceneReader sceneReader = new SceneReader(_manager, _template, _overrides); long num = sceneReader.FindRoot(step.Str("prefab")); if (num == 0L) { throw new SetupException("setup.error.template", "Prefab " + step.Str("prefab") + " missing from the template"); } RgbaImage image = new PortraitRenderer { TargetY = step.Num("targetY", 1.4), Distance = step.Num("distance", 1.45), Yaw = step.Num("yaw", 32.0), Pitch = step.Num("pitch", -8.0), Fov = step.Num("fov", 28.0) }.Render(sceneReader, num, Layer(step.Str("under")), Layer(step.Str("over"))); TextureCodec.Write(Target(step.Target), image); } private void Bake(RecipeStep step) { AssetTypeValueField assetTypeValueField = Target(step.Id("from")); AssetTypeValueField assetTypeValueField2 = Target(step.Target); List<Mat4> list = new List<Mat4>(); foreach (object item in MiniJson.Arr(step.Args, "bones")) { list.Add(Mat4.FromColumnMajor(((List<object>)item).Select((object v) => (double)v).ToList())); } MeshVertexData meshVertexData = new MeshVertexData(assetTypeValueField["m_VertexData"]); MeshVertexData meshVertexData2 = new MeshVertexData(assetTypeValueField2["m_VertexData"]); if (meshVertexData.VertexCount != meshVertexData2.VertexCount) { throw new SetupException("setup.error.version", $"Mesh {step.Label}: {meshVertexData.VertexCount} vertices, expected {meshVertexData2.VertexCount}"); } meshVertexData2.Data = new byte[meshVertexData2.RequiredSize]; int num = meshVertexData.Dimension(13); int num2 = meshVertexData.Dimension(12); double val = double.MaxValue; double val2 = double.MaxValue; double val3 = double.MaxValue; double val4 = double.MinValue; double val5 = double.MinValue; double val6 = double.MinValue; (double, double, double)[] array = new(double, double, double)[meshVertexData.VertexCount]; for (int num3 = 0; num3 < meshVertexData.VertexCount; num3++) { double[] array2 = new double[16]; double num4 = 0.0; for (int num5 = 0; num5 < Math.Max(num, 1); num5++) { int num6 = ((num > 0) ? ((int)meshVertexData.Get(13, num3, num5)) : 0); double num7 = ((num2 > num5) ? meshVertexData.Get(12, num3, num5) : ((float)((num5 == 0 && num2 == 0) ? 1 : 0))); if (num5 == num - 1 && num2 > 0 && num2 < num) { num7 = 1.0 - num4; } if (num7 != 0.0 && num6 < list.Count) { num4 += num7; for (int num8 = 0; num8 < 16; num8++) { array2[num8] += list[num6].M[num8] * num7; } } } Mat4 mat = new Mat4 { M = array2 }; for (int num9 = 0; num9 < meshVertexData2.Channels.Length; num9++) { if (!meshVertexData2.Has(num9) || !meshVertexData.Has(num9)) { continue; } int num10 = Math.Min(meshVertexData2.Dimension(num9), meshVertexData.Dimension(num9)); switch (num9) { case 0: { (double, double, double) tuple = (array[num3] = mat.Point(meshVertexData.Get(0, num3, 0), meshVertexData.Get(0, num3, 1), meshVertexData.Get(0, num3, 2))); meshVertexData2.Set(0, num3, 0, (float)tuple.Item1); meshVertexData2.Set(0, num3, 1, (float)tuple.Item2); meshVertexData2.Set(0, num3, 2, (float)tuple.Item3); val = Math.Min(val, tuple.Item1); val2 = Math.Min(val2, tuple.Item2); val3 = Math.Min(val3, tuple.Item3); val4 = Math.Max(val4, tuple.Item1); val5 = Math.Max(val5, tuple.Item2); val6 = Math.Max(val6, tuple.Item3); break; } case 1: case 2: { (double, double, double) tuple2 = mat.Vector(meshVertexData.Get(num9, num3, 0), meshVertexData.Get(num9, num3, 1), meshVertexData.Get(num9, num3, 2)); double num12 = Math.Sqrt(tuple2.Item1 * tuple2.Item1 + tuple2.Item2 * tuple2.Item2 + tuple2.Item3 * tuple2.Item3); if (num12 > 1E-12) { tuple2 = (tuple2.Item1 / num12, tuple2.Item2 / num12, tuple2.Item3 / num12); } meshVertexData2.Set(num9, num3, 0, (float)tuple2.Item1); meshVertexData2.Set(num9, num3, 1, (float)tuple2.Item2); meshVertexData2.Set(num9, num3, 2, (float)tuple2.Item3); if (num10 > 3) { meshVertexData2.Set(num9, num3, 3, meshVertexData.Get(num9, num3, 3)); } break; } default: { for (int num11 = 0; num11 < num10; num11++) { meshVertexData2.Set(num9, num3, num11, meshVertexData.Get(num9, num3, num11)); } break; } } } } FieldCopier.WriteBytes(assetTypeValueField2["m_VertexData"]["m_DataSize"], meshVertexData2.Data); FieldCopier.WriteBytes(assetTypeValueField2["m_IndexBuffer"], FieldCopier.ReadBytes(assetTypeValueField["m_IndexBuffer"])); assetTypeValueField2["m_IndexFormat"].AsInt = assetTypeValueField["m_IndexFormat"].AsInt; new FieldCopier().Copy(assetTypeValueField2["m_SubMeshes"], assetTypeValueField["m_SubMeshes"]); foreach (AssetTypeValueField child in assetTypeValueField2["m_SubMeshes"]["Array"].Children) { SetBounds(first: (int)child["firstVertex"].AsUInt, count: (int)child["vertexCount"].AsUInt, aabb: child["localAABB"], p: array); } SetBounds(assetTypeValueField2["m_LocalAABB"], array, 0, array.Length); CopyRules.ClearStream(assetTypeValueField2); } private static void SetBounds(AssetTypeValueField aabb, (double x, double y, double z)[] p, int first, int count) { double num = double.MaxValue; double num2 = double.MaxValue; double num3 = double.MaxValue; double num4 = double.MinValue; double num5 = double.MinValue; double num6 = double.MinValue; for (int i = first; i < first + count && i < p.Length; i++) { num = Math.Min(num, p[i].x); num2 = Math.Min(num2, p[i].y); num3 = Math.Min(num3, p[i].z); num4 = Math.Max(num4, p[i].x); num5 = Math.Max(num5, p[i].y); num6 = Math.Max(num6, p[i].z); } if (count <= 0 || num > num4) { num = (num2 = (num3 = (num4 = (num5 = (num6 = 0.0))))); } aabb["m_Center"]["x"].AsFloat = (float)((num + num4) / 2.0); aabb["m_Center"]["y"].AsFloat = (float)((num2 + num5) / 2.0); aabb["m_Center"]["z"].AsFloat = (float)((num3 + num6) / 2.0); aabb["m_Extent"]["x"].AsFloat = (float)((num4 - num) / 2.0); aabb["m_Extent"]["y"].AsFloat = (float)((num5 - num2) / 2.0); aabb["m_Extent"]["z"].AsFloat = (float)((num6 - num3) / 2.0); } private void PickupIcon(RecipeStep step) { SceneReader sceneReader = new SceneReader(_manager, _template, _overrides); long num = sceneReader.FindRoot(step.Str("prefab")); if (num == 0L) { throw new SetupException("setup.error.template", "Pickup prefab " + step.Str("prefab") + " missing from the template"); } float[] array = step.Color("color"); RgbaImage image = new PickupIconRenderer(sceneReader).Render(num, array[0], array[1], array[2]); TextureCodec.Write(Target(step.Target), image); } } public sealed class AssetState { public const string FileName = "assets.json"; public string AssetVersion; public string RecipeSha256; public string BundleFile; public string BundleSha256; public long BundleSize; public string ModVersion; public string CreatedUtc; public string GameUnityVersion; public string GameBuildId; public int ChangedSources; public int SkippedOptional; public List<string> Warnings = new List<string>(); public static AssetState Load(string folder) { string path = Path.Combine(folder, "assets.json"); if (!File.Exists(path)) { return null; } try { object o = MiniJson.Parse(File.ReadAllText(path)); AssetState assetState = new AssetState { AssetVersion = MiniJson.Str(o, "assetVersion"), RecipeSha256 = MiniJson.Str(o, "recipeSha256"), BundleFile = MiniJson.Str(o, "bundleFile"), BundleSha256 = MiniJson.Str(o, "bundleSha256"), BundleSize = MiniJson.Long(o, "bundleSize"), ModVersion = MiniJson.Str(o, "modVersion"), CreatedUtc = MiniJson.Str(o, "createdUtc"), GameUnityVersion = MiniJson.Str(o, "gameUnityVersion"), GameBuildId = MiniJson.Str(o, "gameBuildId"), ChangedSources = (int)MiniJson.Num(o, "changedSources"), SkippedOptional = (int)MiniJson.Num(o, "skippedOptional") }; foreach (object item in MiniJson.Arr(o, "warnings") ?? new List<object>()) { assetState.Warnings.Add(Convert.ToString(item, CultureInfo.InvariantCulture)); } return assetState; } catch (Exception) { return null; } } public void Save(string folder) { Dictionary<string, object> value = new Dictionary<string, object> { ["assetVersion"] = AssetVersion, ["recipeSha256"] = RecipeSha256, ["bundleFile"] = BundleFile, ["bundleSha256"] = BundleSha256, ["bundleSize"] = BundleSize, ["modVersion"] = ModVersion, ["createdUtc"] = CreatedUtc, ["gameUnityVersion"] = GameUnityVersion, ["gameBuildId"] = GameBuildId, ["changedSources"] = ChangedSources, ["skippedOptional"] = SkippedOptional, ["warnings"] = Warnings }; AtomicFile.WriteAllText(Path.Combine(folder, "assets.json"), MiniJson.Write(value)); } } public enum AssetStatus { Ready, Missing, Outdated, Damaged, SetupFilesMissing } public sealed class AssetCheck { public AssetStatus Status; public string BundlePath; public AssetState State; public string Detail; } public static class AssetDetector { public static AssetCheck Check(string recipeFolder, string assetFolder, bool fullHash = true) { AssetCheck assetCheck = new AssetCheck(); string path = Path.Combine(recipeFolder ?? "", "recipe.json"); if (recipeFolder == null || !File.Exists(path)) { assetCheck.Status = AssetStatus.SetupFilesMissing; assetCheck.Detail = "recipe.json not found in " + recipeFolder; return assetCheck; } Recipe recipe; try { recipe = Recipe.Load(recipeFolder); } catch (Exception ex) { assetCheck.Status = AssetStatus.SetupFilesMissing; assetCheck.Detail = "recipe.json unreadable: " + ex.Message; return assetCheck; } assetCheck.BundlePath = Path.Combine(assetFolder, recipe.OutputFile); assetCheck.State = AssetState.Load(assetFolder); if (assetCheck.State == null) { assetCheck.Status = ((!File.Exists(assetCheck.BundlePath)) ? AssetStatus.Missing : AssetStatus.Damaged); assetCheck.Detail = "no assets.json"; return assetCheck; } if (assetCheck.State.AssetVersion != recipe.AssetVersion || assetCheck.State.RecipeSha256 != RecipeSha(recipeFolder)) { assetCheck.Status = AssetStatus.Outdated; assetCheck.Detail = "assets " + assetCheck.State.AssetVersion + ", mod needs " + recipe.AssetVersion; return assetCheck; } FileInfo fileInfo = new FileInfo(assetCheck.BundlePath); if (!fileInfo.Exists || fileInfo.Length != assetCheck.State.BundleSize) { assetCheck.Status = AssetStatus.Damaged; assetCheck.Detail = (fileInfo.Exists ? "bundle size changed" : "bundle missing"); return assetCheck; } if (fullHash && SourceBundles.Sha256(File.ReadAllBytes(assetCheck.BundlePath)) != assetCheck.State.BundleSha256) { assetCheck.Status = AssetStatus.Damaged; assetCheck.Detail = "bundle checksum changed"; return assetCheck; } assetCheck.Status = AssetStatus.Ready; return assetCheck; } public static string RecipeSha(string recipeFolder) { return SourceBundles.Sha256(File.ReadAllBytes(Path.Combine(recipeFolder, "recipe.json"))); } } public static class AssetInstaller { public static void Install(string builtFile, string assetFolder, AssetState state, Action<string> log) { string text = Path.Combine(assetFolder, state.BundleFile); string text2 = text + ".bak"; string text3 = Path.Combine(assetFolder, "assets.json"); string text4 = text3 + ".bak"; byte[] array = new byte[7]; using (FileStream fileStream = File.OpenRead(builtFile)) { if (fileStream.Length < 1024 || fileStream.Read(array, 0, 7) != 7 || Encoding.ASCII.GetString(array) != "UnityFS") { throw new SetupException("setup.error.write", "The generated bundle is not valid"); } } state.BundleSize = new FileInfo(builtFile).Length; state.BundleSha256 = SourceBundles.Sha256(File.ReadAllBytes(builtFile)); bool flag = File.Exists(text); bool flag2 = File.Exists(text3); try { if (flag) { Replace(text, text2); } if (flag2) { Replace(text3, text4); } File.Move(builtFile, text); state.Save(assetFolder); TryDelete(text2); TryDelete(text4); log?.Invoke("Installed " + text); } catch (Exception ex) { log?.Invoke("Install failed, restoring the previous assets: " + ex.Message); TryDelete(text); if (flag && File.Exists(text2)) { File.Move(text2, text); } TryDelete(text3); if (flag2 && File.Exists(text4)) { File.Move(text4, text3); } throw new SetupException("setup.error.write", "Cannot install the generated files: " + ex.Message, ex); } } public static void Recover(string assetFolder, string bundleFile, Action<string> log) { if (!Directory.Exists(assetFolder)) { return; } string text = Path.Combine(assetFolder, bundleFile); string text2 = Path.Combine(assetFolder, "assets.json"); if (!File.Exists(text) && File.Exists(text + ".bak")) { File.Move(text + ".bak", text); log?.Invoke("Restored backup bundle"); } if (!File.Exists(text2) && File.Exists(text2 + ".bak")) { File.Move(text2 + ".bak", text2); log?.Invoke("Restored backup state"); } string[] array = new string[4] { "*.building", "*.raw", "*.bak", "*.tmp" }; foreach (string searchPattern in array) { string[] files = Directory.GetFiles(assetFolder, searchPattern); for (int j = 0; j < files.Length; j++) { TryDelete(files[j]); } } } private static void Replace(string from, string to) { TryDelete(to); File.Move(from, to); } private static void TryDelete(string path) { try { if (File.Exists(path)) { File.Delete(path); } } catch (IOException) { } catch (UnauthorizedAccessException) { } } } public static class AtomicFile { public static void WriteAllText(string path, string text) { string text2 = path + ".tmp"; File.WriteAllText(text2, text); if (File.Exists(path)) { File.Delete(path); } File.Move(text2, path); } } public static class GameLocator { public const string SteamAppId = "1229490"; public const string FolderName = "ULTRAKILL"; public static bool IsGameFolder(string folder) { if (string.IsNullOrEmpty(folder)) { return false; } try { string text = SourceBundles.AddressablesFolder(folder); return Directory.Exists(text) && File.Exists(Path.Combine(text, "assets_assets_assets", "models.bundle")); } catch (Exception) { return false; } } public static string Normalize(string chosen) { if (string.IsNullOrWhiteSpace(chosen)) { return null; } try { string text = Path.GetFullPath(chosen.Trim().Trim(new char[1] { '"' })); if (File.Exists(text)) { text = Path.GetDirectoryName(text); } for (int i = 0; i < 6; i++) { if (string.IsNullOrEmpty(text)) { break; } if (IsGameFolder(text)) { return text; } string text2 = Path.Combine(text, "ULTRAKILL"); if (IsGameFolder(text2)) { return text2; } text = Path.GetDirectoryName(text); } } catch (Exception) { } return null; } public static string Find(string preferred, Action<string> log = null) { string text = Normalize(preferred); if (text != null) { return text; } foreach (string item in SteamLibraries(log)) { string path = Path.Combine(item, "steamapps", "appmanifest_1229490.acf"); string path2 = "ULTRAKILL"; try { if (File.Exists(path)) { Match match = Regex.Match(File.ReadAllText(path), "\"installdir\"\\s+\"([^\"]+)\""); if (match.Success) { path2 = match.Groups[1].Value; } } } catch (Exception) { } string text2 = Path.Combine(item, "steamapps", "common", path2); if (IsGameFolder(text2)) { return text2; } } foreach (string item2 in OtherLocations()) { if (IsGameFolder(item2)) { return item2; } } return null; } public static string BuildId(string gameFolder) { try { string path = Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(gameFolder)), "appmanifest_1229490.acf"); if (!File.Exists(path)) { return ""; } Match match = Regex.Match(File.ReadAllText(path), "\"buildid\"\\s+\"(\\d+)\""); return match.Success ? match.Groups[1].Value : ""; } catch (Exception) { return ""; } } public static List<string> SteamLibraries(Action<string> log = null) { List<string> roots = new List<string>(); AddRoot(RegistryString("HKEY_CURRENT_USER\\Software\\Valve\\Steam", "SteamPath")); AddRoot(RegistryString("HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\Valve\\Steam", "InstallPath")); AddRoot(RegistryString("HKEY_LOCAL_MACHINE\\SOFTWARE\\Valve\\Steam", "InstallPath")); AddRoot("C:\\Program Files (x86)\\Steam"); AddRoot("C:\\Program Files\\Steam"); foreach (string item in LinuxHomes()) { AddRoot(Path.Combine(item, ".steam", "steam")); AddRoot(Path.Combine(item, ".local", "share", "Steam")); AddRoot(Path.Combine(item, ".var", "app", "com.valvesoftware.Steam", ".local", "share", "Steam")); } List<string> list = new List<string>(roots); foreach (string item2 in roots) { string text = Path.Combine(item2, "steamapps", "libraryfolders.vdf"); try { if (!File.Exists(text)) { continue; } foreach (Match item3 in Regex.Matches(File.ReadAllText(text), "\"path\"\\s+\"([^\"]+)\"")) { string text2 = item3.Groups[1].Value.Replace("\\\\", "\\"); if (text2.StartsWith("/")) { text2 = "Z:" + text2; } AddLibrary(list, text2); } } catch (Exception ex) { log?.Invoke("Cannot read " + text + ": " + ex.Message); } } foreach (DriveInfo item4 in SafeDrives()) { AddLibrary(list, Path.Combine(item4.RootDirectory.FullName, "SteamLibrary")); AddLibrary(list, Path.Combine(item4.RootDirectory.FullName, "Steam")); } return list; void AddRoot(string path) { if (string.IsNullOrEmpty(path)) { return; } try { path = Path.GetFullPath(path.Replace('/', Path.DirectorySeparatorChar)); if (Directory.Exists(path) && !roots.Exists((string r) => string.Equals(r, path, StringComparison.OrdinalIgnoreCase))) { roots.Add(path); } } catch (Exception) { } } } private static void AddLibrary(List<string> libraries, string path) { try { path = Path.GetFullPath(path); if (Directory.Exists(Path.Combine(path, "steamapps")) && !libraries.Exists((string r) => string.Equals(r, path, StringComparison.OrdinalIgnoreCase))) { libraries.Add(path); } } catch (Exception) { } } private static IEnumerable<string> OtherLocations() { foreach (DriveInfo item in SafeDrives()) { string root = item.RootDirectory.FullName; yield return Path.Combine(root, "GOG Games", "ULTRAKILL"); yield return Path.Combine(root, "Games", "ULTRAKILL"); yield return Path.Combine(root, "ULTRAKILL"); } } private static IEnumerable<string> LinuxHomes() { char directorySeparatorChar = Path.DirectorySeparatorChar; string path = Path.Combine("Z:" + directorySeparatorChar, "home"); try { return Directory.Exists(path) ? ((IEnumerable<string>)Directory.GetDirectories(path)) : ((IEnumerable<string>)new string[0]); } catch (Exception) { return new string[0]; } } private static IEnumerable<DriveInfo> SafeDrives() { List<DriveInfo> list = new List<DriveInfo>(); try { DriveInfo[] drives = DriveInfo.GetDrives(); foreach (DriveInfo driveInfo in drives) { try { if (driveInfo.IsReady && (driveInfo.DriveType == DriveType.Fixed || driveInfo.DriveType == DriveType.Removable)) { list.Add(driveInfo); } } catch (Exception) { } } } catch (Exception) { } return list; } private static string RegistryString(string key, string name) { try { return (Type.GetType("Microsoft.Win32.Registry, mscorlib") ?? Type.GetType("Microsoft.Win32.Registry, Microsoft.Win32.Registry"))?.GetMethod("GetValue", new Type[3] { typeof(string), typeof(string), typeof(object) })?.Invoke(null, new object[3] { key, name, null }) as string; } catch (Exception) { return null; } } } public sealed class SourceRef { public string Bundle; public long PathId; public string Type; public string Name; public string Signature; public string Sha256; public static SourceRef From(Dictionary<string, object> o) { if (o == null) { return null; } return new SourceRef {