CleverRaven / Cataclysm-DDA

Cataclysm - Dark Days Ahead. A turn-based survival game set in a post-apocalyptic world.
http://cataclysmdda.org
Other
10.63k stars 4.17k forks source link

Limiting animal husbandry resources without killing the processor #59643

Closed I-am-Erk closed 1 year ago

I-am-Erk commented 2 years ago

Is your feature request related to a problem? Please describe.

Currently, keeping a handful of cows in your base gives you infinite milk. Since you don't have to feed them more than once, and they produce milk every day, this resource is similar to the cockroach farms of olde, a completely immersion-breaking flood of raw milk to the point that I wish I could use it to fill my moat.

Solution you would like.

The problem we often face when considering ways to do animal husbandry is that tracking animal caloric intake is a huge burden on the processor. I suggest that this is an area we could maintain a degree of gamification, wherein we don't track calories but instead require a feeding activity from the player to trigger production of animal resources, particularly milk but potentially also things like wool, eggs, etc. if we can keep the actual player input down.

Essentially, I suggest that we do not require feeding for animals to survive, but we steal a page from minecraft sheep. If you want your animal to breed or to produce, you feed it. With cows, we may eventually also wish to track lactation and only have them produce milk if they have calved recently. However I think we can ignore that on a first pass.

The concept here is:

  1. Animals start un-fed.
  2. Friendly animals begin producing resources when they have the Fed status. Time to produce resources is the same as current.
  3. If the animal loses the Fed status, it stops producing resources
  4. The Fed status lasts 24 hours and is produced by interacting with the animal and using appropriate feed
  5. There is no penalty for leaving an animal un-fed. Partially completed resources just stay partially completed.

Future directions would include

Describe alternatives you have considered.

We could actually track animal food and calories for friendly animals only, and ignore it for neutral animals. That would limit the processor intensitivity. However I am not sure it actually adds a noticeable benefit to the simulation and I am pretty confident that doing it actively like this - ie. only tracking it as a boolean with a timer and only when you turn it on - would actually be tough to distinguish on the player end.

We could potentially have the Lactating effect have intensities and drop over time, reducing milk yields, leaving room for agricultural hormones to keep it up. Since we're dealing with very few variables here that sort of simulation becomes more easily possible. Similarly we could potentially have a couple different Fed statuses instead of just a boolean, to distinguish between Well-Fed and poorly-fed animals. Maybe if your cow is eating twice a day it produces 50% faster, or if the feed is higher quality.

Additional context

Basically we could abstract this as if the animal keeps itself alive by passive grazing but is just skin and bones unless it gets food.

Actually adding grazing is likely to be a huge processor hog, but in the distant future we could do something like a "pasture zone" and have a limit to how many grazing animals can be in a pasture zone based on the number of grass tiles, then count those grazing animals as always fed if they are under the limit.

NOTE: This system is only intended for player faction livestock. Animals that are not part of the player faction should have a wholly different system based on general area populations of food things, and again this would be passive, not measured on a per-animal basis but averaged out for an ecosystem. The livestock system wouldn't be appropriae for them, and their system would not be appropriate for livestock

chrispikula commented 2 years ago

I'd extend it to restrict farm-animal breeding unless they get fed as well.

Also, how is this going to work for animals that eat differing, incompatible foods? Cows vs, say, Pigs, or chickens?

Is there any reason it couldn't be done the same way the calorie storage was done for basecamps*? Just with more limited food deposit types? Don't even have any extra pathfinding involved at all, just a decrement on the food storage based on how many animals it is feeding.

*iirc, been a while since I basecamped.

AtomicFox556 commented 2 years ago

One crude but simple way to simulate passive grazing as mentioned would be to just check if there is a grass tile within the distance a few tiles from the animal; this would be done approximately hourly or even less often to reduce amount of processing power used.

Last time the animal was near grass in such a way or was actually fed by player is remembered; if on a check it happens to be too long ago, then the animal dies from hunger. I'm less sure about this, but this may be have low enough CPU burden to be tracked for non-friendly grazing animals as well.

While it'd still be necessary to prevent animals that aren't properly fed from producing things (otherwise you could sustain many animals off of a single tile of grass), this way it'd at least be undesirable to lock up animals indoors for eternity until/if you can find something to feed them with.

I-am-Erk commented 2 years ago

I'd extend it to restrict farm-animal breeding unless they get fed as well.

Mentioned in there.

Also, how is this going to work for animals that eat differing, incompatible foods? Cows vs, say, Pigs, or chickens?

I don't really understand. how would it not work? You just give them the appropriate feed.

Is there any reason it couldn't be done the same way the calorie storage was done for basecamps*? Just with more limited food deposit types? Don't even have any extra pathfinding involved at all, just a decrement on the food storage based on how many animals it is feeding.

As discussed in the thing, I think that adds significant complexity without actually improving outcome. Then we need to consider things like calorie expenditure, which animals are serviced by a particular stockpile, track a bunch of separate stockpiles, you can't easily move food from one trough to another, etc.

I-am-Erk commented 2 years ago

One crude but simple way to simulate passive grazing as mentioned would be to just check if there is a grass tile within the distance a few tiles from the animal; this would be done approximately hourly or even less often to reduce amount of processing power used.

Last time the animal was near grass in such a way or was actually fed by player is remembered; if on a check it happens to be too long ago, then the animal dies from hunger. I'm less sure about this, but this may be have low enough CPU burden to be tracked for non-friendly grazing animals as well.

While it'd still be necessary to prevent animals that aren't properly fed from producing things (otherwise you could sustain many animals off of a single tile of grass), this way it'd at least be undesirable to lock up animals indoors for eternity until/if you can find something to feed them with.

That is already a lot more checks and pathfinding per creature than is in here, and would also require adding a meta flag to grass tiles for a duration to mark them as having been used, to keep one grass tile from infinitely feeding infinite monsters. That's a huge amount more overhead than what I am describing

AtomicFox556 commented 2 years ago

That is already a lot more checks and pathfinding per creature than is in here, and would also require adding a meta flag to grass tiles for a duration to mark them as having been used, to keep one grass tile from infinitely feeding infinite monsters. That's a huge amount more overhead than what I am describing

Actually my whole point there was to avoid doing any of those things with significant overhead, even if it's not really a solution on its own. It's just exactly that: once in a while check if there's any grass nearby, just so that it's not reasonable to lock up grazing animals inside forever unless you actually feed them.

Yes, the animal wouldn't navigate on its own towards the grass, but grass is common enough outside that it shouldn't matter. I guess you could make animal do so only if it's about to die from hunger, which would hopefully be a rare enough case so as to not have a major performance impact.

Yes, if done like that, one tile of grass could potentially keep alive an indefinite amount of animals, however, they'd make no products if they receive no actual food. The mere presence of grass just keeps them alive if no actual food is available.

chrispikula commented 2 years ago

Also, how is this going to work for animals that eat differing, incompatible foods? Cows vs, say, Pigs, or chickens?

I don't really understand. how would it not work? You just give them the appropriate feed.

Is there any reason it couldn't be done the same way the calorie storage was done for basecamps*? Just with more limited food deposit types? Don't even have any extra pathfinding involved at all, just a decrement on the food storage based on how many animals it is feeding.

As discussed in the thing, I think that adds significant complexity without actually improving outcome. Then we need to consider things like calorie expenditure, which animals are serviced by a particular stockpile, track a bunch of separate stockpiles, you can't easily move food from one trough to another, etc.

So the way I was imaging it was that it'd not track activity or whatnot, just a pure -x calories/body size rate per animal. The trough would have a small but finite store, and you could check the feed on it, at which it would update. It would also self-update once per day, and if it hit zero in either case, no more production. If animals were killed or added to the field, then it would update, and re-calculate the new negative slope decline. Sure, you'd have to have a way to associate animals with troughs, especially if their are multiple per field, but a simple way would just be to sum it all up.

This would work out, over time, to just a slope function, which is pretty light, yeah?

As for the food types, I was thinking of it backwards, it should be easier/easy enough to make a food->animal food recipe or transformation of some sort. Disposing of less wanted food, corpses?

kevingranade commented 2 years ago

I think "feed to harvest" is too abbreviated for a final solution to this problem but I think it's also a simple enough thing to patch in that I don't mind going in in the who the heck knows how long interim until we get a more complete system.

The specific problem I see is that it's going to immediately devolve into Minecraft style animal husbandry where you leave your animals locked in a barren room and forget about them except when you want something out of them.

I'm actually less interested in the "try to path toward some nearby grass" solution because it's it's too easy for that to be considered a good enough solution even though it still devolves into sticking individual cattle in stalls with single patches of grass.

