예제 #1
0
        protected virtual void OnTick(float dt)
        {
            if (Api.Side == EnumAppSide.Client)
            {
                // We don't have to do this client side. The item stack renderer already updates those states for us
                return;
            }

            room = roomReg.GetRoomForPosition(Pos);
            if (room.AnyChunkUnloaded != 0)
            {
                return;
            }

            foreach (ItemSlot slot in Inventory)
            {
                if (slot.Itemstack == null)
                {
                    continue;
                }

                AssetLocation codeBefore = slot.Itemstack.Collectible.Code;
                slot.Itemstack.Collectible.UpdateAndGetTransitionStates(Api.World, slot);

                if (slot.Itemstack?.Collectible.Code != codeBefore)
                {
                    MarkDirty(true);
                }
            }
        }
예제 #2
0
        public float applyGreenhouseTempBonus(float temp)
        {
            if (Api.World.BlockAccessor.GetRainMapHeightAt(be.Pos) > be.Pos.Y) // Fast pre-check
            {
                Room room     = roomreg?.GetRoomForPosition(be.Pos);
                int  roomness = (room != null && room.SkylightCount > room.NonSkylightCount && room.ExitCount == 0) ? 1 : 0;
                if (roomness > 0)
                {
                    temp += 5;
                }
            }

            return(temp);
        }
예제 #3
0
        public override void Initialize(ICoreAPI api)
        {
            base.Initialize(api);

            Inventory.LateInitialize(InventoryClassName + "-" + Pos.X + "/" + Pos.Y + "/" + Pos.Z, api);
            Inventory.Pos = Pos;
            Inventory.ResolveBlocksOrItems();
            Inventory.OnAcquireTransitionSpeed = Inventory_OnAcquireTransitionSpeed;
            if (api.Side == EnumAppSide.Client)
            {
                Inventory.OnInventoryOpened += Inventory_OnInventoryOpenedClient;
            }

            RegisterGameTickListener(OnTick, 10000);

            roomReg = Api.ModLoader.GetModSystem <RoomRegistry>();
            room    = roomReg.GetRoomForPosition(Pos);
        }
예제 #4
0
        private void OnScanForEmptySkep(float dt)
        {
            Room room = roomreg?.GetRoomForPosition(Pos);

            roomness = (room != null && room.SkylightCount > room.NonSkylightCount && room.ExitCount == 0) ? 1 : 0;

            if (actvitiyLevel < 1)
            {
                return;
            }
            if (Api.Side == EnumAppSide.Client)
            {
                return;
            }
            if (Api.World.Calendar.TotalHours < cooldownUntilTotalHours)
            {
                return;
            }

            if (scanIteration == 0)
            {
                scanQuantityNearbyFlowers = 0;
                scanQuantityNearbyHives   = 0;
                scanEmptySkeps.Clear();
            }

            // Let's count/collect 3 things in a 20x20x20 cube
            // 1. All positions of empty skeps
            // 2. Amount of living beehives (skeps or wild)
            // 3. Amount of flowers

            // Default Spread speed: Once every 4 in game days * factor
            // Don't spread at all if 3 * livinghives + 3 > flowers

            // factor = Clamped(livinghives / Math.Sqrt(flowers - 3 * livinghives - 3), 1, 1000)
            // After spreading: 4 extra days cooldown

            int minX = -8 + 8 * (scanIteration / 2);
            int minZ = -8 + 8 * (scanIteration % 2);
            int size = 8;

            Block emptySkepN = Api.World.GetBlock(new AssetLocation("skep-empty-north"));
            Block emptySkepE = Api.World.GetBlock(new AssetLocation("skep-empty-east"));
            Block emptySkepS = Api.World.GetBlock(new AssetLocation("skep-empty-south"));
            Block emptySkepW = Api.World.GetBlock(new AssetLocation("skep-empty-west"));

            Block fullSkepN = Api.World.GetBlock(new AssetLocation("skep-populated-north"));
            Block fullSkepE = Api.World.GetBlock(new AssetLocation("skep-populated-east"));
            Block fullSkepS = Api.World.GetBlock(new AssetLocation("skep-populated-south"));
            Block fullSkepW = Api.World.GetBlock(new AssetLocation("skep-populated-west"));


            Block wildhive1 = Api.World.GetBlock(new AssetLocation("wildbeehive-medium"));
            Block wildhive2 = Api.World.GetBlock(new AssetLocation("wildbeehive-large"));


            Api.World.BlockAccessor.WalkBlocks(Pos.AddCopy(minX, -5, minZ), Pos.AddCopy(minX + size - 1, 5, minZ + size - 1), (block, pos) =>
            {
                if (block.Id == 0)
                {
                    return;
                }

                if (block.Attributes?.IsTrue("beeFeed") == true)
                {
                    scanQuantityNearbyFlowers++;
                }

                if (block == emptySkepN || block == emptySkepE || block == emptySkepS || block == emptySkepW)
                {
                    scanEmptySkeps.Add(pos.Copy());
                }
                if (block == fullSkepN || block == fullSkepE || block == fullSkepS || block == fullSkepW || block == wildhive1 || block == wildhive2)
                {
                    scanQuantityNearbyHives++;
                }
            });

            scanIteration++;

            if (scanIteration == 4)
            {
                scanIteration = 0;
                OnScanComplete();
            }
        }
