← Back to changelog

What changed

MLRSCargo · v1.0.7 → v1.0.8 (current)

Diff 3 added · 2 removed · 737 → 738 total lines
1 1 /*
2 2 ▄▄▄▄ ███▄ ▄███▓ ▄████ ▄▄▄██▀▀▀▓█████▄▄▄█████▓
3 3 ▓█████▄ ▓██▒▀█▀ ██▒ ██▒ ▀█▒ ▒██ ▓█ ▀▓ ██▒ ▓▒
4 4 ▒██▒ ▄██▓██ ▓██░▒██░▄▄▄░ ░██ ▒███ ▒ ▓██░ ▒░
5 5 ▒██░█▀ ▒██ ▒██ ░▓█ ██▓▓██▄██▓ ▒▓█ ▄░ ▓██▓ ░
6 6 ░▓█ ▀█▓▒██▒ ░██▒░▒▓███▀▒ ▓███▒ ░▒████▒ ▒██▒ ░
7 7 ░▒▓███▀▒░ ▒░ ░ ░ ░▒ ▒ ▒▓▒▒░ ░░ ▒░ ░ ▒ ░░
8 8 ▒░▒ ░ ░ ░ ░ ░ ░ ▒ ░▒░ ░ ░ ░ ░
9 9 ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
10 10 ░ ░ ░ ░ ░ ░ ░
11 11 Permissions:
12 12 MLRSCargo.admin Required for chat commands
13 13
14 14 Chat Commands:
15 15 /mlrscargotest - Give Admin MLRS Air Strike Smoke Signal
16 16 /mlrscargoreset - Resets the MLRS on all cargoships
17 17 */
18 18 using Facepunch;
19 19 using Newtonsoft.Json;
20 20 using Oxide.Core.Plugins;
21 21 using System.Collections.Generic;
22 22 using System.Linq;
23 23 using UnityEngine;
24 24 namespace Oxide.Plugins
25 25 {
26 - [Info("MLRSCargo", "bmgjet", "1.0.7")]
26 + [Info("MLRSCargo", "bmgjet", "1.0.8")]
27 27 [Description("Places a MLRS on the front of CargoShip")]
28 28 public class MLRSCargo : RustPlugin
29 29 {
30 30 public static MLRSCargo _plugin;
31 31 public List<MLRS> mlrs = new List<MLRS>();
32 32 //Active MRLS to limit to 1 user at a time.
33 33 public static bool active = false;
34 34 //Last player to trigger event
35 35 public BasePlayer thrower;
36 36 //Show Debug Info
37 37 bool showDebug = false;
38 38 //Admining Permission
39 39 public const string AdminPerm = "MLRSCargo.admin";
40 40
41 41 //Reference Kits plugin so kits can be applied to NPCs
42 42 [PluginReference]
43 43 private Plugin Kits;
44 44
45 45 #region Configuration
46 46 private Configuration config;
47 47 private class Configuration
48 48 {
49 49 [JsonProperty("Use Remote Controlled Mode With a Air Strike Signal (Requires you also enabled SpawnReady)")]
50 50 public bool RemoteMode = false;
51 51
52 52 [JsonProperty("SpawnReady (MLRS Spawns Ready To Fire)")]
53 53 public bool Ready = false;
54 54
55 55 [JsonProperty("Cool Down Between MLRS Attacks")]
56 56 public int Delay = 600;
57 57
58 58 //Show announcements in public chat
59 59 [JsonProperty("Show Announcements In Public Chat")]
60 60 public bool PublicAnouncements = false;
61 61
62 62 [JsonProperty("Steam ID To Use As Profile Pic")]
63 63 //Steam ID to use profile pic as announcment chat icon
64 64 public string AnnouncementIcon = "76561199219302299";
65 65
66 66 //Cargo NPCs health multiplyer
67 67 [JsonProperty("Cargo NPCs Health Multiplyer (Boosts Health Of NPCs With In Radius)")]
68 68 public float healthmulti = 3f;
69 69
70 70 [JsonProperty("Cargo NPCs Scan Radius")]
71 71 //Radius to cast NPC Scan
72 72 public float NPCRadius = 50f; //Scan first 1/4 of ship around MLRS
73 73
74 74 [JsonProperty("Give Kit To Found NPCs (Leave Empty For Default)")]
75 75 public string Kitname = "";
76 76
77 77 [JsonProperty("Play Air Raid Siren On AirStrike Signal")]
78 78 //Play SFX over boombox from remote MLRS grenade
79 79 public bool PlaySFX = true;
80 80
81 81 [JsonProperty("URL To Air Raid Sound Effect (Must Be Raw MP3 Stream)")]
82 82 //URL to SFX (Must be a raw mp3 file)
83 83 public string SFXURL = "https://github.com/bmgjet/RocketFail/blob/main/AirRaid.mp3?raw=true";
84 84
85 85 [JsonProperty("MLRS Offset On Cargoship")]
86 86 //Position on cargoship moved from dead center
87 87 public Vector3 CargoOffset = new Vector3(0f, 9.5f, 76f);
88 88
89 89 public string ToJson() => JsonConvert.SerializeObject(this);
90 90
91 91 public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson());
92 92 }
93 93
94 94 protected override void LoadDefaultConfig() { config = new Configuration(); }
95 95 protected override void LoadConfig()
96 96 {
97 97 base.LoadConfig();
98 98 try
99 99 {
100 100 config = Config.ReadObject<Configuration>();
101 101 if (config == null) { throw new JsonException(); }
102 102
103 103 if (!config.ToDictionary().Keys.SequenceEqual(Config.ToDictionary(x => x.Key, x => x.Value).Keys))
104 104 {
105 105 PrintWarning("Configuration appears to be outdated; updating and saving");
106 106 SaveConfig();
107 107 }
108 108 }
109 109 catch
110 110 {
111 111 PrintWarning($"Configuration file {Name}.json is invalid; using defaults");
112 112 LoadDefaultConfig();
113 113 }
114 114 }
115 115 protected override void SaveConfig()
116 116 {
117 117 PrintWarning($"Configuration changes saved to {Name}.json");
118 118 Config.WriteObject(config, true);
119 119 }
120 120 #endregion Configuration
121 121
122 122 private void Init()
123 123 {
124 124 _plugin = this;
125 125 Unsubscribe("OnEntitySpawned");
126 126 permission.RegisterPermission(AdminPerm, this);
127 127 }
128 128
129 129 //Remote control only disable mounting
130 130 object CanMountEntity(BasePlayer bp, BaseMountable bm)
131 131 {
132 132 if (bm.prefabID == 223554808)
133 133 {
134 134 if (config.RemoteMode && config.Ready)
135 135 {
136 136 //Checks its a MLRS thats apart of the event
137 137 MLRSShoot s = bm.GetComponent<MLRSShoot>();
138 138 if (s != null)
139 139 {
140 140 //Has token avaliable.
141 141 if (s.Token)
142 142 {
143 143 s.Token = false;
144 144 //Gives tokwn to player
145 145 GiveMLRSSignal(bp);
146 146 //Disable Mount
147 147 return true;
148 148 }
149 149 else
150 150 {
151 151 rust.SendChatMessage(bp, "<color=orange>Cargo MLRS</color>", "MLRS signal has already been claimed", config.AnnouncementIcon);
152 152 //Disable Mount
153 153 return true;
154 154 }
155 155 }
156 156 }
157 157 }
158 158 //Allow Mount
159 159 return null;
160 160 }
161 161
162 162 //Loads MLRS on cargo as it spawns
163 163 void OnEntitySpawned(CargoShip cs)
164 164 {
165 165 //checks if cargo
166 166 if (cs != null)
167 167 {
168 168 //Creates MLRS
169 169 timer.Once(10f, () =>
170 170 {
171 171 AddMLRS(cs);
172 172 });
173 173 }
174 174 }
175 175
176 176 void OnEntitySpawned(MLRSRocket rocket)
177 177 {
178 178 //Checks if its one of the events rockets
179 179 if (rocket != null && active)
180 180 {
181 181 if (rocket.OwnerID == 0)
182 182 {
183 183 //Sets rocket as last user to trigger the event
184 184 rocket.OwnerID = thrower.userID;
185 185 rocket.creatorEntity = thrower;
186 186 //Create map markers since that event was missed with remote trigger.
187 187 BaseEntity baseEntity2 = GameManager.server.CreateEntity("assets/content/vehicles/mlrs/mlrsrocketmarker.prefab", rocket.transform.position, Quaternion.identity, true);
188 188 baseEntity2.OwnerID = thrower.userID;
189 189 baseEntity2.Spawn();
190 190 baseEntity2.SetParent(rocket, true, false);
191 191 }
192 192 }
193 193 }
194 194
195 195 //Prevent Looting Cargos MLRS when SpawnReady Set
196 196 private void OnLootEntity(BasePlayer player, BaseEntity entity)
197 197 {
198 198 try
199 199 {
200 200 if (!entity.GetParentEntity().GetComponent<MLRSShoot>())
201 201 return;
202 202 if (config.Ready)
203 203 NextTick(player.EndLooting);
204 204 }
205 205 catch { }
206 206 }
207 207
208 208 void OnServerInitialized()
209 209 {
210 210 //Validate Settings
211 211 if (config.Delay < 10)
212 212 {
213 213 Puts("Invalid Delay Must be 10 or greater!");
214 214 config.Delay = 10;
215 215 }
216 216 if (config.RemoteMode && !config.Ready)
217 217 {
218 218 Puts("Spawn Ready Must Be Used With Remote Mode Since CockPit Is Unavaliable!");
219 219 config.Ready = true;
220 220 }
221 221 //try catch since throws errors if server had crashed instead of clean shutdown while cargo is out.
222 222 try
223 223 {
224 224 Subscribe("OnEntitySpawned");
225 225 timer.Once(30, () => { ResetCargoMLRS(); });
226 226 }
227 227 catch { }
228 228 }
229 229
230 230 void Unload()
231 231 {
232 232 //Remove component
233 233 foreach (var ent in mlrs)
234 234 {
235 235 if (ent != null) { continue; }
236 236 if (!ent.IsDestroyed) { ent.Kill(); }
237 237 }
238 238 _plugin = null;
239 239 }
240 240
241 241 void OnExplosiveDropped(BasePlayer bp, BaseEntity be)
242 242 {
243 243 if (bp == null || be == null || be.ShortPrefabName != "grenade.smoke.deployed") return;
244 244 RemoteEvent(bp, be);
245 245 }
246 246 void OnExplosiveThrown(BasePlayer bp, BaseEntity be)
247 247 {
248 248 if (bp == null || be == null || be.ShortPrefabName != "grenade.smoke.deployed") return;
249 249 RemoteEvent(bp, be);
250 250 }
251 251
252 252 private void OnCargoShipEgress(CargoShip cs)
253 253 {
254 254 if (cs != null)
255 255 {
256 256 for (int i = cs.children.Count - 1; i >= 0; i--)
257 257 {
258 258 if (cs.children[i] != null && !cs.children[i].IsDestroyed && cs.children[i] is MLRS)
259 259 {
260 260 cs.children[i].Kill();
261 261 }
262 262 }
263 263 }
264 264 }
265 265
266 266 void RemoteEvent(BasePlayer bp, BaseEntity be)
267 267 {
268 268 //Check if its an event token
269 269 if (be.skinID != 2647098356) return;
270 270 if (showDebug) Puts("Remote Cargo MLRS trigged by " + bp.displayName);
271 271
272 272 RunEffect("assets/bundled/prefabs/fx/smoke_signal_full.prefab", be);
273 273 //Checks if already active event
274 274 if ((be != null && !active) || !config.RemoteMode)
275 275 {
276 276 bool Sucess = false;
277 277 thrower = bp;
278 278 active = true;
279 279 if (config.PlaySFX)
280 280 {
281 281 CreateSound(be, config.SFXURL);
282 282 }
283 283 //delay to allow smoke grenade to travel
284 284 timer.Once(8f, () =>
285 285 {
286 286 if (be == null)
287 287 {
288 288 active = false;
289 289 rust.SendChatMessage(bp, "<color=orange>Cargo MLRS</color>", "Your MLRS signal <color=red>FAILED</color>", config.AnnouncementIcon);
290 290 return;
291 291 }
292 292 Vector3 pos = be.transform.position;
293 293 if (pos != null)
294 294 {
295 295 if (config.PublicAnouncements)
296 296 {
297 297 CreateAnouncment("<color=red>" + bp.displayName + "</color> has called a MLRS strike near <color=orange>" + getGrid(pos) + "</color>");
298 298 }
299 299 //Find all cargo ships and use them
300 300 CargoShip[] cargo = UnityEngine.Object.FindObjectsOfType<CargoShip>();
301 301 foreach (CargoShip ship in cargo)
302 302 {
303 303 //Search if they have MLRS attached
304 304 List<BaseEntity> bef = ship.children;
305 305 //Do a for loop incase future has more then 1 MLRS
306 306 foreach (BaseEntity b in bef.ToArray())
307 307 {
308 308 //Found a MLRS
309 309 if (b is MLRS)
310 310 {
311 311 MLRS TMLRS = b as MLRS;
312 312 if (TMLRS != null)
313 313 {
314 314 //Add the functions componant
315 315 MLRSShoot s = TMLRS.GetComponent<MLRSShoot>();
316 316 if (s == null)
317 317 {
318 318 //Check if any shots have been fired in the foreach loop to prevent refunds
319 319 if (!Sucess)
320 320 {
321 321 rust.SendChatMessage(bp, "<color=orange>Cargo MLRS</color>", "<color=red>Not Avaliable</color=red>", config.AnnouncementIcon);
322 322 bp.GiveItem(CreateItem());
323 323 NextTick(() => { be?.Kill(); });
324 324 }
325 325 return;
326 326 }
327 327 //Sets up target and shoots
328 328 s.SetTarget(pos);
329 329 s.ForceFireRepeat(bp);
330 330 if (showDebug) Puts("Remote Cargo MLRS Shooting");
331 331 Sucess = true;
332 332 timer.Once(10f, () =>
333 333 {
334 334 if (config.PublicAnouncements)
335 335 {
336 336 CreateAnouncment("Status: <color=red>Disabled</color>");
337 337 }
338 338 });
339 339 //Resets the cargo after delay
340 340 if (config.Ready)
341 341 {
342 342 timer.Once(10 + config.Delay, () =>
343 343 {
344 344 ResetCargoMLRS();
345 345 });
346 346 }
347 347 }
348 348 }
349 349 }
350 350 }
351 351 }
352 352 });
353 353 }
354 354 else
355 355 {
356 356 //Returns players token
357 357 rust.SendChatMessage(bp, "<color=orange>Cargo MLRS</color>", "<color=red>Not Avaliable</color=red>", config.AnnouncementIcon);
358 358 bp.GiveItem(CreateItem());
359 359 NextTick(() => { be?.Kill(); });
360 360 }
361 361 }
362 362
363 363 //Find all cargoships on map
364 364 void ResetCargoMLRS()
365 365 {
366 366 CargoShip[] cargo = UnityEngine.Object.FindObjectsOfType<CargoShip>();
367 367 foreach (CargoShip ship in cargo.ToArray())
368 368 {
369 369 //Search if they have MLRS attached
370 370 List<BaseEntity> be = ship.children;
371 371 foreach (BaseEntity b in be.ToArray())
372 372 {
373 373 if (b is MLRS)
374 374 {
375 375 //Kill old one and respawn new to stop mountable spam
376 376 try
377 377 {
378 378 b.Kill();
379 379 }
380 380 catch { }
381 381 AddMLRS(ship);
382 382 }
383 383 }
384 384 }
385 385 }
386 386
387 387 //Removes any duplicated MLRS caused from currupted sav files.
388 388 void CleanDupedMLRS(Vector3 pos)
389 389 {
390 390 int cleaned = 0;
391 391 RaycastHit[] hits = UnityEngine.Physics.SphereCastAll(pos, 150f, Vector3.one);
392 392 foreach (RaycastHit hit in hits.ToArray())
393 393 {
394 394 //Check each hit is a MLRS
395 395 CargoShip cargo = hit.GetEntity()?.GetComponent<CargoShip>();
396 396
397 397 if (cargo != null)
398 398 {
399 399 List<BaseEntity> bef = cargo.children;
400 400 {
401 401 foreach (BaseEntity b in bef)
402 402 {
403 403 if (b is MLRS)
404 404 {
405 405 b.Kill();
406 406 cleaned++;
407 407 }
408 408 }
409 409 }
410 410 }
411 411 }
412 412 MLRS[] MLRSList = UnityEngine.Object.FindObjectsOfType<MLRS>();
413 413 foreach (MLRS mlrs in MLRSList)
414 414 {
415 415 if (mlrs == null) { continue; }
416 416 if (mlrs.transform.position.y == 0 && TerrainMeta.HeightMap.GetHeight(mlrs.transform.position) < -5)
417 417 {
418 418 mlrs.Kill();
419 419 cleaned++;
420 420 }
421 421 }
422 422 if (showDebug) Puts("Cleaned " + cleaned.ToString() + " dupes");
423 423 }
424 424
425 425 void AddMLRS(CargoShip cs)
426 426 {
427 427 if (cs == null) { return; }
428 428 CleanDupedMLRS(cs.transform.position + config.CargoOffset);
429 429 //Rotate MLRS so turret is at front of ship not to hit the mast.
430 430 Vector3 rot = cs.transform.rotation.eulerAngles;
431 431 rot = new Vector3(rot.x, rot.y + 180, rot.z);
432 432 Vector3 pos = new Vector3(cs.transform.position.x, cs.transform.position.y, cs.transform.position.z);
433 433 //Spawn MLRS
434 434 MLRS replacement = GameManager.server.CreateEntity("assets/content/vehicles/mlrs/mlrs.entity.prefab", pos, cs.transform.rotation) as MLRS;
435 435 if (replacement == null) { return; }
436 436 replacement.Spawn();
437 437 replacement.SetParent(cs);
438 438 mlrs.Add(replacement);
439 439 replacement.transform.position = pos;
440 440 replacement.transform.localPosition += config.CargoOffset;
441 441 replacement.transform.rotation = Quaternion.Euler(rot);
442 442 replacement.enabled = true;
443 443 replacement.EnableSaving(false);
444 444 //Add component to over-ride the shoot button and few functions
445 445 replacement.gameObject.AddComponent<MLRSShoot>();
446 446 if (config.Ready || config.RemoteMode)
447 447 {
448 448 //Try catch since some times errors OnServerInitialized if server crashed,
449 449 try
450 450 {
451 451 active = false;
452 452 thrower = null;
453 453 replacement.AdminFixUp();
454 454 }
455 455 catch { }
456 456 }
457 457 replacement.SendNetworkUpdateImmediate();
458 458 if (config.PublicAnouncements)
459 459 {
460 460 CreateAnouncment("Status: <color=green>Active</color>");
461 461 }
462 462 if (showDebug) Puts("Spawned new Cago MLRS");
463 463 ////Change NPCs health to make them a little harder for MLRS event
464 464 if (config.healthmulti != 1f)
465 465 {
466 466 //Delay to allow NPCs to spawn
467 467 timer.Once(2f, () =>
468 468 {
469 469 if (replacement != null)
470 470 {
471 471 BoostNPCs(replacement);
472 472 }
473 473 });
474 474 }
475 475 }
476 476
477 477 private void GiveMLRSSignal(BasePlayer player)
478 478 {
479 479 if (showDebug) Puts("Remote Token Given to " + player.displayName);
480 480 //Give skinned smoke grenade item to player
481 481 var item = CreateItem();
482 482 if (item != null && player != null)
483 483 {
484 484 player.GiveItem(item);
485 485 if (config.PublicAnouncements)
486 486 {
487 487 CreateAnouncment("MLRS Remote signal claimed by <color=red>" + player.displayName + "</color>");
488 488 //PrintToChat("MLRS Remote signal claimed by " + player.displayName);
489 489 }
490 490 }
491 491 }
492 492
493 493 private Item CreateItem()
494 494 {
495 495 //create item
496 496 var item = ItemManager.CreateByName("grenade.smoke", 1, 2647098356);
497 497 if (item != null)
498 498 {
499 499 item.text = "MLRS Strike";
500 500 item.name = item.text;
501 501 }
502 502 return item;
503 503 }
504 504
505 505 //Create boombox below grenade for some sfx
506 506 private void CreateSound(BaseEntity player, string url)
507 507 {
508 508 //Checks provided URL
509 509 if (url == "") return;
510 510 //Creates Resizeable Sphere
511 511 SphereEntity sph = (SphereEntity)GameManager.server.CreateEntity("assets/prefabs/visualization/sphere.prefab", default(Vector3), default(Quaternion), true);
512 512 DestroyMeshCollider(sph);
513 513 sph.Spawn();
514 514 //Create boombox
515 515 DeployableBoomBox boombox = GameManager.server.CreateEntity("assets/prefabs/voiceaudio/boombox/boombox.deployed.prefab", default(Vector3), default(Quaternion), true) as DeployableBoomBox;
516 516 DestroyMeshCollider(boombox);
517 517 //Asigns boombox to sphere
518 518 boombox.SetParent(sph);
519 519 boombox.Spawn();
520 520 //Setup boombox
521 521 boombox.pickup.enabled = false;
522 522 boombox.BoxController.ServerTogglePlay(false);
523 523 boombox.BoxController.AssignedRadioBy = player.OwnerID;
524 524 //Resize shpere
525 525 sph.LerpRadiusTo(0.01f, 1f);
526 526 //Delay to allow clean resize
527 527 timer.Once(1f, () =>
528 528 {
529 529 if (sph != null)
530 530 sph.SetParent(player);
531 531 sph.transform.localPosition = new Vector3(0, -1.5f, 0f);
532 532 //Change boombox channel to url
533 533 boombox.BoxController.CurrentRadioIp = url;
534 534 boombox.BoxController.baseEntity.ClientRPC(RpcTarget.NetworkGroup("OnRadioIPChanged"), boombox.BoxController.CurrentRadioIp);
535 535 boombox.BoxController.ServerTogglePlay(true);
536 536 //Suiside timer for sphere and boombox
537 537 timer.Once(40f, () =>
538 538 {
539 539 try
540 540 {
541 541 if (sph != null)
542 542 sph?.Kill();
543 543 }
544 544 catch { }
545 545 });
546 546 });
547 547 sph.SendNetworkUpdateImmediate();
548 548 }
549 549
550 550 //Play visual effects on entity
551 551 private static void RunEffect(string name, BaseEntity entity = null, Vector3 position = new Vector3(), Vector3 offset = new Vector3())
552 552 {
553 553 if (entity != null)
554 554 Effect.server.Run(name, entity, 0, offset, position, null, true);
555 555 else Effect.server.Run(name, position, Vector3.up, null, true);
556 556 }
557 557
558 558 //Gets grid letter from world position
559 559 string getGrid(Vector3 pos)
560 560 {
561 561 //Set base letter
562 562 char letter = 'A';
563 563 var x = Mathf.Floor((pos.x + (ConVar.Server.worldsize / 2)) / 146.3f) % 26;
564 564 var z = (Mathf.Floor(ConVar.Server.worldsize / 146.3f)) - Mathf.Floor((pos.z + (ConVar.Server.worldsize / 2)) / 146.3f);
565 565 letter = (char)(((int)letter) + x);
566 566 //-1 since starts at 0
567 567 return $"{letter}{z - 1}";
568 568 }
569 569
570 570 //Destory collider so player can pass though
571 571 void DestroyMeshCollider(BaseEntity ent)
572 572 {
573 573 foreach (var mesh in ent.GetComponentsInChildren<MeshCollider>())
574 574 {
575 575 UnityEngine.Object.DestroyImmediate(mesh);
576 576 }
577 577
578 578
579 579 }
580 580
581 581 //Sends message to all active players under a steamID
582 582 void CreateAnouncment(string msg)
583 583 {
584 584 foreach (BasePlayer current in BasePlayer.activePlayerList.ToArray())
585 585 {
586 586 if (current.IsConnected)
587 587 {
588 588 rust.SendChatMessage(current, "<color=orange>Cargo MLRS</color>", msg, config.AnnouncementIcon);
589 589 }
590 590 }
591 591 }
592 592
593 593 //FindNPCs and boost them
594 594 private void BoostNPCs(MLRS pos)
595 595 {
596 596 int boosted = 0;
597 597 //Scan area
598 598 RaycastHit[] hits = UnityEngine.Physics.SphereCastAll(pos.transform.position, config.NPCRadius, Vector3.one);
599 599 foreach (RaycastHit hit in hits.ToArray())
600 600 {
601 601 //Check each hit is NPC
602 602 NPCPlayer bnpc = hit.GetEntity()?.GetComponent<NPCPlayer>();
603 603 if (bnpc != null)
604 604 {
605 605 //Checks if kit string is valid
606 606 if (config.Kitname != "")
607 607 {
608 608 //Calls kits plugin
609 609 object success = Kits?.Call("GiveKit", bnpc, config.Kitname);
610 610 //Trys to equip stuff
611 611 Item projectileItem = null;
612 612 foreach (var item in bnpc.inventory.containerBelt.itemList)
613 613 {
614 614 if (item.GetHeldEntity() is BaseProjectile)
615 615 {
616 616 projectileItem = item;
617 617 break;
618 618 }
619 619 }
620 620 if (projectileItem != null)
621 621 {
622 622 bnpc.UpdateActiveItem(projectileItem.uid);
623 623 }
624 624 else bnpc.EquipWeapon();
625 625 }
626 626 //Give a better name
627 627 bnpc.displayName = RandomUsernames.Get(bnpc.userID);
628 628 //Mod health
629 629 bnpc.startHealth *= config.healthmulti;
630 630 bnpc.InitializeHealth(100 * config.healthmulti, 100 * config.healthmulti);
631 631 boosted++;
632 632 }
633 633 }
634 634 if (showDebug) Puts("Boosted " + boosted.ToString() + " NPCs");
635 635 }
636 636
637 637 //Functions for Cargo MLRS
638 638 public class MLRSShoot : FacepunchBehaviour
639 639 {
640 640 public bool Token = true;
641 641 public MLRS me;
642 642 public BasePlayer seatedplayer;
643 643 private void Awake()
644 644 {
645 645 //Gives reference
646 646 me = GetComponent<MLRS>();
647 647 //Check if should be monitoring Seat
648 648 if (!_plugin.config.RemoteMode)
649 649 {
650 650 //Setup update loop
651 651 InvokeRepeating(update, UnityEngine.Random.Range(0f, 1f), 0.15f);
652 652 }
653 653 }
654 654
655 655 //Manually set the target
656 656 public void SetTarget(Vector3 nt)
657 657 {
658 658 me.trueTargetHitPos = nt;
659 659 me.SetUserTargetHitPos(nt);
660 660 me.SetUserTargetHitPos(nt);
661 661 }
662 662
663 663 //Manually fire rockets
664 664 public void ForceFire()
665 665 {
666 666 //_FireNextRocket.Invoke(me, null);
667 667 me.FireNextRocket();
668 668 }
669 669
670 670 public void ForceFireRepeat(BasePlayer bp)
671 671 {
672 672 //Lock taget pos
673 - me.SetFlag(BaseEntity.Flags.Reserved6, true, false, true);
673 + me.SetFlagLocal(BaseEntity.Flags.Reserved6, true, false);
674 674 me.nextRocketIndex = me.RocketAmmoCount - 1;
675 + me.SendNetworkUpdateImmediate();
675 676 //Fire rockets
676 677 InvokeRepeating(ForceFire, 0f, 0.5f);
677 678 }
678 679
679 680 private void update()
680 681 {
681 682 //Try catch to prevent red text on admin cargo remove.
682 683 try
683 684 {
684 685 //Checks for mounted player
685 686 if (me.GetMounted() == null)
686 687 {
687 688 seatedplayer = null;
688 689 return;
689 690 }
690 691 //Check mounted player has changed to stop help text spamming
691 692 if (seatedplayer != me.GetMounted())
692 693 {
693 694 seatedplayer = me.GetMounted();
694 695 seatedplayer.ChatMessage("Hold Left click to <color=red>FIRE!</color>");
695 696 }
696 697 //Triggers manually shooting with left mouse button
697 698 if (seatedplayer != null && seatedplayer.serverInput.IsDown(BUTTON.FIRE_PRIMARY))
698 699 {
699 700 //Triggers normal fire function of press button
700 701 me.IsRealigning = false;
701 702 me.Fire(seatedplayer);
702 703 }
703 704 }
704 705 catch { }
705 706 }
706 707 }
707 708
708 709 //Debug comand
709 710 [ChatCommand("mlrscargotest")]
710 711 private void Cmdmlrscargotest(BasePlayer player, string command, string[] args)
711 712 {
712 713 //Gives admin token without having to visit cargoship
713 714 if (player.IPlayer.HasPermission(AdminPerm))
714 715 {
715 716 GiveMLRSSignal(player);
716 717 }
717 718 else
718 719 {
719 720 player.ChatMessage("No Perm");
720 721 }
721 722 }
722 723 //Reset event
723 724 [ChatCommand("mlrscargoreset")]
724 725 private void Cmdmlrscargoreset(BasePlayer player, string command, string[] args)
725 726 {
726 727 //Respawns MLRS
727 728 if (player.IPlayer.HasPermission(AdminPerm))
728 729 {
729 730 ResetCargoMLRS();
730 731 }
731 732 else
732 733 {
733 734 player.ChatMessage("No Perm");
734 735 }
735 736 }
736 737 }
737 738 }