Unofficial CardLife revival project, pronounced like "celery"
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

166 lignes
8.5KB

  1. using System;
  2. using System.Collections;
  3. using System.Reflection;
  4. using CLre.API.Utility;
  5. using HarmonyLib;
  6. using Svelto.DataStructures;
  7. using Svelto.ECS;
  8. namespace CLre.Fixes
  9. {
  10. [Bugfix(name = "CooldownCrossSlotSync",
  11. more = "https://trello.com/c/65FPrTjK/12-no-cooldown-between-inventory-slots",
  12. description = "Apply cooldown to all weapons",
  13. component = BugfixType.HarmonyPatch, id = 4)]
  14. [HarmonyPatch]
  15. class WeaponCooldownEngine_OnQuickAttackStateChange_Patch
  16. {
  17. public static bool Enable = true;
  18. private static bool isRunningTick = false;
  19. [HarmonyPostfix]
  20. public static void AfterMethodCall(object __instance, int characterId, bool isPerformingQuickAttack)
  21. {
  22. if (!Enable) return;
  23. // TODO optimise this... a lot
  24. // build functions for retrieving internal Game.Handhelds.CharacterWeaponCooldownEntityView object
  25. IEntitiesDB entitiesDB = (IEntitiesDB)
  26. AccessTools.PropertyGetter(AccessTools.TypeByName("Game.Handhelds.WeaponCooldownEngine"), "entitiesDB")
  27. .Invoke(__instance, new object[0]);
  28. Reflection.ExistsV2 cwcevExists =
  29. Reflection.MethodAsDelegate<Reflection.ExistsV2>(
  30. "Svelto.ECS.IEntitiesDB:Exists",
  31. new[] {typeof(int), typeof(int)},
  32. new[] {AccessTools.TypeByName("Game.Handhelds.CharacterWeaponCooldownEntityView")},
  33. entitiesDB);
  34. Reflection.QueryEntityViewV2<object> queryCWCEV =
  35. Reflection.MethodAsDelegate<Reflection.QueryEntityViewV2<object>>(
  36. typeof(IEntitiesDB),
  37. "QueryEntityView",
  38. parameters: new[] {typeof(int), typeof(ExclusiveGroup.ExclusiveGroupStruct)},
  39. generics: new[] {AccessTools.TypeByName("Game.Handhelds.CharacterWeaponCooldownEntityView")},
  40. entitiesDB);
  41. // retrieve Game.Handhelds.CharacterWeaponCooldownEntityView
  42. if (!cwcevExists(characterId, (int) DEPRECATED_SveltoExtensions.DEPRECATED_GROUP))
  43. {
  44. //API.Utility.Logging.MetaDebugLog($"No CharacterWeaponCooldownEntityView with id {characterId} found");
  45. return;
  46. }
  47. object cwcevOriginal = queryCWCEV(characterId,
  48. (ExclusiveGroup.ExclusiveGroupStruct) DEPRECATED_SveltoExtensions.DEPRECATED_GROUP);
  49. // get id because that's important
  50. int toolId = Traverse.Create(cwcevOriginal).Field("handheldEquipmentComponent")
  51. .Property("equippedHandheldId")
  52. .GetValue<DispatchOnChange<int>>().value;
  53. //API.Utility.Logging.MetaLog("Got CharacterWeaponCooldownEntityView object using nasty code");
  54. // build functions for retrieving internal Game.Handhelds.WeaponCooldownEntityView object
  55. Reflection.ExistsV2 wcevExists =
  56. Reflection.MethodAsDelegate<Reflection.ExistsV2>(
  57. "Svelto.ECS.IEntitiesDB:Exists",
  58. new[] {typeof(int), typeof(int)},
  59. new[] {AccessTools.TypeByName("Game.Handhelds.WeaponCooldownEntityView")},
  60. entitiesDB);
  61. Reflection.QueryEntityViewV2<object> queryWCEV =
  62. Reflection.MethodAsDelegate<Reflection.QueryEntityViewV2<object>>(
  63. typeof(IEntitiesDB),
  64. "QueryEntityView",
  65. parameters: new[] {typeof(int), typeof(ExclusiveGroup.ExclusiveGroupStruct)},
  66. generics: new[] {AccessTools.TypeByName("Game.Handhelds.WeaponCooldownEntityView")},
  67. entitiesDB);
  68. ExclusiveGroup baseGroup = (ExclusiveGroup) AccessTools
  69. .Field(AccessTools.TypeByName("Game.Handhelds.HandheldGroups"), "BaseGroup").GetValue(null);
  70. // retrieve WeaponCooldownEntityView
  71. if (!wcevExists(toolId, (int) baseGroup))
  72. {
  73. //API.Utility.Logging.MetaDebugLog($"No WeaponCooldownEntityView with id {toolId} found");
  74. return;
  75. }
  76. //object wcevOriginal = queryWCEV(toolId, baseGroup);
  77. //API.Utility.Logging.MetaLog($"Cooling down? {isInCooldown} for {cooldownLeft}s");
  78. // build functions for querying all Game.Handhelds.WeaponCooldownEntityView objects
  79. Type protoRaw =
  80. typeof(Reflection.QueryEntityViews<>)
  81. .MakeGenericType(
  82. AccessTools.TypeByName("Game.Handhelds.WeaponCooldownEntityView"));
  83. Delegate queryAllWCEV =
  84. Reflection.MethodAsDelegateRaw(
  85. typeof(IEntitiesDB),
  86. protoRaw,
  87. "QueryEntityViews",
  88. parameters: new[] {typeof(int)},
  89. generics: new[] {AccessTools.TypeByName("Game.Handhelds.WeaponCooldownEntityView")},
  90. entitiesDB);
  91. object collectionStruct =
  92. queryAllWCEV.DynamicInvoke((int) baseGroup);
  93. int count = Traverse.Create(collectionStruct).Field<int>("_count").Value;
  94. PropertyInfo indexer = typeof(Svelto.DataStructures.ReadOnlyCollectionStruct<>)
  95. .MakeGenericType(AccessTools.TypeByName("Game.Handhelds.WeaponCooldownEntityView"))
  96. .GetIndexer();
  97. if (!isRunningTick)
  98. {
  99. isRunningTick = true;
  100. cooldownTickEverything(characterId, cwcevExists, queryCWCEV, queryWCEV, queryAllWCEV, indexer, baseGroup).Run();
  101. }
  102. }
  103. [HarmonyTargetMethod]
  104. public static MethodBase Target()
  105. {
  106. return AccessTools.Method("Game.Handhelds.WeaponCooldownEngine:OnQuickAttackStateChange",
  107. new[] {typeof(int), typeof(bool)});
  108. }
  109. private static IEnumerator cooldownTickEverything(int characterId, Reflection.ExistsV2 cwcevExists, Reflection.QueryEntityViewV2<object> cwcevQuery, Reflection.QueryEntityViewV2<object> wcevQuery, Delegate wcevQueryAll, PropertyInfo indexer, ExclusiveGroup baseGroup)
  110. {
  111. while (cwcevExists(characterId, (int) DEPRECATED_SveltoExtensions.DEPRECATED_GROUP))
  112. {
  113. // get equipped handheld info
  114. object cwcevOriginal = cwcevQuery(characterId,
  115. (ExclusiveGroup.ExclusiveGroupStruct) DEPRECATED_SveltoExtensions.DEPRECATED_GROUP);
  116. int toolId = Traverse.Create(cwcevOriginal).Field("handheldEquipmentComponent")
  117. .Property("equippedHandheldId")
  118. .GetValue<DispatchOnChange<int>>().value;
  119. // get cooldown info for equipped item
  120. object wcevOriginal = wcevQuery(toolId, baseGroup);
  121. object collectionStruct =
  122. wcevQueryAll.DynamicInvoke((int)baseGroup);
  123. int count = Traverse.Create(collectionStruct).Field<int>("_count").Value;
  124. float cooldownLeft =
  125. Traverse.Create(wcevOriginal).Field("weaponCooldownComponent").Property<float>("cooldownLeft").Value;
  126. bool isInCooldown =
  127. Traverse.Create(wcevOriginal).Field("weaponCooldownComponent").Property<bool>("isInCooldown").Value;
  128. object[] indexParams = {0};
  129. // iterate over other handhelds and sync their cooldowns to the held item
  130. #if DEBUG
  131. API.Utility.Logging.MetaDebugLog($"Syncing {count} weapon cooldowns with the held item {toolId}");
  132. #endif
  133. for (int index = 0; index < count; index++)
  134. {
  135. indexParams[0] = index;
  136. object wcev = indexer.GetValue(collectionStruct, indexParams);
  137. Traverse.Create(wcev)
  138. .Field("weaponCooldownComponent")
  139. .Property<float>("cooldownLeft")
  140. .Value = cooldownLeft;
  141. Traverse.Create(wcev)
  142. .Field("weaponCooldownComponent")
  143. .Property<bool>("isInCooldown").Value = isInCooldown;
  144. }
  145. // stop running this task after one final sync to set every handheld to non-cooldown state
  146. if (!isInCooldown && cooldownLeft <= 0) break;
  147. yield return null;
  148. }
  149. // cleanup
  150. #if DEBUG
  151. API.Utility.Logging.MetaDebugLog("Custom cooldown ticks complete");
  152. #endif
  153. isRunningTick = false;
  154. yield return null;
  155. }
  156. }
  157. }