Skip to content
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

Conversation

Diabolus-Engi
Copy link

@Diabolus-Engi Diabolus-Engi commented Feb 24, 2020

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:

  • No hunger system for monsters to determine when they are hungry.
  • No proper inventory system for monsters.
  • Monsters will only hold onto a maximum of 2 items.

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

@snipercup
Copy link
Contributor

snipercup commented Feb 24, 2020

Documentation will be added in a follow up pr?

@Diabolus-Engi
Copy link
Author

Documentation will be added in a follow up pr?

Forgive my ignorance, what kind of documentation would you be expecting? Happy to add this.

@snipercup
Copy link
Contributor

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.
Diabolus-Engi and others added 2 commits February 24, 2020 15:19
Fixes linting error.

Co-Authored-By: snipercup <50166150+snipercup@users.noreply.github.com>
@KorGgenT
Copy link
Member

just did a quick glance, looks like you'll need to astyle your code.

src/monmove.cpp Outdated Show resolved Hide resolved
src/monmove.cpp Outdated Show resolved Hide resolved
src/monmove.cpp Outdated Show resolved Hide resolved
Ran through astyle.
Removed WIP comment.
src/monmove.cpp Outdated Show resolved Hide resolved
src/monmove.cpp Outdated Show resolved Hide resolved
src/monmove.cpp Outdated Show resolved Hide resolved
src/monmove.cpp Outdated Show resolved Hide resolved
src/monmove.cpp Outdated Show resolved Hide resolved
src/monmove.cpp Outdated Show resolved Hide resolved
src/monmove.cpp Outdated Show resolved Hide resolved
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 );
Copy link
Contributor

@Night-Pryanik Night-Pryanik Feb 24, 2020

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.

Copy link
Contributor

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 Show resolved Hide resolved
src/monmove.cpp Outdated Show resolved Hide resolved
src/monmove.cpp Outdated

//Does monster steal shiny items?
if( steals_shiny &&
( itm.made_of( material_id( "gold" ) ) ||
Copy link
Contributor

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 ) ) {
Copy link
Contributor

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.

Copy link
Author

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

src/monmove.cpp Outdated Show resolved Hide resolved
src/monmove.cpp Outdated Show resolved Hide resolved
src/monmove.cpp Outdated Show resolved Hide resolved
src/monmove.cpp Outdated Show resolved Hide resolved
src/monmove.cpp Outdated Show resolved Hide resolved
src/monmove.cpp Outdated Show resolved Hide resolved
src/monmove.cpp Outdated Show resolved Hide resolved
src/monmove.cpp Outdated Show resolved Hide resolved
src/monmove.cpp Outdated Show resolved Hide resolved
src/monmove.cpp Outdated Show resolved Hide resolved
@I-am-Erk
Copy link
Member

awesome, thanks!
I would say most use cases could be covered by having the 'steals' object accept item categories/flags like you've used for food and shiny, but also material types or a specific itemgroup listing items.

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>
src/monmove.cpp Outdated Show resolved Hide resolved
src/monmove.cpp Outdated Show resolved Hide resolved
src/monster.cpp Outdated Show resolved Hide resolved
Diabolus-Engi and others added 2 commits February 25, 2020 09:09
Applied suggestions and made some changes.
Mostly stable in its post-flexibility stage.
Co-Authored-By: Jianxiang Wang (王健翔) <qrox@sina.com>
@Diabolus-Engi
Copy link
Author

I've broken the flags out into JSON using the below format:

"steals": { "categories":["FOOD"], "materials":["wheat"] },

What else should we be looking for to ensure the system is as flexible as possible for items?

@I-am-Erk
Copy link
Member

Being able to specific an itemgroup would be great too

@Diabolus-Engi
Copy link
Author

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() )
Copy link
Contributor

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 ) {
Copy link
Contributor

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 ) ) ) {
Copy link
Contributor

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;
Copy link
Contributor

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
Copy link
Contributor

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.
@Diabolus-Engi
Copy link
Author

Diabolus-Engi commented Feb 28, 2020

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.

@Night-Pryanik
Copy link
Contributor

Consider adding a [WIP] tag to the PR's title if you're actively working on it.

@Diabolus-Engi Diabolus-Engi changed the title Monster item aware behaviour [WIPMonster item aware behaviour Feb 29, 2020
@Diabolus-Engi Diabolus-Engi changed the title [WIPMonster item aware behaviour [WIP] Monster item aware behaviour Feb 29, 2020
Cleaned up code
Fixed issue with requires_all
@Diabolus-Engi Diabolus-Engi changed the title [WIP] Monster item aware behaviour Monster item aware behaviour Mar 5, 2020
@LyleSY
Copy link
Contributor

LyleSY commented Nov 2, 2020

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[C++] Changes (can be) made in C++. Previously named `Code` Items / Item Actions / Item Qualities Items and how they work and interact Monsters Monsters both friendly and unfriendly. Organization: Bounty Bounties for claim
Projects
None yet
Development

Successfully merging this pull request may close these issues.