private void HouseCmd(CommandArgs args) { var parameters = args.Parameters; var player = args.Player; var playerGroupConfig = Config.Instance.GetGroupConfig(player.Group.Name); var subcommand = parameters.Count > 0 ? parameters[0] : ""; if (subcommand.Equals("1", StringComparison.OrdinalIgnoreCase)) { player.AwaitingTempPoint = 1; player.SendInfoMessage("Hit a block to set the first point."); } else if (subcommand.Equals("2", StringComparison.OrdinalIgnoreCase)) { player.AwaitingTempPoint = 2; player.SendInfoMessage("Hit a block to set the second point."); } else if (subcommand.Equals("allow", StringComparison.OrdinalIgnoreCase)) { if (parameters.Count != 2) { player.SendErrorMessage($"Syntax: {Commands.Specifier}house allow <player-name>"); return; } var session = GetOrCreateSession(player); if (session.CurrentHouse == null) { player.SendErrorMessage("You aren't currently in a house."); return; } var house = session.CurrentHouse; if (player.User?.Name != house.OwnerName && !player.HasPermission("housing.house.admin")) { player.SendErrorMessage( $"You can't allow users for {house.OwnerName}'s [c/{Color.MediumPurple.Hex3()}:{house}] house."); return; } var inputPlayerName = parameters[1]; var players = TShock.Utils.FindPlayer(inputPlayerName); if (players.Count > 1) { player.SendErrorMessage($"Multiple players matched '{inputPlayerName}':"); TShock.Utils.SendMultipleMatchError(player, players); return; } if (players.Count == 0) { player.SendErrorMessage($"Invalid player '{inputPlayerName}'."); return; } var otherPlayer = players[0]; if (otherPlayer.User == null) { player.SendErrorMessage($"{otherPlayer.Name} is not logged in."); return; } house.AllowedUsernames.Add(otherPlayer.User.Name); database.Update(house); player.SendSuccessMessage( $"Allowed {otherPlayer.Name} to modify " + $"{(house.OwnerName == player.User?.Name ? "your" : house.OwnerName + "'s")} " + $"[c/{Color.MediumPurple.Hex3()}:{house}] house."); } else if (subcommand.Equals("buy", StringComparison.OrdinalIgnoreCase)) { if (parameters.Count != 2) { player.SendErrorMessage($"Syntax: {Commands.Specifier}house buy <house-name>"); return; } var inputHouseName = parameters[1]; var session = GetOrCreateSession(player); if (session.CurrentHouse == null) { var plot = TShock.Regions.InAreaRegion(player.TileX, player.TileY) .FirstOrDefault(r => r.Name.StartsWith("__Plot")); if (plot == null) { player.SendErrorMessage("You aren't currently in a house or plot."); return; } player.TempPoints[0] = new Point(plot.Area.X, plot.Area.Y); player.TempPoints[1] = new Point(plot.Area.Right - 1, plot.Area.Bottom - 1); HouseCmd(new CommandArgs("", player, new List <string> { "set", inputHouseName })); return; } var house = session.CurrentHouse; if (!house.ForSale || house.OwnerName == player.User?.Name) { player.SendErrorMessage("You cannot purchase this house."); return; } if (!BankingPlugin.Instance.Bank.CurrencyManager.TryFindCurrencyFromString(house.SalePrice, out var saleCurrency)) { player.SendErrorMessage("The House's list price is in an unknown currency format. Please contact the owner."); return; } var currencyConverter = saleCurrency.GetCurrencyConverter(); if (!currencyConverter.TryParse(house.SalePrice, out var purchaseCost)) { player.SendErrorMessage("The House's list price is in an invalid currency format. Please contact the owner."); return; } var salesTax = Math.Round((decimal)playerGroupConfig.TaxRate * purchaseCost); var totalCost = purchaseCost + salesTax; var purchaseCostString = currencyConverter.ToString(purchaseCost); var salesTaxString = currencyConverter.ToString(salesTax); var totalCostString = currencyConverter.ToString(totalCost); player.SendInfoMessage( $"Purchasing {house.OwnerName}'s house [c/{Color.MediumPurple.Hex3()}:{house}] will cost " + $"[c/{Color.OrangeRed.Hex3()}:{purchaseCostString}], with a sales tax of [c/{Color.OrangeRed.Hex3()}:{salesTaxString}]."); player.SendInfoMessage("Do you wish to proceed? Use /yes or /no."); player.AddResponse("yes", args2 => { player.AwaitingResponse.Remove("no"); var account = BankingPlugin.Instance.GetBankAccount(player.Name, saleCurrency.InternalName); if (account == null || account.Balance < totalCost) { player.SendErrorMessage( $"You do not have enough of a balance to purchase {house.OwnerName}'s " + $"[c/{Color.MediumPurple.Hex3()}:{house}] house."); return; } if (!house.ForSale) { player.SendErrorMessage("Unfortunately, the house was purchased while waiting."); return; } house.ForSale = false; if (purchaseCost > 0) { var account2 = BankingPlugin.Instance.GetBankAccount(house.OwnerName, saleCurrency.InternalName); account.TryTransferTo(account2, purchaseCost); } if (salesTax > 0) { TaxService.PayTax(account, salesTax); } database.Remove(house); database.AddHouse(player, inputHouseName, house.Rectangle.X, house.Rectangle.Y, house.Rectangle.Right - 1, house.Rectangle.Bottom - 1); player.SendInfoMessage( $"Purchased {house.OwnerName}'s house [c/{Color.MediumPurple.Hex3()}:{house}] for " + $"[c/{Color.OrangeRed.Hex3()}:{totalCostString}]."); var player2 = TShock.Players.Where(p => p?.Active == true) .FirstOrDefault(p => p.User?.Name == house.OwnerName); player2?.SendInfoMessage( $"{player.Name} purchased your house [c/{Color.MediumPurple.Hex3()}:{house}] for " + $"[c/{Color.OrangeRed.Hex3()}:{totalCostString}]."); }); player.AddResponse("no", args2 => { player.AwaitingResponse.Remove("yes"); player.SendInfoMessage("Canceled purchase."); }); } else if (subcommand.Equals("disallow", StringComparison.OrdinalIgnoreCase)) { if (parameters.Count != 2) { player.SendErrorMessage($"Syntax: {Commands.Specifier}house disallow <username>"); return; } var session = GetOrCreateSession(player); if (session.CurrentHouse == null) { player.SendErrorMessage("You aren't currently in a house."); return; } var house = session.CurrentHouse; if (player.User?.Name != house.OwnerName && !player.HasPermission("housing.house.admin")) { player.SendErrorMessage( $"You can't disallow users for {house.OwnerName}'s [c/{Color.MediumPurple.Hex3()}:{house}] house."); return; } var inputUsername = parameters[1]; house.AllowedUsernames.Remove(inputUsername); database.Update(house); player.SendSuccessMessage( $"Disallowed {inputUsername} from modifying " + $"{(house.OwnerName == player.User?.Name ? "your house" : house.OwnerName + "'s house")} " + $"[c/{Color.MediumPurple.Hex3()}:{house}]."); } else if (subcommand.Equals("info", StringComparison.OrdinalIgnoreCase)) { var session = GetOrCreateSession(player); if (session.CurrentHouse == null) { player.SendErrorMessage("You aren't currently in a house."); return; } var house = session.CurrentHouse; player.SendInfoMessage($"Owner: {house.OwnerName}, Name: {house.Name}"); if (player.User?.Name == house.OwnerName || player.HasPermission("housing.house.admin")) { var ownerConfig = house.GetGroupConfig(); //because a player other than the owner maybe running this command. player.SendInfoMessage($"Debt: [c/{Color.OrangeRed.Hex3()}:{house.Debt}]"); var isStore = database.GetShops().Any(s => house.Rectangle.Contains(s.Rectangle)); var taxRate = isStore ? ownerConfig.StoreTaxRate : ownerConfig.TaxRate; var taxCost = (decimal)Math.Round(house.Area * taxRate); player.SendInfoMessage( $"Tax cost: [c/{Color.OrangeRed.Hex3()}:{taxCost}], Last taxed: {house.LastTaxed}"); player.SendInfoMessage($"Allowed users: {string.Join(", ", house.AllowedUsernames)}"); } } else if (subcommand.Equals("remove", StringComparison.OrdinalIgnoreCase)) { var session = GetOrCreateSession(player); if (session.CurrentHouse == null) { player.SendErrorMessage("You aren't currently in a house."); return; } var house = session.CurrentHouse; if (player.User?.Name != house.OwnerName && !player.HasPermission("housing.house.admin")) { player.SendErrorMessage( $"You can't remove {house.OwnerName}'s [c/{Color.MediumPurple.Hex3()}:{house}] house."); return; } database.Remove(house); player.SendSuccessMessage( $"Removed {(house.OwnerName == player.User?.Name ? "your" : house.OwnerName + "'s")} " + $"[c/{Color.MediumPurple.Hex3()}:{house}] house."); } else if (subcommand.Equals("sell", StringComparison.OrdinalIgnoreCase)) { if (parameters.Count != 2) { player.SendErrorMessage($"Syntax: {Commands.Specifier}house sell <price>"); return; } var session = GetOrCreateSession(player); if (session.CurrentHouse == null) { player.SendErrorMessage("You aren't currently in a house."); return; } var house = session.CurrentHouse; if (player.User?.Name != house.OwnerName && !player.HasPermission("housing.house.admin")) { player.SendErrorMessage( $"You can't sell {house.OwnerName}'s [c/{Color.MediumPurple.Hex3()}:{house}] house."); return; } var inputPrice = parameters[1]; if (!BankingPlugin.Instance.Bank.CurrencyManager.TryFindCurrencyFromString(inputPrice, out var saleCurrency)) { player.SendErrorMessage($"Invalid price '{inputPrice}'. No currency supports this format."); return; } var parsed = saleCurrency.GetCurrencyConverter().TryParse(inputPrice, out var price); if (parsed && price > 0m) { house.ForSale = true; house.SalePrice = saleCurrency.GetCurrencyConverter().ToString(price); //we use the converter.ToString() so //so that the SalePrice uses the largest units available. database.Update(house); player.SendSuccessMessage( $"Selling {(house.OwnerName == player.User?.Name ? "your" : house.OwnerName + "'s")} " + $"[c/{Color.MediumPurple.Hex3()}:{house}] house for [c/{Color.OrangeRed.Hex3()}:{house.SalePrice}]."); } else { player.SendErrorMessage($"Invalid price '{inputPrice}'."); return; } } else if (subcommand.Equals("set", StringComparison.OrdinalIgnoreCase)) { if (parameters.Count != 2) { player.SendErrorMessage($"Syntax: {Commands.Specifier}house set <house-name>"); return; } if (player.TempPoints.Any(p => p == Point.Zero)) { player.SendErrorMessage("Not all points have been set."); return; } var point1 = player.TempPoints[0]; var point2 = player.TempPoints[1]; var inputHouseName = parameters[1]; var x = Math.Min(point1.X, point2.X); var y = Math.Min(point1.Y, point2.Y); var x2 = Math.Max(point1.X, point2.X); var y2 = Math.Max(point1.Y, point2.Y); if (database.GetHouses().Count(h => h.OwnerName == player.User?.Name) >= playerGroupConfig.MaxHouses) { player.SendErrorMessage($"You have too many houses. Maximum allowed is {playerGroupConfig.MaxHouses}."); return; } var area = (x2 - x + 1) * (y2 - y + 1); if (area < playerGroupConfig.MinHouseSize) { player.SendErrorMessage($"Your house is too small. Minimum area is {playerGroupConfig.MinHouseSize}."); return; } if (area > playerGroupConfig.MaxHouseSize) { player.SendErrorMessage($"Your house is too large. Maximum area is {playerGroupConfig.MaxHouseSize}."); return; } var rectangle = new Rectangle(x, y, x2 - x + 1, y2 - y + 1); if (database.GetHouses().Any(h => h.Rectangle.Intersects(rectangle))) { player.SendErrorMessage("Your house must not intersect any other houses."); return; } if (Config.Instance.RequireAdminRegions && TShock.Regions.ListAllRegions(Main.worldID.ToString()).All( r => !r.Name.StartsWith("__Plot") || !r.Area.Contains(rectangle))) { player.SendErrorMessage("Your house must lie entirely on a plot."); } player.TempPoints[0] = Point.Zero; player.TempPoints[1] = Point.Zero; var purchaseCost = (decimal)Math.Round(rectangle.Width * rectangle.Height * playerGroupConfig.PurchaseRate); if (purchaseCost > 0) { var taxCost = (decimal)Math.Round(rectangle.Width * rectangle.Height * playerGroupConfig.TaxRate); player.SendInfoMessage( $"Purchasing this house will require [c/{Color.OrangeRed.Hex3()}:{purchaseCost.ToMoneyString()}]."); player.SendInfoMessage( $"The tax for this house will be [c/{Color.OrangeRed.Hex3()}:{taxCost.ToMoneyString()}]."); player.SendInfoMessage("Do you wish to proceed? Use /yes or /no."); player.AddResponse("yes", args2 => { player.AwaitingResponse.Remove("no"); var account = BankingPlugin.Instance.GetBankAccount(player); if (account == null || account.Balance < purchaseCost) { player.SendErrorMessage("You do not have enough of a balance to purchase the house."); return; } account.TryTransferTo(BankingPlugin.Instance.GetWorldAccount(), purchaseCost); var house = database.AddHouse(player, inputHouseName, x, y, x2, y2); player.SendSuccessMessage($"Purchased house [c/{Color.MediumPurple.Hex3()}:{house}] for " + $"[c/{Color.OrangeRed.Hex3()}:{purchaseCost.ToMoneyString()}]."); }); player.AddResponse("no", args2 => { player.AwaitingResponse.Remove("yes"); player.SendInfoMessage("Canceled purchase."); }); } else { var house = database.AddHouse(player, inputHouseName, x, y, x2, y2); player.SendSuccessMessage($"Added house [c/{Color.MediumPurple.Hex3()}:{house}]."); } } else { player.SendErrorMessage($"Syntax: {Commands.Specifier}house 1/2"); player.SendErrorMessage($"Syntax: {Commands.Specifier}house allow <player-name>"); player.SendErrorMessage($"Syntax: {Commands.Specifier}house buy <house-name>"); player.SendErrorMessage($"Syntax: {Commands.Specifier}house disallow <username>"); player.SendErrorMessage($"Syntax: {Commands.Specifier}house info"); player.SendErrorMessage($"Syntax: {Commands.Specifier}house remove"); player.SendErrorMessage($"Syntax: {Commands.Specifier}house sell <price>"); player.SendErrorMessage($"Syntax: {Commands.Specifier}house set <house-name>"); } }