forked from WhiteFang5/VMods
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added the current VMods code and a README.md
- Loading branch information
1 parent
2e911de
commit e453611
Showing
46 changed files
with
7,416 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
using BepInEx.Configuration; | ||
|
||
namespace VMods.BloodRefill | ||
{ | ||
public static class BloodRefillConfig | ||
{ | ||
#region Properties | ||
|
||
public static ConfigEntry<bool> BloodRefillEnabled { get; private set; } | ||
|
||
public static ConfigEntry<bool> BloodRefillRequiresFeeding { get; private set; } | ||
|
||
public static ConfigEntry<bool> BloodRefillRequiresSameBloodType { get; private set; } | ||
|
||
public static ConfigEntry<bool> BloodRefillExcludeVBloodFromSameBloodTypeCheck { get; private set; } | ||
|
||
public static ConfigEntry<float> BloodRefillDifferentBloodTypeMultiplier { get; private set; } | ||
|
||
public static ConfigEntry<int> BloodRefillVBloodRefillType { get; private set; } | ||
|
||
public static ConfigEntry<float> BloodRefillVBloodRefillMultiplier { get; private set; } | ||
|
||
public static ConfigEntry<bool> BloodRefillRandomRefill { get; private set; } | ||
|
||
public static ConfigEntry<float> BloodRefillAmount { get; private set; } | ||
|
||
public static ConfigEntry<float> BloodRefillMultiplier { get; private set; } | ||
|
||
#endregion | ||
|
||
#region Public Methods | ||
|
||
public static void Initialize(ConfigFile config) | ||
{ | ||
BloodRefillEnabled = config.Bind(nameof(BloodRefillConfig), nameof(BloodRefillEnabled), false, "Enabled/disable the blood refilling system."); | ||
BloodRefillRequiresFeeding = config.Bind(nameof(BloodRefillConfig), nameof(BloodRefillRequiresFeeding), true, "When enabled, blood can only be refilled when feeding (i.e. when aborting the feed)."); | ||
BloodRefillRequiresSameBloodType = config.Bind(nameof(BloodRefillConfig), nameof(BloodRefillRequiresSameBloodType), false, "When enabled, blood can only be refilled when the target has the same blood type."); | ||
BloodRefillExcludeVBloodFromSameBloodTypeCheck = config.Bind(nameof(BloodRefillConfig), nameof(BloodRefillExcludeVBloodFromSameBloodTypeCheck), true, "When enabled, V-blood is excluded from the 'same blood type' check (i.e. it's always considered to be 'the same blood type' as the player's blood type)."); | ||
BloodRefillVBloodRefillType = config.Bind(nameof(BloodRefillConfig), nameof(BloodRefillVBloodRefillType), 2, "0 = disabled (i.e. normal refill); 1 = fully refill; 2 = refill based on V-blood monster level."); | ||
BloodRefillVBloodRefillMultiplier = config.Bind(nameof(BloodRefillConfig), nameof(BloodRefillVBloodRefillMultiplier), 0.1f, $"[Only applies when {nameof(BloodRefillVBloodRefillType)} is set to 2] The multiplier used in the v-blood refill calculation ('EnemyLevel' * '{nameof(BloodRefillVBloodRefillMultiplier)}' * '{nameof(BloodRefillMultiplier)}')."); | ||
BloodRefillRandomRefill = config.Bind(nameof(BloodRefillConfig), nameof(BloodRefillRandomRefill), true, "When enabled, the amount of refilled blood is randomized (between 1 and the calculated refillable amount)."); | ||
BloodRefillAmount = config.Bind(nameof(BloodRefillConfig), nameof(BloodRefillAmount), 2.0f, "The maximum amount of blood to refill with no level difference, a matching blood type and quality (Expressed in Litres of blood)."); | ||
BloodRefillMultiplier = config.Bind(nameof(BloodRefillConfig), nameof(BloodRefillMultiplier), 1.0f, $"The multiplier used in the blood refill calculation. [Formula: (('Enemy Level' / 'Player Level') * ((100 - ('Player Blood Quality %' - 'Enemy Blood Quality %')) / 100)) * '{nameof(BloodRefillAmount)}' * '(If applicable) {nameof(BloodRefillDifferentBloodTypeMultiplier)}' * '{nameof(BloodRefillMultiplier)}']"); | ||
BloodRefillDifferentBloodTypeMultiplier = config.Bind(nameof(BloodRefillConfig), nameof(BloodRefillDifferentBloodTypeMultiplier), 0.1f, $"The multiplier used in the blood refill calculation as a penalty for feeding on a different blood type (only works when {nameof(BloodRefillRequiresSameBloodType)} is disabled)."); | ||
} | ||
|
||
#endregion | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using HarmonyLib; | ||
using ProjectM; | ||
using Unity.Collections; | ||
using Wetstone.API; | ||
|
||
namespace VMods.BloodRefill | ||
{ | ||
[HarmonyPatch] | ||
public static class DeathHook | ||
{ | ||
#region Events | ||
|
||
public delegate void DeathEventHandler(DeathEvent deathEvent); | ||
public static event DeathEventHandler DeathEvent; | ||
private static void FireDeathEvent(DeathEvent deathEvent) => DeathEvent?.Invoke(deathEvent); | ||
|
||
#endregion | ||
|
||
#region Public Methods | ||
|
||
[HarmonyPatch(typeof(DeathEventListenerSystem), nameof(DeathEventListenerSystem.OnUpdate))] | ||
[HarmonyPostfix] | ||
private static void OnUpdate(DeathEventListenerSystem __instance) | ||
{ | ||
if(!VWorld.IsServer || __instance._DeathEventQuery == null) | ||
{ | ||
return; | ||
} | ||
|
||
NativeArray<DeathEvent> deathEvents = __instance._DeathEventQuery.ToComponentDataArray<DeathEvent>(Allocator.Temp); | ||
foreach(DeathEvent deathEvent in deathEvents) | ||
{ | ||
FireDeathEvent(deathEvent); | ||
} | ||
} | ||
|
||
#endregion | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
using BepInEx; | ||
using BepInEx.IL2CPP; | ||
using HarmonyLib; | ||
using System.Reflection; | ||
using VMods.Shared; | ||
using Wetstone.API; | ||
|
||
namespace VMods.BloodRefill | ||
{ | ||
[BepInPlugin(PluginInfo.PLUGIN_GUID, PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)] | ||
[BepInDependency("xyz.molenzwiebel.wetstone")] | ||
[Reloadable] | ||
public class Plugin : BasePlugin | ||
{ | ||
#region Variables | ||
|
||
private Harmony _hooks; | ||
|
||
#endregion | ||
|
||
#region Public Methods | ||
|
||
public sealed override void Load() | ||
{ | ||
if(VWorld.IsClient) | ||
{ | ||
Log.LogMessage($"{PluginInfo.PLUGIN_NAME} only needs to be installed server side."); | ||
return; | ||
} | ||
Utils.Initialize(Log, PluginInfo.PLUGIN_NAME); | ||
|
||
CommandSystemConfig.Initialize(Config); | ||
BloodRefillConfig.Initialize(Config); | ||
|
||
CommandSystem.Initialize(); | ||
BloodRefillSystem.Initialize(); | ||
|
||
_hooks = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly()); | ||
|
||
Log.LogInfo($"Plugin {PluginInfo.PLUGIN_NAME} (v{PluginInfo.PLUGIN_VERSION}) is loaded!"); | ||
} | ||
|
||
public sealed override bool Unload() | ||
{ | ||
if(VWorld.IsClient) | ||
{ | ||
return true; | ||
} | ||
_hooks?.UnpatchSelf(); | ||
BloodRefillSystem.Deinitialize(); | ||
CommandSystem.Deinitialize(); | ||
Config.Clear(); | ||
Utils.Deinitialize(); | ||
return true; | ||
} | ||
|
||
#endregion | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,242 @@ | ||
using ProjectM; | ||
using ProjectM.Network; | ||
using System; | ||
using System.Linq; | ||
using Unity.Entities; | ||
using VMods.Shared; | ||
using Wetstone.API; | ||
using Random = UnityEngine.Random; | ||
|
||
namespace VMods.BloodRefill | ||
{ | ||
public static class BloodRefillSystem | ||
{ | ||
#region Public Methods | ||
|
||
public static void Initialize() | ||
{ | ||
DeathHook.DeathEvent += OnDeath; | ||
} | ||
|
||
public static void Deinitialize() | ||
{ | ||
DeathHook.DeathEvent -= OnDeath; | ||
} | ||
|
||
#endregion | ||
|
||
#region Private Methods | ||
|
||
private static void OnDeath(DeathEvent deathEvent) | ||
{ | ||
EntityManager entityManager = VWorld.Server.EntityManager; | ||
|
||
// Make sure a player killed an appropriate monster | ||
if(!BloodRefillConfig.BloodRefillEnabled.Value || | ||
!entityManager.HasComponent<PlayerCharacter>(deathEvent.Killer) || | ||
!entityManager.HasComponent<Equipment>(deathEvent.Killer) || | ||
!entityManager.HasComponent<Blood>(deathEvent.Killer) || | ||
!entityManager.HasComponent<Movement>(deathEvent.Died) || | ||
!entityManager.HasComponent<UnitLevel>(deathEvent.Died) || | ||
!entityManager.HasComponent<BloodConsumeSource>(deathEvent.Died)) | ||
{ | ||
return; | ||
} | ||
|
||
PlayerCharacter playerCharacter = entityManager.GetComponentData<PlayerCharacter>(deathEvent.Killer); | ||
Equipment playerEquipment = entityManager.GetComponentData<Equipment>(deathEvent.Killer); | ||
Blood playerBlood = entityManager.GetComponentData<Blood>(deathEvent.Killer); | ||
UnitLevel unitLevel = entityManager.GetComponentData<UnitLevel>(deathEvent.Died); | ||
BloodConsumeSource bloodConsumeSource = entityManager.GetComponentData<BloodConsumeSource>(deathEvent.Died); | ||
|
||
#if DEBUG | ||
Utils.Logger.LogMessage($"DE.Killer = {deathEvent.Killer.Index}"); | ||
Utils.Logger.LogMessage($"DE.Died = {deathEvent.Died.Index}"); | ||
Utils.Logger.LogMessage($"DE.Source = {deathEvent.Source.Index}"); | ||
#endif | ||
|
||
Entity userEntity = playerCharacter.UserEntity._Entity; | ||
User user = entityManager.GetComponentData<User>(userEntity); | ||
|
||
bool killedByFeeding = deathEvent.Killer.Index == deathEvent.Source.Index; | ||
|
||
if(!playerBlood.BloodType.ParseBloodType(out BloodType playerBloodType)) | ||
{ | ||
// Invalid/unknown blood type | ||
return; | ||
} | ||
|
||
if(!bloodConsumeSource.UnitBloodType.ParseBloodType(out BloodType bloodType)) | ||
{ | ||
// Invalid/unknown blood type | ||
return; | ||
} | ||
|
||
bool isVBlood = bloodType == BloodType.VBlood; | ||
|
||
// Allow V-Bloods to skip the 'killed by feeding' check, otherwise additional feeders won't get a refill. | ||
if(!isVBlood && BloodRefillConfig.BloodRefillRequiresFeeding.Value && !killedByFeeding) | ||
{ | ||
// Can only gain blood when killing the enemy while feeding (i.e. abort the feed) | ||
return; | ||
} | ||
|
||
bool isSameBloodType = playerBloodType == bloodType || (BloodRefillConfig.BloodRefillExcludeVBloodFromSameBloodTypeCheck.Value && isVBlood); | ||
|
||
if(BloodRefillConfig.BloodRefillRequiresSameBloodType.Value && !isSameBloodType) | ||
{ | ||
// Can only gain blood when killing an enemy of the same blood type | ||
return; | ||
} | ||
|
||
float bloodTypeMultiplier = isSameBloodType ? 1f : BloodRefillConfig.BloodRefillDifferentBloodTypeMultiplier.Value; | ||
|
||
float playerLevel = playerEquipment.WeaponLevel + playerEquipment.ArmorLevel + playerEquipment.SpellLevel; | ||
float enemyLevel = unitLevel.Level; | ||
|
||
#if DEBUG | ||
Utils.Logger.LogMessage($"Player Blood Quality: {playerBlood.Quality}"); | ||
Utils.Logger.LogMessage($"Player Blood Value: {playerBlood.Value}"); | ||
Utils.Logger.LogMessage($"Player Level: {playerLevel}"); | ||
|
||
Utils.Logger.LogMessage($"Enemy Blood Quality: {bloodConsumeSource.BloodQuality}"); | ||
Utils.Logger.LogMessage($"Enemy Level {enemyLevel}"); | ||
#endif | ||
|
||
float levelRatio = enemyLevel / playerLevel; | ||
|
||
float qualityRatio = (100f - (playerBlood.Quality - bloodConsumeSource.BloodQuality)) / 100f; | ||
|
||
float refillRatio = levelRatio * qualityRatio; | ||
|
||
// Config amount is expressed in 'Litres of blood' -> the game's formule is 'blood value / 10', hence the * 10 multiplier here. | ||
float refillAmount = BloodRefillConfig.BloodRefillAmount.Value * 10f * refillRatio; | ||
|
||
refillAmount *= bloodTypeMultiplier; | ||
|
||
#if DEBUG | ||
Utils.Logger.LogMessage($"Lvl Ratio: {levelRatio}"); | ||
Utils.Logger.LogMessage($"Quality Ratio: {qualityRatio}"); | ||
Utils.Logger.LogMessage($"Refill Ratio: {refillRatio}"); | ||
Utils.Logger.LogMessage($"Blood Type Multiplier: {bloodTypeMultiplier}"); | ||
Utils.Logger.LogMessage($"Refill Amount: {refillAmount}"); | ||
#endif | ||
|
||
if(BloodRefillConfig.BloodRefillRandomRefill.Value) | ||
{ | ||
refillAmount = Random.RandomRange(1f, refillAmount); | ||
|
||
#if DEBUG | ||
Utils.Logger.LogMessage($"Refill Roll: {refillAmount}"); | ||
#endif | ||
} | ||
|
||
if(isVBlood) | ||
{ | ||
switch(BloodRefillConfig.BloodRefillVBloodRefillType.Value) | ||
{ | ||
case 1: // V-blood fully refills the blood pool | ||
refillAmount = playerBlood.MaxBlood - playerBlood.Value; | ||
break; | ||
|
||
case 2: // V-blood refills based on the unit's level | ||
refillAmount = enemyLevel * BloodRefillConfig.BloodRefillVBloodRefillMultiplier.Value; | ||
break; | ||
} | ||
} | ||
|
||
refillAmount *= BloodRefillConfig.BloodRefillMultiplier.Value; | ||
|
||
if(refillAmount > 0f) | ||
{ | ||
int roundedRefillAmount = (int)Math.Ceiling(refillAmount); | ||
|
||
if(roundedRefillAmount > 0) | ||
{ | ||
#if DEBUG | ||
Utils.Logger.LogMessage($"New Blood Amount: {playerBlood.Value + roundedRefillAmount}"); | ||
#endif | ||
|
||
float newTotalBlood = Math.Min(playerBlood.MaxBlood, playerBlood.Value + roundedRefillAmount); | ||
float actualBloodGained = newTotalBlood - playerBlood.Value; | ||
float refillAmountInLitres = (int)(actualBloodGained * 10f) / 100f; | ||
float newTotalBloodInLitres = (int)Math.Round(newTotalBlood) / 10f; | ||
Utils.SendMessage(userEntity, $"+{refillAmountInLitres}L Blood ({newTotalBloodInLitres}L)", ServerChatMessageType.Lore); | ||
|
||
ChangeBloodType(user, playerBloodType, playerBlood.Quality, roundedRefillAmount); | ||
return; | ||
} | ||
} | ||
|
||
Utils.SendMessage(userEntity, $"No blood gained from the enemy.", ServerChatMessageType.Lore); | ||
} | ||
|
||
private static void ChangeBloodType(User user, BloodType bloodType, float quality, int addAmount) | ||
{ | ||
ChangeBloodDebugEvent bloodChangeEvent = new() | ||
{ | ||
Source = bloodType.ToPrefabGUID(), | ||
Quality = quality, | ||
Amount = addAmount, | ||
}; | ||
VWorld.Server.GetExistingSystem<DebugEventsSystem>().ChangeBloodEvent(user.Index, ref bloodChangeEvent); | ||
} | ||
|
||
[Command("setblood", "setblood <blood-type> <blood-quality> [<gain-amount>]", "Sets your blood type to the specified blood-type and blood-quality, and optionally adds a given amount of blood (in Litres).", true)] | ||
private static void OnSetBloodCommand(Command command) | ||
{ | ||
var user = command.User; | ||
var argCount = command.Args.Length; | ||
if(argCount >= 2) | ||
{ | ||
var searchBloodType = command.Args[0]; | ||
var validBloodTypes = BloodTypeExtensions.BloodTypeToPrefabGUIDMapping.Keys.ToList(); | ||
if(Enum.TryParse(searchBloodType.ToLowerInvariant(), true, out BloodType bloodType) && validBloodTypes.Contains(bloodType)) | ||
{ | ||
var searchBloodQuality = command.Args[1]; | ||
if(int.TryParse(searchBloodQuality.Replace("%", string.Empty), out var bloodQuality) && bloodQuality >= 1 && bloodQuality <= 100) | ||
{ | ||
float? addBloodAmount = null; | ||
if(argCount >= 3) | ||
{ | ||
var searchLitres = command.Args[2]; | ||
if(float.TryParse(searchLitres.Replace("L", string.Empty), out float parsedAddBloodAmount) && parsedAddBloodAmount >= -10f && parsedAddBloodAmount <= 10f) | ||
{ | ||
addBloodAmount = parsedAddBloodAmount; | ||
} | ||
else | ||
{ | ||
user.SendSystemMessage($"<color=#ff0000>Invalid gain-amount '{searchBloodQuality}'. Should be between -10 and 10</color>"); | ||
} | ||
} | ||
else | ||
{ | ||
addBloodAmount = 10f; | ||
} | ||
|
||
if(addBloodAmount.HasValue) | ||
{ | ||
ChangeBloodType(user, bloodType, bloodQuality, (int)(addBloodAmount.Value * 10f)); | ||
user.SendSystemMessage($"Changed blood type to <color=#ff0000>{bloodQuality}%</color> <color=#ffffff>{searchBloodType}</color> and added <color=#ff0000>{addBloodAmount.Value}L</color>"); | ||
} | ||
} | ||
else | ||
{ | ||
user.SendSystemMessage($"<color=#ff0000>Invalid blood-quality '{searchBloodQuality}'. Should be between 1 and 100</color>"); | ||
} | ||
} | ||
else | ||
{ | ||
user.SendSystemMessage($"<color=#ff0000>Invalid blood-type '{searchBloodType}'. Options are: {string.Join(", ", validBloodTypes.Select(x => x.ToString()))}</color>"); | ||
} | ||
} | ||
else | ||
{ | ||
CommandSystem.SendInvalidCommandMessage(command); | ||
} | ||
command.Use(); | ||
} | ||
|
||
#endregion | ||
} | ||
} |
Oops, something went wrong.