using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Timers; using GTANetworkAPI; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Newtonsoft.Json; using ReallifeGamemode.Database.Entities; using ReallifeGamemode.Database.Models; using ReallifeGamemode.Server.Extensions; using ReallifeGamemode.Server.Inventory.Interfaces; using ReallifeGamemode.Server.Inventory.Items; using ReallifeGamemode.Server.Services; using ReallifeGamemode.Server.Types; using ReallifeGamemode.Server.Util; namespace ReallifeGamemode.Server.Managers { public class HanfManager : BaseScript { /// /// Punkte zum Samen kaufen /// private readonly static List _seedsBuyPoints = new List(); private readonly static List _cannabisSellPoints = new List(); /// /// Aktueller Samen-Preis /// public static int SEED_PRICE = 0; private const int SEED_PRICE_MIN = 40; private const int SEED_PRICE_MAX = 75; /// /// Aktueller Hanf-Verkaufspreis /// public static int CANNABIS_PRICE = 0; private const int CANNABIS_PRICE_MIN = 50; private const int CANNABIS_PRICE_MAX = 125; /// /// Wie viele Samen ein Spieler pro Tag maximal kaufen darf /// private const int MAX_SEEDS_PER_DAY = 50; /// /// Wie viel Cannabis man für einen Joint braucht /// private const int CANNABIS_NEEDED_FOR_JOINT = 10; /// /// Flag, ob der Verarbeiter aktuell benutzt wird /// private static bool _manufacturerCurrentlyUsed = false; /// /// Marihuana -> Joint /// private readonly static Vector3 _jointManufacturerPoint = new Vector3(-127.726105, 1921.5142, 197.31104); /// /// Animations-ID des Verarbeitens /// private const string _manufacturerAnim = "manufacturJoint"; /// /// Data-Key, ob der Spieler grade eine Pflanze anpflanzt /// private const string PLAYER_CURRENTLY_PLANTING_DATA_KEY = "isPlantingCannabis"; /// /// Ab welcher Zeit Pflanzen verwelken /// private static readonly TimeSpan MAX_PLANT_TIME = TimeSpan.FromHours(6); private static readonly TimeSpan MIN_PLANT_TIME_TO_HARVEST = TimeSpan.FromHours(4); /// /// Timer der den Status des Verarbeiters zurücksetzt /// private static Timer _manufacturerDoneTimer = new Timer(TimeSpan.FromSeconds(10).TotalMilliseconds); /// /// Zufallsgenerator für weibliche Pflanze und erhaltenes Cannabis /// private static Random _random = new Random(); private static List _currentCannabisData = new List(); public static readonly Vector3 ASSERVATENKAMMER_POSITION = new Vector3(-5.45, -670.03, 32.33); static HanfManager() { _manufacturerDoneTimer.Elapsed += ManufacturerDoneTimerCallback; } /// /// Ladefunktion zum /// public static void Load() { var priceRandom = new Random(); SEED_PRICE = priceRandom.Next(SEED_PRICE_MIN, SEED_PRICE_MAX + 1); CANNABIS_PRICE = priceRandom.Next(CANNABIS_PRICE_MIN, CANNABIS_PRICE_MAX + 1); logger.LogInformation("Generated hanf prices: seed = {0}, cannabis = {1}", SEED_PRICE, CANNABIS_PRICE); _seedsBuyPoints.Add(new Vector3(-30.21876, -585.3222, 17.917326)); _seedsBuyPoints.Add(new Vector3(-680.89386, -634.6783, 25.29923)); _seedsBuyPoints.Add(new Vector3(-1310.743, -608.9064, 29.382874)); _seedsBuyPoints.Add(new Vector3(-758.6156, -1595.2686, 6.302392)); _seedsBuyPoints.Add(new Vector3(-441.0504, 1595.4435, 358.4195)); ColShape colShape = null; foreach (Vector3 buyPoint in _seedsBuyPoints) { colShape = NAPI.ColShape.CreateSphereColShape(buyPoint, 10.0f); colShape.OnEntityEnterColShape += OnSeedBuyRangeColShapeEnter; } _cannabisSellPoints.Add(new Vector3(2220.04, 5614.24, 54.72)); _cannabisSellPoints.Add(new Vector3(201.77, 2442.06, 60.45)); _cannabisSellPoints.Add(new Vector3(155.87, -3103.26, 7.03)); foreach (Vector3 sellPoint in _cannabisSellPoints) { colShape = NAPI.ColShape.CreateSphereColShape(sellPoint, 10.0f); colShape.OnEntityEnterColShape += OnCannabisSellRangeColShapeEnter; } NAPI.Marker.CreateMarker(GTANetworkAPI.MarkerType.VerticalCylinder, ASSERVATENKAMMER_POSITION.Subtract(new Vector3(0, 0, 3.0)), new Vector3(), new Vector3(), 3.0f, Colors.White); NAPI.TextLabel.CreateTextLabel("Asservatenkammer~n~Drücke ~y~E~s~, um den ~y~Riot~s~ zu entladen", ASSERVATENKAMMER_POSITION, 10.0f, 10.0f, 0, Colors.White); using var dbContext = new DatabaseContext(); HarvestRottenPlants(dbContext); UpdateHanfWorldData(dbContext); Timer updateHanfDataTimer = new Timer(TimeSpan.FromMinutes(1).TotalMilliseconds); updateHanfDataTimer.Elapsed += UpdateHanfDataTimer_Elapsed; updateHanfDataTimer.Start(); } private static void OnCannabisSellRangeColShapeEnter(ColShape colShape, Player player) { if (!player.IsLoggedIn()) { return; } var user = player.GetUser(); if (user?.Faction?.StateOwned == true || user.Faction?.GangOwned == false) { return; } ChatService.SendMessage(player, $"Fremder sagt: Pssst.. Hier kannst du dein Brokkoli loswerden."); } private static void HarvestRottenPlants(DatabaseContext dbContext) { var rottenPlants = dbContext.CannabisPlants.Where(p => EF.Functions.DateDiffHour(p.PlantDate, DateTime.Now) > MAX_PLANT_TIME.TotalHours); foreach (var plant in rottenPlants) { plant.Harvested = true; } dbContext.SaveChanges(); } private static void UpdateHanfDataTimer_Elapsed(object sender, ElapsedEventArgs e) { NAPI.Task.Run(() => { using var dbContext = new DatabaseContext(); UpdateHanfWorldData(dbContext); }); } private static void OnSeedBuyRangeColShapeEnter(ColShape colShape, Player player) { if (!player.IsLoggedIn()) { return; } var user = player.GetUser(); if (user?.Faction?.StateOwned == true || user.Faction?.GangOwned == false) { return; } ChatService.SendMessage(player, $"Fremder sagt: Pssst.. Willst du Samen kaufen?"); } internal static bool IsPlayerNearSeedBuyPoint(Player player) { return _seedsBuyPoints.Any(p => player.Position.DistanceTo(p) <= 2.5f); } internal static bool IsPlayerNearJointManufacturer(Player player) { return _jointManufacturerPoint.DistanceTo(player.Position) <= 2.5f; } internal static void StartCannabisPlanting(Player player) { player.ToggleInventory(InventoryToggleOption.HIDE); if (!player.IsAlive()) { return; } var dbContext = new DatabaseContext(); var user = player.GetUser(dbContext); if (user.Faction?.StateOwned ?? false || user.Faction?.GangOwned == false) { player.SendNotification("~r~Du darfst keine Hanfsamen einpflanzen"); return; } if (player.HasData(PLAYER_CURRENTLY_PLANTING_DATA_KEY) && player.GetData(PLAYER_CURRENTLY_PLANTING_DATA_KEY)) { player.SendNotification("~r~Du pflanzt aktuell schon einen Hanfsamen ein"); return; } player.SetData(PLAYER_CURRENTLY_PLANTING_DATA_KEY, true); player.TriggerEvent("SERVER:Hanf_StartPlanting"); } [RemoteEvent("CLIENT:Hanf_PlantHanf")] public void HanfManagerPlantHanf(Player player, float x, float y, float z) { player.ResetData(PLAYER_CURRENTLY_PLANTING_DATA_KEY); IItem cannabisSeedItem = InventoryManager.GetItem(); UserItem userCannabisSeedsItem = InventoryManager.UserHasThisItem(player, cannabisSeedItem.Id); if (userCannabisSeedsItem == null) { player.SendNotification("~r~Du hast keine Samen mehr"); return; } logger.LogInformation("Player {0} planted a cannabis plant at x: {1}, y: {2}, z: {3}", player.Name, x, y, z); using var dbContext = new DatabaseContext(); var user = player.GetUser(dbContext); InventoryManager.RemoveUserItem(user, userCannabisSeedsItem, 1); CannabisPlant newPlant = new CannabisPlant() { X = x, Y = y, Z = z, PlantedBy = user, PlantDate = DateTime.Now, }; dbContext.CannabisPlants.Add(newPlant); dbContext.SaveChanges(); UpdateHanfWorldData(dbContext); } public static void UpdateHanfWorldData(DatabaseContext dbContext) { var activePlants = dbContext.CannabisPlants.Where(p => !p.Harvested).Select(p => new CannabisData() { Id = p.Id, Time = p.PlantDate, X = p.X, Y = p.Y, Z = p.Z }).ToList(); _currentCannabisData = activePlants; NAPI.Pools.GetAllPlayers().ForEach(p => { UpdateHanfForPlayer(p, activePlants); }); } public static void UpdateHanfForPlayer(Player player, List cannabisData = null) { cannabisData ??= _currentCannabisData; string jsonStr = JsonConvert.SerializeObject(cannabisData); List parts = GetChunks(jsonStr, 500); player.TriggerEvent("SERVER:Hanf_UpdateHanf", parts); } public static List GetChunks(string value, int chunkSize) { List triplets = new List(); while (value.Length > chunkSize) { triplets.Add(value.Substring(0, chunkSize)); value = value.Substring(chunkSize); } if (value != "") { triplets.Add(value); } return triplets; } [RemoteEvent("CLIENT:Hanf_BuySeeds")] public void HanfManagerBuySeeds(Player player, int amount) { if (!player.IsLoggedIn()) { return; } using var dbContext = new DatabaseContext(); var user = player.GetUser(dbContext); if (amount > GetAmountOfCannabisSeedsPlayerCanBuyToday(user)) { player.SendNotification("~r~Du kannst heute nicht mehr so viele Samen kaufen"); return; } IItem seedItem = InventoryManager.GetItem(); var newAmount = seedItem.Gewicht * amount; if (!InventoryManager.CanPlayerHoldMoreWeight(player, newAmount)) { player.TriggerEvent("~r~So viele Samen passen nicht mehr in deine Hosentasche"); return; } var price = amount * SEED_PRICE; if (user.Handmoney < price) { player.SendNotification("~r~Du hast nicht genug Geld dafür"); return; } logger.LogInformation("Player {0} bought {1} cannabis seeds for {2} dollars (price per seed: {3})", player.Name, amount, price, SEED_PRICE); // <-- WICHTIG LOGS if (user.LastTimeBoughtCannabisSeeds == null || user.LastTimeBoughtCannabisSeeds.Value.Date != DateTime.Now.Date) { user.CannabisSeedsBoughtToday = amount; } else { user.CannabisSeedsBoughtToday += amount; } user.LastTimeBoughtCannabisSeeds = DateTime.Now; user.Handmoney -= price; dbContext.SaveChanges(); InventoryManager.AddItemToInventory(player, seedItem.Id, amount); player.SendNotification($"Du hast {amount} Hanfsamen gekauft"); } [RemoteEvent("CLIENT:Hanf_HarvestHanf")] public void HanfManagerHarvestHanf(Player player, long hanfId) { if (!player.IsLoggedIn()) { return; } using var dbContext = new DatabaseContext(); User user = player.GetUser(dbContext); CannabisPlant plant = dbContext.CannabisPlants.Include(p => p.PlantedBy).Where(p => p.Id == hanfId).FirstOrDefault(); if (plant == null) { logger.LogError("Player {0} tried to harvest cannabis plant {1} but it was not found in database", player.Name, hanfId); return; } if (plant.Harvested) { return; } if (player.IsAdminDuty() && player.IsTSupport() && user.IsAdmin(AdminLevel.ADMIN)) { player.SendNotification($"Du hast die Hanf-Pflanze von ~y~{plant.PlantedBy.Name}~s~ entfernt"); plant.Harvested = true; dbContext.SaveChanges(); UpdateHanfWorldData(dbContext); return; } if (user.Faction?.StateOwned == true) { if (!((user.FactionId == 1 || user.FactionId == 3) && player.IsDuty())) { player.SendNotification("~r~Du kannst kein Hanf ernten"); return; } } //player.SyncAnimation(new[] { "harvestPlantEnter", "harvestPlant", "harvestPlantExit" }.ToList()); //player.SyncAnimation("harvestPlant"); //player.AddAttachment("shovel", false); if (user.FactionId != 3 && user.FactionId != 1) // Zivi / Gangmember erntet ab { player.SyncAnimation("harvestPlant"); plant.Harvested = true; bool isPlantRotten = DateTime.Now - plant.PlantDate > MAX_PLANT_TIME; if (isPlantRotten) { player.SendNotification("~r~Die Pflanze ist leider verrottet"); } else { bool isPlantReadyToHarvest = DateTime.Now - plant.PlantDate > MIN_PLANT_TIME_TO_HARVEST; if (!isPlantReadyToHarvest) // Wenn die Pflanze noch nicht ausgewachsen ist { bool getSeedBack = _random.Next(0, 2) == 1; // 50% Chance if (getSeedBack) { IItem cannabisSeedItem = InventoryManager.GetItem(); InventoryManager.AddItemToInventory(player, cannabisSeedItem.Id, 1); player.SendNotification("~g~Du konntest den Samen wieder ausgraben"); } else { player.SendNotification("~r~Du konntest den Samen leider nicht wieder ausgraben"); } } else { bool isFemalePlant = _random.Next(0, 10) + 1 <= 8; // 80% Chance dass es eine weibliche Pflanze ist if (isFemalePlant) { int cannabisAmount = _random.Next(4, 10) + 1; // zwischen 5 und 10g Cannabis IItem cannabisItem = InventoryManager.GetItem(); var newWeight = cannabisAmount * cannabisItem.Gewicht; if (!InventoryManager.CanPlayerHoldMoreWeight(player, newWeight)) { int restWeightPlayerCanHold = InventoryManager.MAX_USER_INVENTORY - InventoryManager.GetUserInventoryWeight(player); cannabisAmount = restWeightPlayerCanHold / cannabisItem.Gewicht; player.SendNotification("~o~Warnung:~s~ Du hast nicht das komplette Cannabis der Pflanze erhalten, da dein Inventar voll ist"); } player.SendNotification("~g~" + cannabisAmount + "g Cannabis geerntet"); InventoryManager.AddItemToInventory(player, cannabisItem.Id, cannabisAmount); } else { player.SendNotification("~r~Du hast die falschen Samen eingesät und keinen Ertrag aus dieser Pflanze erhalten"); } } } } else // FIB / LSPD erntet ab { if (!(player.HasData("IsCarryingPlant") || player.GetData("IsCarryingPlant"))) { player.SetData("IsCarryingPlant", true); player.SendNotification($"Du hast eine Pflanze von ~y~{plant.PlantedBy.Name}~s~ ausgegraben"); player.SyncAnimation("harvestPlant"); plant.Harvested = true; var modelToGet = GetModelFromPlant(plant); player.SetData("HoldingCannabisPlant", modelToGet); player.AddAttachment("CannabisPlantInHand" + modelToGet, false); } } dbContext.SaveChanges(); UpdateHanfWorldData(dbContext); } private int GetModelFromPlant(CannabisPlant data) { var cannabisTime = (DateTime.Now - data.PlantDate).TotalMinutes; if (cannabisTime <= 30) { return 1; } else if (cannabisTime <= 60 * 4) { return 2; } else { return 3; } } internal static bool IsPlayerNearCannabisSellPoint(Player player) { return _cannabisSellPoints.Any(p => p.DistanceTo(player.Position) <= 3); } internal static void BuildJointsFromCannabis(Player player) { if (player.HasAnimation(_manufacturerAnim) || _manufacturerCurrentlyUsed) { return; } using var dbContext = new DatabaseContext(); var user = player.GetUser(dbContext); var userItems = InventoryManager.GetUserItems(player, dbContext); IItem cannabisItem = InventoryManager.GetItem(); IItem jointItem = InventoryManager.GetItem(); UserItem userCannabisItem = userItems.Where(i => i.ItemId == cannabisItem.Id).FirstOrDefault(); if (userCannabisItem == null || userCannabisItem.Amount < CANNABIS_NEEDED_FOR_JOINT) { player.SendNotification($"~r~Du hast nicht genug Cannabis dabei~n~Zur Herstellung eines Joints werden {CANNABIS_NEEDED_FOR_JOINT} Hanfblüten benötigt"); return; } _manufacturerCurrentlyUsed = true; _manufacturerDoneTimer.Start(); player.SyncAnimation(_manufacturerAnim); InventoryManager.RemoveUserItem(user, userCannabisItem, CANNABIS_NEEDED_FOR_JOINT); InventoryManager.AddItemToInventory(player, jointItem.Id, 1); player.SendNotification($"Du hast {CANNABIS_NEEDED_FOR_JOINT} Hanfblüten in einen Joint verarbeitet"); NAPI.ClientEvent.TriggerClientEventInRange(player.Position, 100.0f, "SERVER:Hanf_PlayManufacturerAnim", _manufacturerAnim); } private static void ManufacturerDoneTimerCallback(object sender, EventArgs args) { _manufacturerDoneTimer.Stop(); _manufacturerCurrentlyUsed = false; } [RemoteEvent("CLIENT:Hanf_FinishDiggingAnimation")] public void HanfManagerFinishDiggingAnimation(Player player) { player.AddAttachment("shovel", true); } private static int GetAmountOfCannabisSeedsPlayerCanBuyToday(User user) { if (user.LastTimeBoughtCannabisSeeds == null) { return MAX_SEEDS_PER_DAY; } if (user.LastTimeBoughtCannabisSeeds.Value.Date != DateTime.Now.Date) { return MAX_SEEDS_PER_DAY; } return MAX_SEEDS_PER_DAY - user.CannabisSeedsBoughtToday; } internal static void ShowPlayerBuySeedMenu(Player player) { var user = player.GetUser(); if (user.Faction?.StateOwned == true) { return; } var seedsUserCanBuy = GetAmountOfCannabisSeedsPlayerCanBuyToday(user); if (seedsUserCanBuy == 0) { player.SendNotification("~r~Du kannst heute keine Samen mehr kaufen"); return; } player.TriggerEvent("SERVER:Hanf_BuySeed", seedsUserCanBuy, SEED_PRICE); } internal static void PlayerSellCannabis(Player player) { if (!player.IsLoggedIn()) { return; } using var dbContext = new DatabaseContext(); var user = player.GetUser(dbContext); if (user.Faction?.StateOwned == true) { return; } List items = InventoryManager.GetUserItems(player, dbContext); IItem cannabisItem = InventoryManager.GetItem(); var cannabisAmount = items.Where(i => i.ItemId == cannabisItem.Id).FirstOrDefault()?.Amount ?? 0; if (cannabisAmount == 0) { player.SendNotification("~r~Du hast kein Cannabis dabei"); return; } player.TriggerEvent("SERVER:Hanf_SellCannabisMenu", cannabisAmount, CANNABIS_PRICE); } [RemoteEvent("CLIENT:Hanf_SellCannabis")] public void HanfManagerSellCannabis(Player player, int amount) { if (!player.IsLoggedIn()) { return; } using var dbContext = new DatabaseContext(); var user = player.GetUser(dbContext); List items = InventoryManager.GetUserItems(player, dbContext); IItem cannabisItem = InventoryManager.GetItem(); var cannabisUserItem = items.Where(i => i.ItemId == cannabisItem.Id).FirstOrDefault(); var cannabisAmount = cannabisUserItem?.Amount ?? 0; if (cannabisAmount < amount) { player.SendNotification("~r~Du hast nicht so viel Cannabis dabei"); return; } var price = amount * CANNABIS_PRICE; user.Handmoney += price; InventoryManager.RemoveUserItem(user, cannabisUserItem, amount); logger.LogInformation("Player {0} sold {1} cannabis to the server for {2} dollars", player.Name, amount, price); player.SendNotification($"Du hast ~g~{amount} Hanfblüten~s~ für ~g~{price.ToMoneyString()}~s~ verkauft"); } } }