예제 #1
0
        public override bool ShouldExecute()
        {
            if (entity.World.Rand.NextDouble() < 0.005)
            {
                return(false);
            }
            if (cooldownUntilMs > entity.World.ElapsedMilliseconds)
            {
                return(false);
            }
            if (cooldownUntilTotalHours > entity.World.Calendar.TotalHours)
            {
                return(false);
            }
            if (whenInEmotionState != null && !entity.HasEmotionState(whenInEmotionState))
            {
                return(false);
            }
            if (whenNotInEmotionState != null && entity.HasEmotionState(whenNotInEmotionState))
            {
                return(false);
            }

            EntityBehaviorMultiply bh = entity.GetBehavior <EntityBehaviorMultiply>();

            if (bh != null && !bh.ShouldEat)
            {
                return(false);
            }

            IPointOfInterest nearestPoi = porregistry.GetNearestPoi(entity.ServerPos.XYZ, 32, (poi) =>
            {
                if (poi.Type != "food")
                {
                    return(false);
                }

                if ((target = poi as IAnimalFoodSource)?.IsSuitableFor(entity) == true)
                {
                    FailedAttempt attempt;
                    failedSeekTargets.TryGetValue(target, out attempt);
                    if (attempt == null || (attempt.Count < 4 || attempt.LastTryMs < world.ElapsedMilliseconds - 60000))
                    {
                        return(true);
                    }
                }

                return(false);
            });


            return(nearestPoi != null);
        }
예제 #2
0
        void init()
        {
            if (entity.World.Side == EnumAppSide.Client)
            {
                return;
            }

            EntityBehaviorTaskAI taskAi = entity.GetBehavior <EntityBehaviorTaskAI>();

            taskAi.taskManager.ShouldExecuteTask = (task) => !IsBeingMilked;

            bhmul = entity.GetBehavior <EntityBehaviorMultiply>();
            bhmul.TotalDaysLastBirth = Math.Max(bhmul.TotalDaysLastBirth, entity.World.Calendar.TotalDays);
        }
예제 #3
0
        public override void GetInfoText(StringBuilder infotext)
        {
            if (!entity.Alive)
            {
                return;
            }

            bhmul = entity.GetBehavior <EntityBehaviorMultiply>();
            // Can only be milked for 21 days after giving birth
            double lactatingDaysLeft = lactatingDaysAfterBirth - Math.Max(0, entity.World.Calendar.TotalDays - bhmul.TotalDaysLastBirth);

            if (bhmul != null && lactatingDaysLeft > 0)
            {
                if (entity.World.Calendar.TotalHours - lastMilkedTotalHours >= entity.World.Calendar.HoursPerDay)
                {
                    float stressLevel = entity.WatchedAttributes.GetFloat("stressLevel");
                    if (stressLevel > 0.1)
                    {
                        infotext.AppendLine(Lang.Get("Lactating for {0} days, currently too stressed to be milkable.", (int)lactatingDaysLeft));
                        return;
                    }

                    int generation = entity.WatchedAttributes.GetInt("generation", 0);
                    if (generation < 4f)
                    {
                        if (generation == 0)
                        {
                            infotext.AppendLine(Lang.Get("Lactating for {0} days, can be milked, but will become aggressive.", (int)lactatingDaysLeft));
                        }
                        else
                        {
                            infotext.AppendLine(Lang.Get("Lactating for {0} days, can be milked, but might become aggressive.", (int)lactatingDaysLeft));
                        }
                    }
                    else
                    {
                        infotext.AppendLine(Lang.Get("Lactating for {0} days, can be milked.", (int)lactatingDaysLeft));
                    }
                }
                else
                {
                    infotext.AppendLine(Lang.Get("Lactating for {0} days.", (int)lactatingDaysLeft));
                }
            }
        }
예제 #4
0
        void init()
        {
            lastMilkedTotalHours = entity.WatchedAttributes.GetFloat("lastMilkedTotalHours");
            if (entity.World.Side == EnumAppSide.Client)
            {
                return;
            }

            EntityBehaviorTaskAI taskAi = entity.GetBehavior <EntityBehaviorTaskAI>();

            taskAi.TaskManager.ShouldExecuteTask = (task) => !IsBeingMilked;

            // Make sure TotalDaysLastBirth is not a future date (e.g. when exported from an old world and imported into a new world)
            bhmul = entity.GetBehavior <EntityBehaviorMultiply>();
            bhmul.TotalDaysLastBirth = Math.Min(bhmul.TotalDaysLastBirth, entity.World.Calendar.TotalDays);

            lastMilkedTotalHours = Math.Min(lastMilkedTotalHours, entity.World.Calendar.TotalHours);
        }
