private Dictionary <long, PurchaseCollection> FindFilteredBuyables(PurchaseMode mode, bool filterRandom = true)
        {
            var buyables = FindBuyables();

            if (mode == PurchaseMode.RANDOM)
            {
                if (filterRandom)
                {
                    return(FilterBuyablesForNpcRatio(buyables, mode));
                }
                else
                {
                    return(buyables);
                }
            }

            var filtered = new Dictionary <long, PurchaseCollection>();

            foreach (var entry in buyables)
            {
                long identityId          = entry.Key;
                PurchaseCollection modes = entry.Value;

                if (!modes.Contains(mode))
                {
                    continue;
                }

                filtered.Add(identityId, modes);
            }

            return(FilterBuyablesForNpcRatio(filtered, mode));
        }
        /// <summary>
        /// Called on server when someone requests to place an object from inventory.
        /// Immediately "fails" verification to be queued later, once the data has been retrieved.
        /// </summary>
        /// <param name="vm"></param>
        /// <param name="caller"></param>
        /// <returns></returns>
        public override bool Verify(VM vm, VMAvatar caller)
        {
            if (Verified)
            {
                return(true);          //set internally when transaction succeeds. trust that the verification happened.
            }
            //typically null caller, non-roommate cause failure. some lot specific things may apply.
            Mode = vm.PlatformState.Validator.GetPurchaseMode(Mode, caller, 0, true);
            if (Mode == PurchaseMode.Disallowed ||
                !vm.TSOState.CanPlaceNewUserObject(vm))
            {
                return(false);
            }

            vm.GlobalLink.RetrieveFromInventory(vm, ObjectPID, caller.PersistID, true, (uint guid, byte[] data) =>
            {
                if (guid == 0)
                {
                    return;            //todo: error feedback?
                }
                GUID     = guid;
                Data     = data;
                Verified = true;
                vm.ForwardCommand(this);
            });

            return(false);
        }
        public override PurchaseMode GetPurchaseMode(PurchaseMode desired, VMAvatar ava, uint guid, bool fromInventory)
        {
            if (desired > PurchaseMode.Donate)
            {
                return(PurchaseMode.Disallowed);
            }
            if (ava == null)
            {
                return(PurchaseMode.Disallowed);
            }
            if (ava.AvatarState.Permissions < VMTSOAvatarPermissions.Roommate)
            {
                return(PurchaseMode.Disallowed);
            }

            //todo: build/buy limits for inventory, but allow rare/non-catalog placement
            if (!vm.TS1 && !fromInventory)
            {
                var catalog   = Content.Content.Get().WorldCatalog;
                var item      = catalog.GetItemByGUID(guid);
                var whitelist = (ava.AvatarState.Permissions == VMTSOAvatarPermissions.Roommate) ? RoomieWhiteList : BuilderWhiteList;
                if (item == null || !whitelist.Contains(item.Value.Category))
                {
                    if (ava.AvatarState.Permissions != VMTSOAvatarPermissions.Admin)
                    {
                        return(PurchaseMode.Disallowed);
                    }
                }
            }

            return(PurchaseMode.Normal);
        }
Exemple #4
0
        public override bool Verify(VM vm, VMAvatar caller)
        {
            if (Verified)
            {
                return(true); //set internally when transaction succeeds. trust that the verification happened.
            }
            value = 0;        //do not trust value from net
            if (!vm.TS1)
            {
                Mode = vm.PlatformState.Validator.GetPurchaseMode(Mode, caller, GUID, false);
                if (Mode == PurchaseMode.Disallowed)
                {
                    return(false);
                }
                if (Mode == PurchaseMode.Normal && !vm.PlatformState.CanPlaceNewUserObject(vm))
                {
                    return(false);
                }
                if (Mode == PurchaseMode.Donate && !vm.PlatformState.CanPlaceNewDonatedObject(vm))
                {
                    return(false);
                }
            }

            //TODO: error feedback for client
            var catalog = Content.Content.Get().WorldCatalog;
            var item    = catalog.GetItemByGUID(GUID);

            if (item != null)
            {
                var price     = (int)item.Value.Price;
                var dcPercent = VMBuildableAreaInfo.GetDiscountFor(item.Value, vm);
                value = (price * (100 - dcPercent)) / 100;
                if (Mode == PurchaseMode.Donate)
                {
                    value -= (value * 2) / 3;
                }
            }

            //TODO: fine grained purchase control based on user status

            //perform the transaction. If it succeeds, requeue the command
            vm.GlobalLink.PerformTransaction(vm, false, caller?.PersistID ?? uint.MaxValue, uint.MaxValue, value,
                                             (bool success, int transferAmount, uint uid1, uint budget1, uint uid2, uint budget2) =>
            {
                if (success)
                {
                    Verified = true;
                    vm.ForwardCommand(this);
                }
            });

            return(false);
        }
