← Back to changelog

What changed

GarageDoor2 · v1.3.3 → v1.3.4 (current)

Diff 2 added · 1 removed · 1054 → 1055 total lines
1 1 /*▄▄▄ ███▄ ▄███▓ ▄████ ▄▄▄██▀▀▀▓█████▄▄▄█████▓
2 2 ▓█████▄ ▓██▒▀█▀ ██▒ ██▒ ▀█▒ ▒██ ▓█ ▀▓ ██▒ ▓▒
3 3 ▒██▒ ▄██▓██ ▓██░▒██░▄▄▄░ ░██ ▒███ ▒ ▓██░ ▒░
4 4 ▒██░█▀ ▒██ ▒██ ░▓█ ██▓▓██▄██▓ ▒▓█ ▄░ ▓██▓ ░
5 5 ░▓█ ▀█▓▒██▒ ░██▒░▒▓███▀▒ ▓███▒ ░▒████▒ ▒██▒ ░
6 6 ░▒▓███▀▒░ ▒░ ░ ░ ░▒ ▒ ▒▓▒▒░ ░░ ▒░ ░ ▒ ░░
7 7 ▒░▒ ░ ░ ░ ░ ░ ░ ▒ ░▒░ ░ ░ ░ ░
8 8 ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
9 9 ░ ░ ░ ░ ░ ░ ░
10 10 Perms:
11 11 GarageDoor2.Turn - Required to use the chat command.
12 12 GarageDoor2.Rotate - Hitting with hammer while holding R key rotates garage doors. (Requires Building Perm)
13 13 GarageDoor2.Skin - Allows using gdskin command /gdskin skinid
14 14 GarageDoor2.Break - Break floor frame by hitting with hammer while holding Shift+R
15 15 GarageDoor2.GUI - Allows using GUI, Right click with hammer
16 16
17 17 Chat Commands:
18 18 /td - Converts garage door into horizontal garage door and back.
19 19 /gdskin skinid - Placed rotated garage doors will have this skin (set 0 to disable)
20 20
21 21 Console Commands:
22 22 garagedoor2.give $player - Gives rotated garage door to that player by there ID or Name
23 23 */
24 24 using Facepunch;
25 25 using Newtonsoft.Json;
26 26 using Oxide.Core;
27 27 using Oxide.Game.Rust.Cui;
28 28 using System;
29 29 using System.Collections;
30 30 using System.Collections.Generic;
31 31 using System.Linq;
32 32 using UnityEngine;
33 33 namespace Oxide.Plugins
34 34 {
35 - [Info("GarageDoor2", "bmgjet", "1.3.3")]
35 + [Info("GarageDoor2", "bmgjet", "1.3.4")]
36 36 [Description("Lets you place a horizontal garage door")]
37 37 public class GarageDoor2 : RustPlugin
38 38 {
39 39 private static GarageDoor2 plugin;
40 40 private readonly string Perm = "GarageDoor2.Turn";
41 41 private readonly string PermR = "GarageDoor2.Rotate";
42 42 private readonly string PermS = "GarageDoor2.Skin";
43 43 private readonly string PermB = "GarageDoor2.Break";
44 44 private readonly string PermG = "GarageDoor2.GUI";
45 45 private const string GarageDoor = "assets/prefabs/building/wall.frame.garagedoor/wall.frame.garagedoor.prefab";
46 46 private const string DoorController = "assets/prefabs/deployable/playerioents/doormanipulators/doorcontroller.deployed.prefab";
47 47 private const string Deploy = "assets/prefabs/deployable/playerioents/industrialconveyor/effects/industrial-conveyor-deploy.prefab";
48 48 private Dictionary<ulong, ulong> LastUsedSkin = new Dictionary<ulong, ulong>();
49 49 private List<ulong> Cooldowns = new List<ulong>();
50 50
51 51 #region DataFile
52 52 private StoredData storedData;
53 53 private class StoredData { public Dictionary<ulong, ulong> SID = new Dictionary<ulong, ulong>(); public List<ulong> Rotated = new List<ulong>(); public List<ulong> Scaled = new List<ulong>(); } //Hold Skin Ids of orignal garage door
54 54 #endregion
55 55
56 56 #region Configuration
57 57 private Configuration config;
58 58 private class Configuration
59 59 {
60 60 [JsonProperty("Pick Up Rotated Garage Doors As Rotated Garage Doors")]
61 61 public bool PickUpRotated = true;
62 62
63 63 [JsonProperty("Add Rotated Garage Doors Into Supply Drops")]
64 64 public bool AddSupplyDrops = false;
65 65
66 66 [JsonProperty("Move Door Controllers And Locks Into Roller")]
67 67 public bool MoveController = true;
68 68
69 69 [JsonProperty("Oversize Garage Doors On Frame Break")]
70 70 public bool Oversize = true;
71 71
72 72 [JsonProperty("Spawning In Supply Drop Percentage Chance (0-100)")]
73 73 public int ChancePercentage = 60;
74 74
75 75 [JsonProperty("SkinID To Use For Rotated Garage Doors")]
76 76 public ulong CustomSkinID = 3148709690;
77 77
78 78 [JsonProperty("CoolDown Between GUI Actions (Sec)")]
79 79 public float CooldownDelay = 0.8f;
80 80
81 81 [JsonProperty("GUI Min X")]
82 82 public string MinX = ".9";
83 83
84 84 [JsonProperty("GUI Min Y")]
85 85 public string MinY = ".68";
86 86
87 87 [JsonProperty("GUI Max X")]
88 88 public string MaxX = "1";
89 89
90 90 [JsonProperty("GUI Max Y")]
91 91 public string MaxY = "1";
92 92
93 93 [JsonProperty("SoundFX To Play On Rotation (Blank = None)")]
94 94 public string SoundFX = "assets/bundled/prefabs/fx/build/promote_toptier.prefab";
95 95
96 96 [JsonProperty("SoundFX To Play On Skin (Blank = None)")]
97 97 public string SSoundFX = "assets/prefabs/deployable/repair bench/effects/skinchange_spraypaint.prefab";
98 98
99 99 [JsonProperty("SoundFX To Play On Frame Break (Blank = None)")]
100 100 public string FSoundFX = "assets/bundled/prefabs/fx/building/wood_gib.prefab";
101 101
102 102 [JsonProperty("Allow breaking door frames (Will break stability of bases)")]
103 103 public bool breakall = false;
104 104
105 105 [JsonProperty("Scaled doors only take damage from player (Only enable if you have other plugin re-enabling stability)")]
106 106 public bool playeronlydamage = false;
107 107
108 108 [JsonProperty("Debug messages on what last caused damage")]
109 109 public bool debug = false;
110 110
111 111 public string ToJson() => JsonConvert.SerializeObject(this);
112 112
113 113 public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson());
114 114 }
115 115
116 116 protected override void LoadDefaultConfig() { config = new Configuration(); }
117 117 protected override void LoadConfig()
118 118 {
119 119 base.LoadConfig();
120 120 try
121 121 {
122 122 config = Config.ReadObject<Configuration>();
123 123 if (config == null) { throw new JsonException(); }
124 124
125 125 if (!config.ToDictionary().Keys.SequenceEqual(Config.ToDictionary(x => x.Key, x => x.Value).Keys))
126 126 {
127 127 PrintWarning("Configuration appears to be outdated; updating and saving");
128 128 SaveConfig();
129 129 }
130 130 }
131 131 catch
132 132 {
133 133 PrintWarning($"Configuration file {Name}.json is invalid; using defaults");
134 134 LoadDefaultConfig();
135 135 }
136 136 }
137 137 protected override void SaveConfig()
138 138 {
139 139 PrintWarning($"Configuration changes saved to {Name}.json");
140 140 Config.WriteObject(config, true);
141 141 }
142 142 #endregion Configuration
143 143
144 144 #region Language
145 145 protected override void LoadDefaultMessages()
146 146 {
147 147 lang.RegisterMessages(new Dictionary<string, string>
148 148 {
149 149 {"DoorName", "Rotated Garage Door"},
150 150 {"CantRotate", "Can't Rotate Scaled Doors"},
151 151 {"NeedSkinID", "You must provide a skinid /gdskin 12345"},
152 152 {"InvalidSkinID", "Invalid skinid"},
153 153 {"NoPerm", " You don't have permission to use this command!"},
154 154 {"UpdatedID", "Updated SkinID to {0}"},
155 155 {"Blocked", "Placement Blocked"},
156 156 {"MustHold", "You must be holding a garage door!"},
157 157 {"Rotate", "Rotate"},
158 158 {"Skin", "Skin"},
159 159 {"Break", "Break"},
160 160 {"NoFoundation", "No foundation found, placement blocked!"},
161 161 }, this);
162 162 }
163 163
164 164 private string message(BasePlayer player, string key, params object[] args)
165 165 {
166 166 if (player == null) { return string.Format(lang.GetMessage(key, this), args); }
167 167 return string.Format(lang.GetMessage(key, this, player.UserIDString), args);
168 168 }
169 169 #endregion
170 170
171 171 #region Oxide Hooks
172 172 private void Init()
173 173 {
174 174 plugin = this;
175 175 permission.RegisterPermission(Perm, this);
176 176 permission.RegisterPermission(PermR, this);
177 177 permission.RegisterPermission(PermS, this);
178 178 permission.RegisterPermission(PermB, this);
179 179 permission.RegisterPermission(PermG, this);
180 180 //Unsub if not enabled in config
181 181 if (!config.AddSupplyDrops) { Unsubscribe("OnLootSpawn"); }
182 182 if (!config.MoveController) { Unsubscribe("OnEntitySpawned"); }
183 183 if (!config.playeronlydamage) { Unsubscribe("OnEntityKill"); }
184 184 storedData = Interface.Oxide.DataFileSystem.ReadObject<StoredData>(Name);
185 185 }
186 186
187 187 private object OnEntityKill(Door door)
188 188 {
189 189 if (door.prefabID == 3647679950) //only garage doors
190 190 {
191 191 if (door.lastAttacker == null || UnityEngine.Time.time - door.lastAttackedTime > 10) //No player damage done or in last 10 sec
192 192 {
193 193 if (storedData.Scaled.Contains(door.net.ID.Value))
194 194 {
195 195 if (config.debug) { Puts("Blocked " + door.lastDamage.ToString() + " Damage To GD2"); }
196 196 return true;
197 197 }
198 198 } //No player damage done, give god mode
199 199 }
200 200 return null;
201 201 }
202 202
203 203 private void Unload()
204 204 {
205 205 Interface.Oxide.DataFileSystem.WriteObject(Name, storedData);
206 206 foreach (BasePlayer p in BasePlayer.activePlayerList) { CuiHelper.DestroyUi(p, "GD2P"); }
207 207 plugin = null;
208 208 }
209 209
210 210 private object CanBuild(Planner planner, Construction construction, Construction.Target target)
211 211 {
212 212 if (target.entity != null && IsRotatedGD(target.entity) && construction?.prefabID == 2343730564)
213 213 {
214 214 return false;
215 215 }
216 216 return null;
217 217 }
218 218
219 219 void OnServerInitialized() //Clean up save data from invalid/despawned doors
220 220 {
221 221 // Start cleanup asynchronously
222 222 ServerMgr.Instance.StartCoroutine(CleanUpData());
223 223 }
224 224 private IEnumerator CleanUpData()
225 225 {
226 226 int Cleaned = 0;
227 227
228 228 if (storedData != null)
229 229 {
230 230 List<ulong> Found = Pool.Get<List<ulong>>();
231 231
232 232 // Split workload to prevent blocking
233 233 int count = 0;
234 234 foreach (var s in BaseNetworkable.serverEntities.entityList.Get().Values)
235 235 {
236 236 if (s != null)
237 237 {
238 238 if (s is BasePlayer player)
239 239 {
240 240 List<Item> list = Pool.Get<List<Item>>();
241 241 player.inventory.GetAllItems(list);
242 242 foreach (var i in list)
243 243 {
244 244 if (storedData.SID.ContainsKey(i.uid.Value))
245 245 Found.Add(i.uid.Value);
246 246 }
247 247 Pool.FreeUnmanaged(ref list);
248 248 }
249 249 else if (s is StorageContainer container && container.inventory?.itemList != null)
250 250 {
251 251 foreach (var i in container.inventory.itemList)
252 252 {
253 253 if (storedData.SID.ContainsKey(i.uid.Value))
254 254 Found.Add(i.uid.Value);
255 255 }
256 256 }
257 257 else if (s is Door door && storedData.Scaled.Contains(door.net.ID.Value))
258 258 {
259 259 ScaleDoor(door); //Fix lost scale
260 260 }
261 261 }
262 262
263 263 // Yield every 10K iterations to prevent server lag
264 264 if (++count % 10000 == 0)
265 265 {
266 266 if (Performance.report.frameRate < 15 && ConVar.FPS.limit > 15) { yield return CoroutineEx.waitForSeconds(0.01f); }
267 267 else { yield return CoroutineEx.waitForSeconds(0.0035f); }
268 268 }
269 269 }
270 270
271 271 // Remove invalid entries (single pass instead of repeated O(n) ElementAt lookups)
272 272 List<ulong> staleKeys = Pool.Get<List<ulong>>();
273 273 foreach (var kvp in storedData.SID)
274 274 {
275 275 if (!Found.Contains(kvp.Key)) { staleKeys.Add(kvp.Key); }
276 276 }
277 277 foreach (var key in staleKeys)
278 278 {
279 279 storedData.SID.Remove(key);
280 280 Cleaned++;
281 281 }
282 282 Pool.FreeUnmanaged(ref staleKeys);
283 283 Pool.FreeUnmanaged(ref Found);
284 284
285 285 for (int i = storedData.Rotated.Count - 1; i >= 0; i--)
286 286 {
287 287 if (!BaseNetworkable.serverEntities.Contains(new NetworkableId(storedData.Rotated[i])))
288 288 {
289 289 storedData.Rotated.RemoveAt(i);
290 290 Cleaned++;
291 291 }
292 292 }
293 293
294 294 if (Cleaned > 0)
295 295 {
296 296 Interface.Oxide.DataFileSystem.WriteObject(Name, storedData);
297 297 Puts($"Removed {Cleaned} references to dead/despawned items.");
298 298 }
299 299 }
300 300 }
301 301
302 302 private object CanRecycle(Recycler recycler, Item item)
303 303 {
304 + if (recycler == null || item == null || item.info == null) { return null; }
304 305 if (item.info.itemid == 1948067030 && item.skin == config.CustomSkinID) { if (config.debug) Puts("Block Recycle from GarageDoor2"); return false; } //Prevent recycling rotated doors.
305 306 return null;
306 307 }
307 308
308 309 private void OnNewSave()
309 310 {
310 311 Puts("Cleared Save Data!");
311 312 storedData.SID.Clear();
312 313 }
313 314
314 315 private object CanPickupEntity(BasePlayer player, BaseEntity entity)
315 316 {
316 317 if (entity?.prefabID == 3647679950 && config.playeronlydamage)
317 318 {
318 319 Door door = entity as Door;
319 320 if (door != null)
320 321 {
321 322 door.lastAttacker = player;
322 323 door.lastAttackedTime = UnityEngine.Time.time;
323 324 }
324 325 }
325 326 if (config.PickUpRotated && PickedUpGrageDoor(player, entity)) { return false; }
326 327 return null;
327 328 }
328 329
329 330 private void OnLootSpawn(LootContainer container)
330 331 {
331 332 if (container is SupplyDrop) //Supply drop
332 333 {
333 334 if (RandomChance((uint)UnityEngine.Random.Range(11111, 99999), config.ChancePercentage)) //Roll a random chance
334 335 {
335 336 //Create Rotated Garage Door And Add To Supply Drop
336 337 Item item2 = ItemManager.CreateByItemID(1948067030, 1, config.CustomSkinID);
337 338 item2.name = message(null, "DoorName");
338 339 item2.MarkDirty();
339 340 if (item2 != null) { if (!item2.MoveToContainer(container.inventory, -1, false, false, null, true)) { item2.Remove(0f); } }
340 341 }
341 342 }
342 343 }
343 344
344 345 //Correct local position
345 346 void OnEntitySpawned(CodeLock cl) { MoveLockPos(cl); }
346 347
347 348 void OnEntitySpawned(KeyLock kl) { MoveLockPos(kl); }
348 349
349 350 //Rotation, Break frame hook
350 351 object OnHammerHit(BasePlayer ownerPlayer, HitInfo info)
351 352 {
352 353 if (ownerPlayer == null || !(info?.HitEntity is Door)) { return null; }
353 354 //Check has reload down and permission
354 355 if (info.HitEntity.prefabID == 3647679950 && ownerPlayer.IsBuildingAuthed())
355 356 {
356 357 if (ownerPlayer.serverInput.IsDown(BUTTON.SPRINT) && ownerPlayer.serverInput.IsDown(BUTTON.RELOAD) && permission.UserHasPermission(ownerPlayer.UserIDString, PermB))
357 358 {
358 359 BreakFrame(info?.HitEntity as Door);
359 360 return true;
360 361 }
361 362 if (ownerPlayer.serverInput.IsDown(BUTTON.RELOAD) && permission.UserHasPermission(ownerPlayer.UserIDString, PermR))
362 363 {
363 364 if (config.Oversize && IsRotatedGD(info?.HitEntity as Door)) { ownerPlayer.ChatMessage(message(ownerPlayer, "CantRotate")); return true; }
364 365 RotateDoor(info?.HitEntity as Door);
365 366 return true;
366 367 }
367 368 }
368 369 return null;
369 370 }
370 371
371 372 //Catch adding door controller / Hammer
372 373 private void OnActiveItemChanged(BasePlayer __instance)
373 374 {
374 375 if (__instance?.GetActiveItem()?.info?.itemid == -502177121 && !__instance.IsBuildingBlocked()) //-502177121 Door Controller
375 376 {
376 377 DoorControllerPlacer AP = __instance.GetComponent<DoorControllerPlacer>(); //Check if player has component
377 378 if (AP == null) { AP = __instance.gameObject.AddComponent<DoorControllerPlacer>(); } //Add component if player doesnt have it
378 379 AP.player = __instance; //Set player variable in component
379 380 }
380 381 //200773292 Hammer
381 382 if (__instance?.GetActiveItem()?.info?.itemid == 200773292 && !__instance.IsBuildingBlocked() && permission.UserHasPermission(__instance.UserIDString, PermG))
382 383 {
383 384 GUIWindow AP = __instance.GetComponent<GUIWindow>(); //Check if player has component
384 385 if (AP == null) { AP = __instance.gameObject.AddComponent<GUIWindow>(); } //Add component if player doesnt have it
385 386 AP.player = __instance; //Set player variable in component
386 387 }
387 388 }
388 389
389 390 private void OnEntityBuilt(Planner plan, GameObject go) { CheckDeploy(plan, go.ToBaseEntity()); } //Tried To Place Something
390 391
391 392 //Remove Tool
392 393 private void OnNormalRemovedEntity(BasePlayer player, BaseEntity entity)
393 394 {
394 395 if (PickedUpGrageDoor(player, entity)) { NextFrame(() => { Take(player, -148794216, 1); }); }
395 396 }
396 397
397 398 [ChatCommand("gdskin")] //Set door skin to use on all rotated garage doors
398 399 private void SetGDSkin(BasePlayer player, string command, string[] args)
399 400 {
400 401 if (permission.UserHasPermission(player.UserIDString, PermS))
401 402 {
402 403 if (args.Length != 1)
403 404 {
404 405 player.ChatMessage(message(player, "NeedSkinID"));
405 406 return;
406 407 }
407 408 ulong skinid = 0;
408 409 if (!ulong.TryParse(args[0], out skinid))
409 410 {
410 411 player.ChatMessage(message(player, "InvalidSkinID"));
411 412 return;
412 413 }
413 414 if (!LastUsedSkin.ContainsKey(player.userID)) { LastUsedSkin.Add(player.userID, skinid); }
414 415 else
415 416 {
416 417 if (skinid == 0) { LastUsedSkin.Remove(player.userID); }
417 418 else { LastUsedSkin[player.userID] = skinid; }
418 419 }
419 420 player.ChatMessage(message(player, "UpdatedID", skinid));
420 421 return;
421 422 }
422 423 player.ChatMessage(message(player, "NoPerm"));
423 424 }
424 425
425 426 [ChatCommand("resetlockpos")] //Turn door
426 427 private void resetlockpos(BasePlayer player, string command, string[] args)
427 428 {
428 429 if (player.IsAdmin)
429 430 {
430 431 foreach (var s in BaseNetworkable.serverEntities.entityList.Get().Values)
431 432 {
432 433 if (s != null)
433 434 {
434 435 if (s is Door && IsRotatedGD(s as Door))
435 436 {
436 437 Door door = s as Door;
437 438 if (door.HasSlot(BaseEntity.Slot.Lock))
438 439 {
439 440 MoveLockPos(door.GetSlot(BaseEntity.Slot.Lock));
440 441 }
441 442 }
442 443 }
443 444 }
444 445 player.ChatMessage("Reset All Locks On Rotated Garage Doors");
445 446 }
446 447 }
447 448
448 449 [ChatCommand("td")] //Turn door
449 450 private void GetDoorChatCommand(BasePlayer player, string command, string[] args)
450 451 {
451 452 if (permission.UserHasPermission(player.UserIDString, Perm))
452 453 {
453 454 Item active = player?.GetActiveItem();
454 455 if (active == null) { return; }
455 456 int slot = active.position;
456 457 if (active.info.itemid == -148794216) //Check player is holding garage door
457 458 {
458 459 Item RotatedGarageDoor = ItemManager.CreateByItemID(1948067030, active.amount, config.CustomSkinID);
459 460 RotatedGarageDoor.name = message(player, "DoorName");
460 461 RotatedGarageDoor.MarkDirty();
461 462 GiveItem(player, RotatedGarageDoor, message(player, "DoorName"), slot);
462 463 RotatedGarageDoor.position = slot;
463 464 storedData.SID.Add(RotatedGarageDoor.uid.Value, active.skin); //Create a turned garage door (ladder hatch skinned) and add to save data
464 465 active.amount = 0;
465 466 active.Remove();
466 467 Interface.Oxide.DataFileSystem.WriteObject(Name, storedData);
467 468 return;
468 469 }
469 470 if (active.info.itemid == 1948067030 && active.skin == config.CustomSkinID) //Check player is holding a turned garage door (skinned ladder hatch)
470 471 {
471 472 if (storedData.SID.ContainsKey(active.uid.Value)) //Check there is save data
472 473 {
473 474 Item NewGarageDoor = ItemManager.CreateByItemID(-148794216, active.amount, storedData.SID[active.uid.Value]); //Create garage door with skin
474 475 storedData.SID.Remove(active.uid.Value); //Remove sav data reference
475 476 active.amount = 0;
476 477 active.Remove();
477 478 GiveItem(player, NewGarageDoor, message(player, "DoorName"), slot);
478 479 NewGarageDoor.position = slot;
479 480 Interface.Oxide.DataFileSystem.WriteObject(Name, storedData);
480 481 return;
481 482 }
482 483 else
483 484 {
484 485 Item NewGarageDoor = ItemManager.CreateByItemID(-148794216, active.amount, 0); //Create garage door with skin
485 486 active.amount = 0;
486 487 active.Remove();
487 488 GiveItem(player, NewGarageDoor, message(player, "DoorName"), slot);
488 489 NewGarageDoor.position = slot;
489 490 }
490 491 }
491 492 player.ChatMessage(message(player, "MustHold"));
492 493 return;
493 494 }
494 495 player.ChatMessage(message(player, "NoPerm"));
495 496 }
496 497
497 498 [ConsoleCommand("garagedoor2.give")]
498 499 private void Cmd(ConsoleSystem.Arg arg)
499 500 {
500 501 if (arg.IsAdmin && arg.Args?.Length > 0)
501 502 {
502 503 var player = BasePlayer.Find(arg.Args[0].ToString()) ?? BasePlayer.FindSleeping(arg.Args[0].ToString()); //Get player from arg
503 504 if (player == null)
504 505 {
505 506 PrintWarning($"Can't find player with that name/ID! {arg.Args[0]}");
506 507 return;
507 508 }
508 509 var item = ItemManager.CreateByName("floor.ladder.hatch", 1, config.CustomSkinID); //Create rotated garage door
509 510 if (item != null)
510 511 {
511 512 //Give to player
512 513 item.text = message(player, "DoorName");
513 514 item.name = item.text;
514 515 player.GiveItem(item);
515 516 }
516 517 }
517 518 }
518 519
519 520 [ConsoleCommand("GD2GUI")]
520 521 private void GD2GUICMD(ConsoleSystem.Arg arg)
521 522 {
522 523 BaseNetworkable info = null;
523 524 BasePlayer player = arg.Player();
524 525 if (player == null) { return; } //Checks from player
525 526 ulong playerid = player.userID;
526 527 if (Cooldowns.Contains(playerid)) { return; }
527 528 Cooldowns.Add(playerid);
528 529 timer.Once(config.CooldownDelay, () => { Cooldowns.Remove(playerid); });
529 530 if (arg.Args?.Length > 1)
530 531 {
531 532 ulong targetId;
532 533 if (ulong.TryParse(arg.Args[1].ToString(), out targetId)) { info = BaseNetworkable.serverEntities.Find(new NetworkableId(targetId)); } //Gets door from id
533 534 }
534 535 if (info == null || Vector3.Distance(info.transform.position, player.transform.position) > 5) { return; } //Checks player isnt far away (exploit)
535 536 if (arg.Args.Length > 0) //Arg passed
536 537 {
537 538 switch (arg.Args[0].ToString())
538 539 {
539 540 case "R":
540 541 if (permission.UserHasPermission(player.UserIDString, PermR))
541 542 {
542 543 if (config.Oversize && IsRotatedGD(info as Door)) { player.ChatMessage(message(player, "CantRotate")); return; }
543 544 RotateDoor(info as Door);
544 545 }
545 546 break;
546 547 case "P":
547 548 if (arg.Args.Length > 2)
548 549 {
549 550 DrawCUI(player, info as Door, arg.Args[2].ToString());
550 551 }
551 552 break;
552 553 case "B":
553 554 if (arg.Args.Length > 2 && permission.UserHasPermission(player.UserIDString, PermB))
554 555 {
555 556 BreakFrame((info as Door));
556 557 DrawCUI(player, info as Door, arg.Args[2].ToString());
557 558 }
558 559 break;
559 560 case "S":
560 561 if (arg.Args.Length > 2 && permission.UserHasPermission(player.UserIDString, PermS))
561 562 {
562 563 ulong skinid;
563 564 if (!ulong.TryParse(arg.Args[2].ToString(), out skinid))
564 565 {
565 566 player.ChatMessage(message(player, "InvalidSkinID"));
566 567 return;
567 568 }
568 569 (info as Door).skinID = skinid;
569 570 if (!string.IsNullOrEmpty(config.SSoundFX)) { Effect.server.Run(config.SSoundFX, info.transform.position); }
570 571 info.SendNetworkUpdateImmediate();
571 572 }
572 573 break;
573 574 }
574 575 }
575 576 }
576 577 #endregion
577 578
578 579 #region Methods
579 580 private List<CuiElement> RustUI(string netid, string skinid, string skinperm, string rotateperm, string breakperm, string[] lang)
580 581 {
581 582 ulong nid;
582 583 bool skin = false;
583 584 bool turn = false;
584 585 bool frame = false;
585 586 if (!ulong.TryParse(netid, out nid) || !bool.TryParse(skinperm, out skin) || !bool.TryParse(rotateperm, out turn) || !bool.TryParse(breakperm, out frame)) { return null; }
586 587 BaseNetworkable bn = BaseNetworkable.serverEntities.Find(new NetworkableId(nid));
587 588 if (bn == null || !(bn is Door)) { return null; }
588 589
589 590 var container = new CuiElementContainer();
590 591 container.Add(new CuiElement
591 592 {
592 593 Name = "GD2P",
593 594 Parent = "Overlay",
594 595 DestroyUi = "GD2P",
595 596 Components =
596 597 {
597 598 new CuiNeedsCursorComponent(),
598 599 new CuiImageComponent{ Color = "1 1 1 0" },
599 600 new CuiRectTransformComponent{ AnchorMin = "0 0", AnchorMax = "1 1", OffsetMin = "0 0", OffsetMax = "0 0" }
600 601 }
601 602 });
602 603
603 604 container.Add(new CuiElement
604 605 {
605 606 Name = "GD2GUI",
606 607 Parent = "GD2P",
607 608 DestroyUi = "GD2GUI",
608 609 Components =
609 610 {
610 611 new CuiNeedsCursorComponent(),
611 612 new CuiImageComponent{ Color = ".18 .18 .18 1" },
612 613 new CuiRectTransformComponent{ AnchorMin = string.Format("{0} {1}", lang[3], lang[4]), AnchorMax = string.Format("{0} {1}", lang[5], lang[6]) }
613 614 }
614 615 });
615 616
616 617 container.Add(new CuiButton
617 618 {
618 619 Button = { Color = (turn ? "1 1 1 1" : ".5 .5 .5 1"), Command = (turn ? "GD2GUI R " + netid : "") },
619 620 Text = { Text = lang[0], FontSize = 18, Align = TextAnchor.MiddleCenter, Color = "0 0 0 1" },
620 621 RectTransform = { AnchorMin = ".5 .5", AnchorMax = ".5 .5", OffsetMin = "-42.84 -72", OffsetMax = "43.23 -48" }
621 622 }, "GD2GUI", "RBtn");
622 623
623 624 container.Add(new CuiButton
624 625 {
625 626 Button = { Color = (skin ? "1 1 1 1" : ".5 .5 .5 1"), Command = (skin ? "GD2GUI S " + netid + " " + skinid : "") },
626 627 Text = { Text = lang[1], FontSize = 18, Align = TextAnchor.MiddleCenter, Color = "0 0 0 1" },
627 628 RectTransform = { AnchorMin = ".5 .5", AnchorMax = ".5 .5", OffsetMin = "-42.84 -45", OffsetMax = "43.23 -21" }
628 629 },
629 630 "GD2GUI",
630 631 "SBtn");
631 632
632 633 container.Add(new CuiButton
633 634 {
634 635 Button = { Color = (frame ? "1 1 1 1" : ".5 .5 .5 1"), Command = (frame ? "GD2GUI B " + netid + " " + skinid : "") },
635 636 Text = { Text = lang[2], FontSize = 18, Align = TextAnchor.MiddleCenter, Color = "0 0 0 1" },
636 637 RectTransform = { AnchorMin = ".5 .5", AnchorMax = ".5 .5", OffsetMin = "-42.84 -99", OffsetMax = "43.23 -75" }
637 638 }, "GD2GUI", "BBtn");
638 639
639 640 container.Add(new CuiElement
640 641 {
641 642 Name = "SIcon",
642 643 Parent = "GD2GUI",
643 644 Components = {
644 645 new CuiImageComponent { Color = "1 1 1 1", SkinId = ulong.Parse(skinid), ItemId = -148794216 },
645 646 new CuiOutlineComponent { Color = "0 0 0 .5", Distance = "1 -1" },
646 647 new CuiRectTransformComponent { AnchorMin = ".5 .5", AnchorMax = ".5 .5", OffsetMin = "-42 10", OffsetMax = "42 90" }
647 648 }
648 649 });
649 650
650 651 container.Add(new CuiElement
651 652 {
652 653 Name = "SLabel",
653 654 Parent = "GD2GUI",
654 655 Components = {
655 656 new CuiTextComponent { Text = lang[1]+":", FontSize = 12, Align = TextAnchor.MiddleLeft, Color = "1 1 1 1" },
656 657 new CuiOutlineComponent { Color = "0 0 0 .5", Distance = "1 -1" },
657 658 new CuiRectTransformComponent { AnchorMin = ".5 .5", AnchorMax = ".5 .5", OffsetMin = "-43.48 -10", OffsetMax = "-.48 10" }
658 659 }
659 660 });
660 661
661 662 container.Add(new CuiButton
662 663 {
663 664 Button = { Color = "1 1 1 1", Close = "GD2P" },
664 665 Text = { Text = "X", FontSize = 7, Align = TextAnchor.MiddleCenter, Color = "1 0 0 1" },
665 666 RectTransform = { AnchorMin = ".5 .5", AnchorMax = ".5 .5", OffsetMin = "43.72 101.11", OffsetMax = "53.99 111.55" }
666 667 }, "GD2GUI", "CBtn");
667 668
668 669 container.Add(new CuiElement
669 670 {
670 671 Name = "SIn",
671 672 Parent = "GD2GUI",
672 673 Components = {
673 674 new CuiInputFieldComponent { Color = "1 1 1 1", FontSize = 10, Align = TextAnchor.UpperLeft, CharsLimit = 11, IsPassword = false, ReadOnly = !skin, Text = skinid , Command = "GD2GUI P " + netid },
674 675 new CuiRectTransformComponent { AnchorMin = ".5 .5", AnchorMax = ".5 .5", OffsetMin = "-15.04 -20", OffsetMax = "43.71 5" }
675 676 }
676 677 });
677 678 return container;
678 679 }
679 680
680 681 private void DrawCUI(BasePlayer player, Door garagedoor, string skinid)
681 682 {
682 683 if (player == null || garagedoor == null) { return; }
683 684 CuiHelper.AddUi(player, RustUI(garagedoor.net.ID.Value.ToString(), skinid, plugin.permission.UserHasPermission(player.UserIDString, plugin.PermS).ToString(), (plugin.permission.UserHasPermission(player.UserIDString, plugin.PermR) && !IsScaledGD(garagedoor)).ToString(), (plugin.permission.UserHasPermission(player.UserIDString, plugin.PermB) && (garagedoor?.GetParentEntity()?.prefabID == 372561515 || config.breakall)).ToString(), new string[] { plugin.message(player, "Rotate"), plugin.message(player, "Skin"), plugin.message(player, "Break"), plugin.config.MinX, plugin.config.MinY, plugin.config.MaxX, plugin.config.MaxY }));
684 685 }
685 686 private bool RandomChance(uint Seed, int Percentage)
686 687 {
687 688 //Create Chance List
688 689 List<bool> chance = Pool.Get<List<bool>>();
689 690 for (int i = 0; i < 100; i++)
690 691 {
691 692 if (i < Percentage)
692 693 {
693 694 chance.Add(true);
694 695 continue;
695 696 }
696 697 chance.Add(false);
697 698 }
698 699 chance.Shuffle(Seed); //shuffle
699 700 bool result = chance.ElementAt(UnityEngine.Random.Range(1, 100)); //Pick Random In Chance List
700 701 Pool.FreeUnmanaged(ref chance);
701 702 return result;
702 703 }
703 704
704 705 private bool IsRotatedGD(BaseEntity entity)
705 706 {
706 707 if (entity?.prefabID == 3647679950 && storedData.Rotated.Contains(entity.net.ID.Value)) // Rotated Garage Door
707 708 {
708 709 return true;
709 710 }
710 711 return false;
711 712 }
712 713
713 714 private bool IsScaledGD(BaseEntity entity)
714 715 {
715 716 if (config.Oversize && entity?.prefabID == 3647679950)
716 717 {
717 718 if (storedData.Scaled.Contains(entity.net.ID.Value))
718 719 {
719 720 return true;
720 721 }
721 722 }
722 723 return false;
723 724 }
724 725
725 726 private void MoveLockPos(BaseEntity baselock)
726 727 {
727 728 NextFrame(() =>
728 729 {
729 730 BaseEntity door = baselock.GetParentEntity();
730 731 if (door != null && IsRotatedGD(door))
731 732 {
732 733 baselock.transform.localPosition = door.transform.right * 1.40f;
733 734 baselock.SendNetworkUpdateImmediate();
734 735 }
735 736 });
736 737 }
737 738
738 739 private void RotateDoor(Door gd)
739 740 {
740 741 if (!string.IsNullOrEmpty(config.SoundFX)) { Effect.server.Run(config.SoundFX, gd.transform.position); }
741 742 gd.transform.localRotation *= Quaternion.Euler(90, 0, 0);
742 743 gd.transform.position += gd.transform.forward * -1.5f + gd.transform.up * -1.5f;
743 744 gd.SendNetworkUpdateImmediate();
744 745 }
745 746
746 747 private bool PickedUpGrageDoor(BasePlayer player, BaseEntity entity)
747 748 {
748 749 if (IsRotatedGD(entity)) //Garage Door
749 750 {
750 751 Item RotatedGarageDoor = ItemManager.CreateByItemID(1948067030, 1, config.CustomSkinID);
751 752 RotatedGarageDoor.name = message(player, "DoorName");
752 753 RotatedGarageDoor.MarkDirty();
753 754 GiveItem(player, RotatedGarageDoor, message(player, "DoorName"), -1, false); //Create a turned garage door (ladder hatch skinned) and add to save data
754 755 storedData.SID.Add(RotatedGarageDoor.uid.Value, entity.skinID);
755 756 storedData.Rotated.Remove(entity.net.ID.Value);
756 757 Interface.Oxide.DataFileSystem.WriteObject(Name, storedData);
757 758 NextFrame(() => { entity.Kill(BaseNetworkable.DestroyMode.None); }); //Destroy the entity that tried to be picked up
758 759 return true; //PickedUp
759 760 }
760 761 return false;
761 762 }
762 763
763 764 private void BreakFrame(Door door)
764 765 {
765 766 if (door != null)
766 767 {
767 768 //Get floor frame from parent
768 769 BaseEntity be = door.GetParentEntity();
769 770 if (be == null)
770 771 {
771 772 List<BuildingBlock> bb = Pool.Get<List<BuildingBlock>>();
772 773 Vis.Entities<BuildingBlock>(door.transform.position, 5, bb);
773 774 foreach (BuildingBlock bp in bb)
774 775 {
775 776 if (bp.prefabID == 919059809)
776 777 {
777 778 be = bp;
778 779 break;
779 780 }
780 781 }
781 782 Pool.FreeUnmanaged(ref bb);
782 783 }
783 784 if (be is BuildingBlock)
784 785 {
785 786 if (be.prefabID == 372561515 || config.breakall) //Floor Frame
786 787 {
787 788 door.grounded = true;
788 789 door.SetParent(null, true, true); //Unparent
789 790 ////Recenter door if scaling
790 791 if (config.Oversize)
791 792 {
792 793 door.transform.position = door.transform.position + door.transform.up * -.25f;
793 794 ScaleDoor(door);
794 795 storedData.Scaled.Add(door.net.ID.Value);
795 796 Interface.Oxide.DataFileSystem.WriteObject(Name, storedData);
796 797 }
797 798 NextFrame(() => //Remove floor frame
798 799 {
799 800 if (!string.IsNullOrEmpty(config.FSoundFX)) { Effect.server.Run(config.FSoundFX, be.transform.position); }
800 801 be.Kill();
801 802 });
802 803 }
803 804 }
804 805 }
805 806 }
806 807
807 808 private void ScaleDoor(Door door)
808 809 {
809 810 door.networkEntityScale = true;
810 811 door.transform.localScale = new Vector3(1.11f, 1.11f, 1.11f);
811 812 door.SendNetworkUpdateImmediate();
812 813 }
813 814
814 815 private void GiveItem(BasePlayer player, Item item, string message, int slot, bool SFX = true)
815 816 {
816 817 if (SFX && !string.IsNullOrEmpty(config.SoundFX)) { Effect.server.Run(config.SoundFX, player.transform.position); } //Check if should use SFX
817 818 timer.Once(1f, () =>
818 819 {
819 820 //Wait 1 sec before trying to move item
820 821 if (item.MoveToContainer(player.inventory.containerBelt, slot))
821 822 {
822 823 MessageUI(player, item, message);
823 824 return;
824 825 }
825 826 if (item.MoveToContainer(player.inventory.containerMain, slot)) //Couldnt move into hotbar try main
826 827 {
827 828 MessageUI(player, item, message);
828 829 return;
829 830 }
830 831 item.Drop(player.inventory.containerMain.dropPosition, player.inventory.containerMain.dropVelocity); //Couldnt move onto player so drop
831 832 MessageUI(player, item, message);
832 833 });
833 834 }
834 835
835 836 private void MessageUI(BasePlayer player, Item item, string message) //Give UI notice
836 837 {
837 838 if (!string.IsNullOrEmpty(message)) { player.Command("note.inv", item.info.itemid, item.amount, message, 0); }
838 839 else { player.Command("note.inv", item.info.itemid, item.amount, string.Empty, 0); }
839 840 }
840 841
841 842 private void CheckDeploy(Planner plan, BaseEntity entity) //Check if should place
842 843 {
843 844 if (entity == null || plan == null || entity?.prefabID != 1655365262) { return; } //Only run on ladder hatches
844 845 BasePlayer player = plan?.GetParentEntity() as BasePlayer; //Get player
845 846 if (player == null) { return; } //No player so dont run
846 847 Item LadderHatch = plan.GetItem(); //Get dummy item
847 848 StabilityEntity stabilityEntity = (entity as StabilityEntity);
848 849 //Check if is blocked already
849 850 if (stabilityEntity?.GetNearbyBuildingBlock()?.children?.Count != 0) //Frame is already full
850 851 {
851 852 //Fix for detecting barges which have no foundation
852 853 bool Hasfoundation = false;
853 854 foreach (var bb in stabilityEntity.GetBuilding()?.buildingBlocks)
854 855 {
855 856 if (bb.PrefabName.Contains("foundation"))
856 857 {
857 858 Hasfoundation = true;
858 859 break;
859 860 }
860 861 }
861 862 if (!Hasfoundation && entity.skinID != config.CustomSkinID)
862 863 {
863 864 //Barge so ignore rest of my code
864 865 return;
865 866 }
866 867 Item active = player?.GetActiveItem(); //Get held item incase of stack
867 868 if (active != null && active.amount > 0 && active.info.itemid == 1948067030)
868 869 {
869 870 //Give back to stack
870 871 active.amount++;
871 872 active.MarkDirty();
872 873 }
873 874 else
874 875 {
875 876 //Create new item
876 877 Item NewLadderHatch = ItemManager.CreateByItemID(1948067030, 1, 0); //Create Item To Give Back Since Place Was Blocked
877 878 NewLadderHatch.name = message(player, "DoorName");
878 879 NewLadderHatch.MarkDirty();
879 880 //Check if its a custom ladder hatch if so update sav data file.
880 881 if (storedData.SID.ContainsKey(LadderHatch.uid.Value))
881 882 {
882 883 storedData.SID.Add(NewLadderHatch.uid.Value, storedData.SID[LadderHatch.uid.Value]);
883 884 storedData.SID.Remove(LadderHatch.uid.Value);
884 885 NewLadderHatch.skin = config.CustomSkinID;
885 886 Interface.Oxide.DataFileSystem.WriteObject(Name, storedData);
886 887 }
887 888 //Give item back
888 889 GiveItem(player, NewLadderHatch, message(player, "Blocked"), LadderHatch.position, false);
889 890 }
890 891 NextTick(() =>
891 892 {
892 893 if (!Hasfoundation) { player.ChatMessage(message(player, "NoFoundation")); }
893 894 entity?.Kill();
894 895 }); //Destroy Dummy
895 896 return;
896 897 }
897 898 if (entity.skinID == config.CustomSkinID)
898 899 {
899 900 //Create horizontal garage door in frame
900 901 StabilityEntity Gdoor = GameManager.server.CreateEntity(GarageDoor, entity.transform.position + (entity.transform.right * 1.5f) + (entity.transform.up * 0.18f), entity.transform.rotation * Quaternion.Euler(0, 0, 90f)) as StabilityEntity;
901 902 if (Gdoor == null) { NextTick(() => { entity?.Kill(); }); return; } //Prefab failed to load/spawn, bail out instead of crashing
902 903 Gdoor.Spawn();
903 904 if (LastUsedSkin.ContainsKey(player.userID)) { Gdoor.skinID = LastUsedSkin[player.userID]; }
904 905 else if (storedData.SID.ContainsKey(LadderHatch.uid.Value))
905 906 {
906 907 Gdoor.skinID = storedData.SID[LadderHatch.uid.Value];
907 908 storedData.SID.Remove(LadderHatch.uid.Value);
908 909 }
909 910 Gdoor.creatorEntity = player;
910 911 Gdoor.OwnerID = entity.OwnerID;
911 912 Gdoor.grounded = true;
912 913 Gdoor.pickup.enabled = true;
913 914 storedData.Rotated.Add(Gdoor.net.ID.Value);
914 915 Interface.Oxide.DataFileSystem.WriteObject(Name, storedData);
915 916 BuildingBlock buildingBlock = (entity as StabilityEntity).GetNearbyBuildingBlock();
916 917 if (buildingBlock == null || buildingBlock.prefabID == 372561515)//.ShortPrefabName != "floor.frame") //Fix for stability bunkers parenting to wrong building block.
917 918 {
918 919 int distance = 15;
919 920 foreach (var block in (entity as StabilityEntity).GetBuilding()?.buildingBlocks)
920 921 {
921 922 if (block.prefabID == 372561515)
922 923 {
923 924 float d = Vector3.Distance(block.transform.position, entity.transform.position);
924 925 if (d < distance)
925 926 {
926 927 distance = (int)d;
927 928 buildingBlock = block;
928 929 }
929 930 }
930 931 }
931 932 }
932 933 Gdoor.SetParent((entity as StabilityEntity).GetNearbyBuildingBlock(), true, true); //Parent to frame to stop other things placing
933 934 NextTick(() => { entity?.Kill(); }); //Remove dummy ladder hatch.
934 935 }
935 936 }
936 937
937 938 private void Take(BasePlayer player, int itemid, int amount)
938 939 {
939 940 var collect = Facepunch.Pool.Get<List<Item>>();
940 941 player.inventory.Take(collect, itemid, amount); // Take only calls RemoveFromContainer
941 942 foreach (Item item in collect)
942 943 {
943 944 item.Remove();
944 945 }
945 946 Facepunch.Pool.FreeUnmanaged(ref collect);
946 947 }
947 948
948 949 private BaseEntity FindBaseEntity(Ray ray)
949 950 {
950 951 //Ray cast to find BaseEntity
951 952 RaycastHit hit;
952 953 var raycast = UnityEngine.Physics.Raycast(ray, out hit, 4, -1);
953 954 BaseEntity entity = raycast ? hit.GetEntity() : null;
954 955 if (entity != null) { return entity; }
955 956 return null;
956 957 }
957 958
958 959 private bool AddDoorController(Door garagedoor)
959 960 {
960 961 var pos = garagedoor.transform.position + garagedoor.transform.up * 2.9f + garagedoor.transform.forward * -1.3f; //Roller position
961 962 //Set to normal position
962 963 if (!config.MoveController)
963 964 {
964 965 pos = garagedoor.transform.position + garagedoor.transform.up * 1.2f + garagedoor.transform.forward * -1.3f;
965 966 }
966 967 if (IsScaledGD(garagedoor))
967 968 {
968 969 pos += garagedoor.transform.up * 0.3f; //Add extra offset if door already scaled
969 970 }
970 971 //Create and setup door controller
971 972 DoorManipulator dc = GameManager.server.CreateEntity(DoorController, pos, garagedoor.transform.rotation) as DoorManipulator;
972 973 if (dc != null)
973 974 {
974 975 dc.Spawn();
975 976 dc.OwnerID = garagedoor.OwnerID;
976 977 dc.SetParent(garagedoor, true, false);
977 978 dc.transform.localRotation *= Quaternion.Euler(0, 90, 0);
978 979 dc.SendNetworkUpdateImmediate();
979 980 garagedoor.Invoke(delegate { dc.SetTargetDoor(garagedoor); }, 0.25f);
980 981 return true;
981 982 }
982 983 return false;
983 984 }
984 985 #endregion
985 986
986 987 #region Class
987 988 public class DoorControllerPlacer : MonoBehaviour
988 989 {
989 990 public BasePlayer player;
990 991
991 992 public void FixedUpdate()
992 993 {
993 994 try //Try and catch incase something in the future changes to prevent null references
994 995 {
995 996 if (player == null || player.IsSleeping() || player?.GetActiveItem()?.info?.itemid != -502177121) { Destroy(this); } //Destroy component when conditions are no longer meet
996 997 else if (player.serverInput.WasJustReleased(BUTTON.FIRE_PRIMARY)) //Catch mouse fire button
997 998 {
998 999 if (player.IsBuildingBlocked()) { return; } //Check player isnt building blocked
999 1000 Door door = plugin.FindBaseEntity(player.eyes.HeadRay()) as Door; //Get Door being looked at
1000 1001 if (door != null && plugin.IsRotatedGD(door)) //Garage Door
1001 1002 {
1002 1003 foreach (var child in door.children) { if (child != null && child is DoorManipulator) { return; } } //Make sure doesnt already have door controller
1003 1004 if (plugin.AddDoorController(door))
1004 1005 {
1005 1006 Effect.server.Run(Deploy, player.PivotPoint(), player.transform.up, null, true);
1006 1007 player.inventory.Take(null, -502177121, 1); //Remove door controller from player inventory if it attached
1007 1008 if (player.inventory.GetAmount(-502177121) == 0) { Destroy(this); return; } //Checks player has any door controllers left
1008 1009 }
1009 1010 }
1010 1011 }
1011 1012 }
1012 1013 catch { Destroy(this); } //Something went wrong so remove CUI
1013 1014 }
1014 1015 }
1015 1016
1016 1017 public class GUIWindow : MonoBehaviour
1017 1018 {
1018 1019 public BasePlayer player;
1019 1020 public float tick;
1020 1021 public Vector3 pos = Vector3.zero;
1021 1022
1022 1023 public void OnDestroy() { if (player != null) { CuiHelper.DestroyUi(player, "GD2P"); } }
1023 1024
1024 1025 public void FixedUpdate()
1025 1026 {
1026 1027 try //Try and catch incase something in the future changes to prevent null references
1027 1028 {
1028 1029 //Only check twice a sec
1029 1030 if (tick > UnityEngine.Time.realtimeSinceStartup) { return; }
1030 1031 tick = UnityEngine.Time.realtimeSinceStartup + 0.5f;
1031 1032 if (player == null || player.IsSleeping() || player?.GetActiveItem()?.info?.itemid != 200773292) { Destroy(this); } //Destroy component when conditions are no longer meet
1032 1033 if (pos != Vector3.zero && Vector3Ex.Distance2D(player.transform.position, pos) > 0.5f) //Moved to far away
1033 1034 {
1034 1035 CuiHelper.DestroyUi(player, "GD2P"); //Destory CUI
1035 1036 pos = Vector3.zero; //Reset Position
1036 1037 return;
1037 1038 }
1038 1039 if (player.serverInput.IsDown(BUTTON.FIRE_SECONDARY) || player.serverInput.IsDown(BUTTON.FIRE_THIRD)) //Catch mouse right button
1039 1040 {
1040 1041 if (!player.IsBuildingAuthed()) { return; } //Check player is building Auth
1041 1042 Door door = plugin.FindBaseEntity(player.eyes.HeadRay()) as Door; //Get Door being looked at
1042 1043 if (door != null && door.prefabID == 3647679950) //Garage Door
1043 1044 {
1044 1045 pos = player.transform.position;
1045 1046 plugin.DrawCUI(player, door, door.skinID.ToString());
1046 1047 }
1047 1048 }
1048 1049 }
1049 1050 catch { Destroy(this); } //Something went wrong so remove CUI
1050 1051 }
1051 1052 }
1052 1053 #endregion
1053 1054 }
1054 1055 }