Иконка ресурса

FREE DeliveryJobs : 0.0.1 0.0.1

Нет прав для скачивания
C#:
using Oxide.Core.Plugins;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
using ConVar;
using Pool = Facepunch.Pool;
using Oxide.Core;
using System.Diagnostics;
using Oxide.Game.Rust.Cui;
using Random = UnityEngine.Random;
using ProtoBuf;
using Environment = System.Environment;
using System.Collections;
using Newtonsoft.Json;
using Physics = UnityEngine.Physics;
using Oxide.Core.Libraries.Covalence;
using System.Reflection;
namespace Oxide.Plugins {
    [Info("Delivery Jobs", "Shady", "0.0.1")]
    internal class DeliveryJobs : RustPlugin {
        /*
         * Copyright 2024 PRISM
         *
         * This file is part of PRISM's plugins.*
         * PRISM's plugins is free software: you can redistribute it and/or modify
         * it under the terms of the GNU General Public License as published by
         * the Free Software Foundation, either version 3 of the License, or
         * (at your option) any later version.*
         * PRISM's plugins is distributed in the hope that it will be useful,
         * but WITHOUT ANY WARRANTY; without even the implied warranty of
         * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
         * GNU General Public License for more details.*
         * You should have received a copy of the GNU General Public License
         * along with PRISM's plugins.If not, see <https://www.gnu.org/licenses/>.*/
        /*
         * The below is not a license (that can be found above), but simply a notice to the reader of this code:
         * The development of PRISM has been gradual and started in 2014.* Some of the code provided in this repository may be outdated and may not be the best representation of the author's current coding practices.* Some of the code provided in this repository may have been initially developed in 2014.* Files not containing the above license are not uniquely part of PRISM's plugins and are subject to the license of the original plugin, where applicable.* No copyright infringement is ever intended and the author of PRISM's plugins will comply with any requests to remove code.* PRISM's plugins are provided as is and no functionality is guaranteed.* Many of these plugins will not work without significant changes as there are numerous instances of hard-coded functions and may rely on external API calls that are not a part of this project itself.* 
         * 
         * With those notices out of the way, I, Shady, the author of this plugin, would like you to read and acknowledge the following:
         * These plugins are part of a decade long passion project where we, PRISM, created a unique experience to all players on our Rust servers.* This was a significant part of my life.I dedicated a decade to developing and maintaining these plugins and the servers themselves.* I implore you that, should you use any of the code or plugins provided in this repository, where it belongs solely to PRISM, that you respect the time and effort put into it.* Please remember that I, a single human, invested a significant amount of time and effort into these plugins and the servers they were used on.* Please feel free to critique my code as it is written; I am aware it is not perfect.Much of it is old, and I'm capable of doing far better now.* Moreover, I ask, sincerely, that you do not use these plugins or code without acknowledging that someone dedicated themselves to this passion project.* In asking that, it is my simple desire that you do not let these plugins be used in a way that is inconsistent with my own desires and love.* Please do not use them and claim them as your own.I wish for you to use them on your server and make that server something of your own.* Something unique and beautiful.However, I ask that you do not use these plugins and claim them as your own work.* Should you make changes to them, you are, of course, the author of those changes.But, if your code is based on one of PRISM's original plugins, please do not claim it as your own.* I also request that you do not use the plugins in a way that would be detrimental to the Rust community or the community of server owners.* In doing so, I ask that you please do not monetize the functions within these plugins with any changes you should make.* Should you use any of PRISM's plugins or its code, please do not lock anything behind a paywall or in a way that is unfair to the community of Rust.* My origin is that of one where I saw, daily, very young players who only wished to enjoy a few hours of their life on a video game and in many cases had little or no expendable income.* For many people, even purchasing the game is significantly difficult.It is, in my eyes, unfair to create fun, custom content, and suggest that you care about the community while also charging for it or creating an unfair environment where players who pay are significantly advantaged compared to those who cannot afford to do so.* I cannot control what you do with this code, but I ask that you respect the spirit in which it was created:
         * That is one of sincere love, care, and appreciation for the people who played on PRISM's servers.* Our community, much like yours, is your entire server.Your people matter, just as you matter.Please remember that *everyone* deserves a fair, fun environment.* I ask that should you use these plugins, you do so with the same love and care that I put into them.I ask that you do so with the same respect, love, care, and appreciation for the people who will play on your server.*
         * One of my greatest helps over the years was MBR.He has been a wonderful person, friend, and developer; I am beyond grateful for his contributions to us, but moreover, his friendship.* Some of these plugins were authored solely by him, or with his help.Where this has occurred, it should be noted in the author field at the top of the plugin.* Worthy of note is that he is solely responsible for our top-tier Discord integration.* If you so wish to see his GitHub page, here it is linked below:
         * https://github.com/MBR-0001
         * 
         * 
         * Should you wish to support the work that I have put into this project of the past decade, please consider doing so here:
         * https://www.buymeacoffee.com/shady757
         * 
         * I neither expect nor anticipate any such donations.You are by no means obligated, it is truly a donation.It should be done out of the kindness of your heart, if you so choose.* 
         * PRISM, as a community, has not ceased and continues to exist.We have no plans of ceasing our community.We have, however, ceased our Rust servers and thus made these plugins open source.* If you ever wish to contact me, or simply join our community and become one of our beloved members, please join us here:
         * https://discord.gg/DUCnZhZ
         * 
         * To quote Rush: "There is magic at your fingers".* With love,
         * Shady and all of PRISM.Thank you for everything, always.* 
         */
        //todo: check todos in other areas of code.//todo: remove delivery/job items placed by job in cars (i.e if a job is taken & the car is loaded with 10k fuel, remove it on unload/when job is canceled) (mostly done?)
        //todo: make it so you cannot take fuel OUT of company truck if spawns with fuel.//todo: send message/alert when dismounting car with ActiveDeliveryJob
        //todo: time remaining shows "less than a second" with no time limit jobs; partly not an issue.//todo: send a friendly job time remaining reminder every 0.25 of fraction? (0.75, 0.5, 0.25)
        //todo: fail if car is fully submerged in water
        //todo: more damage (especially to module?) = less pay (implemented, can be refactored).//minor issue: water can be drank
        //todo: send map markers to passengers? - done, needs testing.//todo: parking marker is global - fix.only network to people in car and owner.//todo: use talk gestures for quality
        //radio? boombox.static inside a car?
        //todo: proper battery rewards (currency) for completion (done)
        //use fx when selecting job?: assets/bundled/prefabs/fx/item_unlock.prefab
        //todo: company truck needs to be locked so only person who spawned it can take it
        //todo: company trucks.//todo: all company trucks should have specific identifier - perhaps OwnerID?
        //NOTE:
        //code for getting intended rotations seems to result in negatives in the xyz values whereas their actual rotation is unchanged.i don't understand this, but it works.//for example: you can do /lookat on an object to see its rotation.if you do /monrottest, it'll give you that same rotation, but with negative values.however, setting it to this does not change anything - it is still rotated correctly.//todo:
        //make a JSON similar to Deathmatch structure where you can add multiple delivery spots (limited to monuments)
        //we can use local pos inside the monument to always get exact same pos with every wipe (except if a mounment change is made to game)
        //allow setting min-max rng tier options for each delivery "spawn point"?
        //needs a /dm_edit like command./dj_edit? /dj?
        //company cars should always have low quality comps, perhaps except jobs where you *must* use a company car.private const uint TANKER_MODULE_PREFAB_ID = 4177081746;
        private const uint TANKER_STORAGE_CONTAINER_PREFAB_ID = 1803881164;
        //find equiv for storage if ever necessary
        private const uint STORAGE_MODULE_PREFAB_ID = 2578160356;
        private const uint BANDIT_CONVERSATIONALIST_PREFAB_ID = 251735616;
        private const int WATER_ITEM_ID = -1779180711;
        private const int SALT_WATER_ITEM_ID = -277057363;
        private const int LGF_ITEM_ID = -946369541;
        private const int CRUDE_OIL_ITEM_ID = -321733511;
        private const int DIESEL_ITEM_ID = 1568388703;
        private const int BATTERY_SMALL_ITEM_ID = 609049394;
        private const string CHAT_STEAM_ID = "76561198865536053";
        private const string MISSION_ACCEPT_FX = "assets/prefabs/missions/effects/mission_accept.prefab";
        private const string MISSION_VICTORY_FX = "assets/prefabs/missions/effects/mission_victory.prefab";
        private const string MISSION_OBJECT_COMPLETE_FX = "assets/prefabs/missions/effects/mission_objective_complete.prefab";
        private const string MISSION_FAILED_FX = "assets/prefabs/missions/effects/mission_failed.prefab";
        private const string CODE_LOCK_DENIED_FX = "assets/prefabs/locks/keypad/effects/lock.code.denied.prefab";
        public static DeliveryJobs Instance {
            get;
            set;
        }
        = null;
        private readonly HashSet<Coroutine> _coroutines = new();
        [PluginReference]
        private readonly Plugin Luck;
        [PluginReference]
        private readonly Plugin PlayersByDatabase;
        [PluginReference]
        private readonly Plugin RustPlusExtended;
        private const string FX_MISSING = "assets/bundled/prefabs/fx/missing.prefab";
        private readonly HashSet<int> _ignoreItems = new() {
            878301596, -2027988285, -44066823, -1364246987, 62577426, 1776460938, -17123659, 999690781, -810326667, 996757362, 1015352446, 1414245162, -1449152644, -187031121, 1768112091, 1770744540, -602717596, 1223900335, 1036321299, 204970153, 236677901, -399173933, 1561022037, 1046904719, 1394042569, -561148628, 861513346, -1366326648, -1884328185, 1878053256, 550753330, 696029452, 755224797, 1230691307, 1364514421, -455286320, 1762167092, -282193997, 180752235, -1386082991, 70102328, 22947882, 81423963, 1409529282, -1779180711, -277057363, -544317637, 1113514903
        }
        ;
        private static readonly System.Random _rarityRng = new();
        private readonly Regex _colorRegex = new("(<color=.+?>)", RegexOptions.Compiled);
        private readonly Regex _sizeRegex = new("(<size=.+?>)", RegexOptions.Compiled);
        private readonly HashSet<string> _forbiddenTags = new(6) {
            "</color>", "</size>", "<b>", "</b>", "<i>", "</i>"
        }
        ;
        private MonumentInfo _harbor2;
        private MonumentInfo Harbor2 {
            get {
                return _harbor2 ??= FindMonumentInfo("harbor_2");
            }
        }
        //NEW, USE:
        private class MonumentDeliveryLocations {
            public Dictionary<string, List<DeliveryLocation>> _monumentNameDeliveryLocations = new();
            public MonumentDeliveryLocations() {
            }
            public void OnLoaded() {
                foreach (var kvp in _monumentNameDeliveryLocations) for (int i = 0; i < kvp.Value.Count; i++) kvp.Value[i].SetMonument(FindMonumentInfo(kvp.Key));
            }
            public List<DeliveryLocation> GetMonumentDeliveriesByName(string monumentName) => _monumentNameDeliveryLocations.TryGetValue(monumentName, out var deliveryLocations) ? deliveryLocations : null;
            public void AddDeliveryLocationByMonument(MonumentInfo monument, Vector3 localPos) {
                if (monument == null) throw new ArgumentNullException(nameof(monument));
                var shortName = GetMonumentShortPrefabName(monument.name);
                AddDeliveryLocation(shortName, localPos);
            }
            public void AddDeliveryLocation(string shortName, Vector3 localPos) {
                if (string.IsNullOrWhiteSpace(shortName)) {
                    Interface.Oxide.LogError(nameof(shortName) + " is null/empty!!!");
                    return;
                }
                if (!_monumentNameDeliveryLocations.TryGetValue(shortName, out _)) _monumentNameDeliveryLocations[shortName] = new List<DeliveryLocation>();
                var newLoc = new DeliveryLocation(shortName, localPos);
                _monumentNameDeliveryLocations[shortName].Add(newLoc);
            }
            public bool RemoveDeliveryLocation(string shortName, DeliveryLocation loc) {
                if (string.IsNullOrWhiteSpace(shortName)) throw new ArgumentNullException(nameof(shortName));
                if (!_monumentNameDeliveryLocations.TryGetValue(shortName, out var deliveryLocations)) return false;
                return deliveryLocations.Remove(loc);
            }
        }
        private MonumentDeliveryLocations _deliveryData;
        private static Dictionary<string, MonumentInfo> _monumentCache = new();
        private static string GetMonumentShortPrefabName(string monName) {
            if (string.IsNullOrWhiteSpace(monName)) return monName;
            var sb = Pool.Get<StringBuilder>();
            try {
                var lastIndex = monName.LastIndexOf("/");
                return sb.Clear().Append(lastIndex == -1 ? monName : monName.Substring(lastIndex + 1)).Replace(".prefab", string.Empty).ToString();
            }
            finally {
                Pool.Free(ref sb);
            }
        }
        private static MonumentInfo FindMonumentInfo(string partialOrFullName) {
            if (string.IsNullOrWhiteSpace(partialOrFullName)) throw new ArgumentNullException(nameof(partialOrFullName));
            if (_monumentCache == null) _monumentCache = new();
            else if (_monumentCache.TryGetValue(partialOrFullName, out var monumentInfo)) return monumentInfo;
            MonumentInfo info = null;
            for (int i = 0; i < TerrainMeta.Path.Monuments.Count; i++) {
                var monument = TerrainMeta.Path.Monuments[i];
                var monDisplayName = monument?.displayPhrase?.english ?? string.Empty;
                var monName = monument?.name ?? string.Empty;
                if (!string.IsNullOrWhiteSpace(monName)) monName = GetMonumentShortPrefabName(monName);
                else continue;
                if (monName.Equals(partialOrFullName, StringComparison.OrdinalIgnoreCase) || monDisplayName.Equals(partialOrFullName, StringComparison.OrdinalIgnoreCase) || monDisplayName.IndexOf(partialOrFullName, StringComparison.OrdinalIgnoreCase) >= 0) {
                    if (info != null) {
                        Interface.Oxide.LogWarning("found more than 1 monument by this name and will return null!: " + partialOrFullName);
                        return null;
                    }
                    info = monument;
                }
            }
            if (info != null) _monumentCache[partialOrFullName] = info;
            return info;
        }
        public static bool IsCargoModule(uint prefabId) {
            return prefabId switch {
                TANKER_MODULE_PREFAB_ID or STORAGE_MODULE_PREFAB_ID => true, _ => false,
            }
            ;
        }
        private void Init() {
            Instance = this;
            Unsubscribe(nameof(OnEntityKill));
            AddCovalenceCommand("djedit", nameof(CmdDeliveryJobEdit));
            AddCovalenceCommand("djadd", nameof(CmdDeliveryJobAdd));
        }
        private void Unload() {
            try {
                try {
                    foreach (var coroutine in _coroutines) {
                        try {
                            if (coroutine == null) continue;
                            ServerMgr.Instance?.StopCoroutine(coroutine);
                        }
                        catch(Exception ex) {
                            PrintError(ex.ToString());
                        }
                    }
                }
                catch(Exception ex) {
                    PrintError(ex.ToString());
                }
                foreach (var car in ModularCar.allCarsList) {
                    try {
                        var job = car?.GetComponent<ActiveDeliveryJob>();
                        if ((job?.IsCompanyCar() ?? false)) job?.KillCar();
                        job?.DoDestroy();
                    }
                    catch (Exception ex) {
                        PrintError(ex.ToString());
                    }
                }
                try {
                    _monumentCache?.Clear();
                    _monumentCache = null;
                }
                catch (Exception ex) {
                    PrintError(ex.ToString());
                }
                try {
                    for (int i = 0; i < BasePlayer.activePlayerList.Count; i++) StopJobGUI(BasePlayer.activePlayerList[i]);
                }
                catch (Exception ex) {
                    PrintError(ex.ToString());
                }
                try {
                    foreach (var userId in _invLocked) {
                        var p = RelationshipManager.FindByID(userId);
                        if (p == null) continue;
                        try {
                            if (p.IsConnected) p.EndLooting();
                        }
                        finally {
                            LockMainAndBeltContainers(p, false);
                        }
                    }
                }
                catch (Exception ex) {
                    PrintError(ex.ToString());
                }
                foreach (var ent in _temporaryEntities) {
                    try {
                        if (!(ent?.IsDestroyed ?? true)) ent.Kill();
                    }
                    catch (Exception ex) {
                        PrintError(ex.ToString());
                    }
                }
                try {
                    SaveDeliveryData();
                }
                catch (Exception ex) {
                    PrintError(ex.ToString());
                }
            }
            finally {
                Instance = null;
            }
        }
        private void OnEntityTakeDamage(BaseVehicleModule module, HitInfo info) {
            if (module?.GetParentEntity() is not ModularCar car || !IsCarOnJob(car, out var job)) return;
            module?.Invoke(() => {
                var dmgTotal = info?.damageTypes?.Total() ?? 0f;
                if (dmgTotal > 0f) job.OnModuleDamaged(module, dmgTotal);
            }
            , 0.01f);
        }
        private void OnEntitySpawned(BaseNetworkable entity) {
            if (entity == null) return;
        }
        private void OnEntityMounted(BaseMountable entity, BasePlayer player) {
            if (entity is not ModularCarSeat carSeat) return;
            var car = carSeat?.associatedSeatingModule?.Car;
            if (car == null || !IsCarOnJob(car, out var job)) return;
            if (job.JobOwner == player) {
                //colorize?
                var sb = Pool.Get<StringBuilder>();
                try {
                    var timeRem = sb.Clear().Append("Delivery Time Remaining:\n").Append(GetTimeSpanFromMs(job.TimeRemaining.TotalMilliseconds, true)).ToString();
                    var alertFrac = 0.2;
                    ShowToast(player, timeRem, job.TimeRemainingFraction <= alertFrac ? 1 : 0);
                    //1 alert if <= 0.2 (20%).}
                    finally {
                        Pool.Free(ref sb);
                    }
                }
                Interface.Oxide.LogWarning("Running ensure");
                job.EnsureAllMountedHaveMapMarker();
                Interface.Oxide.LogWarning("Ran ensure");
            }
            private void OnEntityDismounted(BaseMountable entity, BasePlayer player) {
                if (entity is not ModularCarSeat carSeat) return;
                var car = carSeat?.associatedSeatingModule?.Car;
                if (car == null || !IsCarOnJob(car, out var job)) return;
                if (job.JobOwner == player) {
                    PrintWarning(nameof(OnEntityDismounted) + " JobOwner is player dismounted, so NOT removing.");
                    return;
                }
                Interface.Oxide.LogWarning("Running remove");
                job.RemoveMarker(player, job.ParkingDestinationMarker);
                Interface.Oxide.LogWarning("Ran Remove");
            }
            private readonly HashSet<ulong> _invLocked = new();
            private void LockMainAndBeltContainers(BasePlayer player, bool desiredState) {
                if (player == null) throw new ArgumentNullException(nameof(player));
                if (player?.inventory == null) return;
                player.inventory.containerBelt.SetLocked(desiredState);
                player.inventory.containerMain.SetLocked(desiredState);
            }
            private void OnLootEntityEnd(BasePlayer player, BaseEntity entity) {
                if (_invLocked.Remove(player.userID)) LockMainAndBeltContainers(player, false);
            }
            private void OnLootEntity(BasePlayer player, BaseEntity entity) {
                if (_invLocked.Contains(player.userID) || (entity?.GetParentEntity() is not VehicleModuleStorage modStorage || !IsCarOnJob(modStorage?.Car))) return;
                _invLocked.Add(player.userID);
                LockMainAndBeltContainers(player, true);
            }
            private object CanLootEntity(BasePlayer player, ContainerIOEntity ioContainer) {
                //todo: also deny if company truck; regardless of job status.if (ioContainer is ModularCarGarage carGarage && IsCarOnJob(carGarage?.carOccupant))
                {
                    SendLocalEffectOnPlayer(player, CODE_LOCK_DENIED_FX, boneID : StringPool.Get("head"));
                    ShowToast(player, "Trucks can't be modified while a job is active.Please finish or cancel your job.\nIf needed, repair your truck with a hammer.", 1);
                    return false;
                }
                return null;
            }
            //custom harmony hook via DropItemsPatch.cs
            private object OnDropItems(IItemContainerEntity containerEntity, BaseEntity initiator = null) {
                //getparent because we get the actual storage container and not the module at first.if (containerEntity?.inventory?.entityOwner?.GetParentEntity() is not VehicleModuleStorage modStorage)
                return null;
                //true to override.null to let it be.//todo: if company truck, even if somehow ActiveDeliveryJob is null, deny droppage.return IsCarOnJob(modStorage?.Car) ? true : null;
            }
            private bool IsCarOnJob(ModularCar car) => car?.net != null && car.net.ID.IsValid && ActiveDeliveryJob.DeliveryCars.Contains(car.net.ID);
            private bool IsCarOnJob(ModularCar car, out ActiveDeliveryJob job) {
                if (car?.net != null && car.net.ID.IsValid && ActiveDeliveryJob.DeliveryCars.Contains(car.net.ID)) {
                    job = car?.GetComponent<ActiveDeliveryJob>();
                    return job != null;
                }
                job = null;
                return false;
            }
            private void OnEntityKill(ModularCar car) {
                car?.GetComponent<ActiveDeliveryJob>()?.DoDestroy();
            }
            private void OnEntityDeath(ModularCar car, HitInfo info) {
                car?.GetComponent<ActiveDeliveryJob>()?.DoDestroy();
            }
            //likely unnecessary (car cannot be used on lift while on job):
            private object OnVehicleModuleMove(BaseVehicleModule module, BaseModularVehicle vehicle, BasePlayer player) {
                if (vehicle is ModularCar car && IsCarOnJob(car)) {
                    ShowToast(player, "Cannot be modified at this time!", 1);
                    return true;
                    //override! do not allow.}
                    return null;
                }
                private readonly Dictionary<NPCTalking, TimeCachedValue<bool>> _npcTalkingInBounds = new();
                private object OnNpcConversationStart(NPCTalking npc, BasePlayer player, ConversationData convFor) {
                    if (npc == null || player == null || convFor == null || npc.prefabID != BANDIT_CONVERSATIONALIST_PREFAB_ID) return null;
                    if (!_npcTalkingInBounds.TryGetValue(npc, out _)) _npcTalkingInBounds[npc] = new TimeCachedValue<bool>() {
                        refreshCooldown = 20f, refreshRandomRange = 5f, updateValue = new Func<bool>(() => {
                            return Harbor2?.IsInBounds((npc?.transform?.position ?? Vector3.zero)) ?? false;
                        }
                        )
                    }
                    ;
                    if (!_npcTalkingInBounds[npc].Get(false)) return null;
                    var toRemove = Pool.Get<HashSet<DeliveryJob>>();
                    try {
                        toRemove.Clear();
                        foreach (var job in _availableJobs) {
                            if (job.Started) toRemove.Add(job);
                        }
                        foreach (var job in toRemove) _availableJobs.Remove(job);
                    }
                    finally {
                        Pool.Free(ref toRemove);
                    }
                    var _allJobs = Pool.Get<HashSet<DeliveryJob>>();
                    try {
                        _allJobs.Clear();
                        if (_availableJobs.Count < 1) GenerateRandomDeliveryJobsNoAlloc(ref _allJobs, Random.Range(3, 13));
                        //yes, we intentionally ref _allJobs.check code immediately below
                        foreach (var job in _allJobs) {
                            if (!job.Started) _availableJobs.Add(job);
                        }
                        DeliveryJobsGUI_New(player);
                        //    JobGUI(player);
                    }
                    finally {
                        Pool.Free(ref _allJobs);
                    }
                    return true;
                }
                private const string JOB_CUI_NAME = "DJ_GUI";
                [ConsoleCommand("deliveryjobs.stopgui")]
                private void cmdStopJobsGUI(ConsoleSystem.Arg arg) {
                    var player = arg?.Player() ?? null;
                    if (player == null) return;
                    StopJobGUI(player);
                }
                private readonly Dictionary<string, float> _lastTakeJobCmd = new();
                //also incredibly messy at this time
                [ConsoleCommand("deliveryjobs.takejob")]
                private void cmdTakeJob(ConsoleSystem.Arg arg) {
                    var player = arg?.Player() ?? null;
                    if (player == null) return;
                    if (_lastTakeJobCmd.TryGetValue(player.UserIDString, out var time) && (UnityEngine.Time.realtimeSinceStartup - time) < 2) {
                        PrintWarning(nameof(cmdTakeJob) + " cooldown!!!");
                        return;
                    }
                    else _lastTakeJobCmd[player.UserIDString] = UnityEngine.Time.realtimeSinceStartup;
                    if (arg?.Args == null || arg.Args.Length < 1 || !int.TryParse(arg.Args[0], out var jobIndex)) {
                        PrintWarning(nameof(cmdTakeJob) + " with no args or no int arg0");
                        return;
                    }
                    if (jobIndex > _availableJobs.Count) {
                        PrintWarning(nameof(jobIndex) + " higher than " + nameof(_availableJobs) + "!");
                        return;
                    }
                    DeliveryJob desiredJob = null;
                    var i = 0;
                    foreach (var job in _availableJobs) {
                        if (i == jobIndex) {
                            desiredJob = job;
                            break;
                        }
                        i++;
                    }
                    if (desiredJob == null) {
                        PrintError(nameof(desiredJob) + " is null!!!! index: " + jobIndex);
                        return;
                    }
                    if (HarborArea == null) {
                        PrintError(nameof(HarborArea) + " is null!?!?!?");
                        return;
                    }
                    var car = HarborArea?.CarLift?.carOccupant;
                    if (car == null) {
                        SendReply(player, "Please park your truck on the car lift nearby!");
                        ShowToast(player, "No truck on lift!", 1);
                        return;
                    }
                    if (car?.OwnerID != player.userID) {
                        SendReply(player, "Someone else's car is currently parked on the lift.Please wait!");
                        ShowToast(player, "Someone else's car is parked on the lift!", 1);
                        return;
                    }
                    if (!(car?.net?.ID.IsValid ?? false)) {
                        SendReply(player, "Car has invalid netID?!");
                        return;
                    }
                    if (ActiveDeliveryJob.DeliveryCars.Contains(car.net.ID)) {
                        SendReply(player, "This car is already on a job!");
                        return;
                    }
                    if (ActiveDeliveryJob.PlayerToJob.TryGetValue(player, out _)) {
                        //future todo(?): reply with info about current job.SendReply(player, "You're already on a job! Please cancel your current job first.\nYou can cancel it by removing the map marker on your map.");
                        return;
                    }
                    var hasAppropriateModule = false;
                    var desiredModule = desiredJob.GetDesiredCargoModulePrefabID();
                    if (car?.AttachedModuleEntities != null) {
                        for (int j = 0; j < car.AttachedModuleEntities.Count; j++) {
                            var mod = car.AttachedModuleEntities[j];
                            if (mod.prefabID == desiredModule) {
                                hasAppropriateModule = true;
                                break;
                            }
                        }
                    }
                    if (!hasAppropriateModule) {
                        SendReply(player, "Your car does not have the appropriate module for this type of job! Liquid jobs require a tanker.Solids require at least one storage module.");
                        return;
                    }
                    StopJobGUI(player);
                    var delJob = car?.gameObject?.AddComponent<ActiveDeliveryJob>();
                    if (delJob == null) {
                        SendReply(player, "Somehow car job is null, report to an admin!!");
                        return;
                    }
                    if (!ActiveDeliveryJob.DeliveryCars.Add(car.net.ID)) PrintError(nameof(ActiveDeliveryJob.DeliveryCars) + " already contained netID?!");
                    else delJob.InitializeJob(desiredJob, player);
                    SendLocalEffectOnPlayer(player, MISSION_ACCEPT_FX);
                }
                //probably not very useful if not providing ownerid
                private ModularCar GetCarNearby(Vector3 pos, float radius, ulong ownerId = 0) {
                    var cars = Pool.GetList<ModularCar>();
                    try {
                        Vis.Entities(pos, radius, cars);
                        for (int i = 0; i < cars.Count; i++) {
                            var car = cars[i];
                            if (ownerId != 0 && car.OwnerID != ownerId) continue;
                            return car;
                        }
                    }
                    finally {
                        Pool.FreeList(ref cars);
                    }
                    return null;
                }
                private void JobGUI(BasePlayer player, string additionalText = "") {
                    if (player == null || !player.IsConnected) return;
                    var GUISkinElement = new CuiElementContainer();
                    var GUISkin = GUISkinElement.Add(new CuiPanel {
                        Image = {
                            Color = "0 0 0 0.92"
                        }
                        , RectTransform = {
                            AnchorMin = "0.0 0.0", AnchorMax = "1 1"
                        }
                        , CursorEnabled = true
                    }
                    , "Hud", JOB_CUI_NAME);
                    GUISkinElement.Add(new CuiButton {
                        Button = {
                            Command = "deliveryjobs.page prev", Color = "0.6 0.34 0.75 0.425"
                        }
                        , Text = {
                            Text = "Previous Page", FontSize = 15, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1"
                        }
                        , RectTransform = {
                            AnchorMin = "0.4 0.1", AnchorMax = "0.6 0.15"
                        }
                    }
                    , GUISkin);
                    GUISkinElement.Add(new CuiButton {
                        Button = {
                            Command = "deliveryjobs.stopgui", Color = "0.6 0.34 0.75 0.425"
                        }
                        , Text = {
                            Text = "Close", FontSize = 15, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1"
                        }
                        , RectTransform = {
                            AnchorMin = "0.4 0", AnchorMax = "0.6 0.05"
                        }
                    }
                    , GUISkin);
                    GUISkinElement.Add(new CuiButton {
                        Button = {
                            Command = "none.none", Color = "0.6 0.34 0.75 0.425"
                        }
                        , Text = {
                            Text = "Next Page", FontSize = 15, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1"
                        }
                        , RectTransform = {
                            AnchorMin = "0.4 0.2", AnchorMax = "0.6 0.25"
                        }
                    }
                    , GUISkin);
                    GUISkinElement.Add(new CuiButton {
                        Button = {
                            Command = "none.none", Color = "0.8 0.24 0.45 0.425"
                        }
                        , Text = {
                            Text = "<color=yellow>button</color>", FontSize = 15, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1"
                        }
                        , RectTransform = {
                            AnchorMin = "0 0", AnchorMax = "0.156 0.056"
                        }
                    }
                    , GUISkin);
                    var sb = Pool.Get<StringBuilder>();
                    try {
                        GUISkinElement.Add(new CuiLabel {
                            Text = {
                                Text = "Delivery Job GUI",  //sb.Clear().Append("<color=").Append(LUCK_COLOR).Append("><color=#ff80bf>").Append(tree).Append("</color> Skill tree\nCurrent Points: </color>").Append(luckInfo.SkillPoints.ToString("N0")).Append("\n<color=").Append(LUCK_COLOR).Append(">Luck XP:</color> ").Append(luckInfo.XP.ToString("N0")).Append("<color=").Append(LUCK_COLOR).Append(">/</color>").Append(GetPointsForLevel(luckInfo.Level + 1).ToString("N0")).Append(" <color=").Append(LUCK_COLOR).Append(">(</color>").Append(GetExperiencePercentString(player)).Append("<color=").Append(LUCK_COLOR).Append(">)\nCatchup multiplier:</color> ").Append(getCatchupMultiplier(player.userID).ToString("N0")).Append("%\n<color=").Append(LUCK_COLOR).Append(">XP Rate:</color> ").Append(GetXPMultiplier(player.UserIDString).ToString("N0")).Append("%\n<color=").Append(LUCK_COLOR).Append(">Item Rate:</color> ").Append(getItemMultiplier(player.UserIDString).ToString("N0")).Append("%\n<color=").Append(LUCK_COLOR).Append(">Chance multiplier:</color> ").Append(GetChanceBonuses(player.UserIDString).ToString("0.00").Replace(".00", string.Empty)).Append("%\n<color=#ff9933>Read <color=").Append(LUCK_COLOR).Append(">/skt help</color> before using this.</color>").Append(!string.IsNullOrEmpty(additionalText) ? "\n" : string.Empty).Append(additionalText).ToString(),
                                FontSize = 20, Align = TextAnchor.UpperLeft, Color = "1 1 1 1", VerticalOverflow = VerticalWrapMode.Overflow
                            }
                            , RectTransform = {
                                AnchorMin = "0.705 0.65", AnchorMax = "1 1"
                            }
                        }
                        , GUISkin);
                    }
                    finally {
                        Pool.Free(ref sb);
                    }
                    var buttonYMin = 0.958;
                    var buttonYMax = 1d;
                    var button2YMin = 0.917;
                    var button2YMax = 0.958;
                    var labelXMin = 0.2;
                    var labelXMax = 0.475;
                    var labelYMin = 0.961;
                    var labelYMax = 0.983;
                    sb = Pool.Get<StringBuilder>();
                    try {
                        var i = 0;
                        foreach (var job in _availableJobs) {
                            var cmdText = sb.Clear().Append("deliveryjobs.takejob ").Append(i).ToString();
                            var nearestMonName = job?.LocationDelivery?.GetMonument()?.displayPhrase?.english ?? job?.LocationDelivery?.MonumentShortPrefabName ?? "Unknown";
                            var labelText = sb.Clear().Append("Delivery to: ").Append(nearestMonName).ToString();
                            GUISkinElement.Add(new CuiLabel {
                                Text = {
                                    Text = sb.Clear().Append("<-- ").Append(labelText).ToString(), Align = TextAnchor.MiddleLeft, FontSize = 14, Color = "1 1 1 1", VerticalOverflow = VerticalWrapMode.Overflow
                                }
                                , RectTransform = {
                                    AnchorMin = sb.Clear().Append(labelXMin).Append(" ").Append(labelYMin).ToString(), AnchorMax = sb.Clear().Append(labelXMax).Append(" ").Append(labelYMax).ToString()
                                }
                            }
                            , GUISkin);
                            GUISkinElement.Add(new CuiButton {
                                Button = {
                                    Command = cmdText, Color = "0.2 0.4 0.65 0.75"
                                }
                                , Text = {
                                    Text = sb.Clear().Append("<color=#F6D1BE>").Append(job.Tier).Append(" delivery of ").Append(job.TypeCargo).Append(" to ").Append(nearestMonName).Append(" (Time Limit: ").Append(job.LocationDelivery.TimeLimit.TotalSeconds > 0 ? job.LocationDelivery.TimeLimit.ToString() : "None").Append(")</color>").ToString(), FontSize = 15, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1", VerticalOverflow = VerticalWrapMode.Overflow
                                }
                                , RectTransform = {
                                    AnchorMin = sb.Clear().Append("0.0 ").Append(buttonYMin).ToString(), AnchorMax = sb.Clear().Append("0.195 ").Append(buttonYMax).ToString()
                                }
                            }
                            , GUISkin);
                            GUISkinElement.Add(new CuiButton {
                                Button = {
                                    Command = "no command", Color = "0.15 0.35 0.72 0.75"
                                }
                                , Text = {
                                    Text = "show more job info",  //sb.Clear().Append("<size=16><color=#99d6ff>More <i>").Append(buttonSkillName).Append("</i> info</color> </size>(<color=#33adff>See top right</color>)").ToString(),
                                    FontSize = 12, Align = TextAnchor.MiddleCenter, Color = "1 1 1 1", VerticalOverflow = VerticalWrapMode.Overflow
                                }
                                , RectTransform = {
                                    AnchorMin = sb.Clear().Append("0.0 ").Append(button2YMin).ToString(), AnchorMax = sb.Clear().Append("0.195 ").Append(button2YMax).ToString()
                                }
                            }
                            , GUISkin);
                            buttonYMin -= 0.1;
                            buttonYMax -= 0.1;
                            button2YMin -= 0.1;
                            button2YMax -= 0.1;
                            labelYMin -= 0.1;
                            labelYMax -= 0.1;
                            i++;
                        }
                    }
                    finally {
                        Pool.Free(ref sb);
                    }
                    CuiHelper.DestroyUi(player, JOB_CUI_NAME);
                    //destroy only after it's ready to be added
                    CuiHelper.AddUi(player, GUISkinElement);
                }
                private void StopJobGUI(BasePlayer player) => CuiHelper.DestroyUi(player, JOB_CUI_NAME);
                private readonly HashSet<MapNote> _hasAttempedToRemove = new();
                private object OnMapMarkerRemove(BasePlayer player, MapNote note) {
                    if (player == null || note == null) return null;
                    if (note.totalDuration != 1337f) return null;
                    if (_hasAttempedToRemove.Add(note)) {
                        ShowToast(player, "Try again to confirm remove.\nTHIS WILL CANCEL YOUR JOB.", 1);
                        player?.Invoke(() => {
                            _hasAttempedToRemove?.Remove(note);
                        }
                        , 4f);
                        return true;
                    }
                    else if (_hasAttempedToRemove.Remove(note)) {
                        //cancel job.if (ActiveDeliveryJob.PlayerToJob.TryGetValue(player, out var job))
                        {
                            job.CancelJob();
                            ShowToast(player, "Job canceled!", 1);
                        }
                        else {
                            ShowToast(player, "Couldn't cancel job!!!\nPlease report this to an admin.", 1);
                            PrintWarning("Failed to cancel job on map note removal!!! no _playerToJob get");
                            return null;
                        }
                        return true;
                        //not preventing the original remove means CancelJob() calls it first and causes an RPC error on client.}
                        return null;
                    }
                    private readonly HashSet<MapNote> _unremovableMapNotes = new();
                    private MapNote AddPermanentMapMarker(BasePlayer player, Vector3 worldPos, string label = "", int icon = 1, int noteType = 1, int colorIndex = -1, bool isPing = false, bool canBeRemoved = true) {
                        if (player == null) {
                            throw new ArgumentNullException(nameof(player));
                        }
                        if (!player.IsConnected || player?.State == null) return null;
                        var mapN = new MapNote {
                            colourIndex = colorIndex, totalDuration = 99999, icon = icon, isPing = isPing, worldPosition = worldPos, label = label, timeRemaining = 99999,  //unsure if necessary
                            noteType = noteType
                        }
                        ;
                        if (player.State.pointsOfInterest == null) player.State.pointsOfInterest = Pool.GetList<MapNote>();
                        player.State.pointsOfInterest.Add(mapN);
                        player.DirtyPlayerState();
                        player.SendMarkersToClient();
                        player.TeamUpdate();
                        if (!canBeRemoved) _unremovableMapNotes.Add(mapN);
                        return mapN;
                    }
                    private readonly HashSet<BaseEntity> _temporaryEntities = new();
                    private BaseEntity CreateTemporaryEntity(string strPrefab, Vector3 pos = default, Quaternion rot = default, bool startActive = true, ulong ownerId = 0) {
                        var ent = GameManager.server.CreateEntity(strPrefab, pos, rot, startActive);
                        ent.OwnerID = ownerId;
                        _temporaryEntities.Add(ent);
                        return ent;
                    }
                    public class HarborPickupArea {
                        public MonumentInfo Harbor2 {
                            get;
                            private set;
                        }
                        public ModularCarGarage CarLift {
                            get;
                            set;
                        }
                        public HarborPickupArea(MonumentInfo harbor, ModularCarGarage lift = null) {
                            Harbor2 = harbor;
                            CarLift = lift;
                        }
                    }
                    public HarborPickupArea HarborArea {
                        get;
                        set;
                    }
                    pr
// ... [Code preview truncated due to length. Full source in attached file] ...

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху