
鱼类调整
一些可配置的鱼类调整,例如可见的鱼类精灵。
查看大图鱼微调
描述:
我逐渐意识到,我喜欢采矿/务农的原因是,我可以自己挑选想要开采/耕种的东西。
我不讨厌这个迷你游戏,但既然游戏把鱼生成为实体……
你基本上在1-5个"未知数"中挑选,等待它们被下一批随机生成的"未知数"取代...
结果却发现,在本赛季结束之前,你一直在找的那一款始终没有出现……
现在你可以在决定捕捉之前,查看那些‘未知’精灵是什么,以及其他可配置的调整(F1):
精灵设置:
0: 永远不要使用修改过的精灵图。
1: 在拥有“锐利之眼”技能前,不要使用模组化的精灵。之后显示黑暗模组化的精灵。
2: 在获得'敏锐目光'技能前显示黑暗修改过的精灵。之后显示修改过的精灵。
3:总是显示深色模组精灵。
4: 始终显示模组化精灵。应用光泽:
应用"锐利目光"技能的"稀有光泽"效果。如果你启用了非暗色鱼类精灵,这个功能可能毫无意义。
此光芒效果也得到了改进,现在能正确在水面上方绘制并反射稀有度的颜色方案(取代了之前带有错误且单一颜色的表现)。几率:
根据你的钓鱼等级,会获得一个钓鱼生成的小额加成。
根据所使用的鱼饵,鱼群出现的概率会有小幅提升。
应用于整体鱼类生成几率的倍数。
这是我为了自己游玩快速拼凑的东西,不太可能继续开发或维护。
安装:
- 下载并安装 最新版本的 BepInEx。
- 下载 此模组从 文件 中,并将模组的 .dll 文件 解压 到你的 BepInEx\plugins 文件夹中。
兼容性:
- 应当与大多数钓鱼模组兼容。
- 除非他们编辑同一位置,在这种情况下,关闭某些功能可以帮助解决。
来源:
点击展开源代码
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using DG.Tweening;
using HarmonyLib;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using UnityEngine;
using Wish;
[BepInPlugin(MOD_GUID, MOD_NAME, MOD_VER)]
public class GenericPortraitsPlugin : BaseUnityPlugin
{
private const string MOD_GUID = "barteke22.sunhaven.fish_tweaks";
private const string MOD_VER = "1.0.2";
private const string MOD_NAME = "鱼微调";
private Harmony _harmony = new Harmony("barteke22.sunhaven.fish_tweaks");
public static ManualLogSource logger;
private static ConfigEntry<bool> conf_visual_override;
private static ConfigEntry<SpriteMode> conf_visual_applySprite;
private static ConfigEntry<bool> conf_visual_applyShine;
private static ConfigEntry<bool> conf_visual_keepUncaughtDark;
private static ConfigEntry<bool> conf_chance_levelScale;
private static ConfigEntry<bool> conf_chance_baitBonus;
private static ConfigEntry<float> conf_chance_mult;
private static Dictionary<MeshGenerator, FishEntry> _meshToFishDict = new Dictionary<MeshGenerator, FishEntry>();
private void Awake()
{
logger = Logger;
try
{
Config.Bind("Info", "Info", "<-- 悬停以查看信息。", "这些设置中的大部分仅在你更换地点后才会生效。禁用'几率'下的所有选项将禁用对FishSpawnManager Initialize()和SpawnRoutine()代码的任何前缀修改。");
conf_visual_override = Config.Bind("Visual", "覆盖咬钩", true, "在BiteRoutine前添加前缀,将咬钩动画应用于鱼类精灵。建议保持此选项启用,除非你使用了另一个修改BiteRoutine的钓鱼模组。");
conf_visual_applySprite = Config.Bind("Visual", "Apply Sprite", SpriteMode.Dark_KeenEye_Light, "0: 永远不使用修改过的精灵图。" + Environment.NewLine
+ "1: 在获得‘敏锐目光’技能之前,不要使用修改的精灵。之后显示暗色的修改精灵。" + Environment.NewLine
+ "2: 在获得'锐利之眼'技能前,显示经过黑暗修改的精灵图。之后显示经过修改的精灵图。" + Environment.NewLine
+ "3: 始终显示黑暗模组精灵。" + Environment.NewLine
+ "4:始终显示模组精灵。");
conf_visual_applyShine = Config.Bind("Visual", "Apply Shine", true, "应用来自'锐眼'技能的'稀有度闪光'。如果启用了非深色鱼类精灵,这可能会显得多余。");
conf_visual_keepUncaughtDark = Config.Bind("Visual", "Keep Uncaught Dark", true, "无论是否拥有“锐眼”技能,始终将未捕获的鱼类保持为暗色。");
conf_chance_levelScale = Config.Bind("Chance", "Level Bonus", true, "根据你的钓鱼等级,对鱼群生成几率施加一个微小的加成。");
conf_chance_baitBonus = Config.Bind("Chance", "Bait Bonus", true, "根据所使用的诱饵,对鱼类生成概率施加一个小额加成。");
conf_chance_mult = Config.Bind("Chance", "Multiplier", 1f, "应用于整体鱼生成几率的乘数。非常高的乘数可能导致比正常情况更多的鱼生成。为平衡这一点,某些数值下生成延迟可能会增加。");
_harmony.PatchAll();
logger.LogInfo(MOD_GUID + " v" + MOD_VER + " loaded.");
}
catch (Exception e)
{
logger.LogError("** Awake FATAL - " + e);
}
}
public static void LogError(Exception e)
{
logger.LogError(e.Message + Environment.NewLine + e.StackTrace);
}
[HarmonyPatch(typeof(Fish), "Start")]
class HarmonyPatch_Fish_Start
{
private static bool Prefix(Fish __instance)
{
try
{
FishData fishData = __instance.fishData;
if (conf_visual_applyShine.Value)
{
if (fishData.rarity > ItemRarity.Uncommon && GameSave.Fishing.GetNode("Fishing8b"))
{
var particles = SingletonBehaviour<Prefabs>.Instance.fishShineParticles;
particles.GetComponent<ParticleSystemRenderer>().sortingOrder = 1000;
var main = particles.main;
switch (fishData.rarity)
{
case ItemRarity.Rare:
main.startColor = new Color(0f, 50f, 255f);
break;
case ItemRarity.Epic:
main.startColor = new Color(144f, 0f, 255f);
break;
case ItemRarity.Legendary:
main.startColor = new Color(255f, 200f, 0f);
break;
}
Instantiate(particles, __instance.transform.position + new Vector3(0f, -1f, -1f), Quaternion.identity, __instance.transform);
}
}
typeof(Fish).GetProperty("Caught").SetValue(__instance, false);
if (conf_visual_applySprite.Value != 0 && (conf_visual_applySprite.Value > SpriteMode.Disabled_KeenEye_Dark || GameSave.Fishing.GetNode("Fishing8b")))
{
var offsetBugFix = "ElvenForest2".Equals(ScenePortalManager.ActiveSceneName, StringComparison.Ordinal) ? -1.2f : 0.1f;
var obj = Instantiate(fishData.fishVisualPrefab, __instance.transform.position + new Vector3(0f, 0f, offsetBugFix), Quaternion.identity, __instance.transform);
var meshGen = obj.GetComponentInChildren<MeshGenerator>();
meshGen.transform.localScale = new Vector3(0.6f, 0.6f, 0.6f);
meshGen.GetComponent<MeshRenderer>().material.color = new Color(0f, 0f, 0f, 0.65f);
_meshToFishDict[meshGen] = new FishEntry() { fish = __instance, oldDirection = __instance.FacingDirection };
__instance.Graphics.position = new Vector3(0f, 0f, -99999f);
}
#if DEBUG
logger.LogWarning(fishData.Name + " (" + fishData.rarity + ") in " + ScenePortalManager.ActiveSceneName);
#endif
return false;
}
catch (Exception e)
{
LogError(e);
return true;
}
}
}
[HarmonyPatch(typeof(FishSpawnManager), "Initialize")]
class HarmonyPatch_FishSpawnManager_Initialize
{
private static bool Prefix(FishSpawnManager __instance)
{
try
{
if (conf_chance_levelScale.Value || conf_chance_baitBonus.Value || conf_chance_mult.Value != 1f)
{
var _spawnRoutine = typeof(FishSpawnManager).GetField("_spawnRoutine", BindingFlags.NonPublic | BindingFlags.Instance);
var _fishSpawners = typeof(FishSpawnManager).GetField("_fishSpawners", BindingFlags.NonPublic | BindingFlags.Instance);
var currentFishDictionary = (Dictionary<int, Fish>)typeof(FishSpawnManager).GetField("currentFishDictionary", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance);
if (_spawnRoutine.GetValue(__instance) is Coroutine r)
{
__instance.StopCoroutine(r);
}
_fishSpawners.SetValue(__instance, UnityEngine.Object.FindObjectsOfType<FishSpawner>());
var _fishSpawnersVal = (FishSpawner[])_fishSpawners.GetValue(__instance);
for (int i = 0; i < _fishSpawnersVal.Length; i++)
{
FishSpawner fishSpawner = _fishSpawnersVal[i];
fishSpawner.ID = (ushort)(i + ScenePortalManager.ActiveSceneIndex * 100);
if (currentFishDictionary.TryGetValue(fishSpawner.ID, out var value))
{
fishSpawner.CurrentFish = value;
}
}
_spawnRoutine.SetValue(__instance, __instance.StartCoroutine(FishSpawnManager_SpawnRoutine.SpawnRoutine(__instance)));
return false;
}
return true;
}
catch (Exception e)
{
LogError(e);
return true;
}
}
}
[HarmonyPatch(typeof(MeshGenerator), "ChangeSprite")]
class HarmonyPatch_MeshGenerator_ChangeSprite
{
private static void Postfix(MeshGenerator __instance)
{
try
{
if (_meshToFishDict.TryGetValue(__instance, out var fishEntry))
{
var curDir = fishEntry.fish.FacingDirection;
var oldDir = fishEntry.oldDirection;
fishEntry.oldDirection = curDir;
if (curDir < Direction.East)
{
if (curDir != oldDir) __instance.flip = UnityEngine.Random.Range(0, 2) == 1;
if ((__instance.flip && curDir == Direction.North) || (!__instance.flip && curDir == Direction.South))
{
__instance.transform.eulerAngles = new Vector3(-5f, -80f, -60f);
}
else
{
__instance.transform.eulerAngles = new Vector3(-5f, 80f, 60f);
}
}
else
{
__instance.flip = curDir == Direction.West;
__instance.transform.eulerAngles = Vector3.zero;
}
if (conf_visual_applySprite.Value == SpriteMode.Disabled_KeenEye_Dark
|| conf_visual_applySprite.Value == SpriteMode.Always_Dark
|| (conf_visual_applySprite.Value == SpriteMode.Dark_KeenEye_Light && !GameSave.Fishing.GetNode("Fishing8b"))
|| (conf_visual_keepUncaughtDark.Value && !GameSave.CurrentCharacter.Encylopdeia.ContainsKey((short)fishEntry.fish.fishData.id)))
{
__instance.GetComponent<MeshRenderer>().material.color = new Color(0f, 0f, 0f, 0.65f);
}
}
}
catch (Exception e)
{
LogError(e);
}
}
}
[HarmonyPatch(typeof(MeshGenerator), "OnDestroy")]
class HarmonyPatch_MeshGenerator_OnDestroy
{
private static void Postfix(MeshGenerator __instance)
{
try
{
_meshToFishDict.Remove(__instance);
}
catch (Exception e)
{
LogError(e);
}
}
}
[HarmonyPatch(typeof(Fish), "BiteRoutine")]
class HarmonyPatch_Fish_BiteRoutine
{
private static bool Prefix(Fish __instance, ref IEnumerator __result)
{
try
{
if (conf_visual_override.Value)
{
__result = BiteRoutine(__instance);
return false;
}
return true;
}
catch (Exception e)
{
LogError(e);
return true;
}
}
private static IEnumerator BiteRoutine(Fish __instance)
{
if (!__instance.isActiveAndEnabled)
yield break;
float seconds = UnityEngine.Random.Range(0.1f, 0.2f);
yield return new WaitForSeconds(seconds);
var _targetBobber = typeof(Fish).GetField("_targetBobber", BindingFlags.NonPublic | BindingFlags.Instance);
var _targetBobberInstance = (Bobber)_targetBobber.GetValue(__instance);
Transform graphics = null;
if (_meshToFishDict.FirstOrDefault(f => f.Value.fish == __instance) is KeyValuePair<MeshGenerator, FishEntry> fishEntry)
{
graphics = fishEntry.Value.fish.transform;
}
else graphics = __instance.Graphics;
if (Wish.Utilities.Chance(0.65f))
{
_targetBobberInstance.SmallBite();
float num = UnityEngine.Random.Range(1f, 2f);
switch (__instance.FacingDirection)
{
case Direction.North:
graphics.DOBlendableLocalMoveBy(new Vector3(0f, -0.35f, 0f), num);
break;
case Direction.South:
graphics.DOBlendableLocalMoveBy(new Vector3(0f, 0.35f, 0f), num);
break;
case Direction.East:
graphics.DOBlendableLocalMoveBy(new Vector3(-0.35f, 0f, 0f), num);
break;
case Direction.West:
graphics.DOBlendableLocalMoveBy(new Vector3(0.35f, 0f, 0f), num);
break;
}
yield return new WaitForSeconds(num);
switch (__instance.FacingDirection)
{
case Direction.North:
graphics.DOBlendableLocalMoveBy(new Vector3(0f, 0.35f, 0f), 0.275f);
break;
case Direction.South:
graphics.DOBlendableLocalMoveBy(new Vector3(0f, -0.35f, 0f), 0.275f);
break;
case Direction.East:
graphics.DOBlendableLocalMoveBy(new Vector3(0.35f, 0f, 0f), 0.275f);
break;
case Direction.West:
graphics.DOBlendableLocalMoveBy(new Vector3(-0.35f, 0f, 0f), 0.275f);
break;
}
yield return new WaitForSeconds(0.275f);
if (Wish.Utilities.Chance(0.4f))
{
_targetBobberInstance.SmallBite();
float num2 = UnityEngine.Random.Range(0.7f, 2.6f);
switch (__instance.FacingDirection)
{
case Direction.North:
graphics.DOBlendableLocalMoveBy(new Vector3(0f, -0.35f, 0f), num2);
break;
case Direction.South:
graphics.DOBlendableLocalMoveBy(new Vector3(0f, 0.35f, 0f), num2);
break;
case Direction.East:
graphics.DOBlendableLocalMoveBy(new Vector3(-0.35f, 0f, 0f), num2);
break;
case Direction.West:
graphics.DOBlendableLocalMoveBy(new Vector3(0.35f, 0f, 0f), num2);
break;
}
yield return new WaitForSeconds(num2);
switch (__instance.FacingDirection)
{
case Direction.North:
graphics.DOBlendableLocalMoveBy(new Vector3(0f, 0.35f, 0f), 0.275f);
break;
case Direction.South:
graphics.DOBlendableLocalMoveBy(new Vector3(0f, -0.35f, 0f), 0.275f);
break;
case Direction.East:
graphics.DOBlendableLocalMoveBy(new Vector3(0.35f, 0f, 0f), 0.275f);
break;
case Direction.West:
graphics.DOBlendableLocalMoveBy(new Vector3(-0.35f, 0f, 0f), 0.275f);
break;
}
yield return new WaitForSeconds(0.275f);
}
_targetBobberInstance.Bite();
_targetBobberInstance.FishingRod.HasFish(__instance);
typeof(Fish).GetField("fishRunAwayTween", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(__instance, DOVirtual.DelayedCall(1f, delegate
{
if (!(_targetBobber != null) || !_targetBobberInstance.FishingRod.Reeling)
__instance.Flee();
var canBite = typeof(Fish).GetField("canBite", BindingFlags.NonPublic | BindingFlags.Instance);
canBite.SetValue(__instance, false);
DOVirtual.DelayedCall(4f, delegate
{
canBite.SetValue(__instance, true);
});
if (_targetBobber != null)
{
if (_targetBobberInstance.FishingRod.CurrentFish == __instance)
_targetBobberInstance.FishingRod.CurrentFish = null;
if (_targetBobberInstance.FishingRod.TargetFish == __instance)
_targetBobberInstance.FishingRod.TargetFish = null;
}
__instance.SetTarget(__instance.transform.position + new Vector3(UnityEngine.Random.Range(-1f, 1f), UnityEngine.Random.Range(-1f, 1f)).normalized * 2.25f, 0f);
_targetBobberInstance?.FailMiniGame();
_targetBobber.SetValue(__instance, null);
}));
}
}
}
private class FishSpawnManager_SpawnRoutine
{
public static IEnumerator SpawnRoutine(FishSpawnManager __instance)
{
var time = Math.Max(0.25f, 0.25f * (conf_chance_mult.Value < 3f ? conf_chance_mult.Value / 1.5f : conf_chance_mult.Value / 3));
var ResetCurrentFishDictionary = typeof(FishSpawnManager).GetMethod("ResetCurrentFishDictionary", BindingFlags.NonPublic | BindingFlags.Instance);
var _fishSpawners = (FishSpawner[])typeof(FishSpawnManager).GetField("_fishSpawners", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance);
var numFish = typeof(FishSpawnManager).GetField("numFish", BindingFlags.NonPublic | BindingFlags.Instance);
var buffs = (Dictionary<BuffType, Buff>)typeof(Player).GetField("_buffs", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(Player.Instance);
while (true)
{
var skillBonus = (conf_chance_levelScale.Value ? (SingletonBehaviour<GameSave>.Instance.CurrentSave.characterData.Professions[ProfessionType.Fishing].level * 0.0003f - 0.0006f) : 0f);
ResetCurrentFishDictionary.Invoke(__instance, null);
if (_fishSpawners.Length != 0)
{
var numFishVal = (int)numFish.GetValue(__instance);
float fishSpawnMultiplier = 1f;
if (GameSave.Fishing.GetNode("Fishing9b"))
{
fishSpawnMultiplier = 1.05f + 0.05f * (float)GameSave.Fishing.GetNodeAmount("Fishing9b");
}
if (numFishVal < 10)
{
List<int> list = Enumerable.Range(0, _fishSpawners.Length).ToList();
list.Shuffle();
foreach (int item in list)
{
if (item >= 0 && item < _fishSpawners.Length)
{
FishSpawner fishSpawner = _fishSpawners[item];
if (fishSpawner == null) continue;
float num = (0.03f * fishSpawnMultiplier * FishSpawnManager.fishSpawnGlobalMultiplier + skillBonus) * conf_chance_mult.Value;
if (conf_chance_baitBonus.Value)
{
num += 0.05f * buffs.Count(f => nameof(f.Key).EndsWith("Bait", StringComparison.Ordinal) && !f.Value.Complete());
}
if ((bool)Player.Instance)
{
FishingRod fishingRod = Player.Instance.FishingRod;
if ((bool)fishingRod)
{
switch (fishingRod.fishingRodType)
{
case FishingRodType.Withergate:
if (SceneSettingsManager.Instance.GetCurrentSceneSettings != null && SceneSettingsManager.Instance.GetCurrentSceneSettings.townType == TownType.Withergate)
num *= 1.15f;
break;
case FishingRodType.Nelvari:
if (SceneSettingsManager.Instance.GetCurrentSceneSettings != null && SceneSettingsManager.Instance.GetCurrentSceneSettings.townType == TownType.Nelvari)
num *= 1.15f;
break;
}
}
}
float num2 = ((numFishVal == 0) ? 1.25f : (1f / numFishVal));
var currentFishDictionary = (Dictionary<int, Fish>)typeof(FishSpawnManager).GetField("currentFishDictionary", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(__instance);
if (FishSpawnManager.CanSpawnFish && fishSpawner.CurrentFish == null && (!currentFishDictionary.TryGetValue(fishSpawner.ID, out var value) || value == null) && UnityEngine.Random.value < num * num2)
{
FishData fish = fishSpawner.GetFish();
if ((bool)fish)
{
Vector3 position = fishSpawner.transform.position;
__instance.SpawnFish(fish.name, position, fishSpawner.ID);
}
}
if ((bool)fishSpawner.CurrentFish)
{
numFishVal++;
numFish.SetValue(__instance, numFishVal);
}
}
}
}
yield return new WaitForSeconds(time + skillBonus);
}
else
{
yield return new WaitForSeconds(time + (skillBonus * 10f));
}
}
}
}
enum SpriteMode
{
[Description("0:已禁用。")]
Disabled,
[Description("1:此前禁用,此后黑暗。")]
Disabled_KeenEye_Dark,
[Description("2: 之前黑暗,之后光明。")]
Dark_KeenEye_Light,
[Description("3: 始终黑暗。")]
Always_Dark,
[Description("4: 始终明亮。")]
Always_Light
}
private class FishEntry
{
public Fish fish;
public Direction oldDirection;
}
}
还没有人评论,去客户端里说两句吧。
评论在新手盒子客户端中发表,这里同步展示。