Exemple #5
0
        public override void Deserialize(BinaryReader reader)
        {
            base.Deserialize(reader);
            GUID  = reader.ReadUInt32();
            x     = reader.ReadInt16();
            y     = reader.ReadInt16();
            level = reader.ReadSByte();
            dir   = (Direction)reader.ReadByte();
            value = reader.ReadInt32();

            Mode = (PurchaseMode)reader.ReadByte();
        }
        private void ListInternal(PurchaseMode mode, bool filterRandom = true)
        {
            var buyables = FindFilteredBuyables(mode, filterRandom);

            List <string> lines = new List <string>();

            foreach (var entry in buyables)
            {
                long identityId = entry.Key;

                List <PurchaseMode> modes = entry.Value.ToList();
                modes.Sort();

                IMyFaction faction = FactionUtils.GetPlayerFaction(identityId);

                string factionString = "";
                if (faction != null)
                {
                    factionString = "[" + faction.Tag + "]";
                }

                string name     = PlayerUtils.GetPlayerNameById(identityId) + " " + factionString;
                string modesStr = string.Join(", ", modes);

                lines.Add(name + " -- " + modesStr);
            }

            lines.Sort();

            StringBuilder sb = new StringBuilder();

            foreach (var line in lines)
            {
                sb.AppendLine(line);
            }

            if (Context.Player == null)
            {
                Context.Respond($"Buyable GPS for " + mode);
                Context.Respond(sb.ToString());
            }
            else
            {
                ModCommunication.SendMessageTo(new DialogMessage("Buyable GPS", "For " + mode, sb.ToString()), Context.Player.SteamUserId);
            }
        }
        public override void Deserialize(BinaryReader reader)
        {
            base.Deserialize(reader);
            ObjectPID = reader.ReadUInt32();
            x         = reader.ReadInt16();
            y         = reader.ReadInt16();
            level     = reader.ReadSByte();
            dir       = (Direction)reader.ReadByte();

            GUID = reader.ReadUInt32();
            var length = reader.ReadInt32();

            if (length > 4096)
            {
                throw new Exception("Object data cannot be this large!");
            }
            Data = reader.ReadBytes(length);

            Mode = (PurchaseMode)reader.ReadByte();
        }
        private Dictionary <long, PurchaseCollection> FindFilteredBuyablesForPlayer(PurchaseMode mode)
        {
            /* We dont want to have filtering for random in here to not filter twice. */
            var buyables = FindFilteredBuyables(mode, false);

            var filtered = new Dictionary <long, PurchaseCollection>();

            var player     = Context.Player;
            var identityId = player.IdentityId;

            HashSet <long> factionMembers = new HashSet <long>();

            if (Plugin.Config.FilterFactionMembers)
            {
                try {
                    var faction = FactionUtils.GetPlayerFaction(identityId);

                    factionMembers = new HashSet <long>(faction.Members.Keys);
                } catch (Exception e) {
                    Log.Error(e, "Faction could not be checked, it potentially has no founder!");
                }
            }

            factionMembers.Add(identityId);

            foreach (var entry in buyables)
            {
                long id = entry.Key;

                if (factionMembers.Contains(id))
                {
                    continue;
                }

                filtered.Add(entry.Key, entry.Value);
            }

            /* After removing Faction Members and the player himself we may have to check again if NPCs needs to go */
            return(FilterBuyablesForNpcRatio(filtered, mode));
        }
        private bool CheckConformation(ICooldownKey cooldownKey, PurchaseMode mode, long price)
        {
            var cooldownManager = Plugin.ConfirmationManager;

            var commandKey = mode.ToString() + price;

            if (!cooldownManager.CheckCooldown(cooldownKey, commandKey, out _))
            {
                cooldownManager.StopCooldown(cooldownKey);
                return(true);
            }

            Context.Respond("Are you sure you want to buy a gps for '" + mode + "' for " + price.ToString("#,##0") + " SC? Make sure you read the information in !gps help. Enter the command again within 30 seconds to confirm!");

            if (mode == PurchaseMode.RANDOM)
            {
                Context.Respond("Make sure to check up your chances with '!gps list chances' to not get disappointed!");
            }

            cooldownManager.StartCooldown(cooldownKey, commandKey, 30 * 1000);

            return(false);
        }