What I eventually want to happen here is a zone tracking feature that automatically populates only when certain types of terrain are created or destroyed.
In this specific case what that looks like is when you complete a perimeter you get a zone marked as impassable to cattle (not specifically cattle but cattle are going to have some kind of movement flags and we're going to mark it as being ineligible for cattle to leave the area), Then on top of that we could flood fill the thing and discover that it has grass/pasture eligible tiles inside of it and even count how many there are, and at some stage it gets a count of cattle in it. With that very infrequently invalidated cache of data in hand now if an animal needs to decide whether it's contained or not it can do a near instantaneous look up to see if it's in a zone marked contained if it's not it's considered free range and we bail out. If it is contained then it needs to decide whether it has enough food whether it has an afoot available and it can just query that zone data structure for above how much food of the appropriate kind is available and how many other animals are there competing for it and use that to decide whether it has "not enough food" and it's starving at a certain rate or if it's got "enough food" to maintain or if it has enough of "a surplus of food" to for example produce excess milk or be eligible for reproduction.

I-am-Erk commented 2 years ago

In terms of "don't leave animals locked in a barren room", we could potentially have a fairly simple check for 'is indoors' where if the animal is either indoors or unable to move from the tile it is in, then once per day we check when it was last fed and add a level of "underfed" for each day that has passed since the last meal, with a certain (fairly high) level killing the animal. That would require you to at least have a pen large enough that the animal can move around, and to feed your indoor pets every day, making it slightly harder to hide your cows underground to keep them safe from zombies. Plus it would mean you can't just leave them tied up all the time because they can't access food then. The optimal behaviour would probably be an outdoor space that's just a tiny bit larger than the cows then, but that's still less faulty than stuffing them in a room and forgetting them until you need something. Plus in this instance I'd say we make it so that feeding a starving animal removes one level of starvation. There's a small room for glitch here if the animal happens to be immobile or indoors right at the time the check runs, but we could say that if it fails the check we check again in 1 hour, or just not worry about it since it would require quite a few levels of underfed before any real consequences develop.

I'd also kind of want that to apply in winter regardless, because the real challenge of having animals should be keeping your herd alive through the winter months.

I would not implement this without also first implementing feeding troughs so that you can automate the process by dumping food in a nearby trough.

PatrikLundell commented 2 years ago

Apart from not killing the processor, you should also take care not to kill player incentive to deal with animal husbandry, as adding chores without compensating somewhat with benefits can easily result in it just not being worth dealing with.

Currently you have to tie animals down to keep them from following you and keep them from wandering away during the early phase. Once you have built or acquired enclosures you can keep them in there (but you have to climb the fence, as they'll follow you through the gates). A zone with an electronic fence function might work as a replacement/complement. Note that the current method of tying down animals does not permit them to move at all.

Daily feeding will get old very fast. While it's realistic, it's something you'd definitely would want to be able to hand over to companions.

Currently keeping animals is mostly flavor as you don't actually get much out of it:

If you increase the burden from collecting animals you should also increase the actual utility you can get out of them.

Zireael07 commented 2 years ago

I imagine daily feeding could be handled as a zone/companions thing. (And maybe a zone could be used to prevent "animals walk out through gate after you" problem, too?)

AtomicFox556 commented 2 years ago

In terms of "don't leave animals locked in a barren room", we could potentially have a fairly simple check for 'is indoors' where if the animal is either indoors or unable to move from the tile it is in

Then you'd have players bringing their animals to roofs, so that it both counts as "outdoors", is large enough for animals to move around, and is out of reach for zombies, despite the roof itself having nothing for animals to graze on. I think it is inevitable to just check for grass availability at some point if you want to correctly handle such scenarios.

What you said is probably a sensible approach for non-grazing animals such as cats or dogs, though; we could assume that they're able to hunt tiny animals to keep themselves alive under those conditions, so the presence of grass is indeed not necessary.

I-am-Erk commented 2 years ago

Currently you have to tie animals down to keep them from following you and keep them from wandering away during the early phase. Once you have built or acquired enclosures you can keep them in there (but you have to climb the fence, as they'll follow you through the gates). A zone with an electronic fence function might work as a replacement/complement. Note that the current method of tying down animals does not permit them to move at all.

I currently have ten cows in an enclosure and have since day 2 of my game without any real effort. I only had to tie them down when I needed to drive a car past them through the field. I disagree that tieing them down is the only may to make it work... There are many suitable preexisting enclosures a player can take over, and a player who can't make fences probably isn't at a game phase where they should have a large amount of livestock.

Daily feeding will get old very fast. While it's realistic, it's something you'd definitely would want to be able to hand over to companions.

That's a major part of the issue. Dump a bunch of feed in a trough and walk away. I agree it would be nice if your companions can help.

Currently keeping animals is mostly flavor as you don't actually get much out of it:

  • While you can milk animals regularly, it's a player only activity that takes a significant amount of time that's fixed regardless of yield (spend 20 minutes to get access to the cow's milk container so you can get its content) and the only uses for the milk is player consumption and dumping into the base calorie store

Those are both pretty major uses. A few cows can essentially provide you with infinite calories, especially for the base camp. Your phrasing makes it sound like this is minor, when capturing a dairy farm in the early game means you're now set for life.

(at least in 0.E-2 making cheese out of milk cost more calories than you got out of the resultant cheese, resulting in a net calorie loss for companions set on the task when the result was dumped into the store)

Separate issue worth reviewing mainly because cheesemaking shouldn't be highly active.

  • While eggs are produced in large quantities, collecting them is a player only activity (I guess you might be able to use the zone system, somehow) with no way to get companions to process the eggs into something useful. If feeding is required you can just collect eggs from the exploding hordes of chicken and turkeys in the areas around well traveled routes instead for less work.

Collection is a separate issue, although zone commands are how I do it. I commented above on wild animal spawns, that is a separate issue that also needs resolution.

  • Animal breeding happens very infrequently. I've managed to collect two piglets (100% of what I've ever seen), but I've never seen any cow, horse, sheep, goat, or even dog offspring (but hordes of useless cats).

If we had systems like this to refine it, we'd be able to have a great deal more function in domestic animal breeding... But we're not going to add something like that until animals aren't just infinite effortless food piñatas

If you increase the burden from collecting animals you should also increase the actual utility you can get out of them.

Everything you listed here is a separate issue, and shouldn't be rolled into this one, otherwise it becomes a daunting single feature request. Feel free to post suggestions and we can roll it into a project.

PatrikLundell commented 2 years ago

Tying down: That's what acquire is for. However, you may have to decide whether to camp in an existing facility of build a base camp, and if you chose the latter in a place some way from the enclosure it may take time before you've built the livestock expansion (which will essentially have to be scrapped/redone if these changes are introduced, but that's fine).

I don't think milk is calorie dense enough to provide you with enough calories, and since it's fluid you're restricted to what you can drink. I have to admit I haven't dumped milk into the calorie store, though, just noted that you can.

I agree all different parts shouldn't be a single PR, but PRs that change the balance significantly shouldn't be introduced in isolation unless a clear decision has been made to actually change the balance, rather than that being a side effect of a desired change. I'll write some feature requests in the coming days.

Sniper27IT commented 2 years ago

I'd start with the easiest things: tamed farm animals (i'd say cows, chickens and sheeps) get an internal "fed" flag like Erk said, allowing them to produce food or wool only if they are fed and that lasts for a day or a little less longer (36h). Pigs have no use for this as atm they are only walking meat. This is something that could be done easily and in a matter of days. Then we could focus on animal husbandry. For the sake of "realism-vs-gamification" i'd choose gamification and there are few models we could look at: Minecraft style: fed the animals, they can make sweet love. I don't know how practical is the "animal x gave birth, now it can produce milk" path as it would require lot of processing, not to mention the time issues: how many players go the long distance? This would require some shortened times also. Dwarf Fortress/Rimworld style: using the zone function, we could create pens and pasture zones where animal can keep themself fed and eventually breed. This also has many issues: grass should have a flag to regrow after some time, extra functions for the animals to look for food/pathfind to it/stay in the pasture zone, all things that may impact the processing power.

github-actions[bot] commented 1 year ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. Please do not bump or comment on this issue unless you are actively working on it. Stale issues, and stale issues that are closed are still considered.

ekaratzas commented 1 year ago

I took a crack at a dynamic pasture system, somewhat similar to the idea @kevingranade shared further up and hit some roadblocks. Sharing here in case anyone has any suggestions to make.

The main issue that I'm facing is partial loading of the animals that are enclosed in the same pasture due to the way that submaps spawn monsters as well how monster egg/milk production is currently only handled at monster::OnLoad().

Quick example to illustrate my point. Imagine a fenced in 15x15 pasture. Two cows are tied in to the western end. Two cows are tied to the eastern end. Four cows in total. As the player approaches the pasture from the far west, eventually the 2 western-most cows are going to get spawned. Even after all the creatures of this submap are spawned however, there is no way to check whether there's actually more cows on the eastern side of the pasture as they haven't spawned yet and thus they don't exist until the player moves closer to the pasture.

This causes a problem if we want to handle checks with regards to milk/egg production during monster::OnLoad() or even right after submap generate_monsters.

My original approach was to detect whether an animal is in a pasture + how many more animals are there + how many grass tiles are there during monster::OnLoad(). I used map::reachable_flood_steps(), get_creature_tracker.creature_at() and so on for all that, however I hit the issue with both of these APIs due to partial loading of the pasture. The idea seems to work for smaller pastures where everything gets loaded as the player approaches, but breaks apart with larger ones. Same issue would apply if we wanted to introduce troughs that wouldn't exist in the game world until loaded.

One alternative I'm debating is to move the 'can i make milk/eggs' logic for farm animals away from monster::Onload() and essentially defer it by sticking a check under monster movement so the pasture state is checked X turns after monster spawn instead of right away but that doesn't seem like a great approach. It might work most of the time but not foolproof or pretty.

Looking forward to hearing some feedback from project devs. Thanks

github-actions[bot] commented 1 year ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. Please do not bump or comment on this issue unless you are actively working on it. Stale issues, and stale issues that are closed are still considered.