예제 #5
0
        public bool TryBeginMilking()
        {
            lastIsMilkingStateTotalMs = entity.World.ElapsedMilliseconds;

            bhmul = entity.GetBehavior <EntityBehaviorMultiply>();
            // Can not be milked when stressed (= caused by aggressive or fleeing emotion states)
            float stressLevel = entity.WatchedAttributes.GetFloat("stressLevel");

            if (stressLevel > 0.1)
            {
                if (entity.World.Api is ICoreClientAPI capi)
                {
                    capi.TriggerIngameError(this, "notready", Lang.Get("Currently too stressed to be milkable"));
                }
                return(false);
            }

            // Can only be milked for 21 days after giving birth
            double daysSinceBirth = Math.Max(0, entity.World.Calendar.TotalDays - bhmul.TotalDaysLastBirth);

            if (bhmul != null && daysSinceBirth >= lactatingDaysAfterBirth)
            {
                return(false);
            }

            // Can only be milked once every day
            if (entity.World.Calendar.TotalHours - lastMilkedTotalHours < entity.World.Calendar.HoursPerDay)
            {
                return(false);
            }
            int generation = entity.WatchedAttributes.GetInt("generation", 0);

            aggroChance = Math.Min(1 - generation / 3f, 0.95f);
            aggroTested = false;
            clientCanContinueMilking = true;


            if (entity.World.Side == EnumAppSide.Server)
            {
                AiTaskManager tmgr = entity.GetBehavior <EntityBehaviorTaskAI>().TaskManager;
                tmgr.StopTask(typeof(AiTaskWander));
                tmgr.StopTask(typeof(AiTaskSeekEntity));
                tmgr.StopTask(typeof(AiTaskSeekFoodAndEat));
                tmgr.StopTask(typeof(AiTaskStayCloseToEntity));
            }
            else
            {
                if (entity.World is IClientWorldAccessor cworld)
                {
                    milkSound?.Dispose();
                    milkSound = cworld.LoadSound(new SoundParams()
                    {
                        DisposeOnFinish = true,
                        Location        = new AssetLocation("sounds/creature/sheep/milking.ogg"),
                        Position        = entity.Pos.XYZFloat,
                        SoundType       = EnumSoundType.Sound
                    });

                    milkSound.Start();
                }
            }

            return(true);
        }
예제 #6
0
        public override bool ShouldExecute()
        {
            if (entity.World.Rand.NextDouble() < 0.005)
            {
                return(false);
            }
            if (cooldownUntilMs > entity.World.ElapsedMilliseconds)
            {
                return(false);
            }
            if (cooldownUntilTotalHours > entity.World.Calendar.TotalHours)
            {
                return(false);
            }
            if (whenInEmotionState != null && !entity.HasEmotionState(whenInEmotionState))
            {
                return(false);
            }
            if (whenNotInEmotionState != null && entity.HasEmotionState(whenNotInEmotionState))
            {
                return(false);
            }

            EntityBehaviorMultiply bh = entity.GetBehavior <EntityBehaviorMultiply>();

            if (bh != null && !bh.ShouldEat && entity.World.Rand.NextDouble() < 0.996)
            {
                return(false);                                                                       // 0.4% chance go to the food source anyway just because (without eating anything).
            }
            ItemSlot leftSlot = entity.LeftHandItemSlot;

            if (leftSlot.Empty)
            {
                return(false);
            }

            isEdible = false;

            EnumFoodCategory?cat = leftSlot.Itemstack.Collectible?.NutritionProps?.FoodCategory;

            if (cat != null && eatItemCategories.Contains((EnumFoodCategory)cat))
            {
                isEdible = true;
                return(true);
            }

            AssetLocation code = leftSlot.Itemstack?.Collectible?.Code;

            if (code != null && eatItemCodes.Contains(code))
            {
                isEdible = true;
                return(true);
            }

            if (!leftSlot.Empty)
            {
                entity.World.SpawnItemEntity(leftSlot.TakeOutWhole(), entity.ServerPos.XYZ);
            }

            return(false);
        }
