commit e3c48b54dc36ad3bd2e67b880145a8161ae75c8a Author: Nathaniel Cosford Date: Wed Jun 4 16:13:36 2025 +0930 Upload from upload_mods.ps1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f69e0d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,80 @@ +# Build and Object Folders +bin/ +obj/ + +# Nuget packages directory +packages/ + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +x64/ +*_i.c +*_p.c +*.ilk +*.meta +*.obj +*.pch +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.log +*.vspscc +*.vssscc +.builds + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper* + +# NCrunch +*.ncrunch* +.*crunch*.local.xml + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help +UpgradeLog*.XML + +# Lightswitch +_Pvt_Extensions +GeneratedArtifacts +*.xap +ModelManifest.xml + +#Backup file +*.bak + +#zzzili +v15/ + diff --git a/FullautoLauncher.csproj b/FullautoLauncher.csproj new file mode 100644 index 0000000..a39e5c8 --- /dev/null +++ b/FullautoLauncher.csproj @@ -0,0 +1,86 @@ + + + + + Debug + AnyCPU + {F485DD15-9EDF-42C3-A31B-66AE0DFB7356} + Library + Properties + FullautoLauncher + FullautoLauncher + v4.8 + 512 + true + + + true + full + false + .\ + DEBUG;TRACE + prompt + 4 + + + portable + true + .\ + TRACE + prompt + 4 + true + + + + ..\0_TFP_Harmony\0Harmony.dll + False + + + ..\..\7DaysToDie_Data\Managed\Assembly-CSharp.dll + False + + + ..\..\7DaysToDie_Data\Managed\LogLibrary.dll + False + + + + + + + + + + + ..\..\7DaysToDie_Data\Managed\UnityEngine.dll + False + + + ..\..\7DaysToDie_Data\Managed\UnityEngine.AnimationModule.dll + False + + + ..\..\7DaysToDie_Data\Managed\UnityEngine.CoreModule.dll + False + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/FullautoLauncher.dll b/FullautoLauncher.dll new file mode 100644 index 0000000..3ce6192 Binary files /dev/null and b/FullautoLauncher.dll differ diff --git a/FullautoLauncher.pdb b/FullautoLauncher.pdb new file mode 100644 index 0000000..e6fe169 Binary files /dev/null and b/FullautoLauncher.pdb differ diff --git a/Harmony/Init.cs b/Harmony/Init.cs new file mode 100644 index 0000000..c4be087 --- /dev/null +++ b/Harmony/Init.cs @@ -0,0 +1,21 @@ +using FullautoLauncher.Scripts.ProjectileManager; +using System.Reflection; + +public class FullautoLauncherInit : IModApi +{ + private static bool inited = false; + public void InitMod(Mod _modInstance) + { + if(inited) + { + return; + } + inited = true; + Log.Out(" Loading Patch: " + GetType()); + var harmony = new HarmonyLib.Harmony(GetType().ToString()); + harmony.PatchAll(Assembly.GetExecutingAssembly()); + + ModEvents.UnityUpdate.RegisterHandler(CustomProjectileManager.Update); + } +} + diff --git a/Harmony/Patches.cs b/Harmony/Patches.cs new file mode 100644 index 0000000..2d96560 --- /dev/null +++ b/Harmony/Patches.cs @@ -0,0 +1,208 @@ +using FullautoLauncher.Scripts.ProjectileManager; +using HarmonyLib; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Reflection.Emit; +using System.Xml.Linq; +using UnityEngine; + +[HarmonyPatch] +class ItemActionLauncherProjectilePatch +{ + public static FieldInfo fldinfo_meta = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.Meta)); + public static MethodInfo mtdinfo_gbc = AccessTools.Method(typeof(ItemActionRanged), nameof(ItemActionRanged.GetBurstCount), new Type[] { typeof(ItemActionData) }); + public static MethodInfo mtdinfo_gac = AccessTools.Method(typeof(AnimatorRangedReloadState), "GetAmmoCount", new Type[] { typeof(EntityAlive), typeof(ItemValue), typeof(Int32) }); + public static MethodInfo mtdinfo_sta = AccessTools.Method(typeof(GameObject), nameof(GameObject.SetActive), new Type[] { typeof(bool) }); + public static int getProjectileCount(ItemActionData _data) + { + int rps = 1; + ItemInventoryData invD = _data != null ? _data.invData : null; + if (invD != null) + { + ItemClass item = invD.itemValue != null ? invD.itemValue.ItemClass : null; + rps = (int)EffectManager.GetValue(PassiveEffects.RoundRayCount, invD.itemValue, rps, invD.holdingEntity, null, item != null ? item.ItemTags | _data.ActionTags : default); + } + return rps > 0 ? rps : 1; + } + + [HarmonyPatch(typeof(ItemActionLauncher), nameof(ItemActionLauncher.StartHolding))] + [HarmonyTranspiler] + private static IEnumerable Transpiler_StartHolding_ItemActionLauncher(IEnumerable instructions, ILGenerator generator) + { + var codes = new List(instructions); + + LocalBuilder lbd_rps = generator.DeclareLocal(typeof(int)); + + var list_insert = new List + { + new CodeInstruction(OpCodes.Ldloc_S, lbd_rps), + new CodeInstruction(OpCodes.Mul) + }; + + for (int i = 0; i < codes.Count; i++) + { + if ( codes[i].LoadsField(fldinfo_meta)) + { + codes.InsertRange(i + 1, list_insert); + i += list_insert.Count; + } + } + + codes.InsertRange(0, new CodeInstruction[] + { + new CodeInstruction(OpCodes.Ldarg_1), + CodeInstruction.Call(typeof(ItemActionLauncherProjectilePatch), nameof(getProjectileCount), new Type[] { typeof(ItemActionData) }), + new CodeInstruction(OpCodes.Stloc_S, lbd_rps) + }); + + return codes; + } + + [HarmonyPatch(typeof(ItemActionLauncher), nameof(ItemActionLauncher.ItemActionEffects))] + [HarmonyTranspiler] + private static IEnumerable Transpiler_ItemActionEffects_ItemActionLauncher(IEnumerable instructions) + { + var codes = new List(instructions); + + for(int i = 0, totali = codes.Count; i < totali; i++) + { + if (codes[i].Calls(mtdinfo_gbc)) + { + codes.InsertRange(i + 1, new CodeInstruction[] + { + new CodeInstruction(OpCodes.Ldarg_2), + CodeInstruction.Call(typeof(ItemActionLauncherProjectilePatch), nameof(getProjectileCount), new Type[] { typeof(ItemActionData) }) + }); + codes.RemoveRange(i - 2, 3); + break; + } + } + + return codes; + } + + [HarmonyPatch(typeof(ItemActionLauncher), nameof(ItemActionLauncher.ConsumeAmmo))] + [HarmonyTranspiler] + private static IEnumerable Transpiler_ConsumeAmmo_ItemActionLauncher(IEnumerable instructions) + { + var codes = new List() + { + new CodeInstruction(OpCodes.Ldarg_0), + new CodeInstruction(OpCodes.Ldarg_1), + CodeInstruction.Call(typeof(ItemActionRanged), nameof(ItemActionRanged.ConsumeAmmo), new Type[]{ typeof(ItemActionData) }), + new CodeInstruction(OpCodes.Ret) + }; + + return codes; + } + + [HarmonyPatch(typeof(ItemClass), nameof(ItemClass.ExecuteAction))] + [HarmonyTranspiler] + private static IEnumerable Transpiler_ExecuteAction_ItemClass(IEnumerable instructions) + { + var codes = new List(instructions); + + for (int i = 0; i < codes.Count; i++) + { + if (codes[i].opcode == OpCodes.Isinst && codes[i].OperandIs(typeof(ItemActionLauncher))) + { + codes.RemoveRange(i - 2, 4); + break; + } + } + + return codes; + } + + [HarmonyPatch(typeof(AnimatorRangedReloadState), nameof(AnimatorRangedReloadState.OnStateEnter))] + [HarmonyTranspiler] + private static IEnumerable Transpiler_OnStateEnter_AnimatorRangedReloadState(IEnumerable instructions, ILGenerator generator) + { + var codes = new List(instructions); + + LocalBuilder lbd_rps = generator.DeclareLocal(typeof(int)); + + for (int i = 0, totali = codes.Count; i < totali; i++) + { + if (codes[i].Calls(mtdinfo_gac)) + { + codes.InsertRange(i + 1, new CodeInstruction[] + { + new CodeInstruction(OpCodes.Ldloc_S, 6), + CodeInstruction.Call(typeof(ItemActionLauncherProjectilePatch), nameof(getProjectileCount), new Type[] { typeof(ItemActionData) }), + new CodeInstruction(OpCodes.Stloc_S, lbd_rps), + new CodeInstruction(OpCodes.Ldloc_S, lbd_rps), + new CodeInstruction(OpCodes.Mul) + }); + totali += 5; + break; + } + //else if (codes[i].opcode == OpCodes.Ldc_R4 && codes[i].OperandIs(0.005f)) + //{ + // codes.Insert(i + 2, new CodeInstruction(OpCodes.Ldc_R4, 0f)); + // codes.RemoveRange(i - 2, 4); + // break; + //} + } + + return codes; + } + + [HarmonyPatch(typeof(ItemActionLauncher), nameof(ItemActionLauncher.instantiateProjectile))] + [HarmonyPrefix] + private static bool Prefix_instantiateProjectile_ItemActionLauncher(ref Vector3 _positionOffset) + { + _positionOffset = Vector3.zero; + return true; + } + + [HarmonyPatch(typeof(ItemActionLauncher), nameof(ItemActionLauncher.instantiateProjectile))] + [HarmonyPostfix] + private static void Postfix_instantiateProjectile_ItemActionLauncher(Transform __result, ItemActionLauncher __instance) + { + if (__instance.Properties.Contains("VisibleInMag") && !__instance.Properties.GetBool("VisibleInMag")) + { + __result.gameObject.SetActive(false); + } + } + + [HarmonyPatch(typeof(GameManager), nameof(GameManager.FixedUpdate))] + [HarmonyPostfix] + private static void Postfix_FixedUpdate_GameManager() + { + CustomProjectileManager.FixedUpdate(); + } + + //custom projectile manager workarounds + private static void ParseProjectileType(XElement _node) + { + string itemName = _node.GetAttribute("name"); + if (string.IsNullOrEmpty(itemName)) + { + return; + } + ItemClass item = ItemClass.GetItemClass(itemName); + for (int i = 0; i < item.Actions.Length; i++) + { + if (item.Actions[i] is ItemActionProjectile proj && proj.Properties.Contains("CustomProjectileType")) + { + CustomProjectileManager.InitClass(item, proj.Properties.GetString("CustomProjectileType")); + } + } + } + + [HarmonyPatch(typeof(ItemClassesFromXml), nameof(ItemClassesFromXml.parseItem))] + [HarmonyPostfix] + private static void Postfix_parseItem_ItemClassesFromXml(XElement _node) + { + ParseProjectileType(_node); + } + + [HarmonyPatch(typeof(GameManager), nameof(GameManager.SaveAndCleanupWorld))] + [HarmonyPostfix] + private static void Postfix_SaveAndCleanupWorld_GameManager() + { + CustomProjectileManager.Cleanup(); + } +} \ No newline at end of file diff --git a/ModInfo.xml b/ModInfo.xml new file mode 100644 index 0000000..82916a2 --- /dev/null +++ b/ModInfo.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b3c7a68 --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("FullautoLauncher")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("FullautoLauncher")] +[assembly: AssemblyCopyright("Copyright © 2022")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("f485dd15-9edf-42c3-a31b-66ae0dfb7356")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 +//通过使用 "*",如下所示: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Scripts/ItemActions/ItemActionBetterLauncher.cs b/Scripts/ItemActions/ItemActionBetterLauncher.cs new file mode 100644 index 0000000..342a340 --- /dev/null +++ b/Scripts/ItemActions/ItemActionBetterLauncher.cs @@ -0,0 +1,138 @@ +using Audio; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine.Scripting; +using UnityEngine; +using FullautoLauncher.Scripts.ProjectileManager; +using static ItemActionLauncher; +using static ItemActionRanged; + +[Preserve] +public class ItemActionBetterLauncher : ItemActionRanged +{ + private IProjectileItemGroup group; + public override ItemActionData CreateModifierData(ItemInventoryData _invData, int _indexInEntityOfAction) + { + return new ItemActionDataBetterLauncher(_invData, _indexInEntityOfAction); + } + + public override void ReadFrom(DynamicProperties _props) + { + base.ReadFrom(_props); + } + + public override void StartHolding(ItemActionData _actionData) + { + base.StartHolding(_actionData); + ItemActionDataBetterLauncher ItemActionDataBetterLauncher = (ItemActionDataBetterLauncher)_actionData; + ItemValue launcherValue = ItemActionDataBetterLauncher.invData.itemValue; + ItemClass forId = ItemClass.GetForId(ItemClass.GetItem(MagazineItemNames[launcherValue.SelectedAmmoTypeIndex], false).type); + group = CustomProjectileManager.Get(forId.Name); + if (launcherValue.Meta != 0 && GetMaxAmmoCount(ItemActionDataBetterLauncher) != 0) + { + group.Pool(launcherValue.Meta * (int)EffectManager.GetValue(PassiveEffects.RoundRayCount, launcherValue, 1f, ItemActionDataBetterLauncher.invData.holdingEntity)); + } + ItemActionDataBetterLauncher.info = new ProjectileParams.ItemInfo() + { + actionData = ItemActionDataBetterLauncher, + itemProjectile = forId, + itemActionProjectile = (ItemActionProjectile)((forId.Actions[0] is ItemActionProjectile) ? forId.Actions[0] : forId.Actions[1]), + itemValueLauncher = launcherValue, + itemValueProjectile = new ItemValue(forId.Id) + }; + } + + public override void OnModificationsChanged(ItemActionData _data) + { + base.OnModificationsChanged(_data); + } + + public override void StopHolding(ItemActionData _data) + { + base.StopHolding(_data); + ItemActionDataBetterLauncher itemActionDataLauncher = (ItemActionDataBetterLauncher)_data; + itemActionDataLauncher.info = null; + } + + public override void SwapAmmoType(EntityAlive _entity, int _ammoItemId = -1) + { + base.SwapAmmoType(_entity, _ammoItemId); + ItemActionDataBetterLauncher ItemActionDataBetterLauncher = (ItemActionDataBetterLauncher)_entity.inventory.holdingItemData.actionData[ActionIndex]; + ItemValue itemValue = ItemActionDataBetterLauncher.invData.itemValue; + ItemClass forId = ItemClass.GetForId(ItemClass.GetItem(MagazineItemNames[itemValue.SelectedAmmoTypeIndex], false).type); + group = CustomProjectileManager.Get(forId.Name); + ItemActionDataBetterLauncher.info = new ProjectileParams.ItemInfo() + { + actionData = ItemActionDataBetterLauncher, + itemProjectile = forId, + itemActionProjectile = (ItemActionProjectile)((forId.Actions[0] is ItemActionProjectile) ? forId.Actions[0] : forId.Actions[1]), + itemValueLauncher = itemValue, + itemValueProjectile = new ItemValue(forId.Id) + }; + } + + public override Vector3 fireShot(int _shotIdx, ItemActionDataRanged _actionData, ref bool hitEntity) + { + hitEntity = true; + return Vector3.zero; + } + + public override void ItemActionEffects(GameManager _gameManager, ItemActionData _actionData, int _firingState, Vector3 _startPos, Vector3 _direction, int _userData = 0) + { + base.ItemActionEffects(_gameManager, _actionData, _firingState, _startPos, _direction, _userData); + if (_firingState == 0) + { + return; + } + EntityAlive entity = _actionData.invData.holdingEntity; + if (entity.isEntityRemote && GameManager.IsDedicatedServer) + { + return; + } + ItemActionDataBetterLauncher ItemActionDataBetterLauncher = (ItemActionDataBetterLauncher)_actionData; + ItemValue holdingItemItemValue = _actionData.invData.holdingEntity.inventory.holdingItemItemValue; + ItemClass forId = ItemClass.GetForId(ItemClass.GetItem(MagazineItemNames[holdingItemItemValue.SelectedAmmoTypeIndex], false).type); + int projCount = (int)EffectManager.GetValue(PassiveEffects.RoundRayCount, ItemActionDataBetterLauncher.invData.itemValue, 1f, ItemActionDataBetterLauncher.invData.holdingEntity); + if (projCount <= 0) + { + return; + } + if (ItemActionDataBetterLauncher.info == null) + { + Log.Error("null info!"); + return; + } + if (ItemActionDataBetterLauncher.projectileJoint == null) + { + return; + } + Vector3 realStartPosition = ItemActionDataBetterLauncher.projectileJoint.position + Origin.position; + for (int i = 0; i < projCount; i++) + { + var par = group.Fire(entity.entityId, ItemActionDataBetterLauncher.info, _startPos, realStartPosition, getDirectionOffset(ItemActionDataBetterLauncher, _direction, i), entity, hitmaskOverride); + } + } + + public override void getImageActionEffectsStartPosAndDirection(ItemActionData _actionData, out Vector3 _startPos, out Vector3 _direction) + { + Ray lookRay = _actionData.invData.holdingEntity.GetLookRay(); + _startPos = lookRay.origin; + _direction = lookRay.direction;//getDirectionOffset(ItemActionDataBetterLauncher, lookRay.direction, 0); + } + + public class ItemActionDataBetterLauncher : ItemActionDataRanged + { + public ItemActionDataBetterLauncher(ItemInventoryData _invData, int _indexInEntityOfAction) + : base(_invData, _indexInEntityOfAction) + { + projectileJoint = (_invData.model?.FindInChilds("ProjectileJoint", false)); + } + + public Transform projectileJoint; + public ProjectileParams.ItemInfo info; + } + +} diff --git a/Scripts/ProjectileManager/CustomProjectileManager.cs b/Scripts/ProjectileManager/CustomProjectileManager.cs new file mode 100644 index 0000000..07fd586 --- /dev/null +++ b/Scripts/ProjectileManager/CustomProjectileManager.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FullautoLauncher.Scripts.ProjectileManager +{ + public static class CustomProjectileManager + { + private static readonly Dictionary dict_item_groups = new Dictionary(); + + public static IProjectileItemGroup Get(string name) => dict_item_groups[name]; + + public static void InitClass(ItemClass item, string typename) + { + if (dict_item_groups.ContainsKey(item.Name)) + { + return; + } + Type type = ReflectionHelpers.GetTypeWithPrefix("PIG", typename); + IProjectileItemGroup group = (IProjectileItemGroup)Activator.CreateInstance(type, new object[] {item}); + dict_item_groups.Add(item.Name, group); + } + + public static void Update() + { + foreach (var group in dict_item_groups.Values) + { + group.Update(); + } + } + + public static void FixedUpdate() + { + foreach (var group in dict_item_groups.Values) + { + group.FixedUpdate(); + } + } + + public static void Cleanup() + { + foreach (var group in dict_item_groups.Values) + { + group.Cleanup(); + } + dict_item_groups.Clear(); + } + } +} diff --git a/Scripts/ProjectileManager/PHGameObject.cs b/Scripts/ProjectileManager/PHGameObject.cs new file mode 100644 index 0000000..548bacc --- /dev/null +++ b/Scripts/ProjectileManager/PHGameObject.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace FullautoLauncher.Scripts.ProjectileManager +{ + public class PHGameObject : ParameterHolderAbs, IDisposable + { + public Transform Transform { get; } + public PHGameObject(Transform transform, ProjectileParams par) : base(par) + { + Transform = transform; + } + + public override void Fire() + { + Transform.gameObject.SetActive(true); + UpdatePosition(); + } + + public override void UpdatePosition() + { + Vector3 realPos = par.renderPosition - Origin.position; + Transform.position = realPos; + Transform.LookAt(realPos + par.moveDir); + } + + public void Dispose() + { + GameObject.Destroy(Transform.gameObject); + } + } +} diff --git a/Scripts/ProjectileManager/PHSimpleMesh.cs b/Scripts/ProjectileManager/PHSimpleMesh.cs new file mode 100644 index 0000000..328ebb2 --- /dev/null +++ b/Scripts/ProjectileManager/PHSimpleMesh.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using static AstarManager; + +namespace FullautoLauncher.Scripts.ProjectileManager +{ + public class SimpleMeshTransformData + { + public readonly Matrix4x4 TRS; + public readonly Bounds bounds; + + public SimpleMeshTransformData(Matrix4x4 TRS, Bounds bounds) + { + this.TRS = TRS; + this.bounds = bounds; + } + } + + public class PHSimpleMesh : ParameterHolderAbs + { + public Matrix4x4 finalMat; + public RenderParams renderParams; + private SimpleMeshTransformData data; + + public PHSimpleMesh(ProjectileParams par, SimpleMeshTransformData data, in RenderParams renderPar) : base(par) + { + this.data = data; + this.renderParams = renderPar; + } + + public override void Fire() + { + UpdatePosition(); + } + + public override void UpdatePosition() + { + finalMat = Matrix4x4.Translate(par.renderPosition - Origin.position) * Matrix4x4.Rotate(Quaternion.LookRotation(par.moveDir)) * data.TRS; + Bounds bounds = data.bounds; + bounds.center = finalMat.MultiplyPoint3x4(bounds.center); + renderParams.worldBounds = bounds; + } + } +} diff --git a/Scripts/ProjectileManager/PIGGameObject.cs b/Scripts/ProjectileManager/PIGGameObject.cs new file mode 100644 index 0000000..2f12c67 --- /dev/null +++ b/Scripts/ProjectileManager/PIGGameObject.cs @@ -0,0 +1,49 @@ +using FullautoLauncher.Scripts.ProjectileManager; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +public class PIGGameObject : ProjectileItemGroupAbs +{ + public PIGGameObject(ItemClass item) : base(item) + { + } + + public override void Cleanup() + { + foreach (var proj in queue_pool) + { + proj.Dispose(); + } + queue_pool.Clear(); + + foreach (var set in dict_fired_projectiles.Values) + { + foreach (var proj in set) + { + proj.Dispose(); + } + } + dict_fired_projectiles.Clear(); + } + + public override void Pool(PHGameObject par) + { + base.Pool(par); + par.Transform.gameObject.SetActive(false); + } + + public override void Update() + { + + } + + protected override PHGameObject Create(ProjectileParams par) + { + return new PHGameObject(item.CloneModel(GameManager.Instance.World, new ItemValue(item.Id), Vector3.zero, null), par); + } +} + diff --git a/Scripts/ProjectileManager/PIGSimpleMesh.cs b/Scripts/ProjectileManager/PIGSimpleMesh.cs new file mode 100644 index 0000000..267e36f --- /dev/null +++ b/Scripts/ProjectileManager/PIGSimpleMesh.cs @@ -0,0 +1,54 @@ +using FullautoLauncher.Scripts.ProjectileManager; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +public class PIGSimpleMesh : ProjectileItemGroupAbs +{ + private Transform renderTrans; + private Mesh mesh; + private RenderParams renderParams; + private SimpleMeshTransformData data; + + public PIGSimpleMesh(ItemClass item) : base(item) + { + renderTrans = item.CloneModel(GameManager.Instance.World, new ItemValue(item.Id), Vector3.zero, null); + MeshFilter filter = renderTrans.GetComponentInChildren(); + mesh = filter.sharedMesh; + MeshRenderer renderer = renderTrans.GetComponentInChildren(); + renderParams = new RenderParams(renderer.material) + { + layer = renderer.gameObject.layer, + lightProbeUsage = UnityEngine.Rendering.LightProbeUsage.Off, + shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off, + rendererPriority = renderer.rendererPriority, + renderingLayerMask = renderer.renderingLayerMask, + motionVectorMode = MotionVectorGenerationMode.ForceNoMotion + }; + data = new SimpleMeshTransformData(renderer.localToWorldMatrix, mesh.bounds); + } + + public override void Update() + { + if (GameManager.IsDedicatedServer) + { + return; + } + + foreach (var set in dict_fired_projectiles.Values) + { + foreach (var projectile in set) + { + Graphics.RenderMesh(in projectile.renderParams, mesh, 0, projectile.finalMat); + } + } + } + + protected override PHSimpleMesh Create(ProjectileParams par) + { + return new PHSimpleMesh(par, data, in renderParams); + } +} diff --git a/Scripts/ProjectileManager/ParameterHolderAbs.cs b/Scripts/ProjectileManager/ParameterHolderAbs.cs new file mode 100644 index 0000000..3291ee2 --- /dev/null +++ b/Scripts/ProjectileManager/ParameterHolderAbs.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FullautoLauncher.Scripts.ProjectileManager +{ + public abstract class ParameterHolderAbs + { + public ProjectileParams Params => par; + + protected readonly ProjectileParams par; + + protected ParameterHolderAbs(ProjectileParams par) + { + this.par = par; + } + + public override int GetHashCode() + { + return par.ProjectileID; + } + + public abstract void UpdatePosition(); + + public virtual void Fire() + { + + } + } +} diff --git a/Scripts/ProjectileManager/ProjectileItemGroupAbs.cs b/Scripts/ProjectileManager/ProjectileItemGroupAbs.cs new file mode 100644 index 0000000..50813ce --- /dev/null +++ b/Scripts/ProjectileManager/ProjectileItemGroupAbs.cs @@ -0,0 +1,121 @@ +using System.Collections.Generic; +using UnityEngine; + +namespace FullautoLauncher.Scripts.ProjectileManager +{ + public interface IProjectileItemGroup + { + void Pool(int count); + ProjectileParams Fire(int entityID, ProjectileParams.ItemInfo info, Vector3 _idealStartPosition, Vector3 _realStartPosition, Vector3 _flyDirection, Entity _firingEntity, int _hmOverride = 0, float _radius = 0f); + void Update(); + void FixedUpdate(); + void Cleanup(); + } + + public abstract class ProjectileItemGroupAbs : IProjectileItemGroup where T : ParameterHolderAbs + { + protected readonly Queue queue_pool = new Queue(); + protected readonly Dictionary> dict_fired_projectiles = new Dictionary>(); + protected readonly ItemClass item; + protected readonly int maxPoolCount = 1000; + private int nextID; + private int NextID { get => nextID++; } + + public ProjectileItemGroupAbs(ItemClass item) + { + this.item = item; + } + + protected abstract T Create(ProjectileParams par); + + public virtual void Cleanup() + { + } + + public void Pool(int count) + { + count = Mathf.Min(maxPoolCount - queue_pool.Count, count - queue_pool.Count); + for (int i = 0; i < count; i++) + { + queue_pool.Enqueue(Create(new ProjectileParams(NextID))); + } + } + + public virtual void Pool(T par) + { + if (maxPoolCount > queue_pool.Count) + { + par.Params.bOnIdealPosition = false; + queue_pool.Enqueue(par); + } + } + + public ProjectileParams Fire(int entityID, ProjectileParams.ItemInfo info, Vector3 _idealStartPosition, Vector3 _realStartPosition, Vector3 _flyDirection, Entity _firingEntity, int _hmOverride = 0, float _radius = 0f) + { + T par = queue_pool.Count == 0 ? Create(new ProjectileParams(NextID)) : queue_pool.Dequeue(); + if(!dict_fired_projectiles.TryGetValue(entityID, out HashSet set)) + { + set = new HashSet(); + dict_fired_projectiles.Add(entityID, set); + } + set.Add(par); + par.Params.Fire(info, _idealStartPosition, _realStartPosition, _flyDirection, _firingEntity, _hmOverride, _radius); + par.Fire(); + return par.Params; + } + + public abstract void Update(); + + private List list_remove = new List(); + public void FixedUpdate() + { + if (GameManager.Instance == null || GameManager.Instance.IsPaused() || GameManager.Instance.World == null) + return; + foreach (var pair in dict_fired_projectiles) + { + EntityAlive entityAlive = GameManager.Instance.World.GetEntity(pair.Key) as EntityAlive; + + list_remove.Clear(); + foreach (var projectile in pair.Value) + { + if (projectile.Params.UpdatePosition()) + { + list_remove.Add(projectile); + } + else + { + projectile.UpdatePosition(); + } + } + + if (entityAlive != null) + { + int prevLayer = 0; + if (entityAlive.emodel != null) + { + prevLayer = entityAlive.GetModelLayer(); + entityAlive.SetModelLayer(2, false, null); + } + foreach (var projectile in pair.Value) + { + if (projectile.Params.CheckCollision(entityAlive)) + { + list_remove.Add(projectile); + } + } + if (entityAlive.emodel != null) + { + entityAlive.SetModelLayer(prevLayer, false, null); + } + } + + foreach (var remove in list_remove) + { + pair.Value.Remove(remove); + Pool(remove); + } + list_remove.Clear(); + } + } + } +} diff --git a/Scripts/ProjectileManager/ProjectileParams.cs b/Scripts/ProjectileManager/ProjectileParams.cs new file mode 100644 index 0000000..c28d525 --- /dev/null +++ b/Scripts/ProjectileManager/ProjectileParams.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace FullautoLauncher.Scripts.ProjectileManager +{ + public class ProjectileParams + { + public int ProjectileID; + public ItemInfo info; + public Vector3 flyDirection; + public Vector3 renderPosition; + public Vector3 velocity; + public Vector3 previousPosition; + public Vector3 currentPosition; + public Vector3 gravity; + public Vector3 moveDir; + public float timeShotStarted; + public int hmOverride; + public float radius; + public bool bOnIdealPosition = false; + public CollisionParticleController waterCollisionParticles = new CollisionParticleController(); + + public ProjectileParams(int projectileID) + { + ProjectileID = projectileID; + } + + public void Fire(ItemInfo _info, Vector3 _idealStartPosition, Vector3 _realStartPosition, Vector3 _flyDirection, Entity _firingEntity, int _hmOverride = 0, float _radius = 0f) + { + info = _info; + flyDirection = _flyDirection.normalized; + moveDir = flyDirection; + previousPosition = currentPosition = _idealStartPosition; + renderPosition = _realStartPosition; + velocity = flyDirection.normalized * EffectManager.GetValue(PassiveEffects.ProjectileVelocity, info.itemValueLauncher, info.itemActionProjectile.Velocity, _firingEntity as EntityAlive); + hmOverride = _hmOverride; + radius = _radius; + waterCollisionParticles.Init(_firingEntity.entityId, info.itemProjectile.MadeOfMaterial.SurfaceCategory, "water", 16); + gravity = Vector3.up * EffectManager.GetValue(PassiveEffects.ProjectileGravity, info.itemValueLauncher, info.itemActionProjectile.Gravity, _firingEntity as EntityAlive); + timeShotStarted = Time.time; + } + + public override int GetHashCode() + { + return ProjectileID; + } + + public bool UpdatePosition() + { + float flyTime = Time.time - timeShotStarted; + if (flyTime >= info.itemActionProjectile.LifeTime) + { + return true; + } + if (flyTime >= info.itemActionProjectile.FlyTime) + { + velocity += gravity * Time.fixedDeltaTime; + } + moveDir = velocity * Time.fixedDeltaTime; + previousPosition = currentPosition; + currentPosition += moveDir; + renderPosition += moveDir; + if (!bOnIdealPosition) + { + bOnIdealPosition = flyTime > 0.5f; + } + if(!bOnIdealPosition) + { + renderPosition = Vector3.Lerp(renderPosition, currentPosition, flyTime * 2f); + } + //Log.Out($"projectile {ProjectileID} position {currentPosition} entity position {info.actionData.invData.holdingEntity.position}"); + return false; + } + + public bool CheckCollision(EntityAlive entityAlive) + { + //Already checked in ItemActionBetterLauncher.ItemActionEffects, projectiles are not created on dedi if fired from remote entity. + //if(entityAlive.isEntityRemote && GameManager.IsDedicatedServer) + // return true; + World world = GameManager.Instance.World; + Vector3 dir = currentPosition - previousPosition; + Vector3 dirNorm = dir.normalized; + float magnitude = dir.magnitude; + if (magnitude < 0.04f) + { + return false; + } + + Ray ray = new Ray(previousPosition, dir); + waterCollisionParticles.CheckCollision(ray.origin, ray.direction, magnitude, (entityAlive != null) ? entityAlive.entityId : (-1)); + int hitmask = ((hmOverride == 0) ? 80 : hmOverride); + bool bHit = Voxel.Raycast(world, ray, magnitude, -538750997, hitmask, radius); + if (bHit && (GameUtils.IsBlockOrTerrain(Voxel.voxelRayHitInfo.tag) || Voxel.voxelRayHitInfo.tag.StartsWith("E_"))) + { + if (entityAlive != null && !entityAlive.isEntityRemote) + { + entityAlive.MinEventContext.Other = ItemActionAttack.FindHitEntity(Voxel.voxelRayHitInfo) as EntityAlive; + ItemActionAttack.AttackHitInfo attackHitInfo = new ItemActionAttack.AttackHitInfo + { + WeaponTypeTag = ItemActionAttack.RangedTag + }; + ItemActionAttack.Hit(Voxel.voxelRayHitInfo, + entityAlive.entityId, + EnumDamageTypes.Piercing, + info.itemActionProjectile.GetDamageBlock(info.itemValueLauncher, ItemActionAttack.GetBlockHit(world, Voxel.voxelRayHitInfo), entityAlive, info.actionData.indexInEntityOfAction), + info.itemActionProjectile.GetDamageEntity(info.itemValueLauncher, entityAlive, info.actionData.indexInEntityOfAction), + 1f, + 1f, + EffectManager.GetValue(PassiveEffects.CriticalChance, info.itemValueLauncher, info.itemProjectile.CritChance.Value, entityAlive, null, info.itemProjectile.ItemTags), + ItemAction.GetDismemberChance(info.actionData, Voxel.voxelRayHitInfo), + info.itemProjectile.MadeOfMaterial.SurfaceCategory, + info.itemActionProjectile.GetDamageMultiplier(), + info.itemActionProjectile.BuffActions, + attackHitInfo, + 1, + info.itemActionProjectile.ActionExp, + info.itemActionProjectile.ActionExpBonusMultiplier, + null, + null, + ItemActionAttack.EnumAttackMode.RealNoHarvesting, + null, + -1, + info.itemValueLauncher); + if (entityAlive.MinEventContext.Other == null) + { + entityAlive.FireEvent(MinEventTypes.onSelfPrimaryActionMissEntity, true); + } + entityAlive.FireEvent(MinEventTypes.onProjectileImpact, false); + MinEventParams.CachedEventParam.Self = entityAlive; + MinEventParams.CachedEventParam.Position = Voxel.voxelRayHitInfo.hit.pos; + MinEventParams.CachedEventParam.ItemValue = info.itemValueProjectile; + MinEventParams.CachedEventParam.Other = entityAlive.MinEventContext.Other; + info.itemProjectile.FireEvent(MinEventTypes.onProjectileImpact, MinEventParams.CachedEventParam); + if (info.itemActionProjectile.Explosion.ParticleIndex > 0) + { + Vector3 vector3 = Voxel.voxelRayHitInfo.hit.pos - dirNorm * 0.1f; + Vector3i vector3i = World.worldToBlockPos(vector3); + if (!world.GetBlock(vector3i).isair) + { + BlockFace blockFace; + vector3i = Voxel.OneVoxelStep(vector3i, vector3, -dirNorm, out vector3, out blockFace); + } + GameManager.Instance.ExplosionServer(Voxel.voxelRayHitInfo.hit.clrIdx, vector3, vector3i, Quaternion.identity, info.itemActionProjectile.Explosion, entityAlive.entityId, 0f, false, info.itemValueProjectile); + } + else if (info.itemProjectile.IsSticky) + { + GameRandom gameRandom = world.GetGameRandom(); + if (GameUtils.IsBlockOrTerrain(Voxel.voxelRayHitInfo.tag)) + { + if (gameRandom.RandomFloat < EffectManager.GetValue(PassiveEffects.ProjectileStickChance, info.itemValueLauncher, 0.5f, entityAlive, null, info.itemProjectile.ItemTags | FastTags.Parse(Voxel.voxelRayHitInfo.fmcHit.blockValue.Block.blockMaterial.SurfaceCategory))) + { + global::ProjectileManager.AddProjectileItem(null, -1, Voxel.voxelRayHitInfo.hit.pos, dirNorm, info.itemValueProjectile.type); + } + else + { + GameManager.Instance.SpawnParticleEffectServer(new ParticleEffect("impact_metal_on_wood", Voxel.voxelRayHitInfo.hit.pos, Utils.BlockFaceToRotation(Voxel.voxelRayHitInfo.fmcHit.blockFace), 1f, Color.white, string.Format("{0}hit{1}", Voxel.voxelRayHitInfo.fmcHit.blockValue.Block.blockMaterial.SurfaceCategory, info.itemProjectile.MadeOfMaterial.SurfaceCategory), null), entityAlive.entityId, false, false); + } + } + else if (gameRandom.RandomFloat < EffectManager.GetValue(PassiveEffects.ProjectileStickChance, info.itemValueLauncher, 0.5f, entityAlive, null, info.itemProjectile.ItemTags)) + { + int id = global::ProjectileManager.AddProjectileItem(null, -1, Voxel.voxelRayHitInfo.hit.pos, dirNorm, info.itemValueProjectile.type); + Utils.SetLayerRecursively(global::ProjectileManager.GetProjectile(id).gameObject, 14, null); + } + else + { + GameManager.Instance.SpawnParticleEffectServer(new ParticleEffect("impact_metal_on_wood", Voxel.voxelRayHitInfo.hit.pos, Utils.BlockFaceToRotation(Voxel.voxelRayHitInfo.fmcHit.blockFace), 1f, Color.white, "bullethitwood", null), entityAlive.entityId, false, false); + } + } + } + return true; + } + return false; + } + + public class ItemInfo + { + public ItemActionProjectile itemActionProjectile; + public ItemClass itemProjectile; + public ItemValue itemValueProjectile; + public ItemValue itemValueLauncher; + public ItemActionData actionData; + } + } +}