C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Newtonsoft.Json;
using System.Collections.Generic;
using Oxide.Game.Rust.Cui;
using UnityEngine;
using System;
using Oxide.Core.Plugins;
using System.Collections;
using Oxide.Core;
namespace Oxide.Plugins {
[Info("PersonalAnimal", "walkinrey", "1.3.7")]
public class PersonalAnimal : RustPlugin {
public static PersonalAnimal Instance;
[PluginReference]
private Plugin ImageLibrary, PersonalNPC, Friends, Clans;
private Dictionary<ulong, PlayerAnimalController> _existsControllers = new Dictionary<ulong, PlayerAnimalController>();
private Dictionary<ulong, AnimalOwnerComponent> _existsOwnerComponents = new Dictionary<ulong, AnimalOwnerComponent>();
private List<string> _permissionKeys = new List<string>();
private Dictionary<ulong, DateTime> _cooldownInfo = new Dictionary<ulong, DateTime>();
private BUTTON controlButton = BUTTON.FIRE_THIRD, inventoryButton = BUTTON.RELOAD, mountButton = BUTTON.USE;
#region Config
private Configuration _config;
public class Configuration {
[JsonProperty("Controls setup")]
public ControlsSetup controlsSetup = new ControlsSetup();
[JsonProperty("GUI setup")]
public GUISetup gui = new GUISetup();
[JsonProperty("Protect from Rust anti-cheat kick when player riding an animal")]
public ProtectInfo protect = new ProtectInfo();
[JsonProperty("Spawn settings")]
public SpawnInfo spawnInfo = new SpawnInfo();
[JsonProperty("How frequent animal will update the information? (affects the performance and operation of the animal)")]
public float mainProcessTimer = 0.01f;
[JsonProperty("Setting up personal animals by permission")]
public Dictionary<string, AnimalInfo> animalInfoPerm = new Dictionary<string, AnimalInfo>();
[JsonProperty("Damage scale from animals to other entities")]
public Dictionary<string, float> damageScale = new Dictionary<string, float>();
[JsonProperty("Animal install by item")]
public List<ItemInfo> installItem = new List<ItemInfo>();
[JsonProperty("List of prefabs that the animal can loot (useful if the animal attacks loot instead of looting it)")]
public List<string> lootEntities = new List<string>();
public class SpawnInfo {
[JsonProperty("Can player spawn animal in cupboard range?")]
public bool canSpawnInCupboard = true;
[JsonProperty("Can player spawn animal on construction? (foundations, floors, walls, etc.)")]
public bool canSpawnOnConstruction = false;
[JsonProperty("Can player spawn animal on deployed entities? (wood boxes, tables, workbenches, etc.)")]
public bool canSpawnOnDeployed = false;
}
public class ProtectInfo {
[JsonProperty("Kick if detected noclip?")]
public bool noClip = true;
[JsonProperty("Kick if detected speedhack?")]
public bool speedHack = true;
[JsonProperty("Kick if detected flyhack?")]
public bool flyHack = true;
[JsonProperty("Kick if detected insideterrain?")]
public bool insideTerrain = true;
}
public struct ItemInfo {
[JsonProperty("Item name")]
public string name;
[JsonProperty("Item shortname")]
public string shortname;
[JsonProperty("Item skin")]
public ulong skin;
[JsonProperty("Return item back if player have despawned the animal via command or GUI?")]
public bool returnDespawn;
[JsonProperty("Bot info")]
public AnimalInfo animal;
}
public class GUISetup {
[JsonProperty("How many seconds to update the GUI?")]
public int refreshTime = 6;
[JsonProperty("Panel layer (Hud, Overlay, Overall, Hud.Menu, Under)")]
public string layer = "Overlay";
[JsonProperty("Panel position")]
public CuiRectTransformComponent panelPosition = new CuiRectTransformComponent();
[JsonProperty("Second position of the panel (used if the player has a personal bot)")]
public CuiRectTransformComponent secondPanelPosition = new CuiRectTransformComponent();
[JsonProperty("1 panel color")]
public string panelColor1 = "#7f8c8d";
[JsonProperty("2 panel color")]
public string panelColor2 = "#bdc3c7";
[JsonProperty("Health bar color")]
public string panelHealthColor = "#2ecc71";
[JsonProperty("Shortcut buttons")]
public List<AccessButton> accessButtons = new List<AccessButton>();
public class AccessButton {
[JsonProperty("Text on button")]
public string text = "";
[JsonProperty("Executable chat commands")]
public string[] commands = new string[] {
}
;
public AccessButton(string btnText, string[] btnCommand) {
text = btnText;
commands = btnCommand;
}
}
}
public class ControlsSetup {
[JsonProperty("Which button will assign tasks to the animal, attack / collect, etc.(MIDDLE_MOUSE, SECOND_MOUSE, E, RELOAD, SPRINT)")]
public string key = "MIDDLE_MOUSE";
[JsonProperty("Button to open animal inventory (MIDDLE_MOUSE, SECOND_MOUSE, E, RELOAD, SPRINT)")]
public string inventoryKey = "RELOAD";
[JsonProperty("Button to mount animal (MIDDLE_MOUSE, SECOND_MOUSE, E, RELOAD, SPRINT)")]
public string mountKey = "E";
[JsonProperty("Range of action of the assignment button")]
public float rayLength = 25f;
[JsonProperty("Input tick (affects performance and feedback of the inputs, 0.1 for best performance, 0.01 for best feedback)")]
public float inputTick = 0.05f;
[JsonProperty("Display 3D arrows over a target?")]
public bool showArrow = true;
[JsonProperty("Arrow display duration")]
public int arrowDuration = 2;
[JsonProperty("Distance between player and animal to move animal into idle state")]
public float idleDistance = 1f;
[JsonProperty("Control animal by mouse direction (true) of by WASD buttons (false) when mounted?")]
public bool byDirection = true;
}
public class AnimalInfo {
[JsonProperty("The name of the animal to be selected through the command when spawning")]
public string spawnName = "wolf1";
[JsonProperty("Animal type (bear, boar, chicken, stag, wolf, polar-bear)")]
public string type = "wolf";
[JsonProperty("Animal speed (slowest, slow, normal, fast)")]
public string speed = "normal";
[JsonProperty("Maximum health")]
public int maxHealth = 200;
[JsonProperty("Animal spawn cooldown")]
public float cooldown = 300f;
[JsonProperty("AI Stopping Distance")]
public float stoppingDistance = 0.1f;
[JsonProperty("Addons setup")]
public AddonsSetup addonsSetup = new AddonsSetup();
[JsonProperty("Functions setup")]
public FunctionsSetup functionsSetup = new FunctionsSetup();
[JsonProperty("Damage, interactions and loot setup")]
public InteractionSetup interactionSetup = new InteractionSetup();
[JsonProperty("Death Marker (marker will be only visible for owner)")]
public DeathMarkerSetup deathMarker = new DeathMarkerSetup();
[JsonProperty("Nutrition setup")]
public FoodSetup nutritionSetup = new FoodSetup();
public class DeathMarkerSetup {
[JsonProperty("Show marker on bot's death position?")]
public bool enableMarker = false;
[JsonProperty("Display name on map")]
public string displayName = "Bot's death marker";
[JsonProperty("Marker radius")]
public float radius = 0.35f;
[JsonProperty("Outline color (hex)")]
public string outline = "00FFFFFF";
[JsonProperty("Main color (hex)")]
public string main = "00FFFF";
[JsonProperty("Alpha")]
public float alpha = 0.5f;
[JsonProperty("Duration")]
public int duration = 20;
}
public class FoodSetup {
[JsonProperty("Turn on the animal feeding system?")]
public bool enableFeeding = true;
[JsonProperty("Setting health for food eaten")]
public Dictionary<string, float> foodInfo = new Dictionary<string, float>();
}
public class AddonsSetup {
[JsonProperty("Enable the ability to ride an animal?")]
public bool needChair = false;
[JsonProperty("Dismount owner when entering RaidableBase?")]
public bool dismountOnRaidableBase = false;
[JsonProperty("Add a bag to an animal to store resources?")]
public bool needBag = true;
[JsonProperty("Number of available slots in the bag (maximum 36)")]
public int slotsAmount = 12;
}
public class InteractionSetup {
[JsonProperty("Animal damage rate")]
public float botDamageRate = 2f;
[JsonProperty("Damage rate receive for an animal")]
public float botReceiveDamageRate = 1f;
[JsonProperty("Can the animal damage players?")]
public bool canDamagePlayers = true;
[JsonProperty("Can players damage the animal?")]
public bool canBeDamagedByPlayers = true;
[JsonProperty("Despawn animal corpse after death?")]
public bool despawnCorpse = false;
[JsonProperty("Distance between animal and loot entity (collectible resources, loot boxes etc.)")]
public float collectibleDistance = 3f;
[JsonProperty("Distance between animal and enemy")]
public float enemyDistance = 5f;
[JsonProperty("Setting up resource pickup rates")]
public Dictionary<string, float> collectRates = new Dictionary<string, float>();
[JsonProperty("Setting up loot rates")]
public Dictionary<string, float> lootRates = new Dictionary<string, float>();
[JsonProperty("Black list of items that cannot be put in the bag")]
public List<string> bagBlacklist = new List<string>();
}
public class FunctionsSetup {
[JsonProperty("Can the animal attack objects?")]
public bool canAttackEntities = true;
[JsonProperty("Can the animal attack players?")]
public bool canAttackPlayers = true;
[JsonProperty("Can the animal attack NPCs?")]
public bool canAttackNPC = true;
[JsonProperty("Can an animal loot boxes?")]
public bool canLootBoxes = true;
[JsonProperty("Can the animal pick up resources?")]
public bool canCollectResources = true;
[JsonProperty("Does the animal have to defend itself?")]
public bool canProtectSelf = true;
[JsonProperty("Should the animal protect the owner?")]
public bool canProtectOwner = true;
[JsonProperty("Can an animal collect resources within a specified radius? (/panimal auto-collect)")]
public bool canAutoCollect = false;
[JsonProperty("Collect resources radius (/panimal auto-collect)")]
public float collectRadius = 50f;
[JsonProperty("Only owner can open animal's bag after death?")]
public bool onlyOwnerLootDeadBag = true;
[JsonProperty("Can attack team/clan members or friends?")]
public bool canAttackTeam = true;
}
public string TypeToEntity() {
switch(type) {
case "bear" : return "assets/rust.ai/agents/bear/bear.prefab";
case "boar" : return "assets/rust.ai/agents/boar/boar.prefab";
case "chicken" : return "assets/rust.ai/agents/chicken/chicken.prefab";
case "stag" : return "assets/rust.ai/agents/stag/stag.prefab";
case "wolf" : return "assets/rust.ai/agents/wolf/wolf.prefab";
case "polar-bear" : return "assets/rust.ai/agents/bear/polarbear.prefab";
default : return string.Empty;
}
}
public Vector3 GetChairOffset() {
switch(type) {
case "bear" : return new Vector3(0, 0.65f, 0);
case "boar" : return new Vector3(0, 0.35f, 0);
case "stag" : return new Vector3(0, 0.5f, 0);
case "polar-bear" : return new Vector3(0, 0.7f, 0);
default : return Vector3.zero;
}
}
public Vector3 GetFeedingOffset() {
switch(type) {
case "wolf" : return new Vector3(0, 0.15f, 0.9f);
case "bear" : return new Vector3(0, 0.15f, 1.25f);
case "boar" : return new Vector3(0, 0.15f, 1.1f);
case "stag" : return new Vector3(0, 0.15f, 1f);
case "polar-bear" : return new Vector3(0, 0.15f, 1.3f);
case "chicken" : return new Vector3(0, 0.2f, 0.5f);
default : return Vector3.zero;
}
}
}
}
protected override void LoadDefaultConfig() {
_config = new Configuration();
_config.gui.panelPosition = new CuiRectTransformComponent {
AnchorMin = "1 1", AnchorMax = "1 1", OffsetMin = "-170 -104", OffsetMax = "-10 -10"
}
;
_config.gui.secondPanelPosition = new CuiRectTransformComponent {
AnchorMin = "1 1", AnchorMax = "1 1", OffsetMin = "-360 -104", OffsetMax = "-200 -10"
}
;
_config.damageScale.Add("bradleyapc", 0f);
_config.damageScale.Add("bradley", 0f);
_config.animalInfoPerm.Add("personalanimal.wolf", new Configuration.AnimalInfo {
interactionSetup = new Configuration.AnimalInfo.InteractionSetup {
collectRates = new Dictionary<string, float> {
["stones"]
= 5f
}
, bagBlacklist = new List<string> {
"rocket.launcher"
}
}
, nutritionSetup = new Configuration.AnimalInfo.FoodSetup() {
foodInfo = new Dictionary<string, float> {
["pumpkin"]
= 10f, ["corn"] = 5f, ["apple"] = 5f,
}
}
}
);
_config.installItem.Add(new Configuration.ItemInfo {
name = "Wolf", skin = 1234, shortname = "furnace", animal = new Configuration.AnimalInfo {
interactionSetup = new Configuration.AnimalInfo.InteractionSetup {
collectRates = new Dictionary<string, float> {
["stones"]
= 5f
}
, bagBlacklist = new List<string> {
"rocket.launcher"
}
}
, nutritionSetup = new Configuration.AnimalInfo.FoodSetup() {
foodInfo = new Dictionary<string, float> {
["pumpkin"]
= 10f, ["corn"] = 5f, ["apple"] = 5f,
}
}
}
}
);
_config.gui.accessButtons.Add(new Configuration.GUISetup.AccessButton("Stay on Position", new string[] {
"panimal idle"
}
));
_config.lootEntities.Add("foodbox");
_config.lootEntities.Add("vehicle_parts");
}
protected override void LoadConfig() {
base.LoadConfig();
try {
_config = Config.ReadObject<Configuration>();
if (_config == null) throw new Exception();
SaveConfig();
}
catch (Exception ex) {
PrintError("{0}", ex);
LoadDefaultConfig();
}
}
protected override void SaveConfig() => Config.WriteObject(_config);
#endregion
#region Loc
protected override void LoadDefaultMessages() {
lang.RegisterMessages(new Dictionary<string, string> {
["ChatCommand_Success_Despawn"]
= "Your personal animal has been successfully despawned!", ["ChatCommand_Success_Spawn"] = "Your personal animal has been successfully spawned!", ["ChatCommand_Notice_Cooldown"] = "You need to wait {0} seconds before re-spawning the animal!", ["ChatCommand_Notice_Location"] = "Your animal is in the grid: {0}, distance to it: {1}", ["ChatCommand_Notice_Health"] = "Your animal's health: {0}/{1}", ["ChatCommand_Notice_Follow"] = "The animal is now following you!", ["ChatCommand_Notice_AvailableBots"] = "<size=16>Available animals:</size>\n{BOTS}\n\nEnter /panimal [short name of the animal], to spawn!", ["ChatCommand_Error_AutoPickup"] = "<size=16>Auto-pickup resources</size>\n\nThe animal will begin to collect all resources within a radius of 50 meters from its original point.\n\nAvailable mods: all, wood, stone, metal, sulfur\nDisable - /panimal auto-pickup disable", ["ChatCommand_Error_NoPermission"] = "You do not have permission to spawn a personal animal", ["ChatCommand_Error_CannotUse"] = "Your animal does not have this function, you cannot use it!", ["ChatCommand_Error_NoBot"] = "You don't have a personal animal!", ["ChatCommand_Error_NotFounded"] = "Animal not found!", ["ChatCommand_Error_Blacklist"] = "This item has been blacklisted, you cannot give it to an animal!", ["ChatCommand_Error_CannotSpawn"] = "You can't spawn a personal animal here!", ["ChatCommand_Notice_AutoPickup_Status"] = "Auto-pickup resources: {0}\nResources to collect: {1}", ["ChatCommand_AutoMode_Resources_All"] = "all", ["ChatCommand_AutoMode_Resources_Wood"] = "wood", ["ChatCommand_AutoMode_Resources_Stone"] = "stone", ["ChatCommand_AutoMode_Resources_Sulfur"] = "sulfur", ["ChatCommand_AutoMode_Resources_Metal"] = "metal", ["ChatCommand_AutoMode_Resources_Hemp"] = "hemp", ["ChatCommand_AutoMode_Resources_Berries"] = "berries", ["ChatCommand_AutoMode_Resources_Corn"] = "corn", ["ChatCommand_AutoMode_Resources_Mushroom"] = "mushroom", ["ChatCommand_AutoMode_Resources_Pumpkin"] = "pumpkin", ["ChatCommand_AutoMode_Status_Disabled"] = "disabled", ["ChatCommand_AutoMode_Status_Enabled"] = "enabled", ["Bot_Notice_MissionCompleted"] = "Mission completed, backing to you!", ["Bot_Notice_GoingCollect"] = "Going to collect the resource!", ["Bot_Notice_GoingLootBox"] = "Going to loot the box!", ["Bot_Notice_Following"] = "Following you!", ["Bot_Notice_Staying"] = "Staying on the position.", ["Bot_Notice_StartedAttack"] = "Starting attack!", ["Bot_Notice_GoingPosition"] = "Going to the position.", ["Bot_Error_NoResourcesAround"] = "No resources around!", ["Bot_Error_Dead_NotOwner"] = "You can't loot this bag because you aren't the owner!", ["GUI_Header"] = "Animal Control", ["GUI_Follow"] = "Follow", ["GUI_Kill"] = "Kill", ["Text_Inventory"] = "Open Inventory - R", ["Text_Drive"] = "Saddle the animal - E"
}
, this, "en");
lang.RegisterMessages(new Dictionary<string, string> {
["ChatCommand_Success_Despawn"]
= "Ваше персональное животное успешно задеспавнено!", ["ChatCommand_Success_Spawn"] = "Ваше персональное животное успешно заспавнено!", ["ChatCommand_Notice_Cooldown"] = "Вам нужно подождать {0} секунд, прежде чем повторно заспавнить животное!", ["ChatCommand_Notice_Location"] = "Ваше животное находится в квадрате: {0}, расстояние до него: {1}", ["ChatCommand_Notice_Health"] = "Здоровье вашего животного: {0}/{1}", ["ChatCommand_Notice_Follow"] = "Животное теперь следует за вами!", ["ChatCommand_Notice_AvailableBots"] = "<size=16>Доступные животные:</size>\n{BOTS}\n\nВведите /panimal [короткое название животного], чтобы заспавнить!", ["ChatCommand_Error_AutoPickup"] = "<size=16>Авто-подбор ресурсов</size>\n\nЖивотное начнет собирать все ресурсы в радиусе 50 метров от его первоначальной точки.\n\nДоступные режимы: all, wood, stone, metal, sulfur\nОтключить - /panimal auto-pickup disable", ["ChatCommand_Error_NoPermission"] = "У вас нет разрешения на спавн персонального животного", ["ChatCommand_Error_CannotUse"] = "Ваше животное не обладает такой функцией, вы не можете ее использовать!", ["ChatCommand_Error_NoBot"] = "У вас нет персонального животного!", ["ChatCommand_Error_NotFounded"] = "Животное не найдено!", ["ChatCommand_Error_Blacklist"] = "Этот предмет добавлен в черный список, вы не можете дать его животному!", ["ChatCommand_Error_CannotSpawn"] = "Вы не можете заспавнить персональное животное здесь!", ["ChatCommand_Notice_AutoPickup_Status"] = "Авто-подбор ресурсов: {0}\nРесурсы для сбора: {1}", ["ChatCommand_AutoMode_Resources_All"] = "все", ["ChatCommand_AutoMode_Resources_Wood"] = "дерево", ["ChatCommand_AutoMode_Resources_Stone"] = "камень", ["ChatCommand_AutoMode_Resources_Sulfur"] = "сера", ["ChatCommand_AutoMode_Resources_Metal"] = "металл", ["ChatCommand_AutoMode_Resources_Hemp"] = "ткань", ["ChatCommand_AutoMode_Resources_Berries"] = "ягоды", ["ChatCommand_AutoMode_Resources_Corn"] = "кукуруза", ["ChatCommand_AutoMode_Resources_Mushroom"] = "гриб", ["ChatCommand_AutoMode_Resources_Pumpkin"] = "тыква", ["ChatCommand_AutoMode_Status_Disabled"] = "отключён", ["ChatCommand_AutoMode_Status_Enabled"] = "включён", ["Bot_Notice_MissionCompleted"] = "Цель выполнена, возвращаюсь к вам!", ["Bot_Notice_GoingCollect"] = "Иду собирать ресурс!", ["Bot_Notice_GoingLootBox"] = "Иду лутать ящик!", ["Bot_Notice_Following"] = "Следую за вами!", ["Bot_Notice_Staying"] = "Стою на позиции.", ["Bot_Notice_StartedAttack"] = "Начинаю атаку!", ["Bot_Notice_GoingPosition"] = "Иду на позицию.", ["Bot_Error_NoResourcesAround"] = "Нет ресурсов поблизости!", ["Bot_Error_Dead_NotOwner"] = "Вы не можете залутать эту сумку потому что вы не ее владелец!", ["GUI_Header"] = "Управление животным", ["GUI_Follow"] = "Следовать", ["GUI_Kill"] = "Убить", ["Text_Inventory"] = "Открыть инвентарь - R", ["Text_Drive"] = "Оседлать животное - E"
}
, this, "ru");
}
#endregion
#region Hooks
private void OnServerInitialized() {
if(ImageLibrary == null) {
PrintError("Install ImageLibrary / Установите ImageLibrary!");
NextTick(() => Interface.Oxide.UnloadPlugin(Title));
return;
}
ImageLibrary.CallHook("AddImage", "https://api.rustyplugin.ru/pnpc/arrow.png", "PersonalAnimal_Close");
ImageLibrary.CallHook("AddImage", "https://api.rustyplugin.ru/pnpc/arrow2.png", "PersonalAnimal_Open");
switch(_config.controlsSetup.key) {
case "E" : controlButton = BUTTON.USE;
break;
case "MIDDLE_MOUSE" : controlButton = BUTTON.FIRE_THIRD;
break;
case "RELOAD" : controlButton = BUTTON.RELOAD;
break;
case "SPRINT" : controlButton = BUTTON.SPRINT;
break;
case "SECOND_MOUSE" : controlButton = BUTTON.FIRE_SECONDARY;
break;
}
switch(_config.controlsSetup.inventoryKey) {
case "E" : inventoryButton = BUTTON.USE;
break;
case "MIDDLE_MOUSE" : inventoryButton = BUTTON.FIRE_THIRD;
break;
case "RELOAD" : inventoryButton = BUTTON.RELOAD;
break;
case "SPRINT" : inventoryButton = BUTTON.SPRINT;
break;
case "SECOND_MOUSE" : inventoryButton = BUTTON.FIRE_SECONDARY;
break;
}
switch(_config.controlsSetup.mountKey) {
case "E" : mountButton = BUTTON.USE;
break;
case "MIDDLE_MOUSE" : mountButton = BUTTON.FIRE_THIRD;
break;
case "RELOAD" : mountButton = BUTTON.RELOAD;
break;
case "SPRINT" : mountButton = BUTTON.SPRINT;
break;
case "SECOND_MOUSE" : mountButton = BUTTON.FIRE_SECONDARY;
break;
}
}
private object OnPlayerWantsMount(BasePlayer player, BaseMountable entity) {
if(entity == null || player == null) return null;
if(!_existsOwnerComponents.ContainsKey(entity.net.ID.Value)) return null;
AnimalOwnerComponent ownerComponent = _existsOwnerComponents[entity.net.ID.Value];
if(ownerComponent == null) {
_existsOwnerComponents.Remove(entity.net.ID.Value);
return null;
}
if(ownerComponent.controller == null) return null;
entity.AttemptMount(player, false);
ownerComponent.controller.IsDriving = true;
return false;
}
private object OnNpcTarget(BaseEntity npc, BaseEntity entity) {
if(entity == null || npc == null) return null;
if(!_existsOwnerComponents.ContainsKey(npc.net.ID.Value)) return null;
AnimalOwnerComponent ownerComponent = _existsOwnerComponents[npc.net.ID.Value];
if(ownerComponent == null) {
_existsOwnerComponents.Remove(npc.net.ID.Value);
return null;
}
if(entity == ownerComponent.controller.owner) return false;
return null;
}
private void OnEntityDeath(BaseAnimalNPC entity, HitInfo info) {
if(entity == null) return;
var ownerComponent = entity.GetComponent<AnimalOwnerComponent>();
if(ownerComponent) {
_existsOwnerComponents.Remove(entity.net.ID.Value);
if(ownerComponent.controller && !ownerComponent.isLootSpawned) {
var controller = ownerComponent.controller;
var source = controller.owner.inventory.loot.entitySource;
_existsControllers.Remove(controller.GetComponent<BaseNetworkable>().net.ID.Value);
if(source != null) {
var storage = source as StorageContainer;
if(storage) {
if(storage.name == $"panimal_{controller.owner.userID}") {
controller.owner.EndLooting();
}
}
}
if(controller.bagItems.Count != 0) {
var backpack = GameManager.server.CreateEntity("assets/prefabs/misc/item drop/item_drop_backpack.prefab", entity.transform.position, Quaternion.identity) as DroppedItemContainer;
backpack.inventory = new ItemContainer();
backpack.inventory.ServerInitialize((Item) null, controller.bagItems.Count);
backpack.inventory.GiveUID();
backpack.inventory.SetFlag(ItemContainer.Flag.NoItemInput, true);
foreach(var bagItem in controller.bagItems) {
var obj = bagItem.ToItem();
if (!obj.MoveToContainer(backpack.inventory)) obj.DropAndTossUpwards(backpack.transform.position);
}
backpack.ResetRemovalTime();
backpack.Spawn();
if(controller.animalSetup.functionsSetup.onlyOwnerLootDeadBag) {
backpack.name = $"panimal_{controller.owner.userID}";
backpack.OwnerID = controller.owner.userID;
}
}
}
if(ownerComponent.controller != null) {
if(ownerComponent.controller.animalSetup.interactionSetup.despawnCorpse) {
entity.transform.position = new Vector3(0, -1000, 0);
}
}
if(ownerComponent.controller) UnityEngine.Object.Destroy(ownerComponent.controller);
ownerComponent.isLootSpawned = true;
}
}
private object OnPlayerViolation(BasePlayer player, AntiHackType type, float amount) {
if(player == null || type == AntiHackType.None) return null;
if(!_existsControllers.ContainsKey(player.net.ID.Value)) return null;
var controller = _existsControllers[player.net.ID.Value];
if(controller == null) {
_existsControllers.Remove(player.net.ID.Value);
return null;
}
if(controller.IsDriving) {
if(!_config.protect.flyHack && type == AntiHackType.FlyHack) return false;
if(!_config.protect.insideTerrain && type == AntiHackType.InsideTerrain) return false;
if(!_config.protect.noClip && type == AntiHackType.NoClip) return false;
if(!_config.protect.speedHack && type == AntiHackType.SpeedHack) return false;
}
return null;
}
private void OnLootEntity(BasePlayer player, BaseEntity entity) {
if(player == null || entity == null) return;
if(entity is DroppedItemContainer || entity is LootableCorpse) {
if(entity.name.Contains("panimal")) {
if(entity.OwnerID != player.userID) {
player.ChatMessage(GetMsg("Bot_Error_Dead_NotOwner", player.UserIDString));
NextTick(() => player.EndLooting());
return;
}
}
}
}
private object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info) {
if(entity == null || info == null || info?.Initiator == null || entity.net == null || info?.Initiator?.net == null) return null;
BaseEntity target = info.Initiator;
if(!(target is BaseCombatEntity)) return null;
if(_existsOwnerComponents.ContainsKey(target.net.ID.Value)) {
var initiatorBotOwner = _existsOwnerComponents[target.net.ID.Value];
if(initiatorBotOwner != null && initiatorBotOwner?.controller != null) {
info.damageTypes.ScaleAll(initiatorBotOwner.controller.animalSetup.interactionSetup.botDamageRate);
if(_config.damageScale.ContainsKey(entity.ShortPrefabName)) info.damageTypes.ScaleAll(_config.damageScale[entity.ShortPrefabName]);
if(!initiatorBotOwner.controller.animalSetup.interactionSetup.canDamagePlayers) {
var victim = entity.ToPlayer();
if(victim != null) {
if(!victim.IsNpc && victim.userID.IsSteamId()) {
return false;
}
}
}
}
}
if(_existsControllers.ContainsKey(entity.net.ID.Value)) {
var controller = _existsControllers[entity.net.ID.Value];
if(controller != null) {
if(target == controller?.bot) return false;
controller.OnAttacked(target, info, true);
}
return null;
}
if(_existsOwnerComponents.ContainsKey(entity.net.ID.Value)) {
var ownerComponent = _existsOwnerComponents[entity.net.ID.Value];
if(ownerComponent != null && ownerComponent?.controller != null) {
if(entity == ownerComponent.controller?.owner) return false;
info.damageTypes?.ScaleAll(ownerComponent.controller.animalSetup.interactionSetup.botReceiveDamageRate);
if(!ownerComponent.controller.animalSetup.interactionSetup.canBeDamagedByPlayers) {
var player = target.ToPlayer();
if(player != null) {
if(player.IsNpc == false && player.userID.IsSteamId()) {
return false;
}
}
}
ownerComponent.controller.OnAttacked(target, info);
}
}
return null;
}
private void Unload() {
var finded = _existsControllers;
for (int i = finded.Count - 1; i >= 0; i--) {
var obj = new List<PlayerAnimalController>(_existsControllers.Values)[i];
if(obj.bot != null) obj.bot.Kill();
if(obj) UnityEngine.Object.Destroy(obj);
}
var cachedPlayers = Facepunch.Pool.GetList<BasePlayer>();
cachedPlayers.AddRange(BasePlayer.activePlayerList);
cachedPlayers.ForEach(x => CuiHelper.DestroyUi(x, "PersonalAnimal_ControlPanel"));
Facepunch.Pool.FreeList(ref cachedPlayers);
}
private object CanAcceptItem(ItemContainer container, Item item, int targetPos) {
var player = item?.GetOwnerPlayer();
if(player == null) return null;
if(!_existsControllers.ContainsKey(player.net.ID.Value)) return null;
var controller = _existsControllers[player.net.ID.Value];
if(controller != null) {
var owner = container.GetEntityOwner();
if(owner == null) return null;
if(controller.IsBlacklisted(item) && owner.name == $"panimal_{player.userID}") {
SendMsg(player, "ChatCommand_Error_Blacklist");
return ItemContainer.CanAcceptResult.CannotAcceptRightNow;
}
}
else {
_existsControllers.Remove(player.net.ID.Value);
}
return null;
}
private void Loaded() {
Instance = this;
_permissionKeys = new List<string>(_config.animalInfoPerm.Keys);
_permissionKeys.ForEach(x => permission.RegisterPermission(x, this));
}
private void OnLootEntityEnd(BasePlayer player, BaseCombatEntity entity) {
if(!_existsControllers.ContainsKey(player.net.ID.Value)) return;
var controller = _existsControllers[player.net.ID.Value];
if(controller == null) {
_existsControllers.Remove(player.net.ID.Value);
return;
}
if(!controller.animalSetup.addonsSetup.needBag) return;
var storage = entity as StorageContainer;
if(storage == null) return;
if(storage.name != $"panimal_{player.userID}") return;
controller.UpdateItems(storage.inventory);
}
private void OnEntityBuilt(Planner plan, GameObject go) {
if(_config.installItem.Count == 0) return;
var player = plan.GetOwnerPlayer();
if(player == null) return;
var item = player.GetActiveItem();
if(item == null) return;
foreach(var loopInfo in _config.installItem) {
if(loopInfo.skin == item.skin) {
PlayerAnimalController comp;
if(player.TryGetComponent<PlayerAnimalController>(out comp)) {
if(comp.ItemInfo.returnDespawn) {
Item panimal = ItemManager.CreateByName(comp.ItemInfo.shortname, 1, comp.ItemInfo.skin);
if(!string.IsNullOrEmpty(comp.ItemInfo.name)) panimal.name = comp.ItemInfo.name;
player.GiveItem(panimal);
}
comp.bot.Kill();
//OnEntityDeath(comp.bot, null);
}
NextTick(() => {
var controller = player.gameObject.AddComponent<PlayerAnimalController>();
controller.bot = CreateBot(player, loopInfo.animal, go.transform.position);
controller.owner = player;
controller.mountButton = mountButton;
controller.controlButton = controlButton;
controller.inventoryButton = inventoryButton;
controller.ItemInfo = loopInfo;
_existsControllers.Remove(player.net.ID.Value);
_existsControllers.Add(player.net.ID.Value, controller);
SendMsg(player, "ChatCommand_Success_Spawn");
go.ToBaseEntity().Kill();
}
);
break;
}
}
}
#endregion
#region Methods
[ChatCommand("panimal")]
private void chatCommand(BasePlayer player, string command, string[] args) {
if(args == null || args?.Length == 0) {
PlayerAnimalController playerAnimalController;
if(player.TryGetComponent<PlayerAnimalController>(out playerAnimalController)) {
if(playerAnimalController.ItemInfo.returnDespawn) {
Item panimal = ItemManager.CreateByName(playerAnimalController.ItemInfo.shortname, 1, playerAnimalController.ItemInfo.skin);
if(!string.IsNullOrEmpty(playerAnimalController.ItemInfo.name)) panimal.name = playerAnimalController.ItemInfo.name;
player.GiveItem(panimal);
}
if(playerAnimalController.bot) {
player.EndLooting();
playerAnimalController.bot.Kill();
}
SendMsg(player, "ChatCommand_Success_Despawn");
}
else {
var botSetup = GetBotSetup(player);
if(botSetup != null) {
if(botSetup.Count == 0) SendMsg(player, "ChatCommand_Error_NoPermission");
else {
if(botSetup.Count == 1) {
chatCommand(player, command, new string[] {
botSetup[0].spawnName
}
);
return;
}
string msg = lang.GetMessage("ChatCommand_Notice_AvailableBots", this, player.UserIDString);
string availableBots = "";
foreach(var bot in botSetup) {
availableBots = availableBots + $"\n{botSetup.IndexOf(bot) + 1}.{bot.spawnName}";
}
msg = msg.Replace("{BOTS}", availableBots);
player.ChatMessage(msg);
}
}
}
return;
}
if(args[0] == "idle") {
var controller = player.GetComponent<PlayerAnimalController>();
if(controller == null) {
SendMsg(player, "ChatCommand_Error_NoBot");
return;
}
else {
controller.Idle();
SendMsg(player, "Bot_Notice_Staying");
return;
}
}
if(args[0] == "where") {
var controller = player.GetComponent<PlayerAnimalController>();
if(controller == null) {
SendMsg(player, "ChatCommand_Error_NoBot");
return;
}
else {
SendMsg(player, "ChatCommand_Notice_Location", new string[] {
PersonalAnimal.GetGrid(controller.bot.transform.position), Vector3.Distance(controller.bot.transform.position, player.transform.position).ToString()
}
);
return;
}
}
if(args[0] == "health") {
var controller = player.GetComponent<PlayerAnimalController>();
if(controller == null) {
SendMsg(player, "ChatCommand_Error_NoBot");
return;
}
else {
SendMsg(player, "ChatCommand_Notice_Health", new string[] {
Mathf.RoundToInt(controller.bot.Health()).ToString(), Mathf.RoundToInt(controller.bot.MaxHealth()).ToString()
}
);
return;
}
}
if(args[0] == "follow") {
var controller = player.GetComponent<PlayerAnimalController>();
if(controller == null) {
SendMsg(player, "ChatCommand_Error_NoBot");
return;
}
else {
controller.FollowPlayer();
SendMsg(player, "Bot_Notice_Following");
return;
}
}
if(args[0] == "auto-pickup") {
if(args.Length == 1) {
SendMsg(player, "ChatCommand_Error_AutoPickup");
return;
}
else {
var controller = player.GetComponent<PlayerAnimalController>();
if(controller == null) {
SendMsg(player, "ChatCommand_Error_NoBot");
return;
}
var compMode = controller.mode;
if(compMode == null) {
SendMsg(player, "ChatCommand_Error_CannotUse");
return;
}
else if(!controller.animalSetup.functionsSetup.canAutoCollect) {
SendMsg(player, "ChatCommand_Error_CannotUse");
return;
}
if(args[1] == "disable") {
compMode.Disable();
SendMsg(player, "ChatCommand_Notice_AutoPickup_Status");
return;
}
if(args[1] == "enable") {
compMode.SetMode(AnimalAutoMode.AutoMode.Pickup);
compMode.EnableMode();
SendMsg(player, "ChatCommand_Notice_AutoPickup_Status");
return;
}
if(args[1] == "all") {
compMode.AddResource(AnimalAutoMode.Resources.All);
SendMsg(player, "ChatCommand_Notice_AutoPickup_Status");
return;
}
if(args[1] == "stone") {
compMode.AddResource(AnimalAutoMode.Resources.Stone);
SendMsg(player, "ChatCommand_Notice_AutoPickup_Status");
return;
}
if(args[1] == "metal") {
compMode.AddResource(AnimalAutoMode.Resources.Metal);
SendMsg(player, "ChatCommand_Notice_AutoPickup_Status");
return;
}
if(args[1] == "sulfur") {
compMode.AddResource(AnimalAutoMode.Resources.Sulfur);
SendMsg(player, "ChatCommand_Notice_AutoPickup_Status");
return;
}
if(args[1] == "wood") {
compMode.AddResource(AnimalAutoMode.Resources.Wood);
SendMsg(player, "ChatCommand_Notice_AutoPickup_Status");
return;
}
if(args[1] == "hemp") {
compMode.AddResource(AnimalAutoMode.Resources.Hemp);
SendMsg(player, "ChatCommand_Notice_AutoPickup_Status");
return;
}
if(args[1] == "corn") {
compMode.AddResource(AnimalAutoMode.Resources.Corn);
SendMsg(player, "ChatCommand_Notice_AutoPickup_Status");
return;
}
if(args[1] == "mushroom") {
compMode.AddResource(AnimalAutoMode.Resources.Mushroom);
SendMsg(player, "ChatCommand_Notice_AutoPickup_Status");
return;
}
if(args[1] == "pumpkin") {
compMode.AddResource(AnimalAutoMode.Resources.Pumpkin);
SendMsg(player, "ChatCommand_Notice_AutoPickup_Status");
return;
}
if(args[1] == "berries") {
compMode.AddResource(AnimalAutoMode.Resources.Berries);
SendMsg(player,
// ... [Code preview truncated due to length. Full source in attached file] ...