예제 #7
0
        public override bool ContinueExecute(float dt)
        {
            if (targetPoi.Occupied(entity))
            {
                onBadTarget();
                return(false);
            }

            Vec3d  pos      = targetPoi.Position;
            double distance = pos.HorizontalSquareDistanceTo(entity.ServerPos.X, entity.ServerPos.Z);

            pathTraverser.CurrentTarget.X = pos.X;
            pathTraverser.CurrentTarget.Y = pos.Y;
            pathTraverser.CurrentTarget.Z = pos.Z;

            //Cuboidd targetBox = entity.CollisionBox.ToDouble().Translate(entity.ServerPos.X, entity.ServerPos.Y, entity.ServerPos.Z);
            //double distance = targetBox.ShortestDistanceFrom(pos);

            float minDist = MinDistanceToTarget();

            if (distance <= minDist)
            {
                pathTraverser.Stop();
                if (animMeta != null)
                {
                    entity.AnimManager.StopAnimation(animMeta.Code);
                }

                EntityBehaviorMultiply bh = entity.GetBehavior <EntityBehaviorMultiply>();

                if (targetPoi.IsSuitableFor(entity) != true)
                {
                    onBadTarget();
                    return(false);
                }

                targetPoi.SetOccupier(entity);

                if (sitAnimMeta != null && !sitAnimStarted)
                {
                    entity.AnimManager.StartAnimation(sitAnimMeta);

                    sitAnimStarted = true;
                    sitEndDay      = entity.World.Calendar.TotalDays + sitDays;
                }

                sitTimeNow += dt;

                if (sitTimeNow >= layTime && !laid)
                {
                    laid = true;

                    // Potential gameplay/realism issue: the rooster has to be nearby at the exact moment the egg is laid, instead of looking to see whether there was a rooster / hen interaction ;) recently ...
                    // To mitigate this issue, we increase the rooster search range to 9 blocks in the JSON
                    if (targetPoi.TryAddEgg(entity, GetRequiredEntityNearby() == null ? null : chickCode, incubationDays))
                    {
                        ConsumeFood(PortionsEatenForLay);
                        attemptLayEggTotalHours = -1;
                        MakeLaySound();
                        failedSeekTargets.Remove(targetPoi);
                        return(false);
                    }
                }

                // Stop sitting - this allows a broody hen to go and eat for example
                if (entity.World.Calendar.TotalDays >= sitEndDay)
                {
                    failedSeekTargets.Remove(targetPoi);
                    return(false);
                }
            }
            else
            {
                if (!pathTraverser.Active)
                {
                    float rndx = (float)entity.World.Rand.NextDouble() * 0.3f - 0.15f;
                    float rndz = (float)entity.World.Rand.NextDouble() * 0.3f - 0.15f;
                    pathTraverser.NavigateTo(targetPoi.Position.AddCopy(rndx, 0, rndz), moveSpeed, MinDistanceToTarget() - 0.15f, OnGoalReached, OnStuck, false, 500);
                }
            }


            if (nowStuck)
            {
                return(false);
            }

            if (attemptLayEggTotalHours > 0 && entity.World.Calendar.TotalHours - attemptLayEggTotalHours > 12)
            {
                LayEggOnGround();
                return(false);
            }


            return(true);
        }
        public override bool ContinueExecute(float dt)
        {
            Vec3d pos = targetPoi.Position;

            pathTraverser.CurrentTarget.X = pos.X;
            pathTraverser.CurrentTarget.Y = pos.Y;
            pathTraverser.CurrentTarget.Z = pos.Z;

            Cuboidd targetBox = entity.CollisionBox.ToDouble().Translate(entity.ServerPos.X, entity.ServerPos.Y, entity.ServerPos.Z);
            double  distance  = targetBox.ShortestDistanceFrom(pos);

            float minDist = MinDistanceToTarget();

            if (distance <= minDist)
            {
                pathTraverser.Stop();

                EntityBehaviorMultiply bh = entity.GetBehavior <EntityBehaviorMultiply>();
                if (bh != null && !bh.ShouldEat)
                {
                    return(false);
                }

                if (targetPoi.IsSuitableFor(entity) != true)
                {
                    return(false);
                }

                if (eatAnimMeta != null && !eatAnimStarted)
                {
                    if (animMeta != null)
                    {
                        entity.AnimManager.StopAnimation(animMeta.Code);
                    }

                    entity.AnimManager.StartAnimation((targetPoi is LooseItemFoodSource && eatAnimMetaLooseItems != null) ? eatAnimMetaLooseItems : eatAnimMeta);

                    eatAnimStarted = true;
                }

                eatTimeNow += dt;

                if (targetPoi is LooseItemFoodSource foodSource)
                {
                    entity.World.SpawnCubeParticles(entity.ServerPos.XYZ, foodSource.ItemStack, 0.25f, 1, 0.25f + 0.5f * (float)entity.World.Rand.NextDouble());
                }


                if (eatTimeNow > eatTime * 0.75f && !soundPlayed)
                {
                    soundPlayed = true;
                    if (eatSound != null)
                    {
                        entity.World.PlaySoundAt(eatSound, entity, null, true, 16, 1);
                    }
                }


                if (eatTimeNow >= eatTime)
                {
                    ITreeAttribute tree = entity.WatchedAttributes.GetTreeAttribute("hunger");
                    if (tree == null)
                    {
                        entity.WatchedAttributes["hunger"] = tree = new TreeAttribute();
                    }

                    if (doConsumePortion)
                    {
                        float sat = targetPoi.ConsumeOnePortion();
                        quantityEaten += sat;
                        tree.SetFloat("saturation", sat + tree.GetFloat("saturation", 0));
                        entity.WatchedAttributes.SetDouble("lastMealEatenTotalHours", entity.World.Calendar.TotalHours);
                        entity.WatchedAttributes.MarkPathDirty("hunger");
                    }
                    else
                    {
                        quantityEaten = 1;
                    }

                    failedSeekTargets.Remove(targetPoi);

                    return(false);
                }
            }
            else
            {
                if (!pathTraverser.Active)
                {
                    float rndx = (float)entity.World.Rand.NextDouble() * 0.3f - 0.15f;
                    float rndz = (float)entity.World.Rand.NextDouble() * 0.3f - 0.15f;
                    if (!pathTraverser.NavigateTo(targetPoi.Position.AddCopy(rndx, 0, rndz), moveSpeed, MinDistanceToTarget() - 0.15f, OnGoalReached, OnStuck, false, 500))
                    {
                        return(false);
                    }
                }
            }


            if (nowStuck && entity.World.ElapsedMilliseconds > stuckatMs + eatTime * 1000)
            {
                return(false);
            }


            return(true);
        }
        public override bool ShouldExecute()
        {
            if (entity.World.Rand.NextDouble() < 0.005)
            {
                return(false);
            }
            // Don't search more often than every 15 seconds
            if (lastPOISearchTotalMs + 15000 > entity.World.ElapsedMilliseconds)
            {
                return(false);
            }
            if (cooldownUntilMs > entity.World.ElapsedMilliseconds)
            {
                return(false);
            }
            if (cooldownUntilTotalHours > entity.World.Calendar.TotalHours)
            {
                return(false);
            }
            if (whenInEmotionState != null && !entity.HasEmotionState(whenInEmotionState))
            {
                return(false);
            }
            if (whenNotInEmotionState != null && entity.HasEmotionState(whenNotInEmotionState))
            {
                return(false);
            }

            EntityBehaviorMultiply bh = entity.GetBehavior <EntityBehaviorMultiply>();

            if (bh != null && !bh.ShouldEat && entity.World.Rand.NextDouble() < 0.996)
            {
                return(false);                                                                       // 0.4% chance go to the food source anyway just because (without eating anything).
            }
            targetPoi            = null;
            extraTargetDist      = 0;
            lastPOISearchTotalMs = entity.World.ElapsedMilliseconds;

            entity.World.Api.ModLoader.GetModSystem <EntityPartitioning>().WalkEntities(entity.ServerPos.XYZ, 10, (e) =>
            {
                if (e is EntityItem)
                {
                    EntityItem ei        = (EntityItem)e;
                    EnumFoodCategory?cat = ei.Itemstack?.Collectible?.NutritionProps?.FoodCategory;
                    if (cat != null && eatItemCategories.Contains((EnumFoodCategory)cat))
                    {
                        targetPoi = new LooseItemFoodSource(ei);
                        return(false);
                    }

                    AssetLocation code = ei.Itemstack?.Collectible?.Code;
                    if (code != null && eatItemCodes.Contains(code))
                    {
                        targetPoi = new LooseItemFoodSource(ei);
                        return(false);
                    }
                }

                if (searchPlayerInv && e is EntityPlayer eplr)
                {
                    if (eplr.Player.InventoryManager.Find(slot => slot.Inventory is InventoryBasePlayer && !slot.Empty && eatItemCodes.Contains(slot.Itemstack.Collectible.Code)))
                    {
                        targetPoi = new PlayerPoi(eplr);
                    }
                }

                return(true);
            });

            if (targetPoi == null)
            {
                targetPoi = porregistry.GetNearestPoi(entity.ServerPos.XYZ, 48, (poi) =>
                {
                    if (poi.Type != "food")
                    {
                        return(false);
                    }
                    IAnimalFoodSource foodPoi;

                    if ((foodPoi = poi as IAnimalFoodSource)?.IsSuitableFor(entity) == true)
                    {
                        FailedAttempt attempt;
                        failedSeekTargets.TryGetValue(foodPoi, out attempt);
                        if (attempt == null || (attempt.Count < 4 || attempt.LastTryMs < world.ElapsedMilliseconds - 60000))
                        {
                            return(true);
                        }
                    }

                    return(false);
                }) as IAnimalFoodSource;
            }

            /*if (targetPoi != null)
             * {
             *  if (targetPoi is BlockEntity || targetPoi is Block)
             *  {
             *      Block block = entity.World.BlockAccessor.GetBlock(targetPoi.Position.AsBlockPos);
             *      Cuboidf[] collboxes = block.GetCollisionBoxes(entity.World.BlockAccessor, targetPoi.Position.AsBlockPos);
             *      if (collboxes != null && collboxes.Length != 0 && collboxes[0].Y2 > 0.3f)
             *      {
             *          extraTargetDist = 0.15f;
             *      }
             *  }
             * }*/

            return(targetPoi != null);
        }