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); }
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); }
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)); } } }
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); }
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); }
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); }
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); }