← Back to changelog
What changed
boatcommands · v1.0.1 → v1.0.2 (current)
Diff
2 added · 2 removed · 463 → 463 total lines
1
1
using Oxide.Game.Rust.Cui;
2
2
using UnityEngine;
3
3
using System.Collections.Generic;
4
4
using System.Linq;
5
5
using Facepunch;
6
6
7
7
namespace Oxide.Plugins
8
8
{
9
-
[Info("boatcommands", "bmgjet", "1.0.1")]
9
+
[Info("boatcommands", "bmgjet", "1.0.2")]
10
10
[Description("Boat control UI")]
11
11
public class boatcommands : RustPlugin
12
12
{
13
13
private const string BoatUI = "BoatCommandsUI";
14
14
private const string CannonUI = "BoatCannonsUI";
15
15
private const string PermUse = "boatcommands.use";
16
16
17
17
private const float CommandCooldown = 1f;
18
18
private const float UIRefreshInterval = 3f;
19
19
private const float AnchorDelay = 3f;
20
20
21
21
private readonly Dictionary<ulong, float> lastUse = new();
22
22
private readonly Dictionary<ulong, Timer> refreshTimers = new();
23
23
24
24
private class ManCannonData
25
25
{
26
26
public ulong OwnerId;
27
27
public PlayerBoat Boat;
28
28
public Cannon Cannon;
29
29
public BasePlayer Bot;
30
30
}
31
31
32
32
private readonly Dictionary<int, ManCannonData> manCannons = new();
33
33
private int cannonUidCounter = 0;
34
34
35
35
#region Oxide
36
36
37
37
void Init()
38
38
{
39
39
permission.RegisterPermission(PermUse, this);
40
40
}
41
41
42
42
void Unload()
43
43
{
44
44
if (manCannons.Count != 0)
45
45
{
46
46
foreach (var b in manCannons)
47
47
{
48
48
if (b.Value != null)
49
49
{
50
50
if (b.Value.Cannon != null)
51
51
{
52
52
b.Value.Cannon.DismountAllPlayers();
53
53
}
54
54
if (b.Value.Bot != null)
55
55
{
56
56
b.Value.Bot.Kill();
57
57
}
58
58
}
59
59
}
60
60
}
61
61
62
62
foreach (var player in BasePlayer.activePlayerList)
63
63
{
64
64
StopRefresh(player);
65
65
DestroyBoatUI(player);
66
66
}
67
67
}
68
68
69
69
#endregion
70
70
71
71
private void KitPlayer(BasePlayer npc)
72
72
{
73
73
Item item = ItemManager.CreateByName("hazmatsuit", 1, 0);
74
74
timer.Once(1, () =>
75
75
{
76
76
if (item != null && npc != null && npc.net != null && npc.net.group != null && npc?.inventory?.containerWear != null)
77
77
{
78
78
npc.inventory.containerWear.onItemAddedRemoved = null;
79
79
npc.inventory.containerWear.Insert(item);
80
80
npc?.SendNetworkUpdateImmediate();
81
81
npc.inventory.ServerUpdate(0f);
82
82
item.MarkDirty();
83
83
}
84
84
});
85
85
}
86
86
87
87
private int CreateNPC(Cannon cannon, BasePlayer player, PlayerBoat boat)
88
88
{
89
89
if (cannon.IsMounted()) { return 0; }
90
90
var bot = GameManager.server.CreateEntity("assets/prefabs/player/player.prefab", cannon.mountAnchor.position, cannon.mountAnchor.rotation) as BasePlayer;
91
91
if (bot == null) { return 0; }
92
92
bot.enableSaving = false;
93
93
bot.Spawn();
94
94
bot.displayName = RandomUsernames.Get(bot.userID);
95
95
KitPlayer(bot);
96
96
cannon.MountPlayer(bot);
97
97
cannon.AdminReload(1);
98
98
cannon.SendNetworkUpdate();
99
99
int uid = ++cannonUidCounter;
100
100
manCannons[uid] = new ManCannonData
101
101
{
102
102
OwnerId = player.userID,
103
103
Boat = boat,
104
104
Cannon = cannon,
105
105
Bot = bot
106
106
};
107
107
return uid;
108
108
}
109
109
110
110
#region Commands
111
111
112
112
[ConsoleCommand("boat.firecannon")]
113
113
private void CmdFireCannon(ConsoleSystem.Arg arg)
114
114
{
115
115
CannonFire(arg.Player(), "firecannon", arg.Args);
116
116
}
117
117
118
118
[ChatCommand("manallcannons")]
119
119
void ManAllCannons(BasePlayer player, string cmd, string[] args)
120
120
{
121
121
if (!player.IsAdmin) { return; }
122
122
if (player.GetParentEntity() is PlayerBoat boat)
123
123
{
124
124
foreach (var ent in boat.Deployables.Cached)
125
125
{
126
126
if (ent is Cannon cannon) { CreateNPC(cannon, player, boat); }
127
127
}
128
128
}
129
129
}
130
130
131
131
[ChatCommand("mancannon")]
132
132
void ManCannon(BasePlayer player, string cmd, string[] args)
133
133
{
134
134
if (!player.IsAdmin) { return; }
135
135
136
136
RaycastHit hit;
137
137
if (!Physics.Raycast(player.eyes.HeadRay(), out hit, 4f))
138
138
{
139
139
player.ChatMessage("Look at a cannon");
140
140
return;
141
141
}
142
142
143
143
Cannon cannon = hit.GetEntity() as Cannon;
144
144
if (cannon == null)
145
145
{
146
146
player.ChatMessage("Look at a cannon");
147
147
return;
148
148
}
149
149
150
150
if (!(cannon.GetParentEntity() is PlayerBoat boat))
151
151
{
152
152
player.ChatMessage("Cannon not on a player boat");
153
153
return;
154
154
}
155
155
156
156
if (cannon.IsMounted())
157
157
{
158
158
player.ChatMessage("This cannon already has a gunner");
159
159
return;
160
160
}
161
161
var uid = CreateNPC(cannon, player, boat);
162
162
if (uid != 0) { player.ChatMessage($"Man cannon installed. Fire with /firecannon {uid}"); }
163
163
}
164
164
165
165
[ChatCommand("firecannon")]
166
166
void CannonFire(BasePlayer player, string cmd, StringView[] args)
167
167
{
168
168
if (!player.IsAdmin) { return; }
169
169
170
170
if (args.Length == 0 || !int.TryParse(args[0].ToString(), out int uid))
171
171
{
172
172
return;
173
173
}
174
174
175
175
if (!manCannons.TryGetValue(uid, out var data))
176
176
{
177
177
return;
178
178
}
179
179
180
180
if (!data.Cannon.IsLoaded())
181
181
{
182
182
return;
183
183
}
184
184
185
185
if (data.Bot == null || data.Bot.IsDead())
186
186
{
187
187
manCannons.Remove(uid);
188
188
player.ChatMessage("Cannon Man Dead");
189
189
return;
190
190
}
191
191
192
192
ServerProjectile serverProjectile;
193
193
Cannon cannon = data.Cannon;
194
194
if (cannon.FireProjectile(cannon.AmmoPrefab, cannon.FirePoint.position, cannon.FirePoint.forward, player, 0.25f, 100f, out serverProjectile))
195
195
{
196
196
cannon.SERVER_OnProjectileFired(player.Connection, player);
197
197
}
198
198
if (data.Cannon != null && !data.Cannon.IsDestroyed)
199
199
{
200
200
data.Cannon.AdminReload(1);
201
201
data.Cannon.SendNetworkUpdate();
202
202
}
203
203
}
204
204
205
205
[ChatCommand("engine")]
206
206
void ToggleEngine(BasePlayer player, string cmd, string[] args)
207
207
{
208
208
if (!CanUse(player)) return;
209
209
if (!GetBoat(player, out PlayerBoat boat)) return;
210
210
bool engineOn = boat.EnginesOn();
211
211
212
212
boat.SetAllEnginesOn(!engineOn);
213
213
RefreshUI(player);
214
214
}
215
215
216
216
[ChatCommand("sails")]
217
217
void ToggleSails(BasePlayer player, string cmd, string[] args)
218
218
{
219
219
if (!CanUse(player)) return;
220
220
if (!GetBoat(player, out PlayerBoat boat)) return;
221
221
222
222
boat.SetAllSailsOpen(!boat.SailsOpen());
223
223
RefreshUI(player);
224
224
}
225
225
226
226
[ChatCommand("reverse")]
227
227
void ToggleReverse(BasePlayer player, string cmd, string[] args)
228
228
{
229
229
if (!CanUse(player)) return;
230
230
if (!GetBoat(player, out PlayerBoat boat)) return;
231
231
232
232
bool newReverse = !boat.AnyEngineReverse();
233
233
234
234
// Engines
235
235
foreach (var engine in boat.Engines.Cached)
236
-
engine.SetFlag(BaseEntity.Flags.Reserved3, newReverse);
236
+
engine.SetFlagLocal(BaseEntity.Flags.Reserved3, newReverse);
237
237
238
238
// Sails rotate with reverse
239
239
foreach (var sail in boat.Sails.Cached)
240
240
{
241
241
if (sail.CanRotate(player))
242
242
{
243
243
sail.RotateSail(player);
244
244
}
245
245
}
246
246
247
247
RefreshUI(player);
248
248
}
249
249
250
250
[ChatCommand("boatanchor")]
251
251
void ToggleAnchor(BasePlayer player, string cmd, string[] args)
252
252
{
253
253
if (!CanUse(player)) return;
254
254
if (!GetBoat(player, out PlayerBoat boat)) return;
255
255
256
256
foreach (var anchor in boat.Anchors.Cached)
257
257
{
258
258
if (anchor.Lowered)
259
259
anchor.RaiseAnchor(player);
260
260
else
261
261
anchor.LowerAnchor(player, false);
262
262
}
263
263
264
264
timer.Once(AnchorDelay, () =>
265
265
{
266
266
if (player != null && player.IsConnected)
267
267
RefreshUI(player);
268
268
});
269
269
}
270
270
271
271
#endregion
272
272
273
273
#region UI
274
274
275
275
void CreateBoatUI(BasePlayer player)
276
276
{
277
277
DestroyBoatUI(player);
278
278
if (!HasPerm(player)) return;
279
279
if (!GetBoat(player, out PlayerBoat boat)) return;
280
280
281
281
var container = new CuiElementContainer();
282
282
container.Add(new CuiPanel
283
283
{
284
284
Image = { Color = "0 0 0 0.35" },
285
285
RectTransform =
286
286
{
287
287
AnchorMin = "0.925 0.855",
288
288
AnchorMax = "0.995 0.995"
289
289
}
290
290
}, "Overlay", BoatUI);
291
291
292
292
AddBtn(container, BoatUI, "ENGINE", "engine", boat.EnginesOn(), 0, "0.2 0.7 0.2 0.9");
293
293
AddBtn(container, BoatUI, "SAILS", "sails", boat.SailsOpen(), 1, "0.2 0.6 0.9 0.9");
294
294
AddBtn(container, BoatUI, "REVERSE", "reverse", boat.AnyEngineReverse(), 2, "0.9 0.6 0.1 0.9");
295
295
AddBtn(container, BoatUI, "ANCHOR", "boatanchor", boat.AnchorLowered(), 3, "0.2 0.5 0.8 0.9");
296
296
297
297
if (player.IsAdmin)
298
298
{
299
299
container.Add(new CuiPanel
300
300
{
301
301
Image = { Color = "0 0 0 0.35" },
302
302
RectTransform =
303
303
{
304
304
AnchorMin = "0.20 0.001",
305
305
AnchorMax = "0.80 0.020"
306
306
}
307
307
}, "Overlay", CannonUI);
308
308
309
309
var cannons = manCannons
310
310
.Where(x => x.Value.Boat == boat)
311
311
.OrderBy(x => x.Key)
312
312
.ToList();
313
313
314
314
float width = cannons.Count > 0 ? 1f / cannons.Count : 1f;
315
315
316
316
for (int i = 0; i < cannons.Count; i++)
317
317
{
318
318
float xmin = i * width;
319
319
float xmax = xmin + width;
320
320
321
321
container.Add(new CuiButton
322
322
{
323
323
Button =
324
324
{
325
325
Color = "0.75 0.25 0.25 0.9",
326
326
Command = $"boat.firecannon {cannons[i].Key}"
327
327
},
328
328
RectTransform =
329
329
{
330
330
AnchorMin = $"{xmin} 0.05",
331
331
AnchorMax = $"{xmax} 0.95"
332
332
},
333
333
Text =
334
334
{
335
335
Text = $"CANNON {cannons[i].Key}",
336
336
FontSize = 9,
337
337
Align = TextAnchor.MiddleCenter
338
338
}
339
339
}, CannonUI);
340
340
}
341
341
}
342
342
CuiHelper.AddUi(player, container);
343
343
}
344
344
345
345
void AddBtn(CuiElementContainer c, string p, string t, string cmd, bool active, int i, string onColor)
346
346
{
347
347
float h = 0.17f;
348
348
float spacing = 0.055f;
349
349
float anchorMaxY = 0.93f;
350
350
float yMax = anchorMaxY - i * (h + spacing); float yMin = yMax - h;
351
351
c.Add(new CuiButton { Button = { Color = active ? onColor : "0.2 0.2 0.2 0.8", Command = $"chat.say /{cmd}" }, RectTransform = { AnchorMin = $"0.07 {yMin}", AnchorMax = $"0.93 {yMax}" }, Text = { Text = t, FontSize = 11, Align = TextAnchor.MiddleCenter } }, p);
352
352
}
353
353
354
354
void DestroyBoatUI(BasePlayer p)
355
355
{
356
356
CuiHelper.DestroyUi(p, BoatUI);
357
357
CuiHelper.DestroyUi(p, CannonUI);
358
358
}
359
359
360
360
void RefreshUI(BasePlayer p) => CreateBoatUI(p);
361
361
362
362
363
363
364
364
#endregion
365
365
366
366
#region Hooks
367
367
368
368
void OnEntityMounted(BaseMountable e, BasePlayer p)
369
369
{
370
370
if (e.prefabID != 1346716961) return;
371
371
CreateBoatUI(p);
372
372
StartRefresh(p);
373
373
}
374
374
375
375
void OnEntityDismounted(BaseMountable e, BasePlayer p)
376
376
{
377
377
if (e.prefabID != 1346716961) return;
378
378
StopRefresh(p);
379
379
DestroyBoatUI(p);
380
380
}
381
381
382
382
#endregion
383
383
384
384
#region Refresh Loop
385
385
386
386
void StartRefresh(BasePlayer p)
387
387
{
388
388
StopRefresh(p);
389
389
refreshTimers[p.userID] = timer.Every(UIRefreshInterval, () =>
390
390
{
391
391
if (p == null || !p.IsConnected || !GetBoat(p, out _))
392
392
{
393
393
StopRefresh(p);
394
394
return;
395
395
}
396
396
RefreshUI(p);
397
397
});
398
398
}
399
399
400
400
void StopRefresh(BasePlayer p)
401
401
{
402
402
if (refreshTimers.TryGetValue(p.userID, out var t))
403
403
t.Destroy();
404
404
refreshTimers.Remove(p.userID);
405
405
}
406
406
407
407
#endregion
408
408
409
409
#region Helpers
410
410
411
411
bool HasPerm(BasePlayer p) =>
412
412
permission.UserHasPermission(p.UserIDString, PermUse);
413
413
414
414
bool CanUse(BasePlayer p)
415
415
{
416
416
float now = Time.realtimeSinceStartup;
417
417
if (lastUse.TryGetValue(p.userID, out float last) && now - last < CommandCooldown)
418
418
return false;
419
419
420
420
lastUse[p.userID] = now;
421
421
return true;
422
422
}
423
423
424
424
bool GetBoat(BasePlayer p, out PlayerBoat boat)
425
425
{
426
426
boat = p?.GetMounted()?.GetParentEntity() as PlayerBoat;
427
427
return boat != null && boat.IsPlayerAuthed(p, true);
428
428
}
429
429
430
430
#endregion
431
431
}
432
432
433
433
static class BoatExtensions
434
434
{
435
435
public static bool EnginesOn(this PlayerBoat b)
436
436
{
437
437
foreach (var e in b.Engines.Cached)
438
438
if (e.IsOn()) return true;
439
439
return false;
440
440
}
441
441
442
442
public static bool AnyEngineReverse(this PlayerBoat b)
443
443
{
444
444
foreach (var e in b.Engines.Cached)
445
445
if (e.InReverse) return true;
446
446
return false;
447
447
}
448
448
449
449
public static bool SailsOpen(this PlayerBoat b)
450
450
{
451
451
foreach (var s in b.Sails.Cached)
452
452
if (s.Lowered) return true;
453
453
return false;
454
454
}
455
455
456
456
public static bool AnchorLowered(this PlayerBoat b)
457
457
{
458
458
foreach (var a in b.Anchors.Cached)
459
459
if (a.Lowered) return true;
460
460
return false;
461
461
}
462
462
}
463
463
}