Пример #1
0
        public override void ProcessServer()
        {
            EconomyScript.Instance.ServerLogger.WriteVerbose("Action /Buy started by Steam Id '{0}'.", SenderSteamId);

            // Get player steam ID
            var buyingPlayer = MyAPIGateway.Players.FindPlayerBySteamId(SenderSteamId);

            MyPhysicalItemDefinition definition = null;
            MyObjectBuilderType      result;

            if (MyObjectBuilderType.TryParse(ItemTypeId, out result))
            {
                var id = new MyDefinitionId(result, ItemSubTypeName);
                MyDefinitionManager.Static.TryGetPhysicalItemDefinition(id, out definition);
            }

            if (definition == null)
            {
                // Someone hacking, and passing bad data?
                MessageClientTextMessage.SendMessage(SenderSteamId, "BUY", "Sorry, the item you specified doesn't exist!");
                EconomyScript.Instance.ServerLogger.WriteVerbose("Action /Buy aborted by Steam Id '{0}' -- item doesn't exist.", SenderSteamId);
                return;
            }

            // Do a floating point check on the item item. Tools and components cannot have decimals. They must be whole numbers.
            if (definition.Id.TypeId != typeof(MyObjectBuilder_Ore) && definition.Id.TypeId != typeof(MyObjectBuilder_Ingot))
            {
                if (ItemQuantity != Math.Truncate(ItemQuantity))
                {
                    MessageClientTextMessage.SendMessage(SenderSteamId, "BUY", "You must provide a whole number for the quantity to buy that item.");
                    EconomyScript.Instance.ServerLogger.WriteVerbose("Action /Buy aborted by Steam Id '{0}' -- invalid qantity.", SenderSteamId);
                    return;
                }
                //ItemQuantity = Math.Round(ItemQuantity, 0);  // Or do we just round the number?
            }

            if (ItemQuantity <= 0)
            {
                MessageClientTextMessage.SendMessage(SenderSteamId, "BUY", "You must provide a valid quantity to buy.");
                EconomyScript.Instance.ServerLogger.WriteVerbose("Action /Buy aborted by Steam Id '{0}' -- invalid qantity.", SenderSteamId);
                return;
            }

            // Who are we buying to?
            BankAccountStruct accountToSell;

            if (BuyFromMerchant)
            {
                accountToSell = AccountManager.FindAccount(EconomyConsts.NpcMerchantId);
            }
            else
            {
                accountToSell = AccountManager.FindAccount(FromUserName);
            }

            if (accountToSell == null)
            {
                MessageClientTextMessage.SendMessage(SenderSteamId, "BUY", "Sorry, player does not exist or have an account!");
                EconomyScript.Instance.ServerLogger.WriteVerbose("Action /Buy aborted by Steam Id '{0}' -- no account.", SenderSteamId);
                return;
            }

            if (MarketManager.IsItemBlacklistedOnServer(ItemTypeId, ItemSubTypeName))
            {
                MessageClientTextMessage.SendMessage(SenderSteamId, "BUY", "Sorry, the item you tried to buy is blacklisted on this server.");
                EconomyScript.Instance.ServerLogger.WriteVerbose("Action /Buy aborted by Steam Id '{0}' -- item blacklisted.", SenderSteamId);
                return;
            }

            // Get the player's inventory, regardless of if they are in a ship, or a remote control cube.
            var character = buyingPlayer.GetCharacter();

            // TODO: do players in Cryochambers count as a valid trading partner? They should be alive, but the connected player may be offline.
            // I think we'll have to do lower level checks to see if a physical player is Online.
            if (character == null)
            {
                // Player has no body. Could mean they are dead.
                // Either way, there is no inventory.
                MessageClientTextMessage.SendMessage(SenderSteamId, "BUY", "You are dead. You cannot trade while dead.");
                EconomyScript.Instance.ServerLogger.WriteVerbose("Action /Buy aborted by Steam Id '{0}' -- player is dead.", SenderSteamId);
                return;
            }

            // TODO: is a null check adaqaute?, or do we need to check for IsDead?
            // I don't think the chat console is accessible during respawn, only immediately after death.
            // Is it valid to be able to trade when freshly dead?
            //var identity = buyingPlayer.Identity();
            //MyAPIGateway.Utilities.ShowMessage("CHECK", "Is Dead: {0}", identity.IsDead);

            //if (identity.IsDead)
            //{
            //    MessageClientTextMessage.SendMessage(SenderSteamId, "BUY", "You are dead. You cannot trade while dead.");
            //    return;
            //}

            var position = ((IMyEntity)character).WorldMatrix.Translation;

            MarketItemStruct marketItem = null;

            if (BuyFromMerchant || UseBankSellPrice)
            {
                var markets = MarketManager.FindMarketsFromLocation(position);
                if (markets.Count == 0)
                {
                    MessageClientTextMessage.SendMessage(SenderSteamId, "BUY", "Sorry, your are not in range of any markets!");
                    EconomyScript.Instance.ServerLogger.WriteVerbose("Action /Buy aborted by Steam Id '{0}' -- no market in range.", SenderSteamId);
                    return;
                }

                // TODO: find market with best Sell price that isn't blacklisted.

                var market = markets.FirstOrDefault();
                if (market == null)
                {
                    MessageClientTextMessage.SendMessage(SenderSteamId, "BUY", "Sorry, the market you are accessing does not exist!");
                    EconomyScript.Instance.ServerLogger.WriteVerbose("Action /Buy aborted by Steam Id '{0}' -- no market found.", SenderSteamId);
                    return;
                }

                marketItem = market.MarketItems.FirstOrDefault(e => e.TypeId == ItemTypeId && e.SubtypeName == ItemSubTypeName);
                if (marketItem == null)
                {
                    MessageClientTextMessage.SendMessage(SenderSteamId, "BUY", "Sorry, the items you are trying to buy doesn't have a market entry!");
                    // In reality, this shouldn't happen as all markets have their items synced up on start up of the mod.
                    return;
                }

                if (marketItem.IsBlacklisted)
                {
                    MessageClientTextMessage.SendMessage(SenderSteamId, "BUY", "Sorry, the item you tried to buy is blacklisted in this market.");
                    EconomyScript.Instance.ServerLogger.WriteVerbose("Action /Buy aborted by Steam Id '{0}' -- item is blacklisted in market.", SenderSteamId);
                    return;
                }

                // Verify that the items are in the player inventory.
                // TODO: later check trade block, cockpit inventory, cockpit ship inventory, inventory of targeted cube.

                if (UseBankSellPrice)
                {
                    // The player is buying, but the *Market* will *sell* it to the player at this price.
                    ItemPrice = marketItem.SellPrice;
                }
            }

            var accountToBuy      = AccountManager.FindOrCreateAccount(SenderSteamId, SenderDisplayName, SenderLanguage);
            var transactionAmount = ItemPrice * ItemQuantity;

            // need fix negative amounts before checking if the player can afford it.
            if (!buyingPlayer.IsAdmin())
            {
                transactionAmount = Math.Abs(transactionAmount);
            }

            // TODO: admin check on ability to afford it?
            //[maybe later, our pay and reset commands let us steal money from npc anyway best to keep admin abuse features to minimum]
            //[we could put an admin check on blacklist however, allow admins to spawn even blacklisted gear]
            if (accountToBuy.BankBalance < transactionAmount)
            {
                MessageClientTextMessage.SendMessage(SenderSteamId, "BUY", "Sorry, you cannot afford {0} {1}!", transactionAmount, EconomyScript.Instance.Config.CurrencyName);
                EconomyScript.Instance.ServerLogger.WriteVerbose("Action /Buy aborted by Steam Id '{0}' -- not enough money.", SenderSteamId);
                return;
            }

            if (BuyFromMerchant) // and supply is not exhausted, or unlimited mode is not on.
                                 //This is a quick fix, ideally it should do a partial buy of what is left and post a buy offer for remainder
            {
                // here we look up item price and transfer items and money as appropriate
                if (marketItem.Quantity >= ItemQuantity || !EconomyScript.Instance.Config.LimitedSupply)
                {
                    marketItem.Quantity -= ItemQuantity; // reduce Market content.
                    EconomyScript.Instance.ServerLogger.WriteVerbose("Action /Buy finalizing by Steam Id '{0}' -- adding to inventory.", SenderSteamId);
                    var remainingToCollect = MessageSell.AddToInventories(buyingPlayer, ItemQuantity, definition.Id);

                    //EconomyScript.Instance.Config.LimitedSupply

                    accountToSell.BankBalance += transactionAmount;
                    accountToSell.Date         = DateTime.Now;

                    accountToBuy.BankBalance -= transactionAmount;
                    accountToBuy.Date         = DateTime.Now;
                    MessageClientTextMessage.SendMessage(SenderSteamId, "BUY", "You just purchased {1} '{2}' for {0} {3}", transactionAmount, ItemQuantity, definition.GetDisplayName(), EconomyScript.Instance.Config.CurrencyName);

                    if (remainingToCollect > 0)
                    {
                        MarketManager.CreateStockHeld(buyingPlayer.SteamUserId, ItemTypeId, ItemSubTypeName, remainingToCollect, ItemPrice);
                        // TODO: there should be a common command to collect items. Not use /sell.
                        MessageClientTextMessage.SendMessage(SenderSteamId, "BUY", "There are {0} remaining to collect. Use '/sell collect'", remainingToCollect);
                    }
                    EconomyScript.Instance.ServerLogger.WriteVerbose("Action /Buy complete by Steam Id '{0}' -- items bought.", SenderSteamId);
                }
                else
                {
                    MessageClientTextMessage.SendMessage(SenderSteamId, "BUY", "There isn't '{0}' of {1} available to purchase! Only {2} available to buy!", ItemQuantity, definition.GetDisplayName(), marketItem.Quantity);
                    EconomyScript.Instance.ServerLogger.WriteVerbose("Action /Buy aborted by Steam Id '{0}' -- not enough stock.", SenderSteamId);
                }

                return;
            }
            else if (FindOnMarket)
            {
                // TODO: Here we find the best offer on the zone market

                return;
            }
            else
            {
                // is it a player then?
                if (accountToSell.SteamId == buyingPlayer.SteamUserId)
                {
                    MessageClientTextMessage.SendMessage(SenderSteamId, "BUY", "Sorry, you cannot buy from yourself!");
                    return;
                }

                // check if selling player is online and in range?
                var payingPlayer = MyAPIGateway.Players.FindPlayerBySteamId(accountToSell.SteamId);

                if (EconomyScript.Instance.Config.LimitedRange && !Support.RangeCheck(buyingPlayer, payingPlayer))
                {
                    MessageClientTextMessage.SendMessage(SenderSteamId, "BUY", "Sorry, you are not in range of that player!");
                    return;
                }

                if (payingPlayer == null)
                {
                    // TODO: other player offline.
                }
                else
                {
                    // TODO: other player is online.
                }
            }

            // this is a fall through from the above conditions not yet complete.
            MessageClientTextMessage.SendMessage(SenderSteamId, "BUY", "Not yet complete.");
        }