Working mod
This commit is contained in:
parent
cdf05260aa
commit
5285add77e
@ -1,16 +1,13 @@
|
|||||||
using BepInEx;
|
using BepInEx;
|
||||||
|
using BepInEx.Configuration;
|
||||||
using R2API;
|
using R2API;
|
||||||
|
using R2API.Utils;
|
||||||
using RoR2;
|
using RoR2;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.AddressableAssets;
|
using UnityEngine.Networking;
|
||||||
|
|
||||||
namespace AntiYoink
|
namespace AntiYoink
|
||||||
{
|
{
|
||||||
// This is an example plugin that can be put in
|
|
||||||
// BepInEx/plugins/ExamplePlugin/ExamplePlugin.dll to test out.
|
|
||||||
// It's a small plugin that adds a relatively simple item to the game,
|
|
||||||
// and gives you that item whenever you press F2.
|
|
||||||
|
|
||||||
// This attribute specifies that we have a dependency on a given BepInEx Plugin,
|
// This attribute specifies that we have a dependency on a given BepInEx Plugin,
|
||||||
// We need the R2API ItemAPI dependency because we are using for adding our item to the game.
|
// We need the R2API ItemAPI dependency because we are using for adding our item to the game.
|
||||||
// You don't need this if you're not using R2API in your plugin,
|
// You don't need this if you're not using R2API in your plugin,
|
||||||
@ -24,12 +21,15 @@ namespace AntiYoink
|
|||||||
// This attribute is required, and lists metadata for your plugin.
|
// This attribute is required, and lists metadata for your plugin.
|
||||||
[BepInPlugin(PluginGUID, PluginName, PluginVersion)]
|
[BepInPlugin(PluginGUID, PluginName, PluginVersion)]
|
||||||
|
|
||||||
|
// We only do server-side stuff :)
|
||||||
|
[NetworkCompatibility(compatibility: CompatibilityLevel.NoNeedForSync)]
|
||||||
|
|
||||||
// This is the main declaration of our plugin class.
|
// This is the main declaration of our plugin class.
|
||||||
// BepInEx searches for all classes inheriting from BaseUnityPlugin to initialize on startup.
|
// BepInEx searches for all classes inheriting from BaseUnityPlugin to initialize on startup.
|
||||||
// BaseUnityPlugin itself inherits from MonoBehaviour,
|
// BaseUnityPlugin itself inherits from MonoBehaviour,
|
||||||
// so you can use this as a reference for what you can declare and use in your plugin class
|
// so you can use this as a reference for what you can declare and use in your plugin class
|
||||||
// More information in the Unity Docs: https://docs.unity3d.com/ScriptReference/MonoBehaviour.html
|
// More information in the Unity Docs: https://docs.unity3d.com/ScriptReference/MonoBehaviour.html
|
||||||
public class ExamplePlugin : BaseUnityPlugin
|
public class AntiYoinkPlugin : BaseUnityPlugin
|
||||||
{
|
{
|
||||||
// The Plugin GUID should be a unique ID for this plugin,
|
// The Plugin GUID should be a unique ID for this plugin,
|
||||||
// which is human readable (as it is used in places like the config).
|
// which is human readable (as it is used in places like the config).
|
||||||
@ -38,8 +38,11 @@ namespace AntiYoink
|
|||||||
public const string PluginName = "AntiYoink";
|
public const string PluginName = "AntiYoink";
|
||||||
public const string PluginVersion = "0.1.0";
|
public const string PluginVersion = "0.1.0";
|
||||||
|
|
||||||
// We need our item definition to persist through our functions, and therefore make it a class field.
|
private const bool DevMode = true;
|
||||||
private static ItemDef myItemDef;
|
|
||||||
|
public static ConfigEntry<uint> MaxItemCountDelta { get; set; }
|
||||||
|
|
||||||
|
private float naughtyLast = 0;
|
||||||
|
|
||||||
// The Awake() method is run at the very start when the game is initialized.
|
// The Awake() method is run at the very start when the game is initialized.
|
||||||
public void Awake()
|
public void Awake()
|
||||||
@ -47,94 +50,103 @@ namespace AntiYoink
|
|||||||
// Init our logging class so that we can properly log for debugging
|
// Init our logging class so that we can properly log for debugging
|
||||||
Log.Init(Logger);
|
Log.Init(Logger);
|
||||||
|
|
||||||
// First let's define our item
|
MaxItemCountDelta = Config.Bind<uint>(
|
||||||
myItemDef = ScriptableObject.CreateInstance<ItemDef>();
|
"General",
|
||||||
|
"MaxItemCountDelta",
|
||||||
|
3,
|
||||||
|
"Maximum delta between player with the lowest number of items");
|
||||||
|
|
||||||
// Language Tokens, explained there https://risk-of-thunder.github.io/R2Wiki/Mod-Creation/Assets/Localization/
|
if (DevMode)
|
||||||
myItemDef.name = "EXAMPLE_CLOAKONKILL_NAME";
|
{
|
||||||
myItemDef.nameToken = "EXAMPLE_CLOAKONKILL_NAME";
|
On.RoR2.Networking.NetworkManagerSystemSteam.OnClientConnect += (s, u, t) => { };
|
||||||
myItemDef.pickupToken = "EXAMPLE_CLOAKONKILL_PICKUP";
|
}
|
||||||
myItemDef.descriptionToken = "EXAMPLE_CLOAKONKILL_DESC";
|
|
||||||
myItemDef.loreToken = "EXAMPLE_CLOAKONKILL_LORE";
|
|
||||||
|
|
||||||
// The tier determines what rarity the item is:
|
|
||||||
// Tier1=white, Tier2=green, Tier3=red, Lunar=Lunar, Boss=yellow,
|
|
||||||
// and finally NoTier is generally used for helper items, like the tonic affliction
|
|
||||||
#pragma warning disable Publicizer001 // Accessing a member that was not originally public. Here we ignore this warning because with how this example is setup we are forced to do this
|
|
||||||
myItemDef._itemTierDef = Addressables.LoadAssetAsync<ItemTierDef>("RoR2/Base/Common/Tier2Def.asset").WaitForCompletion();
|
|
||||||
#pragma warning restore Publicizer001
|
|
||||||
// Instead of loading the itemtierdef directly, you can also do this like below as a workaround
|
|
||||||
// myItemDef.deprecatedTier = ItemTier.Tier2;
|
|
||||||
|
|
||||||
// You can create your own icons and prefabs through assetbundles, but to keep this boilerplate brief, we'll be using question marks.
|
|
||||||
myItemDef.pickupIconSprite = Addressables.LoadAssetAsync<Sprite>("RoR2/Base/Common/MiscIcons/texMysteryIcon.png").WaitForCompletion();
|
|
||||||
myItemDef.pickupModelPrefab = Addressables.LoadAssetAsync<GameObject>("RoR2/Base/Mystery/PickupMystery.prefab").WaitForCompletion();
|
|
||||||
|
|
||||||
// Can remove determines
|
|
||||||
// if a shrine of order,
|
|
||||||
// or a printer can take this item,
|
|
||||||
// generally true, except for NoTier items.
|
|
||||||
myItemDef.canRemove = true;
|
|
||||||
|
|
||||||
// Hidden means that there will be no pickup notification,
|
|
||||||
// and it won't appear in the inventory at the top of the screen.
|
|
||||||
// This is useful for certain noTier helper items, such as the DrizzlePlayerHelper.
|
|
||||||
myItemDef.hidden = false;
|
|
||||||
|
|
||||||
// You can add your own display rules here,
|
|
||||||
// where the first argument passed are the default display rules:
|
|
||||||
// the ones used when no specific display rules for a character are found.
|
|
||||||
// For this example, we are omitting them,
|
|
||||||
// as they are quite a pain to set up without tools like https://thunderstore.io/package/KingEnderBrine/ItemDisplayPlacementHelper/
|
|
||||||
var displayRules = new ItemDisplayRuleDict(null);
|
|
||||||
|
|
||||||
// Then finally add it to R2API
|
|
||||||
ItemAPI.Add(new CustomItem(myItemDef, displayRules));
|
|
||||||
|
|
||||||
// But now we have defined an item, but it doesn't do anything yet. So we'll need to define that ourselves.
|
|
||||||
GlobalEventManager.onCharacterDeathGlobal += GlobalEventManager_onCharacterDeathGlobal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GlobalEventManager_onCharacterDeathGlobal(DamageReport report)
|
public void OnEnable()
|
||||||
{
|
{
|
||||||
// If a character was killed by the world, we shouldn't do anything.
|
On.RoR2.GenericPickupController.AttemptGrant += GenericPickupController_AttemptGrant;
|
||||||
if (!report.attacker || !report.attackerBody)
|
On.RoR2.PickupPickerController.OnInteractionBegin += PickupPickerController_OnInteractionBegin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnDisable()
|
||||||
|
{
|
||||||
|
On.RoR2.GenericPickupController.AttemptGrant -= GenericPickupController_AttemptGrant;
|
||||||
|
On.RoR2.PickupPickerController.OnInteractionBegin -= PickupPickerController_OnInteractionBegin;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetTotalItems(Inventory inv)
|
||||||
|
{
|
||||||
|
int itemCount = 0;
|
||||||
|
foreach (var i in inv.itemAcquisitionOrder)
|
||||||
|
{
|
||||||
|
if (ItemCatalog.GetItemDef(i).hidden) continue;
|
||||||
|
itemCount += inv.GetItemCount(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemCount;
|
||||||
|
}
|
||||||
|
private bool AllowedToPickUp(CharacterBody body)
|
||||||
|
{
|
||||||
|
CharacterBody lowestPlayer = body;
|
||||||
|
int yoinkerItemCount = GetTotalItems(body.inventory);
|
||||||
|
int delta = 0;
|
||||||
|
foreach (var player in PlayerCharacterMasterController.instances)
|
||||||
|
{
|
||||||
|
var playerBody = player.master.GetBody();
|
||||||
|
if (!playerBody || !playerBody.healthComponent.alive || playerBody == body) continue;
|
||||||
|
|
||||||
|
int itemCount = GetTotalItems(playerBody.inventory);
|
||||||
|
if (yoinkerItemCount - itemCount > delta)
|
||||||
|
{
|
||||||
|
lowestPlayer = playerBody;
|
||||||
|
delta = yoinkerItemCount - itemCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lowestPlayer != body && delta > MaxItemCountDelta.Value)
|
||||||
|
{
|
||||||
|
if (Run.instance.time - naughtyLast > 1)
|
||||||
|
{
|
||||||
|
ChatMessage.Send($"<color=#ff0000>Naughty, naughty! {body.GetUserName()} has {delta} more items than poor {lowestPlayer.GetUserName()}");
|
||||||
|
naughtyLast = Run.instance.time;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenericPickupController_AttemptGrant(On.RoR2.GenericPickupController.orig_AttemptGrant orig, GenericPickupController self, CharacterBody body)
|
||||||
|
{
|
||||||
|
if (!NetworkServer.active)
|
||||||
|
{
|
||||||
|
Debug.LogWarning("[Server] AntiYoink hook function 'GenericPickupController_AttemptGrant' called on client");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
TeamComponent component = body.GetComponent<TeamComponent>();
|
||||||
|
if (!component || component.teamIndex != TeamIndex.Player)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var attackerCharacterBody = report.attackerBody;
|
if (!AllowedToPickUp(body)) {
|
||||||
|
return;
|
||||||
// We need an inventory to do check for our item
|
|
||||||
if (attackerCharacterBody.inventory)
|
|
||||||
{
|
|
||||||
// Store the amount of our item we have
|
|
||||||
var garbCount = attackerCharacterBody.inventory.GetItemCount(myItemDef.itemIndex);
|
|
||||||
if (garbCount > 0 &&
|
|
||||||
// Roll for our 50% chance.
|
|
||||||
Util.CheckRoll(50, attackerCharacterBody.master))
|
|
||||||
{
|
|
||||||
// Since we passed all checks, we now give our attacker the cloaked buff.
|
|
||||||
// Note how we are scaling the buff duration depending on the number of the custom item in our inventory.
|
|
||||||
attackerCharacterBody.AddTimedBuff(RoR2Content.Buffs.Cloak, 3 + garbCount);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
orig(self, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Update() method is run on every frame of the game.
|
// For Artifact of Command
|
||||||
private void Update()
|
private void PickupPickerController_OnInteractionBegin(On.RoR2.PickupPickerController.orig_OnInteractionBegin orig, PickupPickerController self, Interactor activator)
|
||||||
{
|
{
|
||||||
// This if statement checks if the player has currently pressed F2.
|
if (NetworkServer.active)
|
||||||
if (Input.GetKeyDown(KeyCode.F2))
|
|
||||||
{
|
{
|
||||||
// Get the player body to use a position:
|
CharacterBody body = activator.GetComponent<CharacterBody>();
|
||||||
var transform = PlayerCharacterMasterController.instances[0].master.GetBodyObject().transform;
|
if (body && !AllowedToPickUp(body))
|
||||||
|
{
|
||||||
// And then drop our defined item in front of the player.
|
return;
|
||||||
|
}
|
||||||
Log.Info($"Player pressed F2. Spawning our custom item at coordinates {transform.position}");
|
|
||||||
PickupDropletController.CreatePickupDroplet(PickupCatalog.FindPickupIndex(myItemDef.itemIndex), transform.position, transform.forward * 20f);
|
|
||||||
}
|
}
|
||||||
|
orig(self, activator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user