← Back to changelog

What changed

EggExtra · v1.0.0 → v1.0.1 (current)

Diff 2 added · 2 removed · 314 → 314 total lines
1 1 /*▄▄▄ ███▄ ▄███▓ ▄████ ▄▄▄██▀▀▀▓█████▄▄▄█████▓
2 2 ▓█████▄ ▓██▒▀█▀ ██▒ ██▒ ▀█▒ ▒██ ▓█ ▀▓ ██▒ ▓▒
3 3 ▒██▒ ▄██▓██ ▓██░▒██░▄▄▄░ ░██ ▒███ ▒ ▓██░ ▒░
4 4 ▒██░█▀ ▒██ ▒██ ░▓█ ██▓▓██▄██▓ ▒▓█ ▄░ ▓██▓ ░
5 5 ░▓█ ▀█▓▒██▒ ░██▒░▒▓███▀▒ ▓███▒ ░▒████▒ ▒██▒ ░
6 6 ░▒▓███▀▒░ ▒░ ░ ░ ░▒ ▒ ▒▓▒▒░ ░░ ▒░ ░ ▒ ░░
7 7 ▒░▒ ░ ░ ░ ░ ░ ░ ▒ ░▒░ ░ ░ ░ ░
8 8 ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
9 9 ░ ░ ░ ░ ░ ░ ░*/
10 10 using System;
11 11 using System.Collections.Generic;
12 12 using System.Linq;
13 13 using HarmonyLib;
14 14 using Newtonsoft.Json;
15 15 using Oxide.Core.Plugins;
16 16 using UnityEngine;
17 17 namespace Oxide.Plugins
18 18 {
19 - [Info("EggExtra", "bmgjet", "1.0.0")]
19 + [Info("EggExtra", "bmgjet", "1.0.1")]
20 20 [Description("Allows eggs to be used as bait and compost")]
21 21 class EggExtra : RustPlugin
22 22 {
23 23 public static EggExtra plugin;
24 24 private const int eggItemId = 1858828593;
25 25 private const string guntrapprefab = "assets/prefabs/deployable/single shot trap/guntrap.deployed.prefab";
26 26 private string permKFC = "eggextra.kfc";
27 27
28 28 #region Configuration
29 29 private Configuration config;
30 30 private class Configuration
31 31 {
32 32 [JsonProperty("BaitValue (Higher Value = Better Loot From Fish Traps)")]
33 33 public float BaitValue = 1;
34 34
35 35 [JsonProperty("MaxBaitStack (How Many To Use Per Fish Trap Attempt)")]
36 36 public int MaxBaitStack = 1;
37 37
38 38 [JsonProperty("TotalFertilizerProduced (How Much Fertilizer Per Egg)")]
39 39 public float TotalFertilizerProduced = 0.5f;
40 40
41 41 [JsonProperty("Try Auto Hatch Until Max Chickens")]
42 42 public bool AutoHatch = true;
43 43
44 44 [JsonProperty("Try To Auto Hatch Eggs In Chicken Coop On Chicken Death")]
45 45 public bool DeathHatch = true;
46 46
47 47 public string ToJson() => JsonConvert.SerializeObject(this);
48 48
49 49 public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson());
50 50 }
51 51
52 52 protected override void LoadDefaultConfig() { config = new Configuration(); }
53 53 protected override void LoadConfig()
54 54 {
55 55 base.LoadConfig();
56 56 try
57 57 {
58 58 config = Config.ReadObject<Configuration>();
59 59 if (config == null) { throw new JsonException(); }
60 60
61 61 if (!config.ToDictionary().Keys.SequenceEqual(Config.ToDictionary(x => x.Key, x => x.Value).Keys))
62 62 {
63 63 PrintWarning("Configuration appears to be outdated; updating and saving");
64 64 SaveConfig();
65 65 }
66 66
67 67 SanitizeConfig();
68 68 }
69 69 catch
70 70 {
71 71 PrintWarning($"Configuration file {Name}.json is invalid; using defaults");
72 72 LoadDefaultConfig();
73 73 }
74 74 }
75 75 protected override void SaveConfig()
76 76 {
77 77 PrintWarning($"Configuration changes saved to {Name}.json");
78 78 Config.WriteObject(config, true);
79 79 }
80 80
81 81 private void SanitizeConfig()
82 82 {
83 83 //Prevent a bad hand-edited json from producing broken/negative bait values
84 84 if (config.BaitValue < 0f) { config.BaitValue = 0f; }
85 85 if (config.MaxBaitStack < 1) { config.MaxBaitStack = 1; }
86 86 if (config.TotalFertilizerProduced < 0f) { config.TotalFertilizerProduced = 0f; }
87 87 }
88 88 #endregion Configuration
89 89
90 90 #region OxideHooks
91 91 private void Init()
92 92 {
93 93 plugin = this;
94 94 Unsubscribe(nameof(OnEntitySpawned)); //Stop hooking on server startup
95 95 permission.RegisterPermission(permKFC, this);
96 96 }
97 97
98 98 private void OnServerInitialized()
99 99 {
100 100 PatchEggs(true); //Catch Everything
101 101 Subscribe(nameof(OnEntitySpawned)); //Catch fishtraps from now on.
102 102 }
103 103
104 104 private void OnEntitySpawned(WildlifeTrap trap) { PatchTrap(trap); }
105 105
106 106 private void Unload()
107 107 {
108 108 PatchEggs(false);
109 109 plugin = null;
110 110 }
111 111
112 112 public void HatchEgg(ChickenCoop __instance)
113 113 {
114 114 if (__instance.HasFlag(BaseEntity.Flags.Reserved3) || __instance.HasFlag(BaseEntity.Flags.Reserved1))
115 115 {
116 116 return;
117 117 }
118 118
119 119 Item slot = __instance.inventory.GetSlot(0);
120 120 if (slot != null && slot.info == ChickenCoop.EggDef)
121 121 {
122 122 slot.UseItem();
123 123 __instance.Animals.Add(new ChickenCoop.AnimalStatus
124 124 {
125 125 TimeUntilHatch = __instance.ChickenHatchTimeMinutes * 60f
126 126 });
127 127 using (ChickenCoop.FlagsUpdateScope flagsUpdateScope = __instance.StartSetFlags(BaseEntity.FlagsUpdateMode.SendNetworkUpdate))
128 128 {
129 129 flagsUpdateScope.Set(BaseEntity.Flags.Reserved1, b: true);
130 130 flagsUpdateScope.Set(BaseEntity.Flags.Reserved3, __instance.Animals.Count >= __instance.MaxChickens);
131 131 }
132 132
133 133 if (!__instance.IsInvoking(__instance.CheckEggHatchState))
134 134 {
135 135 __instance.InvokeRepeating(__instance.CheckEggHatchState, 10f, 10f);
136 136 }
137 137
138 138 __instance.SendNetworkUpdate();
139 139 }
140 140 }
141 141
142 142 [ChatCommand("KFC")] //Chat Command
143 143 private void CommandSpawnKFC(BasePlayer player, string command, string[] args)
144 144 {
145 145 if (permission.UserHasPermission(player.UserIDString, permKFC))
146 146 {
147 147 var coop = FindEntity(player.eyes.HeadRay());
148 148 if (coop == null)
149 149 {
150 150 player.ChatMessage("You Must Be Looking At A Chicken Coop!");
151 151 return;
152 152 }
153 153 foreach (var c in coop.children)
154 154 {
155 155 if (c is GunTrap)
156 156 {
157 157 player.ChatMessage("Already Has GunTrap!");
158 158 return;
159 159 }
160 160 }
161 161 if (player.inventory.Take(null, 352499047, 1) > 0)
162 162 {
163 163 SpawnPart(coop, Quaternion.Euler(90, 0, 0), new Vector3(0f, 1f, -1.2f));
164 164 return;
165 165 }
166 166 player.ChatMessage("You Must Have A Gun Trap On You!");
167 167 }
168 168 }
169 169 #endregion
170 170
171 171 #region Methods
172 172 private BaseEntity SpawnPart(BaseEntity parent, Quaternion rotoffset, Vector3 posoffset)
173 173 {
174 174 //Spawns in parts of the quarry
175 175 BaseEntity baseent = GameManager.server.CreateEntity(guntrapprefab, parent.transform.position, parent.transform.rotation);
176 176 if (baseent == null)
177 177 {
178 178 PrintWarning($"Failed to create entity from prefab '{guntrapprefab}'");
179 179 return null;
180 180 }
181 181 baseent.OwnerID = parent.OwnerID;
182 182 baseent.Spawn();
183 183 //Remove componants to stop it breaking
184 184 foreach (var mesh in baseent.GetComponentsInChildren<MeshCollider>()) { UnityEngine.Object.DestroyImmediate(mesh); }
185 185 UnityEngine.Object.DestroyImmediate(baseent.GetComponent<DestroyOnGroundMissing>());
186 186 UnityEngine.Object.DestroyImmediate(baseent.GetComponent<GroundWatch>());
187 187 //Parent it to base entity
188 188 baseent.SetParent(parent, true, true);
189 189 //Adjust offset positions
190 190 if (rotoffset != Quaternion.Euler(0, 0, 0)) { baseent.transform.rotation = baseent.transform.rotation * rotoffset; }
191 191 if (posoffset != new Vector3(0, 0, 0)) { baseent.transform.localPosition = posoffset; }
192 192 baseent.SendNetworkUpdateImmediate();
193 193 return baseent;
194 194 }
195 195
196 196 private ChickenCoop FindEntity(Ray ray)
197 197 {
198 198 //Ray cast to find BaseEntity
199 199 RaycastHit hit;
200 200 var raycast = Physics.Raycast(ray, out hit, 6, -1);
201 201 BaseEntity entity = raycast ? hit.GetEntity() : null;
202 202 if (entity is ChickenCoop)
203 203 {
204 204 return entity as ChickenCoop;
205 205 }
206 206 return null;
207 207 }
208 208
209 - private bool CanAcceptItem(Item item, int slot)
209 + private bool CanAcceptItem(BasePlayer basePlayer, Item item, int slot)
210 210 {
211 211 var compostable = item.info.GetComponent<ItemModCompostable>();
212 212 return compostable?.BaitValue > 0; //Has Bail Value
213 213 }
214 214
215 215 private void PatchTrap(WildlifeTrap trap, bool apply = true)
216 216 {
217 217 //Add/remove custom allow on everything that has bait value which eggs now do.
218 218 if (trap?.inventory == null) { return; }
219 219 var inv = trap.inventory;
220 220 if (apply) { inv.canAcceptItem += CanAcceptItem; }
221 221 else { inv.canAcceptItem -= CanAcceptItem; }
222 222 }
223 223
224 224 private void PatchEggs(bool apply)
225 225 {
226 226 int traps = 0;
227 227 foreach (var entity in BaseNetworkable.serverEntities)
228 228 {
229 229 if (entity is WildlifeTrap trap)
230 230 {
231 231 PatchTrap(trap, apply);
232 232 traps++;
233 233 }
234 234 }
235 235 foreach (var item in ItemManager.GetAllItems())
236 236 {
237 237 if (item.info.itemid != eggItemId) { continue; }
238 238 //Change Values Of Eggs So Can Be Bait And Compost
239 239 var compostable = item.info.GetComponent<ItemModCompostable>();
240 240 if (compostable == null) { continue; }
241 241 //Use Config values or set back to 0 default
242 242 compostable.BaitValue = apply ? config.BaitValue : 0;
243 243 compostable.MaxBaitStack = apply ? config.MaxBaitStack : 0;
244 244 compostable.TotalFertilizerProduced = apply ? config.TotalFertilizerProduced : 0;
245 245 }
246 246 Puts($"{(apply ? "Applied" : "Reverted")} custom values to eggs and {(apply ? "patched" : "unpatched")} {traps} fish traps.");
247 247 }
248 248 #endregion
249 249
250 250 #region Harmony
251 251 [AutoPatch]
252 252 [HarmonyPatch(typeof(ChickenCoop), "OnAnimalDied", typeof(FarmableAnimal))]
253 253 public static class ChickenCoop_OnAnimalDied
254 254 {
255 255 [HarmonyPostfix]
256 256 public static void Postfix(ChickenCoop __instance)
257 257 {
258 258 //Try Hatch New Chicken To Replace Dead
259 259 if (plugin.config.DeathHatch)
260 260 {
261 261 plugin.HatchEgg(__instance);
262 262 }
263 263 }
264 264 }
265 265
266 266 [AutoPatch]
267 267 [HarmonyPatch(typeof(ChickenCoop), "SpawnChicken", typeof(int))]
268 268 public static class ChickenCoop_SpawnChicken
269 269 {
270 270 [HarmonyPostfix]
271 271 public static void Postfix(ChickenCoop __instance)
272 272 {
273 273 if (plugin.config.AutoHatch)
274 274 {
275 275 plugin.NextFrame(() =>
276 276 {
277 277 //Try Maintain Max Chickens
278 278 if (__instance != null)
279 279 {
280 280 if (__instance.Animals.Count < __instance.MaxChickens)
281 281 {
282 282 plugin.HatchEgg(__instance);
283 283 }
284 284 if (__instance.Animals.Count != 0)
285 285 {
286 286 GunTrap trap = null;
287 287 foreach (var c in __instance.children)
288 288 {
289 289 if (c is GunTrap gt) { trap = gt; break; }
290 290 }
291 291
292 292 if (trap != null && plugin.permission.UserHasPermission(__instance.OwnerID.ToString(), plugin.permKFC))
293 293 {
294 294 foreach (ChickenCoop.AnimalStatus animalStatus in __instance.Animals)
295 295 {
296 296 EntityRef<FarmableAnimal> spawnedAnimal = animalStatus.SpawnedAnimal;
297 297 FarmableAnimal farmableAnimal = spawnedAnimal.Get(true);
298 298 if (farmableAnimal != null)
299 299 {
300 300 farmableAnimal.Hurt(9999);
301 301 plugin.NextFrame(() => { Effect.server.Run(trap.gun_fire_effect.resourcePath, trap, StringPool.Get(trap.muzzlePos.gameObject.name), Vector3.zero, Vector3.zero); });
302 302 return;
303 303 }
304 304 }
305 305 }
306 306 }
307 307 }
308 308 });
309 309 }
310 310 }
311 311 }
312 312 #endregion
313 313 }
314 314 }