예제 #5
0
        private void CheckGrow(float dt)
        {
            if (Block.Attributes == null)
            {
#if DEBUG
                Api.World.Logger.Notification("Ghost berry bush block entity at {0}. Block.Attributes is null, will remove game tick listener", Pos);
                foreach (long handlerId in TickHandlers)
                {
                    Api.Event.UnregisterGameTickListener(handlerId);
                }
#endif
                return;
            }

            // In case this block was imported from another older world. In that case lastCheckAtTotalDays would be a future date.
            lastCheckAtTotalDays = Math.Min(lastCheckAtTotalDays, Api.World.Calendar.TotalDays);


            // We don't need to check more than one year because it just begins to loop then
            double daysToCheck = GameMath.Mod(Api.World.Calendar.TotalDays - lastCheckAtTotalDays, Api.World.Calendar.DaysPerYear);

            bool changed = false;

            while (daysToCheck > 1f / Api.World.Calendar.HoursPerDay)
            {
                if (!changed)
                {
                    if (Api.World.BlockAccessor.GetRainMapHeightAt(Pos) > Pos.Y) // Fast pre-check
                    {
                        Room room = roomreg?.GetRoomForPosition(Pos);
                        roomness = (room != null && room.SkylightCount > room.NonSkylightCount && room.ExitCount == 0) ? 1 : 0;
                    }
                    else
                    {
                        roomness = 0;
                    }
                }

                changed = true;

                daysToCheck -= 1f / Api.World.Calendar.HoursPerDay;

                lastCheckAtTotalDays += 1f / Api.World.Calendar.HoursPerDay;
                transitionHoursLeft  -= 1f;

                ClimateCondition conds = Api.World.BlockAccessor.GetClimateAt(Pos, EnumGetClimateMode.ForSuppliedDateValues, lastCheckAtTotalDays);
                if (conds == null)
                {
                    return;
                }
                if (roomness > 0)
                {
                    conds.Temperature += 5;
                }

                bool reset  = conds.Temperature < Block.Attributes["resetBelowTemperature"].AsFloat(-999);
                bool stop   = conds.Temperature < Block.Attributes["stopBelowTemperature"].AsFloat(-999);
                bool revert = conds.Temperature < Block.Attributes["revertBlockBelowTemperature"].AsFloat(-999);

                if (stop || reset)
                {
                    transitionHoursLeft += 1f;

                    if (reset)
                    {
                        transitionHoursLeft = GetHoursForNextStage();
                        if (Block.Variant["state"] != "empty" && revert)
                        {
                            Block nextBlock = Api.World.GetBlock(Block.CodeWithVariant("state", "empty"));
                            Api.World.BlockAccessor.ExchangeBlock(nextBlock.BlockId, Pos);
                        }
                    }

                    continue;
                }

                if (transitionHoursLeft <= 0)
                {
                    if (!DoGrow())
                    {
                        return;
                    }
                    transitionHoursLeft = GetHoursForNextStage();
                }
            }

            if (changed)
            {
                MarkDirty(false);
            }
        }
