-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Monster item aware behaviour #38303
Monster item aware behaviour #38303
Conversation
Merge with upstream
Merge with upstream
Merging with head repository.
Documentation will be added in a follow up pr? |
Forgive my ignorance, what kind of documentation would you be expecting? Happy to add this. |
Please update JSON_FLAGS.md |
Inventory clears on monster death and updated message to player.
…bolus-Engi/Cataclysm-DDA into monster-item-aware-behaviour
Fixes linting error. Co-Authored-By: snipercup <50166150+snipercup@users.noreply.github.com>
just did a quick glance, looks like you'll need to astyle your code. |
Ran through astyle. Removed WIP comment.
src/monmove.cpp
Outdated
if( charges >= 1 ) { | ||
//Adjust volume and weight for units in a stack | ||
units::mass weigh_each = weight / charges; | ||
amount_taken = charges + ( weigh_each / capacity ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please make sure capacity
doesn't equal to zero, otherwise we'll get a division by zero here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure about this calculation? / capacity
means the stronger the monster is (= higher weight capacity), so less charges it will pick up.
Also: why are you adding this value to the existing charges of the item. The result can only be larger than the number of existing charges, so the code will try to pick up more than there is.
amount_taken = std::min( target->charges, capacity / weight_each );
The std::min
is so it selects the smaller value of the amount of existing charges or the amount of charges that can be picked.
src/monmove.cpp
Outdated
|
||
//Does monster steal shiny items? | ||
if( steals_shiny && | ||
( itm.made_of( material_id( "gold" ) ) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably better to JSONize the list of stealable materials, with monster's entry having an array of materials that it'd consider stealing (if any), listing both food and non-food materials. This would also be useful, for instance, to make carnivore food stealers steal only meat-based foods they'd actually want to eat; monsters that actually eat items they stole (if they're edible at all) would have a flag noting this, so you don't have to make two mostly-separate checks for food stealing and "shinies" stealing.
src/monmove.cpp
Outdated
add_effect( effect_looting, 50_turns ); | ||
} | ||
} //Monster has something in inventory, will steal and with some luck... | ||
else if( will_steal && !inv.empty() && one_in( 500 ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably should consider that most monsters that could be interested in food (wolves?) would just start eating food right after moving close to it, rather than picking it up and eating some time later.
Also I'm not sure how food stealing and eating would work with food in sealed tin cans (or other sealed containers); an animal very likely wouldn't be interested at all in such an item (it doesn't smell like food) and certainly shouldn't be able to eat the food inside unless it can chew through the container.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was going to add the option for animals to eat the item rather than pick it up, but felt may be out of scope. I’ll add this and the checking of containers
awesome, thanks! Not all of that functionality needs to be in your initial PR though. It can always be extended later. |
Initial batch of suggestions added, will continue to apply suggestions. Co-Authored-By: Anton Burmistrov <Night_Pryanik@mail.ru> Co-Authored-By: Curtis Merrill <curtis.r.merrill@gmail.com> Co-Authored-By: BevapDin <tho_ki@gmx.de>
Applied suggestions and made some changes. Mostly stable in its post-flexibility stage.
Co-Authored-By: Jianxiang Wang (王健翔) <qrox@sina.com>
I've broken the flags out into JSON using the below format:
What else should we be looking for to ensure the system is as flexible as possible for items? |
Being able to specific an itemgroup would be great too |
"steals": { "categories":["FOOD"], "materials":["wheat"], "comestible_type":["FOOD"], "item_group":["fridgesnacks"] }, |
Adds Json object for monsters to define items that are looted by categories, materials, comestible type and item groups. Removed MF_STEALS_FOOD and MF_STEALS_SHINY flags. Added MF_EATS_FOOD flag, monsters will eat food items instead of picking up. require_all bool for monsters in 'loots' object when true will cause monster to only pickup items that meet all the requirements of the tags defined in categories, materials and comestibles (materials probably only makes sense)
src/monmove.cpp
Outdated
|
||
//TODO: Definitely find a better way to do the below, too much repeatability | ||
|
||
if( !lootable_categories.empty() ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check is unnecessary. Iterating over an empty container is just fine.
src/monmove.cpp
Outdated
|
||
if( !lootable_categories.empty() ) | ||
for( std::string tag : lootable_categories ) { | ||
if( itm.get_category().name() == tag ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
name()
returns a translated string, so it's different when the player uses a different language. The check won't work anymore.
src/monmove.cpp
Outdated
|
||
if( !lootable_materials.empty() ) | ||
for( std::string tag : lootable_materials ) { | ||
if( itm.made_of( material_id( tag ) ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of casting it here, make lootable_materials
contain instances of material_id
instead of strings.
src/monster.cpp
Outdated
lootable_categories = type->lootable_categories; | ||
lootable_materials = type->lootable_materials; | ||
lootable_comestibles = type->lootable_comestibles; | ||
lootable_itemgroups = type->lootable_itemgroups; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this data copied into the monster instance? Is it modified there?
Btw. you should move all your looting related data into a separate class, and only store an instance of that class in mtype
. E.g.
class lootable {
std::vector<std::string> categories;
std::vector<material_id> materials;
std::vector<std::string> comestibles;
std::vector<std::string> itemsgroups;
...
};
Than you can copy them with on statement:
lootable = type->lootable;
And you can extend it easily without changing this copy instruction.
You could also store this as std::optional<lootable>
in mtype
, and get rid of the loots
variable. Instead you check the presence of the lootable
object in the std::optional
. If it is there, the monster loots, otherwise it isn't.
src/monmove.cpp
Outdated
{ | ||
bool lootable = false; | ||
|
||
//TODO: Definitely find a better way to do the below, too much repeatability |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. How about a template function:
template<typename L, typename P>
static bool is_item_lootable( const item &itm, constL &list, const P &predicate, bool requires_all ) {
// Your loop goes here. Use list instead of lootable_categories, use predicate for the if-condition
}
// called like this:
if( !is_item_lootable( itm, lootable_categories, [](const item &itm, const std::string &tag) { return itm.get_category().name() == tag; }, lootables_requires_all ) {
return false;
}
Added paths_to bool in loots objects, monsters who have this set to false will only pickup items next to them.
Documentation to follow. No plans to expand on it any further for this initial PR, other than perhaps work on the picking up of containers that aren’t sealed. unless anyone has any suggestions to improve initial flexibility ? Will focus on getting this PR into an acceptable state. Volume / weight limits for monsters need to be applied in a future PR that will allow further flexibility and opens the way to expansion to item usage/activation by monsters etc. Looting from vehicles needs to be added also, as well as perhaps the ability to loot from the player directly and maybe other monsters. In a test, applied various item groups to raccoons to loot, spawned 7-8 of them and set them loose in a house which was oddly satisfying watching them eat/loot the place. |
Consider adding a [WIP] tag to the PR's title if you're actively working on it. |
Cleaned up code Fixed issue with requires_all
We're in feature freeze now, but if you want to pick this up again it makes a lot of sense with some of the new monsters that have started showing up |
Summary
SUMMARY: Features "Adds new behavior and framework for monsters to find items in a radius around them. Currently allows for picking up and eating."
Purpose of change
Implements #71 - Make some monsters steal food and possibly other items.
Describe the solution
Adds "loots" Json Object which accepts arrays of categories, condiments, item groups and materials to define lootable items.
Adds flag EATS_FOOD to monsters.
Monsters have a chance to start 'Looting' items nearby, which depending on the item the particular monster has defined will search for suitable items within a radius around itself.
Optional paths_to flag which when false restricts the lootable items to a radius of 1 tile.
Provided a desirable item has been selected, the monster will path towards and pickup the item.
Monsters with the EATS_FOOD flag, will eat food items rather than pick them up.
Describe alternatives you've considered
Standardizing inventories across the player, NPC and monsters as there is quite a lot of duplication. Out of scope for this.
Testing
Testing has been performed using a Raccoon and a variety of items of different categories, stack sizes and distances.
Known Issues/Limitations:
Additional context
Likely going to be extending this further as quite a lot of potential such as alternate flags for different behaviors. Perhaps fish could attempt to eat bait, other monsters could be lured with traps for hunting and potentially monsters could use or activate items they pickup (Imagine a monster grabbing a grenade out of your stash and priming it). But not for this PR