Exemple #10
0
 public override PurchaseMode GetPurchaseMode(PurchaseMode desired, VMAvatar ava, uint guid, bool fromInventory)
 {
     if (desired > PurchaseMode.Donate)
     {
         return(PurchaseMode.Disallowed);
     }
     if (ava == null)
     {
         return(PurchaseMode.Disallowed);
     }
     if (ava.AvatarState.Permissions < TSOPlatform.VMTSOAvatarPermissions.Roommate)
     {
         return(PurchaseMode.Disallowed);
     }
     if (base.GetPurchaseMode(desired, ava, guid, fromInventory) == PurchaseMode.Disallowed)
     {
         return(PurchaseMode.Disallowed);
     }
     if (desired == PurchaseMode.Normal && ava.AvatarState.Permissions < TSOPlatform.VMTSOAvatarPermissions.Owner)
     {
         return(PurchaseMode.Donate);
     }
     return(desired);
 }
        private void BuyInternal(long price, PurchaseMode mode)
        {
            if (Context.Player == null)
            {
                Context.Respond("Only an actual player can buy GPS!");
                return;
            }

            if (price < 0)
            {
                Context.Respond("This command was disabled in the settings. Use '!gps list commands' to see which commands are active!");
                return;
            }

            var player   = Context.Player;
            var identity = PlayerUtils.GetIdentityById(player.IdentityId);

            price = GetAdjustedPriceForPlayer(price, identity);

            if (Plugin.Config.MustBeInFactionToBuy)
            {
                var faction = FactionUtils.GetPlayerFaction(identity.IdentityId);

                if (faction == null)
                {
                    Context.Respond("You must be part of a Faction to buy GPS!");
                    return;
                }
            }

            if (mode == PurchaseMode.ONLINE || mode == PurchaseMode.RANDOM)
            {
                var minPlayersOnline = Plugin.Config.MinPlayerOnlineToBuy;

                int onlineCount = MySession.Static.Players.GetOnlinePlayerCount();

                if (onlineCount < minPlayersOnline)
                {
                    Context.Respond("For Online/Random gps at least " + minPlayersOnline + " players must be online!");
                    return;
                }
            }

            var minOnlineTime = Plugin.Config.MinOnlineMinutesToBuy;

            if (minOnlineTime > 0)
            {
                var lastSeen      = Plugin.getLastLoginDate(identity.IdentityId);
                var minOnlineDate = DateTime.Now.AddMinutes(-Plugin.Config.MinOnlineMinutesToBuy);

                if (lastSeen > minOnlineDate)
                {
                    int differenceSeconds = (int)lastSeen.Subtract(minOnlineDate).TotalSeconds;

                    int minutes = (differenceSeconds / 60);
                    int seconds = differenceSeconds % 60;

                    Context.Respond("You are not online for long enough! You must be online for at least " + minutes.ToString("00") + ":" + seconds.ToString("00") + " more minutes!");

                    return;
                }
            }

            var minPCUToBuy = Plugin.Config.MinPCUToBuy;

            if (minPCUToBuy > 0)
            {
                var pcuBuilt  = identity.BlockLimits.PCUBuilt;
                var neededPcu = Plugin.Config.MinPCUToBuy;

                if (neededPcu > pcuBuilt)
                {
                    Context.Respond("You dont have enough PCU to buy! You need at least " + (neededPcu - pcuBuilt) + " more!");

                    return;
                }
            }

            var cooldownManager        = Plugin.CooldownManager;
            var cooldownManagerFaction = Plugin.CooldownManagerFactionChange;
            var steamId = new SteamIdCooldownKey(player.SteamUserId);

            long currentBalance = MyBankingSystem.GetBalance(player.IdentityId);

            if (currentBalance < price)
            {
                Context.Respond("You dont have enough credits to effort a GPS! You need at least " + price.ToString("#,##0") + " SC.");
                return;
            }

            if (!cooldownManagerFaction.CheckCooldown(steamId, GpsRoulettePlugin.COOLDOWN_COMMAND, out long remainingSecondsFaction))
            {
                Log.Info("Faction Cooldown for Player " + player.DisplayName + " still running! " + remainingSecondsFaction + " seconds remaining!");
                Context.Respond("Command is still on cooldown after you changed Factions for " + remainingSecondsFaction + " seconds.");
                return;
            }

            if (!cooldownManager.CheckCooldown(steamId, GpsRoulettePlugin.COOLDOWN_COMMAND, out long remainingSeconds))
            {
                Log.Info("Cooldown for Player " + player.DisplayName + " still running! " + remainingSeconds + " seconds remaining!");
                Context.Respond("Command is still on cooldown for " + remainingSeconds + " seconds.");
                return;
            }

            var buyables = FindFilteredBuyablesForPlayer(mode);

            if (buyables.Count == 0)
            {
                Context.Respond("Currently there is no GPS available for purchase. Please try again later!");
                return;
            }

            if (!CheckConformation(steamId, mode, price))
            {
                return;
            }

            if (BuyRandomFromDict(buyables))
            {
                var config     = Plugin.Config;
                var cooldownMs = config.CooldownMinutes * 60 * 1000L;

                MyBankingSystem.ChangeBalance(player.IdentityId, -price);

                cooldownManager.StartCooldown(steamId, GpsRoulettePlugin.COOLDOWN_COMMAND, cooldownMs);

                Context.Respond("Purchase successful!");
            }
            else
            {
                Context.Respond("The location of the selected player could not be retrieved. The purchase was cancelled. Please try again.");
            }
        }
        private Dictionary <long, PurchaseCollection> FilterBuyablesForNpcRatio(Dictionary <long, PurchaseCollection> buyables, PurchaseMode mode)
        {
            /* If not Random no checks need to be done. */
            if (mode != PurchaseMode.RANDOM)
            {
                return(buyables);
            }

            int ratio = Plugin.Config.MaxPercentageNPCInRandomSelection;

            /* When all is NPC no checks needed */
            if (ratio >= 100)
            {
                return(buyables);
            }

            var returnDictionary = new Dictionary <long, PurchaseCollection>();
            var npcs             = new Dictionary <long, PurchaseCollection>();

            /* Filter whats NPC and what is not. */
            foreach (var buyable in buyables)
            {
                if (buyable.Value.Contains(PurchaseMode.NPC))
                {
                    npcs.Add(buyable.Key, buyable.Value);
                }
                else
                {
                    returnDictionary.Add(buyable.Key, buyable.Value);
                }
            }

            int numberOfEntries = returnDictionary.Count;

            /* If theres no Other entry available just return all buyables. Should only be NPC in there. */
            if (numberOfEntries == 0)
            {
                return(buyables);
            }

            /* We must not round up because the percentage is a MAX value. */
            int numberOfNpcsNeeded = (int)((100.0F / (100.0F - ratio) * numberOfEntries) - numberOfEntries);

            /* However the Min may override that, because its an absolute value compared to the dynamic percentage. */
            numberOfNpcsNeeded = Math.Max(Plugin.Config.MinNpcsInRandomSelection, numberOfNpcsNeeded);

            int numberOfNpcsAdded = 0;

            /* Shuffle the NPCs to not always get the same ones. While a Dictionary is not "sorted" it still has a deterministic order. */
            var npcList = npcs.ToList();

            npcList.ShuffleList();

            foreach (var npc in npcList)
            {
                /* If no NPC is needed we break out immediately. Otherwise when we have the desired amount */
                if (numberOfNpcsAdded >= numberOfNpcsNeeded)
                {
                    break;
                }

                returnDictionary.Add(npc.Key, npc.Value);

                numberOfNpcsAdded++;
            }

            return(returnDictionary);
        }
Exemple #13
0
 public abstract PurchaseMode GetPurchaseMode(PurchaseMode desired, VMAvatar ava, uint guid, bool fromInventory);