예제 #6
0
        private void Update(float dt)
        {
            double hoursNextStage    = GetHoursForNextStage();
            bool   nearbyWaterTested = false;

            double nowTotalHours = Api.World.Calendar.TotalHours;
            double hourIntervall = 3 + rand.NextDouble();

            Block cropBlock  = GetCrop();
            bool  hasCrop    = cropBlock != null;
            bool  skyExposed = Api.World.BlockAccessor.GetRainMapHeightAt(Pos.X, Pos.Z) <= (hasCrop ? Pos.Y + 1 : Pos.Y);

            if ((nowTotalHours - totalHoursLastUpdate) < hourIntervall)
            {
                if (updateMoistureLevel(Api.World.Calendar.TotalDays, lastWaterDistance, skyExposed))
                {
                    UpdateFarmlandBlock();
                }
                return;
            }

            // Slow down growth on bad light levels
            int lightpenalty = 0;

            if (!allowundergroundfarming)
            {
                lightpenalty = Math.Max(0, Api.World.SeaLevel - Pos.Y);
            }

            int    sunlight = Api.World.BlockAccessor.GetLightLevel(upPos, EnumLightLevelType.MaxLight);
            double lightGrowthSpeedFactor = GameMath.Clamp(1 - (delayGrowthBelowSunLight - sunlight - lightpenalty) * lossPerLevel, 0, 1);

            Block upblock       = Api.World.BlockAccessor.GetBlock(upPos);
            Block deadCropBlock = Api.World.GetBlock(new AssetLocation("deadcrop"));


            double lightHoursPenalty = hoursNextStage / lightGrowthSpeedFactor - hoursNextStage;

            double totalHoursNextGrowthState = totalHoursForNextStage + lightHoursPenalty;

            EnumSoilNutrient?currentlyConsumedNutrient = null;

            if (upblock.CropProps != null)
            {
                currentlyConsumedNutrient = upblock.CropProps.RequiredNutrient;
            }

            // Let's increase fertility every 3-4 game hours

            bool growTallGrass = false;

            float[] npkRegain = new float[3];

            float waterDistance = 99;

            // Don't update more than a year
            totalHoursLastUpdate = Math.Max(totalHoursLastUpdate, nowTotalHours - Api.World.Calendar.DaysPerYear * Api.World.Calendar.HoursPerDay);

            bool hasRipeCrop = HasRipeCrop();

            if (!skyExposed) // Fast pre-check
            {
                Room room = roomreg?.GetRoomForPosition(upPos);
                roomness = (room != null && room.SkylightCount > room.NonSkylightCount && room.ExitCount == 0) ? 1 : 0;
            }
            else
            {
                roomness = 0;
            }

            ClimateCondition baseClimate = Api.World.BlockAccessor.GetClimateAt(Pos, EnumGetClimateMode.WorldGenValues);

            if (baseClimate == null)
            {
                return;
            }
            float baseTemperature = baseClimate.Temperature;

            // Fast forward in 3-4 hour intervalls
            while ((nowTotalHours - totalHoursLastUpdate) > hourIntervall)
            {
                if (!nearbyWaterTested)
                {
                    EnumWaterSearchResult res;
                    waterDistance = GetNearbyWaterDistance(out res);
                    if (res == EnumWaterSearchResult.Deferred)
                    {
                        return;                                        // Wait with updating until neighbouring chunks are loaded
                    }
                    if (res == EnumWaterSearchResult.NotFound)
                    {
                        waterDistance = 99;
                    }
                    nearbyWaterTested = true;

                    lastWaterDistance = waterDistance;
                }

                updateMoistureLevel(totalHoursLastUpdate / Api.World.Calendar.HoursPerDay, waterDistance, skyExposed, baseClimate);

                totalHoursLastUpdate += hourIntervall;
                hourIntervall         = 3 + rand.NextDouble();

                baseClimate.Temperature = baseTemperature;
                ClimateCondition conds = Api.World.BlockAccessor.GetClimateAt(Pos, baseClimate, EnumGetClimateMode.ForSuppliedDate_TemperatureOnly, totalHoursLastUpdate / Api.World.Calendar.HoursPerDay);

                if (roomness > 0)
                {
                    conds.Temperature += 5;
                }

                if (!hasCrop)
                {
                    ripeCropColdDamaged   = false;
                    unripeCropColdDamaged = false;
                    unripeHeatDamaged     = false;
                    for (int i = 0; i < damageAccum.Length; i++)
                    {
                        damageAccum[i] = 0;
                    }
                }
                else
                {
                    if (cropBlock?.CropProps != null && conds.Temperature < cropBlock.CropProps.ColdDamageBelow)
                    {
                        if (hasRipeCrop)
                        {
                            ripeCropColdDamaged = true;
                        }
                        else
                        {
                            unripeCropColdDamaged = true;
                            damageAccum[(int)EnumCropStressType.TooCold] += (float)hourIntervall;
                        }
                    }
                    else
                    {
                        damageAccum[(int)EnumCropStressType.TooCold] = Math.Max(0, damageAccum[(int)EnumCropStressType.TooCold] - (float)hourIntervall / 10);
                    }

                    if (cropBlock?.CropProps != null && conds.Temperature > cropBlock.CropProps.HeatDamageAbove && hasCrop)
                    {
                        unripeHeatDamaged = true;
                        damageAccum[(int)EnumCropStressType.TooHot] += (float)hourIntervall;
                    }
                    else
                    {
                        damageAccum[(int)EnumCropStressType.TooHot] = Math.Max(0, damageAccum[(int)EnumCropStressType.TooHot] - (float)hourIntervall / 10);
                    }

                    for (int i = 0; i < damageAccum.Length; i++)
                    {
                        float dmg = damageAccum[i];
                        if (!allowcropDeath)
                        {
                            dmg = damageAccum[i] = 0;
                        }

                        if (dmg > 48)
                        {
                            Api.World.BlockAccessor.SetBlock(deadCropBlock.Id, upPos);
                            var be = Api.World.BlockAccessor.GetBlockEntity(upPos) as BlockEntityDeadCrop;
                            be.Inventory[0].Itemstack = new ItemStack(cropBlock);
                            be.deathReason            = (EnumCropStressType)i;
                            hasCrop = false;
                            break;
                        }
                    }
                }

                // Stop growth and fertility recovery below zero degrees
                // 10% growth speed at 1°C
                // 20% growth speed at 2°C and so on
                float growthChance = GameMath.Clamp(conds.Temperature / 10f, 0, 10);
                if (rand.NextDouble() > growthChance)
                {
                    continue;
                }

                growTallGrass |= rand.NextDouble() < 0.006;

                bool ripe = HasRipeCrop();

                // Rule 1: Fertility increase up to original levels by 1 every 3-4 ingame hours
                // Rule 2: Fertility does not increase with a ripe crop on it
                npkRegain[0] = ripe ? 0 : 0.33f;
                npkRegain[1] = ripe ? 0 : 0.33f;
                npkRegain[2] = ripe ? 0 : 0.33f;

                // Rule 3: Fertility increase up 3 times slower for the currently growing crop
                if (currentlyConsumedNutrient != null)
                {
                    npkRegain[(int)currentlyConsumedNutrient] /= 3;
                }

                for (int i = 0; i < 3; i++)
                {
                    nutrients[i] += Math.Max(0, npkRegain[i] + Math.Min(0, originalFertility[i] - nutrients[i] - npkRegain[i]));

                    // Rule 4: Slow release fertilizer can fertilize up to 100 fertility
                    if (slowReleaseNutrients[i] > 0)
                    {
                        float release = Math.Min(0.33f, slowReleaseNutrients[i]);

                        nutrients[i]            = Math.Min(100, nutrients[i] + release);
                        slowReleaseNutrients[i] = Math.Max(0, slowReleaseNutrients[i] - release);
                    }
                    else
                    {
                        // Rule 5: Once the slow release fertilizer is consumed, the soil will slowly return to its original fertility

                        if (nutrients[i] > originalFertility[i])
                        {
                            nutrients[i] = Math.Max(originalFertility[i], nutrients[i] - 0.05f);
                        }
                    }
                }



                if (moistureLevel < 0.1)
                {
                    // Too dry to grow. Todo: Make it dependent on crop
                    continue;
                }

                if (totalHoursNextGrowthState <= totalHoursLastUpdate)
                {
                    TryGrowCrop(totalHoursForNextStage);
                    totalHoursForNextStage   += hoursNextStage;
                    totalHoursNextGrowthState = totalHoursForNextStage + lightHoursPenalty;

                    hoursNextStage = GetHoursForNextStage();
                }
            }



            if (growTallGrass && upblock.BlockMaterial == EnumBlockMaterial.Air)
            {
                double rnd = rand.NextDouble() * totalWeedChance;
                for (int i = 0; i < weedNames.Length; i++)
                {
                    rnd -= weedNames[i].Chance;
                    if (rnd <= 0)
                    {
                        Block weedsBlock = Api.World.GetBlock(weedNames[i].Code);
                        if (weedsBlock != null)
                        {
                            Api.World.BlockAccessor.SetBlock(weedsBlock.BlockId, upPos);
                        }
                        break;
                    }
                }
            }


            UpdateFarmlandBlock();
            Api.World.BlockAccessor.MarkBlockEntityDirty(Pos);
        }