← Back to changelog

What changed

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

Diff 4 added · 3 removed · 372 → 373 total lines
1 1 /*▄▄▄ ███▄ ▄███▓ ▄████ ▄▄▄██▀▀▀▓█████▄▄▄█████▓
2 2 ▓█████▄ ▓██▒▀█▀ ██▒ ██▒ ▀█▒ ▒██ ▓█ ▀▓ ██▒ ▓▒
3 3 ▒██▒ ▄██▓██ ▓██░▒██░▄▄▄░ ░██ ▒███ ▒ ▓██░ ▒░
4 4 ▒██░█▀ ▒██ ▒██ ░▓█ ██▓▓██▄██▓ ▒▓█ ▄░ ▓██▓ ░
5 5 ░▓█ ▀█▓▒██▒ ░██▒░▒▓███▀▒ ▓███▒ ░▒████▒ ▒██▒ ░
6 6 ░▒▓███▀▒░ ▒░ ░ ░ ░▒ ▒ ▒▓▒▒░ ░░ ▒░ ░ ▒ ░░
7 7 ▒░▒ ░ ░ ░ ░ ░ ░ ▒ ░▒░ ░ ░ ░ ░
8 8 ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
9 9 ░ ░ ░ ░ ░ ░ ░
10 10 Chat Commands:
11 11 /shredinfo - Output shortname, If shreddable and its output.
12 12
13 13 Config Layout:
14 14 {"EntityShortName": "","scrapReward": [{"ShortName": "","Amount": 0},]},
15 15 */
16 16 using Facepunch;
17 17 using HarmonyLib;
18 18 using Newtonsoft.Json;
19 19 using Oxide.Core.Plugins;
20 20 using System;
21 21 using System.Collections;
22 22 using System.Collections.Generic;
23 23 using System.Linq;
24 24 using UnityEngine;
25 25
26 26 namespace Oxide.Plugins
27 27 {
28 - [Info("ShredMe", "bmgjet", "1.0.0")]
28 + [Info("ShredMe", "bmgjet", "1.0.1")]
29 29 [Description("Makes other entitys shredable")]
30 30 class ShredMe : RustPlugin
31 31 {
32 32 //Store default values to restore on unload
33 33 private int _maxDistanceFromOrigin = -1;
34 34 private float _idleFuelPerSec = -1;
35 35 private float _maxFuelPerSec = -1;
36 36 //List of shortnames to improve lookup speed of things to mod
37 37 private List<string> AllowedMod = new List<string>();
38 38 private Dictionary<string, MagnetLiftable> Defaults = new Dictionary<string, MagnetLiftable>();
39 39 //Co-Routine to prevent locking main thing on large entity servers.
40 40 Coroutine _coroutine;
41 41
42 42 #region Configuration
43 43 private Configuration config;
44 44
45 45 private class Configuration
46 46 {
47 47 [JsonProperty("Magnetic Crane Unlimited Roam Range")]
48 48 public bool OutOfJunkyard = false;
49 49
50 50 [JsonProperty("Magnetic Crane Unlimited Fuel")]
51 51 public bool UnlimitedFuel = false;
52 52
53 53 [JsonProperty("Entity To Make Magnet Liftable/Shredable")]
54 54 public List<Shreddable> ShreddableList;
55 55
56 56 public string ToJson() => JsonConvert.SerializeObject(this);
57 57
58 58 public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson());
59 59 }
60 60
61 61 protected override void LoadDefaultConfig() => config = new Configuration();
62 62
63 63 protected override void LoadConfig()
64 64 {
65 65 base.LoadConfig();
66 66 try
67 67 {
68 68 config = Config.ReadObject<Configuration>();
69 69 if (config == null) { throw new JsonException(); }
70 70 if (!config.ToDictionary().Keys.SequenceEqual(Config.ToDictionary(x => x.Key, x => x.Value).Keys))
71 71 {
72 72 PrintWarning("Configuration appears to be outdated; updating and saving");
73 73 SaveConfig();
74 74 }
75 75 }
76 76 catch
77 77 {
78 78 PrintWarning($"Configuration file {Name}.json is invalid; using defaults");
79 79 LoadDefaultConfig();
80 80 }
81 81 if (config.ShreddableList == null)
82 82 {
83 83 PrintWarning("Loading Default Shred List");
84 84 config.ShreddableList = new List<Shreddable>
85 85 {
86 86 new Shreddable().init("minicopter.entity", new Dictionary<string, int> { { "scrap", 150 }, { "metal.fragments", 100 }, { "metalblade", 4 } }),
87 87 new Shreddable().init("scraptransporthelicopter", new Dictionary<string, int> { { "scrap", 250 }, { "metal.fragments", 200 }, { "metalblade", 4 }, { "roadsigns", 5 } }),
88 88 new Shreddable().init("attackhelicopter.entity", new Dictionary<string, int> { { "scrap", 350 }, { "metal.fragments", 300 }, { "metalblade", 4 }, { "roadsigns", 5 }, { "metalpipe", 12 }, { "metal.refined", 5 } }),
89 89 new Shreddable().init("rhib", new Dictionary<string, int> { { "scrap", 80 }, { "metal.fragments", 25 }, { "metalblade", 2 } }),
90 90 new Shreddable().init("rowboat", new Dictionary<string, int> { { "scrap", 50 }, { "wood", 50 }, { "metalblade", 2 } }),
91 91 new Shreddable().init("submarineduo.entity", new Dictionary<string, int> { { "scrap", 60 }, { "metalpipe", 5 }, { "propanetank", 5 } }),
92 92 new Shreddable().init("submarinesolo.entity", new Dictionary<string, int> { { "scrap", 50 }, { "metalpipe", 4 }, { "propanetank", 4 } })
93 93 };
94 94 Config.WriteObject(config, true);
95 95 }
96 96 //Fill List with shortnames
97 97 if (config.ShreddableList != null && config.ShreddableList.Count() != 0)
98 98 {
99 99 foreach (var modlist in config.ShreddableList) { AllowedMod.Add(modlist.EntityShortName); }
100 100 }
101 101 }
102 102
103 103 protected override void SaveConfig()
104 104 {
105 105 PrintWarning($"Configuration changes saved to {Name}.json");
106 106 Config.WriteObject(config, true);
107 107 }
108 108 #endregion Configuration
109 109
110 110 #region Classes
111 111 //Config to hold Info
112 112 public class Shreddable
113 113 {
114 114 public string EntityShortName;
115 115 public List<ScrapReward> scrapReward = new List<ScrapReward>();
116 116 public Shreddable init(string shortname, Dictionary<string, int> reward)
117 117 {
118 118 EntityShortName = shortname;
119 119 foreach (var keyvalue in reward) { scrapReward.Add(new ScrapReward().init(keyvalue.Key, keyvalue.Value)); }
120 120 return this;
121 121 }
122 122 }
123 123
124 124 //Config to hold reward
125 125 public class ScrapReward
126 126 {
127 127 public string ShortName;
128 128 public int Amount;
129 129 public ScrapReward init(string shortname, int amount)
130 130 {
131 131 ShortName = shortname;
132 132 Amount = amount;
133 133 return this;
134 134 }
135 135 }
136 136 #endregion
137 137
138 138 #region Oxide Hooks
139 139 [ChatCommand("shredinfo")] //Gets shortname
140 140 private void shreddableinfo(BasePlayer player, string command, string[] Args)
141 141 {
142 142 if (player.IsAdmin) //limit to admin
143 143 {
144 144 //Find entity being looked at
145 145 RaycastHit hit;
146 146 var raycast = UnityEngine.Physics.Raycast(player.eyes.HeadRay(), out hit, 10, -1);
147 147 BaseEntity entity = raycast ? hit.GetEntity() : null;
148 148 if (entity != null)
149 149 {
150 150 //Output info
151 151 string info = "Shortname: " + entity.ShortPrefabName + " Shredable: ";
152 152 MagnetLiftable comp = entity?.gameObject?.GetComponent<MagnetLiftable>();
153 153 if (comp != null)
154 154 {
155 155 info += (bool)comp + " Output:";
156 156 foreach (var o in comp.shredResources) { info += o.itemDef.shortname + "(" + o.amount + ") "; }
157 157 }
158 158 else { info += (bool)comp; }
159 159 player.ChatMessage(info);
160 160 }
161 161 }
162 162 }
163 163
164 164 private void Init() { Unsubscribe(nameof(OnEntitySpawned)); } //Unsubscribe hook so doesnt waste catching stuff on server startup.
165 165
166 166 private void OnServerInitialized() { if (AllowedMod.Count > 0) { _coroutine = ServerMgr.Instance.StartCoroutine(Startup(true)); } } //Start up in coroutine
167 167
168 168 private void OnEntitySpawned(BaseCombatEntity ent) { NextFrame(() => { ModEntitys(ent, true); }); } //Need to wait a frame for it to fully spawn.
169 169
170 170 private void Unload()
171 171 {
172 172 if (_coroutine != null) { ServerMgr.Instance.StopCoroutine(_coroutine); }; //Stop corutine if still running from startup
173 173 _coroutine = ServerMgr.Instance.StartCoroutine(Startup(false));//Unload everything.
174 174 }
175 175
176 176 #endregion
177 177
178 178 #region Methods
179 179
180 180 //Run in coroutine so large entity count servers to lag playing on load/unload of plugin.
181 181 private IEnumerator Startup(bool Load)
182 182 {
183 183 int checks = 0;
184 184 int last = 0;
185 185 int done = 0;
186 186 int modded = 0;
187 187 int loops = BaseNetworkable.serverEntities.entityList.Get().Count();
188 188 foreach (var ent in BaseNetworkable.serverEntities.entityList.Get().Values.ToList()) //List since might be changed during wait times.
189 189 {
190 190 checks++;
191 191 done++;
192 192 if (Load && ++checks >= 10000) //Check conditions so many loops
193 193 {
194 194 //Limit rate based on FPS
195 195 if (Performance.report.frameRate < 15 && ConVar.FPS.limit > 15) { yield return CoroutineEx.waitForSeconds(0.01f); }
196 196 else { yield return CoroutineEx.waitForSeconds(0.0035f); }
197 197 checks = 0;
198 198 //Output Percentage Debug
199 199 if (done > last)
200 200 {
201 201 last += (loops / 5);
202 202 Puts("Scanning entitys " + (int)Math.Round((double)(100 * done) / loops) + "%");
203 203 }
204 204 }
205 205 try { modded += ModEntitys(ent, Load); } catch { } //Catch catch where something was in process of destroy during the wait.
206 206 }
207 207 Puts((Load ? "Done, Modded " : "Unmodded ") + modded + " Entitys");
208 208 if (Load) { Subscribe(nameof(OnEntitySpawned)); }
209 209 _coroutine = null;
210 210 }
211 211
212 212 private bool IsDivisible(int x, int n) { return (x % n) == 0; }
213 213
214 214 private ItemAmount[] DefaultScrap() { return new ItemAmount[] { new ItemAmount(ItemManager.FindItemDefinition("scrap"), 1) }; } //Default value for if config bad
215 215
216 216 private void AddCustomReward(Shreddable configdata, MagnetLiftable comp)
217 217 {
218 218 if (configdata.scrapReward == null || configdata.scrapReward.Count == 0) //No reward set in config so give a default.
219 219 {
220 220 comp.shredResources = DefaultScrap();
221 221 return;
222 222 }
223 223 //Create config reward
224 224 List<ItemAmount> rewarditems = new List<ItemAmount>();
225 225 foreach (var r in configdata.scrapReward) { if (r != null) { rewarditems.Add(new ItemAmount(ItemManager.FindItemDefinition(r.ShortName), r.Amount)); } }
226 226 comp.shredResources = rewarditems.ToArray();
227 227 //Make sure its not null or empty or shredder gets jammed in null ref.
228 228 if (comp.shredResources == null || comp.shredResources.Length == 0) { comp.shredResources = DefaultScrap(); }
229 229 }
230 230
231 231 private int ModEntitys(BaseNetworkable ent, bool load)
232 232 {
233 233 if (ent == null || ent.IsDestroyed) { return 0; } //Make sure valid
234 234 //Crane mods
235 235 if (ent is MagnetCrane)
236 236 {
237 237 ModCrane((MagnetCrane)ent, load);
238 238 return 1;
239 239 }
240 240 //Everything else
241 241 return MakeShreddable(ent, load);
242 242 }
243 243
244 244 private void ModCrane(MagnetCrane crane, bool load)
245 245 {
246 246 if (config.OutOfJunkyard)
247 247 {
248 248 if (_maxDistanceFromOrigin == -1) { _maxDistanceFromOrigin = (int)crane.maxDistanceFromOrigin; } //Store Value for Unload
249 249 crane.maxDistanceFromOrigin = load ? 0 : _maxDistanceFromOrigin; //0 = unlimited
250 250 }
251 251 if (config.UnlimitedFuel) //Unlimited fuel.
252 252 {
253 253 if (_idleFuelPerSec == -1) //Plugin doesnt have defaults for unload so save them
254 254 {
255 255 _idleFuelPerSec = crane.idleFuelPerSec;
256 256 _maxFuelPerSec = crane.maxFuelPerSec;
257 257 }
258 258 crane.idleFuelPerSec = load ? 0 : _idleFuelPerSec; //0 = unlimited
259 259 crane.maxFuelPerSec = load ? 0 : _maxFuelPerSec; //0 = unlimited
260 260 StorageContainer fuelContainer = BaseNetworkable.serverEntities.Find(crane.GetFuelSystem().GetInstanceID()) as StorageContainer;
261 261 if (fuelContainer != null)
262 262 {
263 263 if (load)
264 264 {
265 265 //Give 1 fuel and lock fuel container
266 266 fuelContainer.inventory.AddItem(fuelContainer.allowedItem, 1);
267 - fuelContainer.SetFlag(BaseEntity.Flags.Locked, true);
267 + fuelContainer.SetFlagLocal(BaseEntity.Flags.Locked, true);
268 268 return;
269 269 }
270 270 //Unload
271 271 if (fuelContainer.HasFlag(BaseEntity.Flags.Locked))
272 272 {
273 273 //Remove fuel item and unlock container
274 274 fuelContainer.inventory.Clear();
275 - fuelContainer.SetFlag(BaseEntity.Flags.Locked, false);
275 + fuelContainer.SetFlagLocal(BaseEntity.Flags.Locked, false);
276 276 }
277 + fuelContainer.SendNetworkUpdateImmediate();
277 278 }
278 279 }
279 280 }
280 281
281 282 private int MakeShreddable(BaseNetworkable ent, bool add = true)
282 283 {
283 284 if (AllowedMod.Contains(ent.ShortPrefabName)) //Check its a entity to target
284 285 {
285 286 MagnetLiftable comp = ent?.gameObject?.GetComponent<MagnetLiftable>(); //Get componant if it already has it
286 287 if (!add) //Unload
287 288 {
288 289 if (comp != null)
289 290 {
290 291 if (Defaults.ContainsKey(ent.ShortPrefabName)){comp.shredResources = Defaults[ent.ShortPrefabName].shredResources;}
291 292 else{GameObject.Destroy(comp);}
292 293 }
293 294 return 1;
294 295 }
295 296 foreach (var configdata in config.ShreddableList) //Find config
296 297 {
297 298 if (configdata != null && configdata.EntityShortName == ent.ShortPrefabName) //Found
298 299 {
299 300 if (comp == null)
300 301 {
301 302 comp = ent.gameObject.AddComponent<MagnetLiftable>(); //Add Componant
302 303 AddCustomReward(configdata, comp);
303 304 return 1;
304 305 }
305 306 else if (comp != null) //Mod native item that is MagnetLiftable
306 307 {
307 308 AddCustomReward(configdata, comp);
308 309 return 1;
309 310 }
310 311 }
311 312 }
312 313 }
313 314 else if (ent is BaseVehicle)
314 315 {
315 316 MagnetLiftable comp = ent?.gameObject?.GetComponent<MagnetLiftable>();
316 317 if (comp != null)
317 318 {
318 319 Defaults.Add(ent.ShortPrefabName, comp);
319 320 Dictionary<string, int> dict = new Dictionary<string, int>();
320 321 foreach (var i in comp.shredResources) { dict.Add(ItemManager.FindItemDefinition(i.itemid).shortname, (int)i.amount); }
321 322 Shreddable shreddable = new Shreddable().init(ent.ShortPrefabName, dict);
322 323 config.ShreddableList.Add(shreddable);
323 324 Puts("Found Default Item " + ent.ShortPrefabName);
324 325 AllowedMod.Add(ent.ShortPrefabName);
325 326 Config.WriteObject(config, true);
326 327 }
327 328 }
328 329 return 0;
329 330 }
330 331 #endregion
331 332
332 333 [AutoPatch]
333 334 [HarmonyPatch(typeof(LargeShredder), "OnEntityEnteredTrigger", new Type[] { typeof(BaseEntity) })]
334 335 internal class LargeShredder_OnEntityEnteredTrigger
335 336 {
336 337 [HarmonyPrefix]
337 338 private static bool Prefix(BaseEntity ent, LargeShredder __instance, ref Quaternion ___entryRotation)
338 339 {
339 340 try
340 341 {
341 342 if (ent.IsDestroyed)
342 343 {
343 344 return false;
344 345 }
345 346 Rigidbody component = ent.GetComponent<Rigidbody>();
346 347 if (__instance.isShredding || __instance.currentlyShredding != null)
347 348 {
348 349 if (!component.isKinematic)
349 350 {
350 351 component.velocity = -component.velocity * 3f;
351 352 }
352 353 return false;
353 354 }
354 355 __instance.shredRail.transform.position = __instance.shredRailStartPos.position;
355 356 __instance.shredRail.transform.rotation = Quaternion.LookRotation(__instance.shredRailStartRotation);
356 357 ___entryRotation = ent.transform.rotation;
357 358 Quaternion rotation = ent.transform.rotation;
358 359 component.isKinematic = true;
359 360 component.SetActive(false);
360 361 __instance.currentlyShredding = ent;
361 362 ent.transform.rotation = rotation;
362 363 __instance.isShredding = true;
363 364 __instance.SetShredding(true);
364 365 __instance.shredStartTime = Time.realtimeSinceStartup;
365 366 return false;
366 367 }
367 368 catch { }
368 369 return true;
369 370 }
370 371 }
371 372 }
372 373 }